Merge "Folder Title Change Listener" into main
diff --git a/Android.bp b/Android.bp
index f45394a..c4ea48a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -21,6 +21,8 @@
 
 // Common source files used to build launcher (java and kotlin)
 // All sources are split so they can be reused in many other libraries/apps in other folders
+
+// Main Launcher source, excluding the build config
 filegroup {
     name: "launcher-src",
     srcs: [
@@ -29,6 +31,7 @@
     ],
 }
 
+// Source code for quickstep build, on top of launcher-src
 filegroup {
     name: "launcher-quickstep-src",
     srcs: [
@@ -37,35 +40,20 @@
     ],
 }
 
+// Alternate source when quickstep is not included
 filegroup {
-    name: "launcher-go-src",
+    name: "launcher-src_no_quickstep",
     srcs: [
-        "go/src/**/*.java",
-        "go/src/**/*.kt",
+        "src_no_quickstep/**/*.java",
+        "src_no_quickstep/**/*.kt",
     ],
 }
 
+// Default build config for Launcher3
 filegroup {
-    name: "launcher-go-quickstep-src",
+    name: "launcher-build-config",
     srcs: [
-        "go/quickstep/src/**/*.java",
-        "go/quickstep/src/**/*.kt",
-    ],
-}
-
-filegroup {
-    name: "launcher-src_shortcuts_overrides",
-    srcs: [
-        "src_shortcuts_overrides/**/*.java",
-        "src_shortcuts_overrides/**/*.kt",
-    ],
-}
-
-filegroup {
-    name: "launcher-src_ui_overrides",
-    srcs: [
-        "src_ui_overrides/**/*.java",
-        "src_ui_overrides/**/*.kt",
+        "src_build_config/**/*.java",
     ],
 }
 
@@ -87,7 +75,7 @@
         "androidx.test.uiautomator_uiautomator",
         "androidx.preference_preference",
         "SystemUISharedLib",
-        "animationlib",
+        "//frameworks/libs/systemui:animationlib",
         "launcher-testing-shared",
     ],
     srcs: [
@@ -162,29 +150,14 @@
         "androidx.cardview_cardview",
         "androidx.window_window",
         "com.google.android.material_material",
-        "iconloader_base",
-        "view_capture",
-        "animationlib",
-    ],
-    manifest: "AndroidManifest-common.xml",
-    sdk_version: "current",
-    min_sdk_version: min_launcher3_sdk_version,
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-    },
-}
-
-//
-// Build rule for Launcher3 dependencies lib.
-//
-android_library {
-    name: "Launcher3CommonDepsLib",
-    srcs: ["src_build_config/**/*.java"],
-    static_libs: [
+        "//frameworks/libs/systemui:iconloader_base",
+        "//frameworks/libs/systemui:view_capture",
+        "//frameworks/libs/systemui:animationlib",
         "SystemUI-statsd",
-        "Launcher3ResLib",
         "launcher-testing-shared",
-        "animationlib",
+        "androidx.lifecycle_lifecycle-common-java8",
+        "androidx.lifecycle_lifecycle-extensions",
+        "androidx.lifecycle_lifecycle-runtime-ktx",
         "kotlinx_coroutines_android",
         "kotlinx_coroutines",
         "com_android_launcher3_flags_lib",
@@ -192,9 +165,9 @@
         "android.appwidget.flags-aconfig-java",
         "com.android.window.flags.window-aconfig-java",
     ],
+    manifest: "AndroidManifest-common.xml",
     sdk_version: "current",
     min_sdk_version: min_launcher3_sdk_version,
-    manifest: "AndroidManifest-common.xml",
     lint: {
         baseline_filename: "lint-baseline.xml",
     },
@@ -207,12 +180,12 @@
     name: "Launcher3",
 
     static_libs: [
-        "Launcher3CommonDepsLib",
+        "Launcher3ResLib",
     ],
     srcs: [
         ":launcher-src",
-        ":launcher-src_shortcuts_overrides",
-        ":launcher-src_ui_overrides",
+        ":launcher-src_no_quickstep",
+        ":launcher-build-config",
     ],
 
     optimize: {
@@ -259,24 +232,21 @@
         "lottie",
         "SystemUISharedLib",
         "SettingsLibSettingsTheme",
-        "SystemUI-statsd",
-        "animationlib",
     ],
     manifest: "quickstep/AndroidManifest.xml",
     min_sdk_version: "current",
 }
 
-// Library with all the dependencies for building Launcher Go
+// Library with all the source code and dependencies for building Launcher Go
 android_library {
-    name: "LauncherGoResLib",
+    name: "Launcher3GoLib",
     srcs: [
         ":launcher-src",
         ":launcher-quickstep-src",
-        ":launcher-go-src",
-        ":launcher-go-quickstep-src",
+        "go/quickstep/src/**/*.java",
+        "go/quickstep/src/**/*.kt",
     ],
     resource_dirs: [
-        "go/res",
         "go/quickstep/res",
     ],
     // Note the ordering here is important when it comes to resource
@@ -284,7 +254,6 @@
     // in QuickstepResLib to take precendece, so it should be the final
     // dependency. See b/205278434 for how this can go wrong.
     static_libs: [
-        "Launcher3CommonDepsLib",
         "QuickstepResLib",
         "androidx.room_room-runtime",
     ],
@@ -299,13 +268,13 @@
     use_resource_processor: false,
 }
 
-// Build rule for Quickstep library
+// Library with all the source code and dependencies for building Quickstep
 android_library {
     name: "Launcher3QuickStepLib",
     srcs: [
         ":launcher-src",
         ":launcher-quickstep-src",
-        ":launcher-src_shortcuts_overrides",
+        ":launcher-build-config",
     ],
     resource_dirs: [],
     libs: [
@@ -317,10 +286,7 @@
     // dependency. See b/208647810 for how this can go wrong.
     static_libs: [
         "SystemUI-statsd",
-        "SystemUISharedLib",
-        "Launcher3CommonDepsLib",
         "QuickstepResLib",
-        "animationlib",
     ],
     manifest: "quickstep/AndroidManifest.xml",
     platform_apis: true,
@@ -329,49 +295,6 @@
     use_resource_processor: false,
 }
 
-// Build rule for Launcher3 Go app for Android Go devices.
-android_app {
-    name: "Launcher3Go",
-
-    static_libs: ["Launcher3CommonDepsLib"],
-
-    srcs: [
-        ":launcher-src",
-        ":launcher-go-src",
-        ":launcher-src_ui_overrides",
-    ],
-
-    resource_dirs: ["go/res"],
-
-    optimize: {
-        proguard_flags_files: ["proguard.flags"],
-    },
-
-    sdk_version: "current",
-    min_sdk_version: "current",
-    target_sdk_version: "current",
-    privileged: true,
-    system_ext_specific: true,
-    overrides: [
-        "Home",
-        "Launcher2",
-        "Launcher3",
-        "Launcher3QuickStep",
-    ],
-    required: ["privapp_whitelist_com.android.launcher3"],
-
-    additional_manifests: [
-        "AndroidManifest.xml",
-        "AndroidManifest-common.xml",
-    ],
-
-    manifest: "go/AndroidManifest.xml",
-    jacoco: {
-        include_filter: ["com.android.launcher3.*"],
-    },
-
-}
-
 // Build rule for Quickstep app.
 android_app {
     name: "Launcher3QuickStep",
@@ -408,28 +331,19 @@
 
 }
 
-// Build rule for Launcher3 Go app with quickstep for Android Go devices.
-android_app {
-    name: "Launcher3QuickStepGo",
 
-    static_libs: [
-        "SystemUI-statsd",
-        "SystemUISharedLib",
-        "LauncherGoResLib",
-    ],
+// Build rule for Launcher3 Go app with quickstep for Android Go devices.
+// Note that the following two rules are exactly same, and should
+// eventually be merged into a single target
+android_app {
+    name: "Launcher3Go",
+
+    static_libs: ["Launcher3GoLib"],
 
     platform_apis: true,
     min_sdk_version: "current",
     target_sdk_version: "current",
 
-    srcs: [],
-
-    resource_dirs: [
-        "go/quickstep/res",
-        "go/res",
-        "quickstep/res",
-    ],
-
     optimize: {
         proguard_flags_files: ["proguard.flags"],
         enabled: true,
@@ -455,5 +369,39 @@
     jacoco: {
         include_filter: ["com.android.launcher3.*"],
     },
+}
+android_app {
+    name: "Launcher3QuickStepGo",
 
+    static_libs: ["Launcher3GoLib"],
+
+    platform_apis: true,
+    min_sdk_version: "current",
+    target_sdk_version: "current",
+
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+        enabled: true,
+    },
+
+    privileged: true,
+    system_ext_specific: true,
+    overrides: [
+        "Home",
+        "Launcher2",
+        "Launcher3",
+        "Launcher3QuickStep",
+    ],
+    required: ["privapp_whitelist_com.android.launcher3"],
+
+    additional_manifests: [
+        "go/AndroidManifest.xml",
+        "go/AndroidManifest-launcher.xml",
+        "AndroidManifest-common.xml",
+    ],
+
+    manifest: "quickstep/AndroidManifest.xml",
+    jacoco: {
+        include_filter: ["com.android.launcher3.*"],
+    },
 }
diff --git a/OWNERS b/OWNERS
index 31e4610..efcf9f3 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,6 +11,7 @@
 winsonc@google.com
 jonmiranda@google.com
 awickham@google.com
+agvard@google.com
 
 # Launcher workspace eng team
 captaincole@google.com
diff --git a/buglist.txt b/buglist.txt
deleted file mode 100644
index 53dcc35..0000000
--- a/buglist.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-171450807
-170675311
-170338029
-170338170
-160544577
-171171594
-170488559
-171131394
-171131394
-171026321
-170648272
-170752716
-170611866
-170702596
-170487752
-170665892
-168608912
-170636685
-169771796
-141126144
-166614700
-168805872
-170263425
-169221288
-143965596
-169221287
-167259591
-156044202
-169438169
-164926736
-168653219
-169963211
-170121063
-169988381
-169980192
-169221288
-169385783
-168167693
-169796517
-169330678
-168818961
-168608912
diff --git a/buglist_unique.txt b/buglist_unique.txt
deleted file mode 100644
index 93dbefb..0000000
--- a/buglist_unique.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-141126144
-143965596
-156044202
-160544577
-164926736
-166614700
-167259591
-168167693
-168608912
-168653219
-168805872
-168818961
-169221287
-169221288
-169330678
-169385783
-169438169
-169771796
-169796517
-169963211
-169980192
-169988381
-170121063
-170263425
-170338029
-170338170
-170487752
-170488559
-170611866
-170636685
-170648272
-170665892
-170675311
-170702596
-170752716
-171026321
-171131394
-171171594
-171450807
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index a453bfd..0000000
--- a/build.gradle
+++ /dev/null
@@ -1,188 +0,0 @@
-buildscript {
-    repositories {
-        mavenCentral()
-        google()
-    }
-    dependencies {
-        classpath GRADLE_CLASS_PATH
-        classpath PROTOBUF_CLASS_PATH
-    }
-}
-
-final String ANDROID_TOP = "${rootDir}/../../.."
-final String FRAMEWORK_PREBUILTS_DIR = "${ANDROID_TOP}/prebuilts/framework_intermediates/"
-
-apply plugin: 'com.android.application'
-apply plugin: 'com.google.protobuf'
-
-android {
-    compileSdkVersion COMPILE_SDK
-    buildToolsVersion BUILD_TOOLS_VERSION
-
-    defaultConfig {
-        minSdkVersion 30
-        targetSdkVersion 33
-        versionCode 1
-        versionName "1.0"
-
-        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-        vectorDrawables.useSupportLibrary = true
-    }
-    buildTypes {
-        debug {
-            minifyEnabled false
-        }
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_8
-        targetCompatibility JavaVersion.VERSION_1_8
-    }
-
-    // The flavor dimensions for build variants (e.g. aospWithQuickstep, aospWithoutQuickstep)
-    // See: https://developer.android.com/studio/build/build-variants#flavor-dimensions
-    flavorDimensions "app", "recents"
-
-    productFlavors {
-        aosp {
-            dimension "app"
-            applicationId 'com.android.launcher3'
-            testApplicationId 'com.android.launcher3.tests'
-        }
-
-        l3go {
-            dimension "app"
-            applicationId 'com.android.launcher3'
-            testApplicationId 'com.android.launcher3.tests'
-        }
-
-        withQuickstep {
-            dimension "recents"
-
-            minSdkVersion 28
-        }
-
-        withoutQuickstep {
-            dimension "recents"
-        }
-    }
-
-    // Disable release builds for now
-    android.variantFilter { variant ->
-        if (variant.buildType.name.endsWith('release')) {
-            variant.setIgnore(true)
-        }
-    }
-
-    sourceSets {
-        main {
-            res.srcDirs = ['res']
-            java.srcDirs = ['src', 'src_plugins']
-            manifest.srcFile 'AndroidManifest-common.xml'
-            proto {
-                srcDirs = ['protos/', 'protos_overrides/']
-            }
-        }
-
-        androidTest {
-            res.srcDirs = ['tests/res']
-            java.srcDirs = ['tests/src', 'tests/tapl']
-            manifest.srcFile "tests/AndroidManifest-common.xml"
-        }
-
-        androidTestDebug {
-            manifest.srcFile "tests/AndroidManifest.xml"
-        }
-
-        aosp {
-            java.srcDirs = ['src_flags', 'src_shortcuts_overrides']
-        }
-
-        aospWithoutQuickstep {
-            manifest.srcFile "AndroidManifest.xml"
-        }
-
-        aospWithQuickstep {
-            manifest.srcFile "quickstep/AndroidManifest-launcher.xml"
-        }
-
-        l3go {
-            res.srcDirs = ['go/res']
-            java.srcDirs = ['go/src']
-            manifest.srcFile "go/AndroidManifest.xml"
-        }
-
-        l3goWithoutQuickstepDebug {
-            manifest.srcFile "AndroidManifest.xml"
-        }
-
-        l3goWithQuickstepDebug {
-            manifest.srcFile "quickstep/AndroidManifest-launcher.xml"
-        }
-
-        withoutQuickstep {
-            java.srcDirs = ['src_ui_overrides']
-        }
-
-        withQuickstep {
-            res.srcDirs = ['quickstep/res', 'quickstep/recents_ui_overrides/res']
-            java.srcDirs = ['quickstep/src', 'quickstep/recents_ui_overrides/src']
-            manifest.srcFile "quickstep/AndroidManifest.xml"
-        }
-    }
-}
-
-allprojects {
-    repositories {
-        maven { url "../../../prebuilts/sdk/current/androidx/m2repository" }
-        maven { url "../../../prebuilts/fullsdk-darwin/extras/android/m2repository" }
-        maven { url "../../../prebuilts/fullsdk-linux/extras/android/m2repository" }
-        mavenCentral()
-        google()
-    }
-}
-
-dependencies {
-    implementation "androidx.dynamicanimation:dynamicanimation:${ANDROID_X_VERSION}"
-    implementation "androidx.recyclerview:recyclerview:${ANDROID_X_VERSION}"
-    implementation "androidx.preference:preference:${ANDROID_X_VERSION}"
-    implementation project(':IconLoader')
-    implementation project(':UiTestsLibLauncher')
-    withQuickstepImplementation project(':SharedLibWrapper')
-
-    // Recents lib dependency
-    withQuickstepImplementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/quickstep/libs", include: 'sysui_shared.jar')
-
-    // Required for AOSP to compile. This is already included in the sysui_shared.jar
-    withoutQuickstepImplementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/libs", include: 'plugin_core.jar')
-
-    testImplementation 'junit:junit:4.12'
-    testImplementation libs.mockitoInlineExtended
-    androidTestImplementation libs.mockitoInlineExtended
-    androidTestImplementation "org.mockito:mockito-core:1.9.5"
-    androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
-    androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2'
-    androidTestImplementation 'com.android.support.test:runner:1.0.0'
-    androidTestImplementation 'com.android.support.test:rules:1.0.0'
-    androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
-    androidTestImplementation "androidx.annotation:annotation:${ANDROID_X_VERSION}"
-
-    api 'com.airbnb.android:lottie:3.3.0'
-}
-
-protobuf {
-    // Configure the protoc executable
-    protoc {
-        artifact = "com.google.protobuf:protoc:${protocVersion}${PROTO_ARCH_SUFFIX}"
-    }
-    generateProtoTasks {
-        all().each { task ->
-            task.builtins {
-                remove java
-                java {
-                    option "lite"
-                }
-            }
-        }
-    }
-}
diff --git a/commitlist.txt b/commitlist.txt
deleted file mode 100644
index 27b8bac..0000000
--- a/commitlist.txt
+++ /dev/null
@@ -1,934 +0,0 @@
-COMMAND>> git log f99351888c3e5a128559678304fefd647472bc7f..4c3952dc60fc78d3816012a86d7e71747ef34c74
-commit 4c3952dc60fc78d3816012a86d7e71747ef34c74
-Merge: cb403d9e5 70e8b1572
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Fri Oct 23 00:27:39 2020 +0000
-
-    Merge "Minor stylistic changes in Workspace.java." into ub-launcher3-master
-
-commit cb403d9e5235c7323bc2fdffe6a264d17bb6d0a6
-Author: Pinyao Ting <pinyaoting@google.com>
-Date:   Thu Oct 22 16:07:08 2020 -0700
-
-    flip default value of minimal device feature flag
-    
-    Test: manual
-    Change-Id: Iaf46dffb935bdf4b46e7c57d547bdc697250ec56
-
-commit a97557a15eb111616d868120a9f4659f1b451fa2
-Merge: f5ce80b8a 932a327eb
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Thu Oct 22 18:22:56 2020 +0000
-
-    Merge "Consider overscroll adjustment of RecentsView for live tile" into ub-launcher3-master
-
-commit 932a327ebf0587b8324b9fea7d31328b2f6719a8
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Wed Oct 21 23:29:00 2020 -0700
-
-    Consider overscroll adjustment of RecentsView for live tile
-    
-    Fixes: 171450807
-    Test: manual
-    Change-Id: I83eebf1f6b61c67f289db51aabe5a971815d0df1
-
-commit f5ce80b8a0a1636fc8159475177a07b281492c88
-Author: Hilary Huo <hhuo@google.com>
-Date:   Wed Oct 14 16:35:55 2020 -0700
-
-    [pixel-search] Latency analysis, add logging statement in launcher
-    
-    Bug: b/170675311
-    Change-Id: I229ace399085bea1c3f9535eb713edd329dff8bd
-
-commit 31b03941ef3aa17edc08c1b509d4fa23766f2d2c
-Merge: e0a50c9e3 0731273d5
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Wed Oct 21 20:03:57 2020 +0000
-
-    Merge "Track live tile better by considering resistance animation" into ub-launcher3-master
-
-commit 0731273d5409149fca32dfb2ad76eab45f6ea79a
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Wed Oct 21 12:03:40 2020 -0700
-
-    Track live tile better by considering resistance animation
-    
-    Fixes: 170338029
-    Test: Manual
-    Change-Id: I66536bae567aa94385d5e0352cec9d46d512927a
-
-commit e0a50c9e3f1d4b9f113d6afae01ff2c4ed452fba
-Merge: d2c27a595 acfac6187
-Author: Alex Chau <alexchau@google.com>
-Date:   Wed Oct 21 17:02:57 2020 +0000
-
-    Merge "Use Diplay.getMetrics in DisplayController" into ub-launcher3-master
-
-commit d2c27a595065d43bbea37dd2a512d37080f5233e
-Merge: ff8febabb 8b488ccc2
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Wed Oct 21 07:19:48 2020 +0000
-
-    Merge "[Live Tile] Support launching running task animation" into ub-launcher3-master
-
-commit 8b488ccc2e433a708c8b06f0b6866f2a305e4b0a
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Wed Oct 14 12:13:04 2020 -0700
-
-    [Live Tile] Support launching running task animation
-    
-    Fixes: 170338170
-    Test: manual
-    Change-Id: I2526b7cfbacaea7899b8e2ed233f913630071d36
-
-commit 70e8b157219e9090ba5e47fdfa51b2b92e98449d
-Author: Andy Wickham <awickham@google.com>
-Date:   Wed Oct 7 23:00:06 2020 -0700
-
-    Minor stylistic changes in Workspace.java.
-    
-    Change-Id: Ib07611f27cbc427d11abccd8b74ea144485752f7
-
-commit acfac6187dd9d13d55b566a77a5da867a1813573
-Author: Alex Chau <alexchau@google.com>
-Date:   Mon Oct 19 18:00:39 2020 +0100
-
-    Use Diplay.getMetrics in DisplayController
-    
-    - This is a workaround of b/163815566, where DisplayMetrics is stale
-      when onDisplayChanged is called.
-    - Instead of relying on stale DisplayConext, get the DisplayMetrics
-      from the Display directly.
-    - Also optimized how DisplayController.Info is created by passing in
-      Display only
-    - Use mDisplayContext.getDisplay directly if availalbe
-    
-    Bug: 163815566, 160544577
-    Test: DPI looks correct on device boot
-    Change-Id: I2a7454bb8cf2073ce592e8662781b87fc998444f
-    (cherry picked from commit 177c38243dc3bf245d1f7db3c265dfb56522f441)
-
-commit ff8febabb039a3c27ee068f85119860a048b917c
-Merge: b03d2b416 102746823
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Tue Oct 20 17:46:09 2020 +0000
-
-    Merge "Makes Plugin Settings gear adjust to dark mode." into ub-launcher3-master
-
-commit b03d2b41616d479ba360fa4f97e57722c7f57b8e
-Merge: fb79f5541 caa1e9c39
-Author: Hyunyoung Song <hyunyoungs@google.com>
-Date:   Tue Oct 20 15:25:56 2020 +0000
-
-    Merge "Search query method should support multiple consumers" into ub-launcher3-master
-
-commit fb79f5541dcbe587002756bb40a3c632d38cc25a
-Merge: f6b05068d cf0b275a4
-Author: Schneider Victor-tulias <victortulias@google.com>
-Date:   Tue Oct 20 13:51:06 2020 +0000
-
-    Merge "Add the ability to specify a list of tutorial steps in the gesture sandbox tutorial intent." into ub-launcher3-master
-
-commit f6b05068d901d4e989b2e107c06f9c7a6e7b113f
-Author: Hyunyoung Song <hyunyoungs@google.com>
-Date:   Tue Oct 20 00:19:29 2020 -0700
-
-    Invert the badging
-    
-    Bug: 171171594
-    Change-Id: If84fdc03254105c843e16f39f479505b16e1cd5f
-
-commit caa1e9c39978cb3b467b5ac441eb39b5e883fa2e
-Author: Hyunyoung Song <hyunyoungs@google.com>
-Date:   Mon Oct 12 13:56:02 2020 -0700
-
-    Search query method should support multiple consumers
-    
-    Bug: 170488559
-    Change-Id: I64bef9523d3c3950c4ca3a4b9ce1d506d1672200
-
-commit 10274682339bb60cb24c50536b4f48f921970f3c
-Author: Andy Wickham <awickham@google.com>
-Date:   Mon Oct 19 19:06:52 2020 -0700
-
-    Makes Plugin Settings gear adjust to dark mode.
-    
-    It wasn't visible in dark mode before because it was
-    black on black. This makes it adjust automatically.
-    
-    Change-Id: I5176cffc01842509ddafc4f30ff5029a0c4b8050
-
-commit 744a0fbeae8efaa942d21c61e25012d86f5ff81e
-Merge: 29c79947e 71f24588c
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Mon Oct 19 22:17:26 2020 +0000
-
-    Merge "Call click event on IME quick select for SearchResultIcon" into ub-launcher3-master
-
-commit 29c79947ecf82f662d02004ba9a7289017fc0783
-Merge: 13a2a010d a68ac3e5d
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Mon Oct 19 18:42:05 2020 +0000
-
-    Merge "Removing condition for CUJ tracing/metrics" into ub-launcher3-master
-
-commit 71f24588c0a66449a0c68bcb360a8c671914ce75
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Mon Oct 19 10:19:31 2020 -0700
-
-    Call click event on IME quick select for SearchResultIcon
-    
-    Bug: 171131394
-    Change-Id: I8a703e8d0ca10570e3f774510610d3fb4c0eaab8
-
-commit 13a2a010decd87eeaf8932430c692f587d2de165
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Sun Oct 18 21:19:57 2020 -0700
-
-    Handle IME event for SearchResultIcon
-    
-    Bug: 171131394
-    Test: Manual
-    Change-Id: I2ed1c61053c78aaecc3324418229d69634a72ae4
-
-commit 1f79eeda76246534697e92740defc7f73c3c8d14
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Fri Oct 16 02:01:31 2020 -0700
-
-    Remove hardcoded itemTypes from SearchTarget
-    
-    - Introduces componentName and userHandle members to SearchTarget
-    - SearchTargetEvent now has searchTarget member
-    - Builder pattern for SearchTarget and SearchTargetEvent
-    - Search backend should add headers manually instead of launcher inferring sections
-    
-    Bug: 171026321
-    Test: Manual
-    Change-Id: I28e0455e82b925277a17703b9aa061c8f9f15262
-
-commit a68ac3e5dd23095cea7c872c0ff1c5042d1695ba
-Author: vadimt <vadimt@google.com>
-Date:   Fri Oct 16 10:48:28 2020 -0700
-
-    Removing condition for CUJ tracing/metrics
-    
-    Is doesn't reflect whether jank monitors is collecting metrics,
-    which will eventually be always true anyways.
-    
-    Change-Id: Iaebdc838ed2b2cebd32c8c48d7e45bdd93f76fb4
-
-commit 9228ff53c2fb26850b7bd92d86214a6aaebb11d3
-Author: Sunny Goyal <sunnygoyal@google.com>
-Date:   Mon Oct 12 13:43:51 2020 -0700
-
-    Trimming activity and task label
-    
-    Bug: 170648272
-    Change-Id: Icd099acee65305e0aa0f98a2a301a0df8a27cf07
-
-commit 7a09177e500a53205f9969bb6cbd4251d54e8fde
-Merge: 37ed5ead3 314761a80
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Thu Oct 15 22:36:14 2020 +0000
-
-    Merge "Setup SearchResultIcon for single cell results" into ub-launcher3-master
-
-commit 37ed5ead391df5747003b2d3a345be0347362f19
-Merge: d5bbe6809 702ed2788
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Thu Oct 15 22:06:12 2020 +0000
-
-    Merge "Fix the issue where shortcuts are removed in minimal device mode" into ub-launcher3-master
-
-commit 314761a80819a6e64a136161f51eebb0f0528c4d
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Wed Oct 14 10:15:07 2020 -0700
-
-    Setup SearchResultIcon for single cell results
-    
-    SearchResultIcon will be able to render apps, shortcuts and remote actions. It can also handle its own focused state drawing.
-    
-    Screenshot: https://screenshot.googleplex.com/C3KgjJtLQTBPgaf
-    
-    Bug: 170752716
-    Test: Manual
-    Change-Id: I460a9c128ea3f5814784e342c5d5fa5b7e310882
-
-commit 702ed2788678ac744c768aad6a6302e7cf91a26b
-Author: Pinyao Ting <pinyaoting@google.com>
-Date:   Wed Oct 14 11:17:04 2020 -0700
-
-    Fix the issue where shortcuts are removed in minimal device mode
-    
-    When loading the workspace, Launcher pins/unpins shortcuts in comply
-    with the loaded workspace. Since minimal device mode creates a mostly
-    empty workspace, existing shortcuts are getting unpinned as a result.
-    
-    To mitigate the issue this CL compares the db name and only invoke
-    sanitizeData when it matches the one defined in InvariantDeviceProfile.
-    
-    Bug: 170611866
-    Test: manual
-    1. add some deep shortcut in workspace (e.g. long tap on chrome, drag
-    "incognito tab" to workspace)
-    2. opt-in to sunshine fishfood (g/sunshine-teamfood)
-    3. enable bedtime mode with minimal device in Settings -> Digital
-    Wellbeing -> Show Your Data -> Bedtime mode -> Customize -> minimal
-    device
-    4. toggle bedtime mode, wait for apps in minimal device to show, then
-    toggle off bedtime mode
-    5. verify the deep shortcut still exist
-    
-    Change-Id: Ie18216ecb288e7481aa2404c4cb3ea418aee85cb
-
-commit cf0b275a48d3c9f91a346f7fc24b9604f6dde25a
-Author: Schneider Victor-tulias <victortulias@google.com>
-Date:   Tue Oct 6 09:33:40 2020 -0400
-
-    Add the ability to specify a list of tutorial steps in the gesture sandbox tutorial intent.
-    
-    Added tutorial_steps string array in the intent to allow specifying an ordered list of tutorial steps.
-    
-    Change-Id: Ic42a65598a74a64f8441a22f58c6cd988a5762e3
-
-commit d5bbe6809dcc056fbfc307909b171651f0fb3044
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Wed Oct 14 15:39:38 2020 -0700
-
-    Rename shrotcut container to deep-shrotcuts
-    
-    Change-Id: If94f0dfa447235f3b1a652f7b6c749695b42d97c
-
-commit 26c1105fa04c2bcc156051e51df90a6a253349bb
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Tue Oct 13 01:12:03 2020 -0700
-
-    [search api part 1] Setup centralized SearchEventTracker
-    
-    - Rename AdapterItemWIthPayload to SearchAdapterItem, PayloadResultHandler to SearchTargetHandler
-    - Setup SliceViewWrapper for self contained slices
-    
-    Bug: 170702596
-    Change-Id: I0baf984ec8123c95011abcc17372f8d055e98ad7
-
-commit 057f2d0d7df67e3680e479ac76b48b30d8bcf884
-Merge: 4bb65ff51 9a6145efb
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Tue Oct 13 01:57:47 2020 +0000
-
-    Merge "Introduce shortcut container for hotseat event reporting" into ub-launcher3-master
-
-commit 4bb65ff516c6d9a429971ab7e04780792d5cb751
-Merge: 69740e62b 2afcab804
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Tue Oct 13 00:07:36 2020 +0000
-
-    Merge "Search UI clean up" into ub-launcher3-master
-
-commit 2afcab804b638ff3b9da5bad40c8f70bdcaae78d
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Mon Oct 12 15:38:14 2020 -0700
-
-    Search UI clean up
-    
-    - Resolve spacing issue when work profile is installed
-    - Cache play icons and use icon shape
-    - Only draw focus indicator for the first result
-    
-    Bug: 170487752
-    Bug: 170665892
-    Change-Id: I864d2e796786637132e127ef9b418c0a76c74d6e
-
-commit 69740e62be3800fc918648009645f7a8e52cb73d
-Merge: 2d7bfc878 979da64d8
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Mon Oct 12 20:53:29 2020 +0000
-
-    Merge "Add app start source info of apps launched from launcher" into ub-launcher3-master
-
-commit 2d7bfc8782e9ed01178672aeb09ba2a6a07f4f4c
-Author: Jon Miranda <jonmiranda@google.com>
-Date:   Mon Oct 12 12:09:22 2020 -0700
-
-    Fix shadowRadius not being used in swipe up animation.
-    
-    Bug: 168608912
-    Change-Id: I08f7bb057237e5061d5f1fc29afb488b204ee385
-
-commit a433fe1fb34715efb38ed094f39da49fce8cd51e
-Merge: 2de606fe7 0471b9836
-Author: Sunny Goyal <sunnygoyal@google.com>
-Date:   Mon Oct 12 18:08:35 2020 +0000
-
-    Merge "Using FrameCallbacks instead of windowCallbacks for surface removal" into ub-launcher3-master
-
-commit 9a6145efb85f2bbdaccc07166a55e22c15fe27db
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Mon Oct 12 09:33:00 2020 -0700
-
-    Introduce shortcut container for hotseat event reporting
-    
-    Bug: 170636685
-    Test: Manual
-    Change-Id: I5abeb17976bbafdc8cc74fb8b9a586d544c682fc
-
-commit 2de606fe731573c081fd2d6ba166e21ea6aa2e9c
-Author: Yogisha Dixit <ydixit@google.com>
-Date:   Mon Oct 12 15:36:07 2020 +0100
-
-    Delete the minimal database to force refresh.
-    
-    Bug: 169771796
-    Test: manual
-    Change-Id: Ic2188bb162f295c208346861fddc137ace19ddcb
-
-commit 0471b9836c9e382dc14bdc3abdf8502fb2b9f266
-Author: Sunny Goyal <sunnygoyal@google.com>
-Date:   Wed Sep 23 13:54:37 2020 -0700
-
-    Using FrameCallbacks instead of windowCallbacks for surface removal
-    
-    WindowCallbacks is called during the draw pass, before the frame has
-    been sent to the surfaceFlinger. Frame callback will provide a closer
-    approximation for when the frame is actually rendered on screen.
-    
-    Bug: 141126144
-    Change-Id: I62aab526c2ca24b00b5e7b312b36080f26c7b439
-
-commit 2727434c44d06882925369bf4b43687a06be4a3f
-Merge: 59f532fe9 1b9e199b3
-Author: Schneider Victor-tulias <victortulias@google.com>
-Date:   Fri Oct 9 20:09:08 2020 +0000
-
-    Merge "Fix hotseat and prediction row to allow updates when empty." into ub-launcher3-master
-
-commit 59f532fe9e2b1817c094641f3c7c517f42e4faf0
-Merge: d2bfce71f b5334e3f0
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Fri Oct 9 19:52:54 2020 +0000
-
-    Merge "Improve search section header" into ub-launcher3-master
-
-commit 979da64d8254599c332d83bf94f3f1fc3fe45fef
-Author: Riddle Hsu <riddlehsu@google.com>
-Date:   Tue Sep 22 21:52:40 2020 +0800
-
-    Add app start source info of apps launched from launcher
-    
-    Bug: 166614700
-    Test: Enable statsd log: "adb shell cmd stats print-logs"
-          adb logcat | grep statsd | grep "(48)"
-          The line may contain 0x100000->1[I] 0x110000->10[I]
-          that means 1=from launcher and 10=latency 10ms.
-    Change-Id: Iddaff7066b66e241ba58ec87129ddbe2c531dc7e
-    (cherry picked from commit 7bdf3574a3bff06a377b4364877687bfa7619d06)
-
-commit d2bfce71f776fd05633dfd915dfc664309274677
-Merge: ed4530fed 222afb970
-Author: Winson Chung <winsonc@google.com>
-Date:   Fri Oct 9 16:39:06 2020 +0000
-
-    Merge "Comply with the ISystemUiProxy.aidl change" into ub-launcher3-master
-
-commit ed4530fedda0bf876f91d0745fc70d0f30d42991
-Merge: 692d2109a 9d4a96ed0
-Author: Winson Chung <winsonc@google.com>
-Date:   Fri Oct 9 16:39:06 2020 +0000
-
-    Merge "Add latency metrics for recents gesture" into ub-launcher3-master
-
-commit 1b9e199b3d9c81c793758d96bb03e0c51c1b3fb1
-Author: Schneider Victor-tulias <victortulias@google.com>
-Date:   Thu Oct 8 15:50:22 2020 -0400
-
-    Fix hotseat and prediction row to allow updates when empty.
-    
-    Rotating the screen in the homescreen empties the hotseat, however it does not get populated while it is visible to the user. The user should not be able to see an empty hotseat or prediction row if predictions are available. It should therefore be possible to populate these when they are empty even if they are visible to the user.
-    
-    Change-Id: I8e5252bd29050c2cd9d443aedcb3f3e305c0e2d7
-
-commit b5334e3f07f0561808a2d6e9bba55f1e3a89191e
-Author: Hyunyoung Song <hyunyoungs@google.com>
-Date:   Fri Oct 9 00:50:48 2020 -0700
-
-    Improve search section header
-    
-    Change-Id: I47cf207f0d0ab792c0e7a47c9d1185eec087ec88
-
-commit 692d2109a6702706d24b3b819d115882f7362509
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Thu Oct 8 18:42:48 2020 -0700
-
-    invalidate itemDecoration on predictedRow focus draw
-    
-    Change-Id: I66c731f00ae1c1292c51ff281957f05fd2d70dfa
-
-commit 8d5b118060bff7f7518a9a14c0be5d265621f14c
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Thu Oct 8 13:11:25 2020 -0700
-
-    Revert PredictionRow shuoldDraw check
-    
-    + Show Rounded play result icons
-    
-    Bug: 168805872
-    Test: Manual
-    Change-Id: I663c7f7ca1f1ac072e5e9c441deabef7c3fbd97b
-
-commit 86f8df6cf954ac27ab092b9ef8a4db3c9979c4cb
-Merge: 4d19854b2 16045060c
-Author: Hilary Huo <hhuo@google.com>
-Date:   Thu Oct 8 18:43:51 2020 +0000
-
-    Merge "[pixel-search] add escape hatch" into ub-launcher3-master
-
-commit 4d19854b25a54599fe9b0ac8be9d60cf6c21d7ba
-Merge: 0827e1e32 ab9ad20be
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Thu Oct 8 18:40:56 2020 +0000
-
-    Merge "Search UI cleanup" into ub-launcher3-master
-
-commit 16045060c35639aea85afc572bea768d16e6c9f9
-Author: Hilary Huo <hhuo@google.com>
-Date:   Thu Oct 8 10:18:41 2020 -0700
-
-    [pixel-search] add escape hatch
-    
-    Change-Id: I33ffea1fc0859564955380d7d1db317293d1a2cb
-
-commit 0827e1e32a5f99fa02418dae37270c6db8c989d2
-Merge: 3463f0a87 68d7a6e5b
-Author: Andy Wickham <awickham@google.com>
-Date:   Thu Oct 8 16:53:29 2020 +0000
-
-    Merge "Adds feature flag for BC Smartspace." into ub-launcher3-master
-
-commit ab9ad20be600d1cbdc6b54a491d5fbb4c2cf9c16
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Wed Oct 7 15:18:24 2020 -0700
-
-    Search UI cleanup
-    
-    - offset all apps header padding with search input margin
-    - avoid check shouldDraw check on HeaderRow. (race condition)
-    
-    Bug: 170263425
-    Change-Id: I11a1fbb448aa6afd18ec0984af9bb8b1d7600f69
-
-commit 68d7a6e5b28af8cc55bdae7efc24cc7ebee81257
-Author: Andy Wickham <awickham@google.com>
-Date:   Wed Oct 7 14:27:17 2020 -0700
-
-    Adds feature flag for BC Smartspace.
-    
-    Change-Id: Iaf9fb7507d0ccd004a4e00188c75dadd6a059246
-
-commit 3463f0a876ff486ce03e160134e0504158271a92
-Merge: 2470d812a 4b7f38b8f
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Wed Oct 7 20:09:04 2020 +0000
-
-    Merge "Align fallback result query with result text" into ub-launcher3-master
-
-commit 2470d812a1ae989e67781e5056b534ad9a960819
-Merge: cae7d74d8 7a6e4c931
-Author: Vadim Tryshev <vadimt@google.com>
-Date:   Wed Oct 7 20:04:09 2020 +0000
-
-    Merge "Annotating Quick Switch CUJ for 3-button mode" into ub-launcher3-master
-
-commit cae7d74d898769727105850ea5473c2c0ae25fdb
-Merge: e9bf2bd14 1fddddb4f
-Author: Tony Wickham <twickham@google.com>
-Date:   Wed Oct 7 18:32:48 2020 +0000
-
-    Merge "Update launcher_trace.proto for quick switch" into ub-launcher3-master
-
-commit 7a6e4c931f13b369bfa4328196b4632d6d848a19
-Author: vadimt <vadimt@google.com>
-Date:   Tue Oct 6 14:09:16 2020 -0700
-
-    Annotating Quick Switch CUJ for 3-button mode
-    
-    Bug: 169221288
-    Change-Id: Ief62345fe6004dde699f44aa0c90329b7cd84e8b
-
-commit 4b7f38b8fa004b514244304fcc07ff514a2fa46b
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Tue Oct 6 18:37:46 2020 -0700
-
-    Align fallback result query with result text
-    
-    screenshot: https://screenshot.googleplex.com/6Daj5vdmz2jmznX
-    bug: 169438169
-    test: Manual
-    Change-Id: Ie621ed3c834aec5e9467607da4f685d05d152183
-
-commit 222afb970434c7972589adfc509bd2c256ca6556
-Author: Hongwei Wang <hwwang@google.com>
-Date:   Fri Oct 2 13:51:36 2020 -0700
-
-    Comply with the ISystemUiProxy.aidl change
-    
-    Two methods are added to support communications between Launcher and
-    SysUI when user swipes an auto PiP-able Activity to home.
-    
-    Bug: 143965596
-    Test: N/A
-    Change-Id: I2c73a287a094e882bde3cd71c27f9f66ae20e64a
-    (cherry picked from commit 88ddae38db924f700082a113670ce5a719116a95)
-
-commit 9d4a96ed029fdad1e369d5eedd082938f0dc9e01
-Author: Riddle Hsu <riddlehsu@google.com>
-Date:   Wed Sep 30 00:32:04 2020 +0800
-
-    Add latency metrics for recents gesture
-    
-    Pass the touch down time to RecentsAnimation#startRecentsActivity.
-    
-    Bug: 169221287
-    Test: Enable statsd log: "adb shell cmd stats print-logs"
-          Touch gesture navigation bar.
-          adb logcat | grep statsd | grep "(48)"
-          The line may contain 0x100000->4[I] 0x110000->20[I]
-          that means 4=by recents and 20=latency 20ms.
-    Change-Id: I81ee804895b7712f4d925736f5b4694c11a12cbe
-    (cherry picked from commit 63623967b83edad56db58173ebb6687c685b9177)
-
-commit e9bf2bd14c9a7a48f8f93687932d41b1418cf4e4
-Merge: 73ae75474 d028937e7
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Wed Oct 7 02:19:04 2020 +0000
-
-    Merge "[Live tile] Finish recents animation when the phone goes to sleep in live tile mode" into ub-launcher3-master
-
-commit 1fddddb4f30505e0fc9bb2e7c0d88b38ad900e54
-Author: Tony Wickham <twickham@google.com>
-Date:   Tue Sep 29 17:29:06 2020 -0700
-
-    Update launcher_trace.proto for quick switch
-    
-    Sample output from one entry:
-    entry {
-      elapsed_realtime_nanos: 440461382888540
-      launcher {
-        touch_interaction_service {
-          service_connected: true
-          overview_component_obvserver {
-            overview_activity_started: true
-            overview_activity_resumed: false
-          }
-          input_consumer {
-            name: "TYPE_OTHER_ACTIVITY:TYPE_ONE_HANDED"
-            swipe_handler {
-              gesture_state {
-                endTarget: NEW_TASK
-              }
-              is_recents_attached_to_app_window: true
-              scroll_offset: 846
-              app_to_overview_progress: 0
-            }
-          }
-        }
-      }
-    }
-    
-    Bug: 167259591
-    Change-Id: I7f199d88f1d736efcea6b9165b8c4b77a5d27c58
-
-commit 73ae75474ec1dd8807d814ea6c22323905d2070c
-Merge: 8a6f3e40d 0ebbc1880
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Tue Oct 6 23:18:44 2020 +0000
-
-    Merge "Removing tracing for a gone flake" into ub-launcher3-master
-
-commit 8a6f3e40d0321217c624055db7929c397e455e0c
-Merge: e29a9f796 565ed4ff6
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Tue Oct 6 22:49:40 2020 +0000
-
-    Merge "Update Search UI" into ub-launcher3-master
-
-commit 0ebbc18803aaf8ef2f6db7d628d7ae1ce322e842
-Author: vadimt <vadimt@google.com>
-Date:   Tue Oct 6 14:52:27 2020 -0700
-
-    Removing tracing for a gone flake
-    
-    Bug: 156044202
-    Change-Id: Ice142bb941fee7b731f46c2073fab17d83bbc871
-
-commit 565ed4ff69b534812818a2b9aa8789a1aea210eb
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Wed Sep 30 10:42:07 2020 -0700
-
-    Update Search UI
-    
-    [preview attached to bug]
-    
-    Bug: 169438169
-    Test: Manual
-    Change-Id: I085f3dd38ac373c1afab82a637ec08715a6e0cc5
-
-commit e29a9f7961e6db0915bc028ef7e871dcb2c8bde0
-Merge: 2c5ed10ff be17bdcd2
-Author: Jayaprakash Sundararaj <jayaprakashs@google.com>
-Date:   Tue Oct 6 21:00:20 2020 +0000
-
-    Merge "[Search] Add logging to People and badding as to icons." into ub-launcher3-master
-
-commit be17bdcd221f501c45876abe2249c1007858d0c0
-Author: jayaprakashs <jayaprakashs@google.com>
-Date:   Mon Oct 5 09:01:52 2020 -0700
-
-    [Search] Add logging to People and badding as to icons.
-    
-    Change-Id: I65948a2faca436216a94aa46139d425b8eade827
-
-commit 2c5ed10ffa1a870de35f9b3c0c558270aff498dd
-Merge: b2b65a1ef 8ed9707cf
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Tue Oct 6 18:40:57 2020 +0000
-
-    Merge "[Live Tile] Support launching another task (other than the current running task) in Overview" into ub-launcher3-master
-
-commit b2b65a1ef58b020923d112051535b6eb83b582df
-Merge: 3cf264f49 4c14f4b9e
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Tue Oct 6 17:45:35 2020 +0000
-
-    Merge "Avoid double search item highlight" into ub-launcher3-master
-
-commit 8ed9707cf3a4300cb61942f08f0752c80eed086b
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Mon Sep 14 23:25:37 2020 -0700
-
-    [Live Tile] Support launching another task (other than the current running task) in Overview
-    
-    - Get rid of the defer cancelation logic
-    - Render animation on the task view of the task being launched upon task view appeared callback
-    - Finish the recents animation upon the end of the recents window animation
-    
-    Fixes: 164926736
-    Test: manual
-    Change-Id: Ibffb6a9c74c235efc8615a22b0306551532c7b61
-
-commit 3cf264f498e37c482fa4c559bf48ffa791279585
-Author: Schneider Victor-tulias <victortulias@google.com>
-Date:   Tue Sep 22 12:58:38 2020 -0700
-
-    Prevent hotseat updates if it is visible to the user.
-    
-    Test: manual
-    
-    Fixes: 168653219
-    
-    Changing app icons under the user's finger could be disruptive. Added a checks for whether the hotseatand all apps predictions are visible and callbacks to update them when they become hidden.
-    
-    Change-Id: Ib9e6e904e9f662ecfaeea6a2fe21d1d81ba39b96
-
-commit b6aff1f56d55a36256446ec3970d92e9da39b98c
-Author: Hyunyoung Song <hyunyoungs@google.com>
-Date:   Mon Oct 5 16:08:35 2020 -0700
-
-    Fix NPE inside RecentsOrientedState
-    
-    Bug: 169963211
-    Change-Id: I86dd337dc1b862f3fa99b91b47fa250076233f96
-
-commit eab40983b9a48b933bde5ca95a82ebd4d83b233d
-Merge: 83ce7c0b5 020e628f2
-Author: Jonathan Miranda <jonmiranda@google.com>
-Date:   Mon Oct 5 22:20:27 2020 +0000
-
-    Merge "Add shadow radius to windows during app launch / close animations." into ub-launcher3-master
-
-commit 83ce7c0b5e461386bb92883a8d6cefe8365cd9ae
-Merge: 679d920bf d6b1f3c08
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Mon Oct 5 19:18:39 2020 +0000
-
-    Merge "Action icon should be used as a badge instead of main icon" into ub-launcher3-master
-
-commit 679d920bf5151cffed4e8186c12c25d8d7907af9
-Merge: e108cc609 0c943966d
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Mon Oct 5 19:13:50 2020 +0000
-
-    Merge "Add null check for input receiver before updating batching" into ub-launcher3-master
-
-commit e108cc609d0a7fd58f0c7e16ce45fa79be6dd272
-Merge: 470403eb5 f622e42bf
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Mon Oct 5 18:39:58 2020 +0000
-
-    Merge "Removing unused proto extensions" into ub-launcher3-master
-
-commit 470403eb58879380e2edac2262dc7f40327b2a15
-Merge: a5130482a 1d7ed30db
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Mon Oct 5 18:29:54 2020 +0000
-
-    Merge "Remove widgets that no longer fit the workspace in their current spans." into ub-launcher3-master
-
-commit 4c14f4b9eda8332347c81e0cf51c5de4dbc06399
-Author: Samuel Fufa <sfufa@google.com>
-Date:   Mon Oct 5 10:50:00 2020 -0700
-
-    Avoid double search item highlight
-    
-    Change-Id: Ic2e28b18f6d5e3ed32cd5646bc3bb4789c378e57
-
-commit 0c943966d373d8ae7eef2b08e88ac44bf57d8a8d
-Author: Winson Chung <winsonc@google.com>
-Date:   Mon Oct 5 10:23:27 2020 -0700
-
-    Add null check for input receiver before updating batching
-    
-    - A change in the system (ie. sysui crash or nav mode change) could
-      cause the input monitor to be disposed before the swipe animation
-      settles
-    
-    Bug: 170121063
-    Test: Kill sysui while swiping up
-    
-    Change-Id: I1417b109fecdb98fae6197c7038dbe9307470853
-
-commit a5130482aee1b0592661bc1c6e178a0de7a163da
-Merge: b21819e18 7fcd74abb
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Mon Oct 5 17:14:21 2020 +0000
-
-    Merge "Suggest result should launch Bug: 169980192" into ub-launcher3-master
-
-commit d028937e74a9ea6d36e463de4c87ed37283bbdf6
-Author: Tracy Zhou <tracyzhou@google.com>
-Date:   Sat Oct 3 00:36:53 2020 -0700
-
-    [Live tile] Finish recents animation when the phone goes to sleep in live tile mode
-    
-    Fixes: 169988381
-    Test: manual
-    Change-Id: Ic71d3e6767eadb6854dbd46581bf9d3242c161a4
-
-commit 7fcd74abb399100ac8243be6ca28c09cc8adc8c8
-Author: Hyunyoung Song <hyunyoungs@google.com>
-Date:   Fri Oct 2 19:20:11 2020 -0700
-
-    Suggest result should launch
-    Bug: 169980192
-    
-    Change-Id: I762245a5cc4740d093c9cb3b44a508e9e3f2b763
-
-commit b21819e181e99504c22c6ca028261a1f2665c6f9
-Merge: 931bce369 a762b0241
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Fri Oct 2 22:07:12 2020 +0000
-
-    Merge "Annotating Quick Switch CUJ for non-3-button modes" into ub-launcher3-master
-
-commit a762b02418695f5a1ff2f96586660de8c3610280
-Author: vadimt <vadimt@google.com>
-Date:   Fri Oct 2 13:56:28 2020 -0700
-
-    Annotating Quick Switch CUJ for non-3-button modes
-    
-    Bug: 169221288
-    Change-Id: I7145a9e28a2f0a789d19d2a0e3d15630c6e50f6a
-
-commit 931bce3697595a214023bc72923dad47a61d5711
-Merge: c935ba6b8 733e3c609
-Author: TreeHugger Robot <treehugger-gerrit@google.com>
-Date:   Fri Oct 2 19:19:50 2020 +0000
-
-    Merge "Moving some initializations to the background thread" into ub-launcher3-master
-
-commit c935ba6b8a2ec163533c0b19309dacb6199e6552
-Merge: a4111f250 58804ac52
-Author: Sunny Goyal <sunnygoyal@google.com>
-Date:   Fri Oct 2 18:26:06 2020 +0000
-
-    Merge "Adding stats log for add item flow" into ub-launcher3-master
-
-commit 733e3c609b7653a36e58747c881458ec00d98df8
-Author: Sunny Goyal <sunnygoyal@google.com>
-Date:   Tue Sep 29 10:32:32 2020 -0700
-
-    Moving some initializations to the background thread
-    
-    HandlerThread.getLooper blocks until the thread is ready. Instead
-    moving all looper dependency to the new thread itself.
-    
-    Change-Id: I240e8c56b855a991433a7fe93875059e6dab146b
-
-commit 58804ac5257f45dddbf7a6db35cf8f369ee1e88e
-Author: Sunny Goyal <sunnygoyal@google.com>
-Date:   Wed Sep 16 16:27:40 2020 -0700
-
-    Adding stats log for add item flow
-    
-    Bug: 169385783
-    Bug: 168167693
-    Change-Id: I37395f1b118727f67e0f14c02f945b8213b165c8
-
-commit a4111f250003328d1aef8bbaab59512208ec46cb
-Merge: 8d14dbe04 f6b72c4ad
-Author: Hilary Huo <hhuo@google.com>
-Date:   Fri Oct 2 17:41:22 2020 +0000
-
-    Merge "[pixel-search] Bug fix: automatically launch screenshot + center&crop remoteaction icon" into ub-launcher3-master
-
-commit f622e42bf6983d3adb95386bfd6375d281f1d4f2
-Author: Sunny Goyal <sunnygoyal@google.com>
-Date:   Fri Oct 2 10:35:56 2020 -0700
-
-    Removing unused proto extensions
-    
-    Change-Id: I6d0319c99934dad5176b6f70b895a4ca772ec45f
-
-commit d6b1f3c086f9ac097cd03e1ee898b153478ec11a
-Author: Hyunyoung Song <hyunyoungs@google.com>
-Date:   Fri Oct 2 00:26:35 2020 -0700
-
-    Action icon should be used as a badge instead of main icon
-    
-    Bug: 169796517
-    Change-Id: I3f07fdc2ae6e1af463701f942c26c3ca5d836ee2
-
-commit f6b72c4ad1d2e082441a64c4d6a5a02ee8a251ca
-Author: Hilary Huo <hhuo@google.com>
-Date:   Thu Oct 1 12:26:48 2020 -0700
-
-    [pixel-search] Bug fix: automatically launch screenshot + center&crop remoteaction icon
-    
-    Bug: b/169330678
-    Change-Id: Id5f8a0ce6d68f7ed9e4d1ff258ee3772229eb63b
-
-commit 1d7ed30dba4b2c71fc7b0981532a872a13e5aedb
-Author: Jon Miranda <jonmiranda@google.com>
-Date:   Wed Sep 23 12:15:43 2020 -0700
-
-    Remove widgets that no longer fit the workspace in their current spans.
-    
-    This can happen when display size changes.
-    We compare span sizes of widget in the db to the min sizes of the widget
-    in the current display size. If the widget can no longer fit in its existing
-    spans, we remove it.
-    
-    Also update test widgets to have minWidth/minHeight of 1dp. This ensures that
-    the spanX, spanY, min* values remain consistent between different test devices.
-    
-    Bug: 168818961
-    Change-Id: I723372e4582658f78b2f23ced9073cb77977a6b8
-
-commit 020e628f22cc7975beab439c6da26af2f9ebc15b
-Author: Jon Miranda <jonmiranda@google.com>
-Date:   Mon Sep 28 17:01:42 2020 -0700
-
-    Add shadow radius to windows during app launch / close animations.
-    
-    Bug: 168608912
-    Change-Id: I2ec50b0b3711c0861659f9c641bbc05fcdeaab45
diff --git a/go/quickstep/res/layout/overview_actions_container.xml b/go/quickstep/res/layout/overview_actions_container.xml
index 077cfae..df09124 100644
--- a/go/quickstep/res/layout/overview_actions_container.xml
+++ b/go/quickstep/res/layout/overview_actions_container.xml
@@ -126,7 +126,7 @@
             style="@style/GoOverviewActionButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:drawableStart="@drawable/ic_save_app_pair"
+            android:drawableStart="@drawable/ic_save_app_pair_up_down"
             android:text="@string/action_save_app_pair"
             android:theme="@style/ThemeControlHighlightWorkspaceColor"
             android:visibility="gone" />
diff --git a/go/quickstep/res/values/config.xml b/go/quickstep/res/values/config.xml
index 796d14d..147dd96 100644
--- a/go/quickstep/res/values/config.xml
+++ b/go/quickstep/res/values/config.xml
@@ -21,4 +21,7 @@
     <bool name="enable_niu_actions">true</bool>
 
     <string name="task_overlay_factory_class" translatable="false">com.android.quickstep.TaskOverlayFactoryGo</string>
+
+    <!-- String representing the intent to delete a package. -->
+    <string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;B.android.intent.extra.RETURN_RESULT=true;end</string>
 </resources>
\ No newline at end of file
diff --git a/go/res/xml/device_profiles.xml b/go/quickstep/res/xml/device_profiles.xml
similarity index 100%
rename from go/res/xml/device_profiles.xml
rename to go/quickstep/res/xml/device_profiles.xml
diff --git a/go/quickstep/src/com/android/launcher3/BuildConfig.java b/go/quickstep/src/com/android/launcher3/BuildConfig.java
new file mode 100644
index 0000000..cfcda39
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/BuildConfig.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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;
+
+public final class BuildConfig {
+    public static final String APPLICATION_ID = "com.android.launcher3";
+
+    public static final boolean IS_STUDIO_BUILD = false;
+    /**
+     * Flag to state if the QSB is on the first screen and placed on the top,
+     * this can be overwritten in other launchers with a different value, if needed.
+     */
+    public static final boolean QSB_ON_FIRST_SCREEN = true;
+
+    /**
+     * Flag to state if the widget on the top of the first screen should be shown.
+     */
+    public static final boolean WIDGET_ON_FIRST_SCREEN = false;
+
+    /**
+     * Flag to control various developer centric features
+     */
+    public static final boolean IS_DEBUG_DEVICE = false;
+
+    // Flag to control widgets support in Launcher
+    public static final boolean WIDGETS_ENABLED = false;
+    // Flag to control notification dots support in Launcher
+    public static final boolean NOTIFICATION_DOTS_ENABLED = false;
+}
diff --git a/go/res/values-v26/bools.xml b/go/res/values-v26/bools.xml
deleted file mode 100644
index 1584734..0000000
--- a/go/res/values-v26/bools.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2017, 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.
-*/
--->
-
-<resources>
-    <bool name="notification_dots_enabled">false</bool>
-</resources>
-
diff --git a/go/res/values/override.xml b/go/res/values/override.xml
deleted file mode 100644
index 268cb98..0000000
--- a/go/res/values/override.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2017 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.
-*/
--->
-<resources>
-    <!-- String representing the intent to delete a package. -->
-    <string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;B.android.intent.extra.RETURN_RESULT=true;end</string>
-</resources>
\ No newline at end of file
diff --git a/go/src/com/android/launcher3/model/LauncherBinder.java b/go/src/com/android/launcher3/model/LauncherBinder.java
deleted file mode 100644
index 7a0dce8..0000000
--- a/go/src/com/android/launcher3/model/LauncherBinder.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 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.model;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.model.BgDataModel.Callbacks;
-
-/**
- * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
- */
-public class LauncherBinder extends BaseLauncherBinder {
-
-    public LauncherBinder(LauncherAppState app, BgDataModel dataModel,
-            AllAppsList allAppsList, Callbacks[] callbacks) {
-        super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
-    }
-
-    @Override
-    public void bindDeepShortcuts() {
-    }
-
-    @Override
-    public void bindWidgets() {
-    }
-
-    @Override
-    public void bindSmartspaceWidget() {
-    }
-}
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
deleted file mode 100644
index 3a28444..0000000
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2018 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.model;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.UserHandle;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.icons.ComponentWithLabelAndIcon;
-import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Predicate;
-
-/**
- * Widgets data model that is used by the adapters of the widget views and controllers.
- *
- * <p> The widgets and shortcuts are organized using package name as its index.
- */
-public class WidgetsModel {
-
-    // True is the widget support is disabled.
-    public static final boolean GO_DISABLE_WIDGETS = true;
-    public static final boolean GO_DISABLE_NOTIFICATION_DOTS = true;
-
-    private static final ArrayList<WidgetsListBaseEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
-
-    /**
-     * Returns a list of {@link WidgetsListBaseEntry} filtered using given widget item filter. All
-     * {@link WidgetItem}s in a single row are sorted (based on label and user), but the overall
-     * list of {@link WidgetsListBaseEntry}s is not sorted.
-     *
-     * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
-     */
-    public synchronized ArrayList<WidgetsListBaseEntry> getFilteredWidgetsListForPicker(
-            Context context,
-            Predicate<WidgetItem> widgetItemFilter) {
-        return EMPTY_WIDGET_LIST;
-    }
-
-    /**
-     * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row are
-     * sorted (based on label and user), but the overall list of {@link WidgetsListBaseEntry}s is
-     * not sorted. This list is sorted at the UI when using
-     * {@link com.android.launcher3.widget.picker.WidgetsDiffReporter}
-     *
-     * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
-     */
-    public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
-        return EMPTY_WIDGET_LIST;
-    }
-
-    /** Returns a mapping of packages to their widgets without static shortcuts. */
-    public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
-        return Map.of();
-    }
-
-    /**
-     * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
-     *                    only widgets and shortcuts associated with the package/user are.
-     */
-    public List<ComponentWithLabelAndIcon> update(LauncherAppState app,
-            @Nullable PackageUserKey packageUser) {
-        return Collections.emptyList();
-    }
-
-
-    public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
-            LauncherAppState app) {
-    }
-
-    public WidgetItem getWidgetProviderInfoByProviderName(
-            ComponentName providerName, UserHandle user) {
-        return null;
-    }
-
-    /** Returns {@link PackageItemInfo} of a pending widget. */
-    public static PackageItemInfo newPendingItemInfo(
-            Context context, ComponentName provider, UserHandle userHandle) {
-        return new PackageItemInfo(provider.getPackageName(), userHandle);
-    }
-}
diff --git a/go/src/com/android/launcher3/util/AbsGridOccupancy.java b/go/src/com/android/launcher3/util/AbsGridOccupancy.java
deleted file mode 100644
index 4a46bd1..0000000
--- a/go/src/com/android/launcher3/util/AbsGridOccupancy.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-/**
- * Defines method to find the next vacant cell on a grid.
- * This uses the default top-down, left-right approach and can be over-written through
- * code swaps in different launchers.
- */
-public abstract class AbsGridOccupancy {
-
-    /**
-     * Find the first vacant cell, if there is one.
-     *
-     * @param vacantOut Holds the x and y coordinate of the vacant cell
-     * @param spanX Horizontal cell span.
-     * @param spanY Vertical cell span.
-     *
-     * @return true if a vacant cell was found
-     */
-    protected boolean findVacantCell(int[] vacantOut, boolean[][] cells, int countX, int countY,
-            int spanX, int spanY) {
-        for (int y = 0; (y + spanY) <= countY; y++) {
-            for (int x = 0; (x + spanX) <= countX; x++) {
-                boolean available = !cells[x][y];
-                out:
-                for (int i = x; i < x + spanX; i++) {
-                    for (int j = y; j < y + spanY; j++) {
-                        available = available && !cells[i][j];
-                        if (!available) break out;
-                    }
-                }
-                if (available) {
-                    vacantOut[0] = x;
-                    vacantOut[1] = y;
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-}
diff --git a/gradle.properties b/gradle.properties
deleted file mode 100644
index d5c1d77..0000000
--- a/gradle.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# Until all the dependencies move to android X
-android.useAndroidX = true
-android.enableJetifier = true
-org.gradle.parallel=true
-
-ANDROID_X_VERSION=1+
-
-GRADLE_CLASS_PATH=com.android.tools.build:gradle:3.5.1
-
-PROTOBUF_CLASS_PATH=com.google.protobuf:protobuf-gradle-plugin:0.8.8
-PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7
-
-BUILD_TOOLS_VERSION=28.0.3
-COMPILE_SDK=android-S
diff --git a/quickstep/res/color/bubblebar_drop_target_bg_color.xml b/quickstep/res/color/bubblebar_drop_target_bg_color.xml
new file mode 100644
index 0000000..ca37c7f
--- /dev/null
+++ b/quickstep/res/color/bubblebar_drop_target_bg_color.xml
@@ -0,0 +1,19 @@
+<?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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:alpha="0.35" android:color="?androidprv:attr/materialColorPrimaryContainer" />
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/bg_bubble_bar_drop_target.xml b/quickstep/res/drawable/bg_bubble_bar_drop_target.xml
new file mode 100644
index 0000000..79e4318
--- /dev/null
+++ b/quickstep/res/drawable/bg_bubble_bar_drop_target.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/bubblebar_drop_target_corner_radius" />
+    <solid android:color="@color/bubblebar_drop_target_bg_color" />
+    <stroke
+        android:width="1dp"
+        android:color="?androidprv:attr/materialColorPrimaryContainer" />
+</shape>
diff --git a/quickstep/res/drawable/ic_save_app_pair.xml b/quickstep/res/drawable/ic_save_app_pair.xml
deleted file mode 100644
index 4a7ee1a..0000000
--- a/quickstep/res/drawable/ic_save_app_pair.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:pathData="M13.329,2.305H4.242C2.751,2.305 1.542,3.514 1.542,5.005V13.005C1.542,14.496 2.751,15.705 4.242,15.705H7.875V19.011C7.875,20.502 9.084,21.711 10.575,21.711H19.662C21.153,21.711 22.362,20.502 22.362,19.011V10.011C22.362,8.52 21.153,7.311 19.662,7.311H16.029V5.005C16.029,3.514 14.821,2.305 13.329,2.305ZM14.329,7.311V5.005C14.329,4.452 13.882,4.005 13.329,4.005H4.242C3.69,4.005 3.242,4.452 3.242,5.005V13.005C3.242,13.557 3.69,14.005 4.242,14.005H7.875V10.011C7.875,8.52 9.084,7.311 10.575,7.311H14.329ZM9.575,14.005V10.011C9.575,9.611 9.81,9.266 10.15,9.106C10.285,9.037 10.438,8.999 10.6,8.999H19.687C20.239,8.999 20.687,9.447 20.687,9.999V18.999C20.687,19.399 20.452,19.744 20.113,19.904C19.977,19.972 19.824,20.011 19.662,20.011H10.575C10.023,20.011 9.575,19.563 9.575,19.011V15.705H9.6V14.005H9.575ZM15.542,11.996V14H17.588V15H15.542V16.996H14.542V15H12.464V14H14.542V11.996H15.542Z"
-      android:fillColor="#000000"
-      android:fillType="evenOdd"/>
-</vector>
diff --git a/quickstep/res/drawable/ic_save_app_pair_left_right.xml b/quickstep/res/drawable/ic_save_app_pair_left_right.xml
new file mode 100644
index 0000000..b104f44
--- /dev/null
+++ b/quickstep/res/drawable/ic_save_app_pair_left_right.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M8.5,4.5H3.5C2.4,4.5 1.5,5.4 1.5,6.5V18.5C1.5,19.6 2.4,20.5 3.5,20.5H8.5C9.6,20.5 10.5,19.6 10.5,18.5V6.5C10.5,5.4 9.6,4.5 8.5,4.5ZM8.5,18.5H3.5V6.5H8.5V18.5ZM14.5,6.5H19.5V13.5H21.5V6.5C21.5,5.4 20.6,4.5 19.5,4.5H14.5C13.4,4.5 12.5,5.4 12.5,6.5V18.5C12.5,19.6 13.4,20.5 14.5,20.5H15.5V18.5H14.5V6.5ZM20.5,14.5V16.5H22.5V18.5H20.5V20.5H18.5V18.5H16.5V16.5H18.5V14.5H20.5Z"
+      android:fillColor="#48473A"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/quickstep/res/drawable/ic_save_app_pair_up_down.xml b/quickstep/res/drawable/ic_save_app_pair_up_down.xml
new file mode 100644
index 0000000..86f110c
--- /dev/null
+++ b/quickstep/res/drawable/ic_save_app_pair_up_down.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,2L6,2C4.9,2 4,2.9 4,4L4,9C4,10.1 4.9,11 6,11L18,11C19.1,11 20,10.1 20,9L20,4C20,2.9 19.1,2 18,2ZM18,9L6,9L6,4L18,4L18,9ZM18,13L6,13C4.9,13 4,13.9 4,15L4,20C4,21.1 4.9,22 6,22L13,22L13,20L6,20L6,15L18,15L18,16L20,16L20,15C20,13.9 19.1,13 18,13ZM16,17L18,17L18,19L20,19L20,21L18,21L18,23L16,23L16,21L14,21L14,19L16,19L16,17Z"
+      android:fillColor="#48473A"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/quickstep/res/drawable/keyboard_quick_switch_thumbnail_background.xml b/quickstep/res/drawable/keyboard_quick_switch_thumbnail_background.xml
new file mode 100644
index 0000000..961f5aa
--- /dev/null
+++ b/quickstep/res/drawable/keyboard_quick_switch_thumbnail_background.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@android:color/white" />
+    <corners android:radius="@dimen/keyboard_quick_switch_task_view_radius" />
+</shape>
diff --git a/quickstep/res/layout/bubble_bar_drop_target.xml b/quickstep/res/layout/bubble_bar_drop_target.xml
new file mode 100644
index 0000000..23f240c
--- /dev/null
+++ b/quickstep/res/layout/bubble_bar_drop_target.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/bubblebar_size"
+    android:layout_height="@dimen/bubblebar_size"
+    android:background="@drawable/bg_bubble_bar_drop_target"
+    android:elevation="@dimen/bubblebar_elevation" />
\ No newline at end of file
diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml b/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml
index dde9cac..8cd8560 100644
--- a/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml
@@ -18,6 +18,6 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:scaleType="centerCrop"
-    android:background="@drawable/keyboard_quick_switch_task_view_background"
+    android:background="@drawable/keyboard_quick_switch_thumbnail_background"
     android:clipToOutline="true"
     android:importantForAccessibility="no"/>
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index 758622b..5d489f5 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -23,6 +23,7 @@
         android:id="@+id/action_buttons"
         android:layout_width="match_parent"
         android:layout_height="@dimen/overview_actions_height"
+        android:layout_gravity="bottom"
         android:gravity="center_horizontal"
         android:orientation="horizontal">
 
diff --git a/quickstep/res/layout/task_view_menu_option.xml b/quickstep/res/layout/task_view_menu_option.xml
index 30ab4b1..ffe2401 100644
--- a/quickstep/res/layout/task_view_menu_option.xml
+++ b/quickstep/res/layout/task_view_menu_option.xml
@@ -41,6 +41,8 @@
         android:layout_marginStart="@dimen/task_menu_option_text_start_margin"
         android:textSize="14sp"
         android:textColor="?androidprv:attr/materialColorOnSurface"
-        android:focusable="false" />
+        android:focusable="false"
+        android:gravity="start"
+        android:ellipsize="end" />
 
 </LinearLayout>
diff --git a/quickstep/res/layout/taskbar_all_apps_button.xml b/quickstep/res/layout/taskbar_all_apps_button.xml
index c50db2e..94596cb 100644
--- a/quickstep/res/layout/taskbar_all_apps_button.xml
+++ b/quickstep/res/layout/taskbar_all_apps_button.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2022 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -15,10 +14,9 @@
 -->
 
 <!-- Note: The actual size will match the taskbar icon sizes in TaskbarView#onLayout(). -->
-<com.android.launcher3.views.IconButtonView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.views.IconButtonView xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/BaseIcon.Workspace.Taskbar"
     android:layout_width="@dimen/taskbar_icon_min_touch_size"
     android:layout_height="@dimen/taskbar_icon_min_touch_size"
-    android:contentDescription="@string/all_apps_button_label"
     android:backgroundTint="@android:color/transparent"
-    />
+    android:contentDescription="@string/all_apps_button_label" />
diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml
index 0a92fa9..330f85f 100644
--- a/quickstep/res/layout/taskbar_divider.xml
+++ b/quickstep/res/layout/taskbar_divider.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2023 The Android Open Source Project
+<?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.
@@ -13,9 +12,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.views.IconButtonView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.views.IconButtonView xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/BaseIcon.Workspace.Taskbar"
     android:layout_width="@dimen/taskbar_icon_min_touch_size"
     android:layout_height="@dimen/taskbar_icon_min_touch_size"
-    android:contentDescription="@string/taskbar_divider_a11y_title"
-    android:backgroundTint="@android:color/transparent" />
\ No newline at end of file
+    android:backgroundTint="@android:color/transparent"
+    android:contentDescription="@string/taskbar_divider_a11y_title" />
\ No newline at end of file
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 9b68f49..048ff8b 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -95,12 +95,12 @@
     <string name="action_share" msgid="2648470652637092375">"مشاركة"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"لقطة شاشة"</string>
     <string name="action_split" msgid="2098009717623550676">"تقسيم"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"حفظ إعدادات الميزة"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"حفظ استخدام التطبيقين معًا"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"انقر على تطبيق آخر لاستخدام وضع تقسيم الشاشة."</string>
-    <string name="toast_contextual_split_select_app" msgid="433510957123687090">"اختَر تطبيقًا آخر لاستخدام \"وضع تقسيم الشاشة\"."</string>
+    <string name="toast_contextual_split_select_app" msgid="433510957123687090">"اختَر تطبيقًا آخر لاستخدام \"وضع تقسيم الشاشة\""</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"إلغاء"</b></string>
     <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"الخروج من وضع تقسيم الشاشة"</string>
-    <string name="toast_split_app_unsupported" msgid="2360229567007828914">"اختَر تطبيقًا آخر لاستخدام \"وضع تقسيم الشاشة\"."</string>
+    <string name="toast_split_app_unsupported" msgid="2360229567007828914">"اختَر تطبيقًا آخر لاستخدام \"وضع تقسيم الشاشة\""</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"لا يسمح التطبيق أو لا تسمح مؤسستك بهذا الإجراء."</string>
     <string name="split_widgets_not_supported" msgid="1355743038053053866">"التطبيقات المصغّرة غير متوفّرة حاليًا، يرجى اختيار تطبيق آخر."</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"هل تريد تخطي الدليل التوجيهي للتنقّل؟"</string>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 8eed155..1afde8f 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"Deli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string>
     <string name="action_split" msgid="2098009717623550676">"Podeli"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"Čuvaj par aplikacija"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"Sačuvaj par aplikacija"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Dodirnite drugu aplikaciju za podeljeni ekran"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Odaberite drugu aplikaciju da biste koristili podeljeni ekran"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Otkaži"</b></string>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index be66004..bd677da 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"Споделяне"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Екранна снимка"</string>
     <string name="action_split" msgid="2098009717623550676">"Разделяне на екрана"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"Двойка прил.: Запис"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"Запис на двойка приложения"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Докоснете друго прил., за да ползвате разд. екран"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"За разделен екран изберете още едно приложение"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Отказ"</b></string>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 1cffbdf..5d6e0d8 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -129,7 +129,7 @@
     <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Es mostra la Barra de tasques"</string>
     <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"S\'ha amagat la Barra de tasques"</string>
     <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegació"</string>
-    <string name="always_show_taskbar" msgid="3608801276107751229">"Mostra Barra de tasques"</string>
+    <string name="always_show_taskbar" msgid="3608801276107751229">"Barra de tasques sempre visible"</string>
     <string name="change_navigation_mode" msgid="9088393078736808968">"Canvia el mode de navegació"</string>
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Separador de la Barra de tasques"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mou a la part superior o a l\'esquerra"</string>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index f3f5777..5ebf7eb 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -112,7 +112,7 @@
     <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Přetáhněte aplikaci na stranu a používejte tak dvě najednou"</string>
     <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Panel aplikací zobrazíte pomalým přejetím prstem nahoru"</string>
     <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Dostávejte návrhy aplikací podle toho, jaké používáte"</string>
-    <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Dlouhým stisknutím oddělovače připnete panel aplikací"</string>
+    <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Dlouhým stisknutím oddělovače panel aplikací připnete"</string>
     <string name="taskbar_edu_features" msgid="3320337287472848162">"Více možností s panelem aplikací"</string>
     <string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Stálé zobrazení panelu aplikací"</string>
     <string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"Pokud chcete, aby se panel aplikací vždy zobrazoval ve spodní části obrazovky, podržte oddělovač."</string>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 499a3d5..c4371ee 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -134,7 +134,7 @@
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskleisten-Teiler"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Nach oben / Nach links verschieben"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Nach unten / Nach rechts verschieben"</string>
-    <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# weitere App anzeigen.}other{# weitere Apps anzeigen.}}"</string>
+    <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# weitere App anzeigen}other{# weitere Apps anzeigen}}"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> und <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
     <string name="desktop_select_app_toast" msgid="2306057322833956910">"Hinzufügen einer App zum Desktop"</string>
     <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Abbrechen"</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 580a8df..5d5c2c1 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -129,7 +129,7 @@
     <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tareas visible"</string>
     <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tareas oculta"</string>
     <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string>
-    <string name="always_show_taskbar" msgid="3608801276107751229">"Ver siempre Barra de tareas"</string>
+    <string name="always_show_taskbar" msgid="3608801276107751229">"Barra de tareas visible"</string>
     <string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar el modo de navegación"</string>
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor de la Barra de tareas"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover a la parte superior o izquierda"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 0900f78..79964d8 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -129,7 +129,7 @@
     <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tareas visible"</string>
     <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tareas oculta"</string>
     <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string>
-    <string name="always_show_taskbar" msgid="3608801276107751229">"Barra de Tareas visible"</string>
+    <string name="always_show_taskbar" msgid="3608801276107751229">"Barra de tareas visible"</string>
     <string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar el modo de navegación"</string>
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor de Barra de Tareas"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index f8e2ecd..65f02eb 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"Jaga"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</string>
     <string name="action_split" msgid="2098009717623550676">"Eralda"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"Salv. rakendusepaar"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"Salvesta rakendusepaar"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Jagatud ekraanikuva kasutamiseks puudutage muud rakendust"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Valige jagatud ekraanikuva jaoks muu rakendus."</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Tühista"</b></string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index e913f85..ca44b7a 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -109,9 +109,9 @@
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ohita"</string>
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"Käännä näyttö"</string>
     <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Tehtäväpalkin ohje"</string>
-    <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Vedä sovellus sivuun, ja voit käyttää kahta sovellusta"</string>
+    <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Vedä sovellus sivuun ja käytä kahta sovellusta"</string>
     <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Näytä tehtäväpalkki pyyhkäisemällä ylös hitaasti"</string>
-    <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Sovellussuosituksia käytön perusteella"</string>
+    <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Vastaanota sovellussuosituksia käytön perusteella"</string>
     <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Kiinnitä tehtäväpalkki painamalla jakajaa pitkään"</string>
     <string name="taskbar_edu_features" msgid="3320337287472848162">"Vinkkejä tehtäväpalkin tehokkaampaan käyttöön"</string>
     <string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Näytä tehtäväpalkki aina"</string>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index b266263..9875823 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Facer captura"</string>
     <string name="action_split" msgid="2098009717623550676">"Dividir"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"Gardar empar. apps"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"Gardar parella apps"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Para usar a pantalla dividida, toca outra app"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Escolle outra aplicación para usar a pantalla dividida."</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Cancelar"</b></string>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 5814779..368cc0a 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"शेयर करें"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट लें"</string>
     <string name="action_split" msgid="2098009717623550676">"स्प्लिट स्क्रीन मोड"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"ऐप पेयर को सेव करें"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"ऐप पेयर सेव करें"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"स्प्लिट स्क्रीन के लिए दूसरे ऐप्लिकेशन पर टैप करें"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"स्प्लिट स्क्रीन इस्तेमाल करने के लिए, दूसरा ऐप्लिकेशन चुनें"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"अभी नहीं"</b></string>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index b25c1c5..34030dd 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -134,7 +134,7 @@
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Razdjelnik trake sa zadacima"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore/lijevo"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje/desno"</string>
-    <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Prikaži više aplikacija (još #).}one{Prikaži više aplikacija (još #).}few{Prikaži više aplikacija (još #).}other{Prikaži više aplikacija (još #).}}"</string>
+    <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Prikaži još # aplikaciju}one{Prikaži još # aplikaciju}few{Prikaži još # aplikacije}other{Prikaži još # aplikacija}}"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
     <string name="desktop_select_app_toast" msgid="2306057322833956910">"Dodavanje aplikacije na radnu površinu"</string>
     <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Odustani"</string>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 017c5f6..a61da68 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -129,7 +129,7 @@
     <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Feladatsáv megjelenítve"</string>
     <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Feladatsáv elrejtve"</string>
     <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigációs sáv"</string>
-    <string name="always_show_taskbar" msgid="3608801276107751229">"Mindig megjelenő feladatsáv"</string>
+    <string name="always_show_taskbar" msgid="3608801276107751229">"Mindig megjelenő Feladatsáv"</string>
     <string name="change_navigation_mode" msgid="9088393078736808968">"Navigációs mód módosítása"</string>
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Feladatsáv-elválasztó"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mozgatás felülre vagy a bal oldalra"</string>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 44ab9b9..64a8235 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -97,10 +97,10 @@
     <string name="action_split" msgid="2098009717623550676">"Pisahkan"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"Simpan pasangan apl"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Ketuk aplikasi lain untuk memakai layar terpisah"</string>
-    <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Pilih aplikasi lain untuk menggunakan layar terpisah"</string>
+    <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Pilih aplikasi lain untuk dibuka di layar terpisah"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Batal"</b></string>
     <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Keluar dari pemilihan layar terpisah"</string>
-    <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pilih aplikasi lain untuk memakai layar terpisah"</string>
+    <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pilih aplikasi lain untuk dibuka di layar terpisah"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak diizinkan oleh aplikasi atau organisasi Anda"</string>
     <string name="split_widgets_not_supported" msgid="1355743038053053866">"Widget saat ini tidak didukung, pilih aplikasi lain"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Lewati tutorial gestur?"</string>
@@ -113,7 +113,7 @@
     <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Geser perlahan ke atas untuk menampilkan Taskbar"</string>
     <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Dapatkan saran aplikasi berdasarkan rutinitas Anda"</string>
     <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Tekan lama pemisah untuk menyematkan Taskbar"</string>
-    <string name="taskbar_edu_features" msgid="3320337287472848162">"Lakukan lebih banyak dengan Taskbar"</string>
+    <string name="taskbar_edu_features" msgid="3320337287472848162">"Lakukan lebih banyak hal dengan Taskbar"</string>
     <string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Selalu tampilkan Taskbar"</string>
     <string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"Untuk selalu menampilkan Taskbar di bagian bawah layar Anda, sentuh &amp; tahan pembatasnya"</string>
     <string name="taskbar_edu_close" msgid="887022990168191073">"Tutup"</string>
@@ -134,7 +134,7 @@
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Pemisah Taskbar"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pindahkan ke atas/kiri"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pindahkan ke bawah/kanan"</string>
-    <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Tampilkan # aplikasi lain.}other{Tampilkan # aplikasi lain.}}"</string>
+    <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Tampilkan # aplikasi lainnya.}other{Tampilkan # aplikasi lainnya.}}"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dan <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
     <string name="desktop_select_app_toast" msgid="2306057322833956910">"Menambahkan aplikasi ke Desktop"</string>
     <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Batalkan"</string>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 903534a..6dd4d52 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -112,7 +112,7 @@
     <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"ಒಂದೇ ಬಾರಿಗೆ 2 ಆ್ಯಪ್‌ಗಳನ್ನು ಬಳಸಲು ಆ್ಯಪ್ ಅನ್ನು ಬದಿಗೆ ಎಳೆಯಿರಿ"</string>
     <string name="taskbar_edu_stashing" msgid="5645461372669217294">"ಟಾಸ್ಕ್‌ಬಾರ್ ಕಾಣುವಂತೆ ಮಾಡಲು ನಿಧಾನವಾಗಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"ನಿಮ್ಮ ದಿನಚರಿಯ ಆಧಾರದ ಮೇಲೆ ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಪಡೆಯಿರಿ"</string>
-    <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ಟಾಸ್ಕ್ ಬಾರ್ ಅನ್ನು ಪಿನ್ ಮಾಡಲು ಡಿವೈಡರ್ ಮೇಲೆ ದೀರ್ಘಕಾಲ ಒತ್ತಿರಿ"</string>
+    <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ಟಾಸ್ಕ್‌‌ಬಾರ್ ಅನ್ನು ಪಿನ್ ಮಾಡಲು ಡಿವೈಡರ್ ಮೇಲೆ ದೀರ್ಘಕಾಲ ಒತ್ತಿರಿ"</string>
     <string name="taskbar_edu_features" msgid="3320337287472848162">"ಟಾಸ್ಕ್‌ಬಾರ್ ಮೂಲಕ ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string>
     <string name="taskbar_edu_pinning_title" msgid="210102174154211712">"ಯಾವಾಗಲೂ ಟಾಸ್ಕ್‌ಬಾರ್ ಅನ್ನು ತೋರಿಸಿ"</string>
     <string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"ಯಾವಾಗಲೂ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ನ ಕೆಳಭಾಗದಲ್ಲಿ ಟಾಸ್ಕ್ ಬಾರ್ ಅನ್ನು ತೋರಿಸಲು, ಡಿವೈಡರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಿ"</string>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 58235bf..5fa61fb 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"Бөлүшүү"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
     <string name="action_split" msgid="2098009717623550676">"Бөлүү"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"Эки колдонмону бир маалда пайдаланууну сактоо"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"Колдонмолорду сактап коюу"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Экранды бөлүү үчүн башка колдонмону таптап коюңуз"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Экранды бөлүү үчүн башка колдонмону тандаңыз"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Жокко чыгаруу"</b></string>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index b4c9569..4a3e62b 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"Сподели"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Слика од екранот"</string>
     <string name="action_split" msgid="2098009717623550676">"Раздели"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"Зачувај пар аплик."</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"Зачувај го паров"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Допрете друга аплик. за да користите поделен екран"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Изберете друга апликација за да користите поделен екран"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Откажи"</b></string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 747c96b..06ed2c5 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"မျှဝေရန်"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
     <string name="action_split" msgid="2098009717623550676">"ခွဲထုတ်ရန်"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"အက်ပ်တွဲချိတ်ခြင်း သိမ်းရန်"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"အက်ပ်အတွဲ သိမ်းရန်"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"မျက်နှာပြင် ခွဲ၍ပြသရန် အက်ပ်နောက်တစ်ခုကို တို့ပါ"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးရန် နောက်အက်ပ်တစ်ခုရွေးပါ"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"မလုပ်တော့"</b></string>
@@ -129,7 +129,7 @@
     <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar ပြထားသည်"</string>
     <string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar ဖျောက်ထားသည်"</string>
     <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"လမ်းညွှန်ဘား"</string>
-    <string name="always_show_taskbar" msgid="3608801276107751229">"Taskbar အမြဲပြပါ"</string>
+    <string name="always_show_taskbar" msgid="3608801276107751229">"Taskbar အမြဲပြရန်"</string>
     <string name="change_navigation_mode" msgid="9088393078736808968">"ရွှေ့ကြည့်သည့်မုဒ် ပြောင်းရန်"</string>
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"လုပ်ဆောင်စရာဘား ပိုင်းခြားစနစ်"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 28f0bbe..fb5ebb3 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -45,7 +45,7 @@
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"सिफारिस गरिएका एपहरू देखाउने सुविधा असक्षम पारिएको छ"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"पूर्वानुमान गरिएको एप: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"आफ्नो डिभाइस रोटेट गर्नुहोस्"</string>
-    <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"इसारामार्फत गरिने नेभिगेसनको ट्युटोरियल पूरा गर्न कृपया आफ्नो डिभाइस रोटेट गर्नुहोस्"</string>
+    <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"जेस्चर नेभिगेसनको ट्युटोरियल पूरा गर्न कृपया आफ्नो डिभाइस रोटेट गर्नुहोस्"</string>
     <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"स्क्रिनको सबैभन्दा दायाँ किनारा वा सबैभन्दा बायाँ किनाराबाट स्वाइप गर्नुहोस्"</string>
     <string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"स्क्रिनको दायाँ वा बायाँ किनाराबाट मध्य भागसम्म स्वाइप गर्नुहोस् अनि औँला उठाउनुहोस्"</string>
     <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"तपाईंले स्क्रिनको दायाँ किनाराबाट स्वाइप गरेर अघिल्लो स्क्रिनमा फर्कने तरिका सिक्नुभयो। अब एउटा एपबाट अर्को एपमा जाने तरिका सिक्नुहोस्।"</string>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 999da4e..74543a5 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -97,7 +97,7 @@
     <string name="action_split" msgid="2098009717623550676">"Splitsen"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"App-paar opslaan"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Tik op nog een app om je scherm te splitsen"</string>
-    <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Kies andere app om gesplitst scherm te gebruiken"</string>
+    <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Kies een andere app om gesplitst scherm te gebruiken"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Annuleren"</b></string>
     <string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Sluit de selectie voor gesplitst scherm"</string>
     <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Kies andere app om gesplitst scherm te gebruiken"</string>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index b7af90d..ce13890 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -36,7 +36,7 @@
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Aceda facilmente às suas apps mais utilizadas, diretamente no ecrã principal. As sugestões mudam em função das suas rotinas. As apps na última fila passam para o ecrã principal."</string>
     <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Aceda facilmente às suas apps mais utilizadas no ecrã principal. As sugestões mudam em função das suas rotinas. As apps na fila dos favoritos passam para o ecrã principal."</string>
     <string name="hotseat_edu_accept" msgid="1611544083278999837">"Obter sugestões de apps"</string>
-    <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Não, obrigado"</string>
+    <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Não"</string>
     <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Definições"</string>
     <string name="hotseat_auto_enrolled" msgid="522100018967146807">"As apps mais utilizadas aparecem aqui e mudam em função das rotinas."</string>
     <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Arraste as apps para fora da última fila para ver sugestões de apps."</string>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index c4ea90d..4e1a213 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"Поделиться"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
     <string name="action_split" msgid="2098009717623550676">"Разделить"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"Сохр. одновр. исп."</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"Сохранить приложения"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Для разделения экрана выберите другое приложение."</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Чтобы использовать разделенный экран, выберите другое приложение."</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Отмена"</b></string>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 716e043..822d25c 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"Дели"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Снимак екрана"</string>
     <string name="action_split" msgid="2098009717623550676">"Подели"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"Чувај пар апликација"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"Сачувај пар апликација"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Додирните другу апликацију за подељени екран"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Одаберите другу апликацију да бисте користили подељени екран"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Откажи"</b></string>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 290ef9c..0464246 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"Dela"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skärmbild"</string>
     <string name="action_split" msgid="2098009717623550676">"Delat"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"Spara par av appar"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"Spara app-par"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Tryck på en annan app för att använda delad skärm"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Välj en annan app för att använda delad skärm"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Avbryt"</b></string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 3f6cc2c..fea79bb 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string>
     <string name="action_split" msgid="2098009717623550676">"拆分"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"保存应用对"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"保存应用组合"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"点按另一个应用即可使用分屏"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"另外选择一个应用才可使用分屏模式"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"取消"</b></string>
@@ -110,7 +110,7 @@
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"旋转屏幕"</string>
     <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"任务栏教程"</string>
     <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"将一个应用拖到一侧,即可同时使用两个应用"</string>
-    <string name="taskbar_edu_stashing" msgid="5645461372669217294">"缓慢向上滑动即可显示任务栏"</string>
+    <string name="taskbar_edu_stashing" msgid="5645461372669217294">"缓慢上滑即可显示任务栏"</string>
     <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"根据您的日常使用习惯获得应用建议"</string>
     <string name="taskbar_edu_pinning" msgid="6708550858580071558">"长按分隔线即可固定任务栏"</string>
     <string name="taskbar_edu_features" msgid="3320337287472848162">"体验任务栏的更多功能"</string>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 050c47c..c1c78ab 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -95,7 +95,7 @@
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string>
     <string name="action_split" msgid="2098009717623550676">"分割"</string>
-    <string name="action_save_app_pair" msgid="5974823919237645229">"儲存應用程式配對"</string>
+    <string name="action_save_app_pair" msgid="5974823919237645229">"儲存應用程式組合"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"輕按其他應用程式以使用分割螢幕"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"選擇其他應用程式才能使用分割螢幕"</string>
     <string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"取消"</b></string>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index c7d79c1..2e89466 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -134,7 +134,7 @@
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"工作列分隔線"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
-    <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{顯示另外 # 個應用程式。}other{顯示另外 # 個應用程式。}}"</string>
+    <string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{再多顯示 # 個應用程式。}other{再多顯示 # 個應用程式。}}"</string>
     <string name="quick_switch_split_task" msgid="5598194724255333896">"「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」和「<xliff:g id="APP_NAME_2">%2$s</xliff:g>」"</string>
     <string name="desktop_select_app_toast" msgid="2306057322833956910">"新增應用程式至桌面"</string>
     <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"取消"</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 93ef735..caa949e 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -417,6 +417,7 @@
     <!-- Container size with pointer included: bubblebar_size + bubblebar_pointer_size -->
     <dimen name="bubblebar_size_with_pointer">80dp</dimen>
     <dimen name="bubblebar_elevation">1dp</dimen>
+    <dimen name="bubblebar_drag_elevation">2dp</dimen>
     <dimen name="bubblebar_hotseat_adjustment_threshold">90dp</dimen>
 
     <dimen name="bubblebar_icon_size">50dp</dimen>
@@ -432,6 +433,11 @@
     <dimen name="bubblebar_dismiss_target_icon_size">24dp</dimen>
     <dimen name="bubblebar_dismiss_target_bottom_margin">50dp</dimen>
     <dimen name="bubblebar_dismiss_floating_gradient_height">548dp</dimen>
+    <dimen name="bubblebar_dismiss_zone_width">192dp</dimen>
+    <dimen name="bubblebar_dismiss_zone_height">242dp</dimen>
+
+    <!-- Bubble bar drop target -->
+    <dimen name="bubblebar_drop_target_corner_radius">36dp</dimen>
 
     <!-- Launcher splash screen -->
     <!-- Note: keep this value in sync with the WindowManager/Shell dimens.xml -->
diff --git a/quickstep/src/com/android/launcher3/HomeTransitionController.java b/quickstep/src/com/android/launcher3/HomeTransitionController.java
deleted file mode 100644
index 2b50283..0000000
--- a/quickstep/src/com/android/launcher3/HomeTransitionController.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.quickstep.SystemUiProxy;
-import com.android.wm.shell.transition.IHomeTransitionListener;
-
-/**
- * Controls launcher response to home activity visibility changing.
- */
-public class HomeTransitionController {
-
-    @Nullable private QuickstepLauncher mLauncher;
-    @Nullable private IHomeTransitionListener mHomeTransitionListener;
-
-    public void registerHomeTransitionListener(QuickstepLauncher launcher) {
-        mLauncher = launcher;
-        mHomeTransitionListener = new IHomeTransitionListener.Stub() {
-            @Override
-            public void onHomeVisibilityChanged(boolean isVisible) {
-                MAIN_EXECUTOR.execute(() -> {
-                    if (mLauncher != null && mLauncher.getTaskbarUIController() != null) {
-                        mLauncher.getTaskbarUIController().onLauncherVisibilityChanged(isVisible);
-                    }
-                });
-            }
-        };
-
-        SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(mHomeTransitionListener);
-    }
-
-    public void unregisterHomeTransitionListener() {
-        SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(null);
-        mHomeTransitionListener = null;
-        mLauncher = null;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index a931f36..a621259 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -41,6 +41,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP;
 import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
 import static com.android.launcher3.model.PredictionHelper.isTrackedForWidgetPrediction;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -184,6 +185,10 @@
             sendEvent(target, atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);
         } else if (event == LAUNCHER_DISMISS_PREDICTION_UNDO) {
             sendEvent(atomInfo, ACTION_UNDISMISS, CONTAINER_HOTSEAT_PREDICTION);
+        } else if (event == LAUNCHER_WIDGET_ADD_BUTTON_TAP) {
+            if (isTrackedForWidgetPrediction(atomInfo)) {
+                sendEvent(atomInfo, ACTION_PIN, CONTAINER_WIDGETS_PREDICTION);
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 0ce1cb8..c0e0587 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -18,8 +18,8 @@
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.formatElapsedTime;
 
-import static com.android.launcher3.LauncherPrefs.nonRestorableItem;
 import static com.android.launcher3.EncryptionType.ENCRYPTED;
+import static com.android.launcher3.LauncherPrefs.nonRestorableItem;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
@@ -65,7 +65,7 @@
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.UserCache;
@@ -233,7 +233,7 @@
             }
             InstanceId instanceId = new InstanceIdSequence().newInstanceId();
             for (ItemInfo info : itemsIdMap) {
-                FolderInfo parent = getContainer(info, itemsIdMap);
+                CollectionInfo parent = getContainer(info, itemsIdMap);
                 StatsLogCompatManager.writeSnapshot(info.buildProto(parent), instanceId);
             }
             additionalSnapshotEvents(instanceId);
@@ -270,7 +270,7 @@
                         }
 
                         for (ItemInfo info : itemsIdMap) {
-                            FolderInfo parent = getContainer(info, itemsIdMap);
+                            CollectionInfo parent = getContainer(info, itemsIdMap);
                             LauncherAtom.ItemInfo itemInfo = info.buildProto(parent);
                             Log.d(TAG, itemInfo.toString());
                             StatsEvent statsEvent = StatsLogCompatManager.buildStatsEvent(itemInfo,
@@ -293,18 +293,19 @@
         }
     }
 
-    private static FolderInfo getContainer(ItemInfo info, IntSparseArrayMap<ItemInfo> itemsIdMap) {
+    private static CollectionInfo getContainer(
+            ItemInfo info, IntSparseArrayMap<ItemInfo> itemsIdMap) {
         if (info.container > 0) {
             ItemInfo containerInfo = itemsIdMap.get(info.container);
 
-            if (!(containerInfo instanceof FolderInfo)) {
+            if (!(containerInfo instanceof CollectionInfo)) {
                 Log.e(TAG, String.format(
                         "Item info: %s found with invalid container: %s",
                         info,
                         containerInfo));
             }
             // Allow crash to help debug b/173838775
-            return (FolderInfo) containerInfo;
+            return (CollectionInfo) containerInfo;
         }
         return null;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index 5b407f0..48fc7d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -23,6 +23,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
@@ -174,21 +175,23 @@
             return;
         }
         if (updateFunction == null) {
-            applyThumbnail(thumbnailView, task.thumbnail);
+            applyThumbnail(thumbnailView, task.colorBackground, task.thumbnail);
             return;
         }
-        updateFunction.updateThumbnailInBackground(
-                task, thumbnailData -> applyThumbnail(thumbnailView, thumbnailData));
+        updateFunction.updateThumbnailInBackground(task, thumbnailData ->
+                applyThumbnail(thumbnailView, task.colorBackground, thumbnailData));
     }
 
     private void applyThumbnail(
             @NonNull ImageView thumbnailView,
-            ThumbnailData thumbnailData) {
+            @ColorInt int backgroundColor,
+            @Nullable ThumbnailData thumbnailData) {
         Bitmap bm = thumbnailData == null ? null : thumbnailData.thumbnail;
 
         if (thumbnailView.getVisibility() != VISIBLE) {
             thumbnailView.setVisibility(VISIBLE);
         }
+        thumbnailView.getBackground().setTint(bm == null ? backgroundColor : Color.TRANSPARENT);
         thumbnailView.setImageDrawable(new BlurredBitmapDrawable(bm, THUMBNAIL_BLUR_RADIUS));
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index a59aead..def6287 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -35,6 +35,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
@@ -49,8 +50,10 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.OnboardingPrefs;
+import com.android.quickstep.HomeVisibilityState;
 import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.util.TISBindHelper;
 import com.android.quickstep.views.RecentsView;
@@ -79,6 +82,7 @@
                     AnimatedFloat.VALUE, DISPLAY_PROGRESS_COUNT, Float::max);
 
     private final QuickstepLauncher mLauncher;
+    private final HomeVisibilityState mHomeState;
 
     private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
             dp -> {
@@ -87,6 +91,8 @@
                     mControllers.taskbarViewController.onRotationChanged(dp);
                 }
             };
+    private final HomeVisibilityState.VisibilityChangeListener  mVisibilityChangeListener =
+            this::onLauncherVisibilityChanged;
 
     // Initialized in init.
     private final TaskbarLauncherStateController
@@ -94,6 +100,7 @@
 
     public LauncherTaskbarUIController(QuickstepLauncher launcher) {
         mLauncher = launcher;
+        mHomeState =  SystemUiProxy.INSTANCE.get(mLauncher).getHomeVisibilityState();
     }
 
     @Override
@@ -104,8 +111,11 @@
                 mControllers.getSharedState().sysuiStateFlags);
 
         mLauncher.setTaskbarUIController(this);
-
-        onLauncherVisibilityChanged(mLauncher.hasBeenResumed(), true /* fromInit */);
+        mHomeState.addListener(mVisibilityChangeListener);
+        onLauncherVisibilityChanged(
+                Flags.useActivityOverlay()
+                        ? mHomeState.isHomeVisible() : mLauncher.hasBeenResumed(),
+                true /* fromInit */);
 
         onStashedInAppChanged(mLauncher.getDeviceProfile());
         mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
@@ -129,6 +139,7 @@
 
         mLauncher.setTaskbarUIController(null);
         mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+        mHomeState.removeListener(mVisibilityChangeListener);
         updateTaskTransitionSpec(true);
     }
 
@@ -234,7 +245,8 @@
 
     @Override
     public void refreshResumedState() {
-        onLauncherVisibilityChanged(mLauncher.hasBeenResumed());
+        onLauncherVisibilityChanged(Flags.useActivityOverlay()
+                ? mHomeState.isHomeVisible() : mLauncher.hasBeenResumed());
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 26212c1..8769f11 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -89,6 +89,7 @@
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.AppPairInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -98,6 +99,7 @@
 import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
 import com.android.launcher3.taskbar.bubbles.BubbleBarController;
+import com.android.launcher3.taskbar.bubbles.BubbleBarPinController;
 import com.android.launcher3.taskbar.bubbles.BubbleBarView;
 import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
 import com.android.launcher3.taskbar.bubbles.BubbleControllers;
@@ -254,7 +256,10 @@
                     new BubbleStashController(this),
                     new BubbleStashedHandleViewController(this, bubbleHandleView),
                     new BubbleDragController(this),
-                    new BubbleDismissController(this, mDragLayer)));
+                    new BubbleDismissController(this, mDragLayer),
+                    new BubbleBarPinController(this, mDragLayer,
+                            () -> getDeviceProfile().getDisplayInfo().currentSize)
+            ));
         }
 
         // Construct controllers.
@@ -1082,19 +1087,19 @@
             ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
                     ActivityOptions.makeBasic());
             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
-        } else if (tag instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_FOLDER) {
+        } else if (tag instanceof FolderInfo) {
             // Tapping an expandable folder icon on Taskbar
             shouldCloseAllOpenViews = false;
             expandFolder((FolderIcon) view);
-        } else if (tag instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_APP_PAIR) {
+        } else if (tag instanceof AppPairInfo api) {
             // Tapping an app pair icon on Taskbar
             if (recents != null && recents.isSplitSelectionActive()) {
                 Toast.makeText(this, "Unable to split with an app pair. Select another app.",
                         Toast.LENGTH_SHORT).show();
             } else {
                 // Else launch the selected app pair
-                launchFromTaskbar(recents, view, fi.contents);
-                mControllers.uiController.onTaskbarIconLaunched(fi);
+                launchFromTaskbar(recents, view, api.getContents());
+                mControllers.uiController.onTaskbarIconLaunched(api);
                 mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
             }
         } else if (tag instanceof WorkspaceItemInfo) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index a14e3fd..1a120da 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -205,13 +205,7 @@
                     mLauncherState = finalState;
                     updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, false);
                     applyState();
-                    boolean disallowLongClick =
-                            FeatureFlags.enableSplitContextually()
-                                    ? mLauncher.isSplitSelectionActive()
-                                    : finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
-                    com.android.launcher3.taskbar.Utilities.setOverviewDragState(
-                            mControllers, finalState.disallowTaskbarGlobalDrag(),
-                            disallowLongClick, finalState.allowTaskbarInitialSplitSelection());
+                    updateOverviewDragState(finalState);
                 }
             };
 
@@ -256,6 +250,7 @@
 
         mCanSyncViews = true;
         mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+        updateOverviewDragState(mLauncherState);
     }
 
     public void onDestroy() {
@@ -328,7 +323,7 @@
         updateStateForSysuiFlags(systemUiStateFlags, /* applyState */ true);
     }
 
-    private  void updateStateForSysuiFlags(int systemUiStateFlags, boolean applyState) {
+    private void updateStateForSysuiFlags(int systemUiStateFlags, boolean applyState) {
         final boolean prevIsAwake = hasAnyFlag(FLAG_AWAKE);
         final boolean currIsAwake = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_AWAKE);
 
@@ -358,6 +353,21 @@
     }
 
     /**
+     * Updates overview drag state on various controllers based on {@link #mLauncherState}.
+     *
+     * @param launcherState The current state launcher is in
+     */
+    private void updateOverviewDragState(LauncherState launcherState) {
+        boolean disallowLongClick =
+                FeatureFlags.enableSplitContextually()
+                        ? mLauncher.isSplitSelectionActive()
+                        : launcherState == LauncherState.OVERVIEW_SPLIT_SELECT;
+        com.android.launcher3.taskbar.Utilities.setOverviewDragState(
+                mControllers, launcherState.disallowTaskbarGlobalDrag(),
+                disallowLongClick, launcherState.allowTaskbarInitialSplitSelection());
+    }
+
+    /**
      * Updates the proper flag to change the state of the task bar.
      *
      * Note that this only updates the flag. {@link #applyState()} needs to be called separately.
@@ -646,7 +656,8 @@
      * Returns if the current Launcher state has hotseat on top of other elemnets.
      */
     public boolean isInHotseatOnTopStates() {
-        return mLauncherState != LauncherState.ALL_APPS;
+        return mLauncherState != LauncherState.ALL_APPS
+                && !mLauncher.getWorkspace().isOverlayShown();
     }
 
     boolean isInOverview() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index ca192c8..4462f20 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -116,9 +116,9 @@
                 }
             } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
                 FolderInfo fi = (FolderInfo) info;
-                if (fi.contents.stream().anyMatch(matcher)) {
+                if (fi.anyMatch(matcher)) {
                     FolderDotInfo folderDotInfo = new FolderDotInfo();
-                    for (WorkspaceItemInfo si : fi.contents) {
+                    for (WorkspaceItemInfo si : fi.getContents()) {
                         folderDotInfo.addDotInfo(mPopupDataProvider.getDotInfoForItem(si));
                     }
                     ((FolderIcon) v).setDotInfo(folderDotInfo);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 3d58464..ba8a074 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -598,7 +598,8 @@
                             ? stashTranslation : 0)
                     .setDuration(duration));
             mAnimator.play(mTaskbarImeBgAlpha.animateToValue(
-                    hasAnyFlag(FLAG_STASHED_IN_APP_IME) ? 0 : 1).setDuration(duration));
+                    (hasAnyFlag(FLAG_STASHED_IN_APP_IME) && isStashed) ? 0 : 1).setDuration(
+                    duration));
             mAnimator.addListener(AnimatorListeners.forEndCallback(() -> {
                 mAnimator = null;
                 mIsStashed = isStashed;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 367bf6c..dc2684e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -18,6 +18,7 @@
 import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
 
+import static com.android.launcher3.BubbleTextView.DISPLAY_TASKBAR;
 import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
@@ -52,6 +53,8 @@
 import com.android.launcher3.apppairs.AppPairIcon;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.PreviewBackground;
+import com.android.launcher3.model.data.AppPairInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -270,7 +273,7 @@
             mAllAppsButton.setHapticFeedbackEnabled(
                     mControllerCallbacks.isAllAppsButtonHapticFeedbackEnabled());
         }
-        if (mTaskbarDivider != null) {
+        if (mTaskbarDivider != null && !mActivityContext.isThreeButtonNav()) {
             mTaskbarDivider.setOnLongClickListener(
                     mControllerCallbacks.getTaskbarDividerLongClickListener());
             mTaskbarDivider.setOnTouchListener(
@@ -282,7 +285,7 @@
         removeView(view);
         view.setOnClickListener(null);
         view.setOnLongClickListener(null);
-        if (!(view.getTag() instanceof FolderInfo)) {
+        if (!(view.getTag() instanceof CollectionInfo)) {
             mActivityContext.getViewCache().recycleView(view.getSourceLayoutResId(), view);
         }
         view.setTag(null);
@@ -316,8 +319,8 @@
             boolean isCollection = false;
             if (hotseatItemInfo.isPredictedItem()) {
                 expectedLayoutResId = R.layout.taskbar_predicted_app_icon;
-            } else if (hotseatItemInfo instanceof FolderInfo fi) {
-                expectedLayoutResId = fi.itemType == ITEM_TYPE_APP_PAIR
+            } else if (hotseatItemInfo instanceof CollectionInfo ci) {
+                expectedLayoutResId = ci.itemType == ITEM_TYPE_APP_PAIR
                         ? R.layout.app_pair_icon
                         : R.layout.folder_icon;
                 isCollection = true;
@@ -345,16 +348,18 @@
 
             if (hotseatView == null) {
                 if (isCollection) {
-                    FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
+                    CollectionInfo collectionInfo = (CollectionInfo) hotseatItemInfo;
                     switch (hotseatItemInfo.itemType) {
                         case ITEM_TYPE_FOLDER:
                             hotseatView = FolderIcon.inflateFolderAndIcon(
-                                    expectedLayoutResId, mActivityContext, this, folderInfo);
+                                    expectedLayoutResId, mActivityContext, this,
+                                    (FolderInfo) collectionInfo);
                             ((FolderIcon) hotseatView).setTextVisible(false);
                             break;
                         case ITEM_TYPE_APP_PAIR:
                             hotseatView = AppPairIcon.inflateIcon(
-                                    expectedLayoutResId, mActivityContext, this, folderInfo);
+                                    expectedLayoutResId, mActivityContext, this,
+                                    (AppPairInfo) collectionInfo, DISPLAY_TASKBAR);
                             ((AppPairIcon) hotseatView).setTextVisible(false);
                             break;
                         default:
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 4b1963b..1f7f0a7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -813,8 +813,8 @@
      * 3) All Apps button
      */
     public View getFirstIconMatch(Predicate<ItemInfo> matcher) {
-        Predicate<ItemInfo> folderMatcher = ItemInfoMatcher.forFolderMatch(matcher);
-        return mTaskbarView.getFirstMatch(matcher, folderMatcher);
+        Predicate<ItemInfo> collectionMatcher = ItemInfoMatcher.forFolderMatch(matcher);
+        return mTaskbarView.getFirstMatch(matcher, collectionMatcher);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index e0e78f9..3b04602 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -35,7 +35,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsRecyclerView;
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.config.FeatureFlags;
@@ -212,11 +211,6 @@
         super.onScaleProgressChanged();
         mAppsView.setClipChildren(!mIsBackProgressing);
         mAppsView.getAppsRecyclerViewContainer().setClipChildren(!mIsBackProgressing);
-        AllAppsRecyclerView rv = mAppsView.getActiveRecyclerView();
-        if (rv != null && rv.getScrollbar() != null) {
-            rv.getScrollbar().setVisibility(
-                    mIsBackProgressing ? INVISIBLE : VISIBLE);
-        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
index 79fdeda..8eeb055 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.taskbar.bubbles
 
+import android.content.Context
 import android.graphics.Canvas
 import android.graphics.Color
 import android.graphics.ColorFilter
@@ -27,12 +28,10 @@
 import com.android.launcher3.Utilities
 import com.android.launcher3.Utilities.mapToRange
 import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
-import com.android.launcher3.taskbar.TaskbarActivityContext
 import com.android.wm.shell.common.TriangleShape
 
 /** Drawable for the background of the bubble bar. */
-class BubbleBarBackground(context: TaskbarActivityContext, private val backgroundHeight: Float) :
-    Drawable() {
+class BubbleBarBackground(context: Context, private val backgroundHeight: Float) : Drawable() {
 
     private val DARK_THEME_SHADOW_ALPHA = 51f
     private val LIGHT_THEME_SHADOW_ALPHA = 25f
@@ -46,6 +45,7 @@
 
     var arrowPositionX: Float = 0f
         private set
+
     private var showingArrow: Boolean = false
     private var arrowDrawable: ShapeDrawable
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 1f3c483..c1c76d0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -220,7 +220,7 @@
             mBubbleStashedHandleViewController.setHiddenForBubbles(
                     !sBubbleBarEnabled || mBubbles.isEmpty());
             mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse(
-                    key -> setSelectedBubble(mBubbles.get(key)));
+                    key -> setSelectedBubbleInternal(mBubbles.get(key)));
         });
     }
 
@@ -390,7 +390,7 @@
             }
         }
         if (bubbleToSelect != null) {
-            setSelectedBubble(bubbleToSelect);
+            setSelectedBubbleInternal(bubbleToSelect);
             if (previouslySelectedBubble == null) {
                 mBubbleStashController.animateToInitialState(update.expanded);
             }
@@ -409,8 +409,7 @@
             if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) {
                 // Animate when receiving updates. Skip it if we received the initial state.
                 boolean animate = !update.initialState;
-                mBubbleBarViewController.setBubbleBarLocation(update.bubbleBarLocation, animate);
-                mBubbleStashController.setBubbleBarLocation(update.bubbleBarLocation);
+                updateBubbleBarLocationInternal(update.bubbleBarLocation, animate);
             }
         }
     }
@@ -436,7 +435,7 @@
     /** Updates the currently selected bubble for launcher views and tells WMShell to show it. */
     public void showAndSelectBubble(BubbleBarItem b) {
         if (DEBUG) Log.w(TAG, "showingSelectedBubble: " + b.getKey());
-        setSelectedBubble(b);
+        setSelectedBubbleInternal(b);
         showSelectedBubble();
     }
 
@@ -445,7 +444,7 @@
      * WMShell that the selection has changed, that should go through either
      * {@link #showSelectedBubble()} or {@link #showAndSelectBubble(BubbleBarItem)}.
      */
-    private void setSelectedBubble(BubbleBarItem b) {
+    private void setSelectedBubbleInternal(BubbleBarItem b) {
         if (!Objects.equals(b, mSelectedBubble)) {
             if (DEBUG) Log.w(TAG, "selectingBubble: " + b.getKey());
             mSelectedBubble = b;
@@ -464,6 +463,21 @@
         return null;
     }
 
+    /**
+     * Set a new bubble bar location.
+     * <p>
+     * Updates the value locally in Launcher and in WMShell.
+     */
+    public void updateBubbleBarLocation(BubbleBarLocation location) {
+        updateBubbleBarLocationInternal(location, false /* animate */);
+        mSystemUiProxy.setBubbleBarLocation(location);
+    }
+
+    private void updateBubbleBarLocationInternal(BubbleBarLocation location, boolean animate) {
+        mBubbleBarViewController.setBubbleBarLocation(location, animate);
+        mBubbleStashController.setBubbleBarLocation(location);
+    }
+
     //
     // Loading data for the bubbles
     //
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
new file mode 100644
index 0000000..8ed9949
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.taskbar.bubbles
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Point
+import android.graphics.RectF
+import android.view.Gravity.BOTTOM
+import android.view.Gravity.LEFT
+import android.view.Gravity.RIGHT
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import androidx.core.view.updateLayoutParams
+import com.android.launcher3.R
+import com.android.wm.shell.common.bubbles.BaseBubblePinController
+import com.android.wm.shell.common.bubbles.BubbleBarLocation
+
+/**
+ * Controller to manage pinning bubble bar to left or right when dragging starts from the bubble bar
+ */
+class BubbleBarPinController(
+    private val context: Context,
+    private val container: FrameLayout,
+    private val screenSizeProvider: () -> Point
+) : BaseBubblePinController() {
+
+    private lateinit var bubbleBarViewController: BubbleBarViewController
+    private lateinit var bubbleStashController: BubbleStashController
+    private var dropTargetView: View? = null
+
+    fun init(bubbleControllers: BubbleControllers) {
+        bubbleBarViewController = bubbleControllers.bubbleBarViewController
+        bubbleStashController = bubbleControllers.bubbleStashController
+    }
+
+    override fun getScreenCenterX(): Int {
+        return screenSizeProvider.invoke().x / 2
+    }
+
+    override fun getExclusionRect(): RectF {
+        val rect =
+            RectF(
+                0f,
+                0f,
+                context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_width),
+                context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_height)
+            )
+        val screenSize = screenSizeProvider.invoke()
+        val middleX = screenSize.x / 2
+        // Center it around the bottom center of the screen
+        rect.offsetTo(middleX - rect.width() / 2, screenSize.y - rect.height())
+        return rect
+    }
+
+    override fun createDropTargetView(): View {
+        return LayoutInflater.from(context)
+            .inflate(R.layout.bubble_bar_drop_target, container, false)
+            .also { view ->
+                dropTargetView = view
+                container.addView(view)
+            }
+    }
+
+    override fun getDropTargetView(): View? {
+        return dropTargetView
+    }
+
+    override fun removeDropTargetView(view: View) {
+        container.removeView(view)
+        dropTargetView = null
+    }
+
+    @SuppressLint("RtlHardcoded")
+    override fun updateLocation(location: BubbleBarLocation) {
+        val onLeft = location.isOnLeft(container.isLayoutRtl)
+
+        val bounds = bubbleBarViewController.bubbleBarBounds
+        val horizontalMargin = bubbleBarViewController.horizontalMargin
+        dropTargetView?.updateLayoutParams<FrameLayout.LayoutParams> {
+            width = bounds.width()
+            height = bounds.height()
+            gravity = BOTTOM or (if (onLeft) LEFT else RIGHT)
+            leftMargin = horizontalMargin
+            rightMargin = horizontalMargin
+            bottomMargin = -bubbleStashController.bubbleBarTranslationY.toInt()
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index a5da65f..c27e9f1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -24,7 +24,9 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.content.Context;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.LayoutDirection;
@@ -39,8 +41,6 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.launcher3.views.ActivityContext;
 import com.android.wm.shell.common.bubbles.BubbleBarLocation;
 
 import java.util.List;
@@ -110,6 +110,7 @@
     private final float mIconSize;
     // The elevation of the bubbles within the bar
     private final float mBubbleElevation;
+    private final float mDragElevation;
     private final int mPointerSize;
 
     // Whether the bar is expanded (i.e. the bubble activity is being displayed).
@@ -140,11 +141,15 @@
     @Nullable
     private Consumer<String> mUpdateSelectedBubbleAfterCollapse;
 
+    private boolean mDragging;
+
     @Nullable
     private BubbleView mDraggedBubbleView;
 
     private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
 
+    private boolean mLocationChangePending;
+
     public BubbleBarView(Context context) {
         this(context, null);
     }
@@ -159,19 +164,18 @@
 
     public BubbleBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        TaskbarActivityContext activityContext = ActivityContext.lookupContext(context);
-
         setAlpha(0);
         setVisibility(INVISIBLE);
         mIconOverlapAmount = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_overlap);
         mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
         mIconSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
         mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation);
+        mDragElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_drag_elevation);
         mPointerSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_pointer_size);
 
         setClipToPadding(false);
 
-        mBubbleBarBackground = new BubbleBarBackground(activityContext,
+        mBubbleBarBackground = new BubbleBarBackground(context,
                 getResources().getDimensionPixelSize(R.dimen.bubblebar_size));
         setBackgroundDrawable(mBubbleBarBackground);
 
@@ -241,12 +245,13 @@
         }
     }
 
+    @SuppressLint("RtlHardcoded")
     private void onBubbleBarLocationChanged() {
+        mLocationChangePending = false;
         final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
         mBubbleBarBackground.setAnchorLeft(onLeft);
         mRelativePivotX = onLeft ? 0f : 1f;
-        ViewGroup.LayoutParams layoutParams = getLayoutParams();
-        if (layoutParams instanceof LayoutParams lp) {
+        if (getLayoutParams() instanceof LayoutParams lp) {
             lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
             setLayoutParams(lp);
         }
@@ -271,11 +276,82 @@
         }
     }
 
+    /**
+     * Set whether this view is being currently being dragged
+     */
+    public void setIsDragging(boolean dragging) {
+        if (mDragging == dragging) {
+            return;
+        }
+        mDragging = dragging;
+        setElevation(dragging ? mDragElevation : mBubbleElevation);
+        if (!dragging && mLocationChangePending) {
+            // During drag finish animation we may update the translation x value to shift the
+            // bubble to the new drop target. Clear the translation here.
+            setTranslationX(0f);
+            onBubbleBarLocationChanged();
+        }
+    }
+
+    /**
+     * Adjust resting position for the bubble bar while it is being dragged.
+     * <p>
+     * Bubble bar is laid out on left or right side of the screen. When it is being dragged to
+     * the opposite side, the resting position should be on that side. Calculate any additional
+     * translation that may be required to move the bubble bar to the new side.
+     *
+     * @param restingPosition relative resting position of the bubble bar from the laid out position
+     */
+    @SuppressLint("RtlHardcoded")
+    void adjustRelativeRestingPosition(PointF restingPosition) {
+        final boolean locationOnLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
+        // Bubble bar is placed left or right with gravity. Check where it is currently.
+        final int absoluteGravity = Gravity.getAbsoluteGravity(
+                ((LayoutParams) getLayoutParams()).gravity, getLayoutDirection());
+        final boolean gravityOnLeft =
+                (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT;
+
+        // Bubble bar is pinned to the same side per gravity and the desired location.
+        // Resting translation does not need to be adjusted.
+        if (locationOnLeft == gravityOnLeft) {
+            return;
+        }
+
+        // Bubble bar is laid out on left or right side of the screen. And the desired new
+        // location is on the other side. Calculate x translation value required to shift the
+        // bubble bar from one side to the other.
+        float x = getDistanceFromOtherSide();
+        if (locationOnLeft) {
+            // New location is on the left, shift left
+            // before -> |......ooo.| after -> |.ooo......|
+            restingPosition.x = -x;
+        } else {
+            // New location is on the right, shift right
+            // before -> |.ooo......| after -> |......ooo.|
+            restingPosition.x = x;
+        }
+    }
+
+    private float getDistanceFromOtherSide() {
+        // Calculate the shift needed to position the bubble bar on the other side
+        int displayWidth = getResources().getDisplayMetrics().widthPixels;
+        int margin = 0;
+        if (getLayoutParams() instanceof MarginLayoutParams lp) {
+            margin += lp.leftMargin;
+            margin += lp.rightMargin;
+        }
+        return (float) (displayWidth - getWidth() - margin);
+    }
+
     private void setBubbleBarLocationInternal(BubbleBarLocation bubbleBarLocation) {
         if (bubbleBarLocation != mBubbleBarLocation) {
             mBubbleBarLocation = bubbleBarLocation;
-            onBubbleBarLocationChanged();
-            invalidate();
+            if (mDragging) {
+                mLocationChangePending = true;
+            } else {
+                onBubbleBarLocationChanged();
+                invalidate();
+            }
         }
     }
 
@@ -379,6 +455,36 @@
         return mRelativePivotY;
     }
 
+    /** Prepares for animating a bubble while being stashed. */
+    public void prepareForAnimatingBubbleWhileStashed(String bubbleKey) {
+        // we're about to animate the new bubble in. the new bubble has already been added to this
+        // view, but we're currently stashed, so before we can start the animation we need make
+        // everything else in the bubble bar invisible, except for the bubble that's being animated.
+        setBackground(null);
+        for (int i = 0; i < getChildCount(); i++) {
+            final BubbleView view = (BubbleView) getChildAt(i);
+            final String key = view.getBubble().getKey();
+            if (!bubbleKey.equals(key)) {
+                view.setVisibility(INVISIBLE);
+            }
+        }
+        setVisibility(VISIBLE);
+        setAlpha(1);
+        setTranslationY(0);
+        setScaleX(1);
+        setScaleY(1);
+    }
+
+    /** Resets the state after the bubble animation completed. */
+    public void onAnimatingBubbleCompleted() {
+        setBackground(mBubbleBarBackground);
+        for (int i = 0; i < getChildCount(); i++) {
+            final BubbleView view = (BubbleView) getChildAt(i);
+            view.setVisibility(VISIBLE);
+            view.setAlpha(1f);
+        }
+    }
+
     // TODO: (b/280605790) animate it
     @Override
     public void addView(View child, int index, ViewGroup.LayoutParams params) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 0f019a3..96d91ea 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.taskbar.TaskbarControllers;
 import com.android.launcher3.taskbar.TaskbarInsetsController;
 import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.SystemUiProxy;
@@ -81,6 +82,8 @@
     private boolean mHiddenForNoBubbles = true;
     private boolean mShouldShowEducation;
 
+    private BubbleBarViewAnimator mBubbleBarViewAnimator;
+
     public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) {
         mActivity = activity;
         mBarView = barView;
@@ -113,6 +116,8 @@
         mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
                 mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
         );
+
+        mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController);
     }
 
     private void onBubbleClicked(View v) {
@@ -316,6 +321,12 @@
                     new FrameLayout.LayoutParams(mIconSize, mIconSize, Gravity.LEFT));
             b.getView().setOnClickListener(mBubbleClickListener);
             mBubbleDragController.setupBubbleView(b.getView());
+
+            boolean isStashedOrGone =
+                    mBubbleStashController.isStashed() || mBarView.getVisibility() != VISIBLE;
+            if (b instanceof BubbleBarBubble && isStashedOrGone) {
+                mBubbleBarViewAnimator.animateBubbleInForStashed((BubbleBarBubble) b);
+            }
         } else {
             Log.w(TAG, "addBubble, bubble was null!");
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
index c47427d..90f1be3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
@@ -29,6 +29,7 @@
     public final BubbleStashedHandleViewController bubbleStashedHandleViewController;
     public final BubbleDragController bubbleDragController;
     public final BubbleDismissController bubbleDismissController;
+    public final BubbleBarPinController bubbleBarPinController;
 
     private final RunnableList mPostInitRunnables = new RunnableList();
 
@@ -43,13 +44,15 @@
             BubbleStashController bubbleStashController,
             BubbleStashedHandleViewController bubbleStashedHandleViewController,
             BubbleDragController bubbleDragController,
-            BubbleDismissController bubbleDismissController) {
+            BubbleDismissController bubbleDismissController,
+            BubbleBarPinController bubbleBarPinController) {
         this.bubbleBarController = bubbleBarController;
         this.bubbleBarViewController = bubbleBarViewController;
         this.bubbleStashController = bubbleStashController;
         this.bubbleStashedHandleViewController = bubbleStashedHandleViewController;
         this.bubbleDragController = bubbleDragController;
         this.bubbleDismissController = bubbleDismissController;
+        this.bubbleBarPinController = bubbleBarPinController;
     }
 
     /**
@@ -64,6 +67,7 @@
         bubbleStashController.init(taskbarControllers, this);
         bubbleDragController.init(/* bubbleControllers = */ this);
         bubbleDismissController.init(/* bubbleControllers = */ this);
+        bubbleBarPinController.init(this);
 
         mPostInitRunnables.executeAllAndDestroy();
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
index 73c71c8..a40f33c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
@@ -67,6 +67,9 @@
     @Nullable
     private BubbleDragAnimator mAnimator;
 
+    @Nullable
+    private Listener mListener;
+
     public BubbleDismissController(TaskbarActivityContext activity, TaskbarDragLayer dragLayer) {
         mActivity = activity;
         mDragLayer = dragLayer;
@@ -82,6 +85,13 @@
     }
 
     /**
+     * Set listener to be notified of dismiss events
+     */
+    public void setListener(@Nullable Listener listener) {
+        mListener = listener;
+    }
+
+    /**
      * Setup the dismiss view and magnetized object that will be attracted to magnetic target.
      * Should be called before handling events or showing/hiding dismiss view.
      *
@@ -189,6 +199,9 @@
                     @NonNull MagnetizedObject<?> draggedObject) {
                 if (mAnimator == null) return;
                 mAnimator.animateDismissCaptured();
+                if (mListener != null) {
+                    mListener.onStuckToDismissChanged(true /* stuck */);
+                }
             }
 
             @Override
@@ -197,6 +210,9 @@
                     float velX, float velY, boolean wasFlungOut) {
                 if (mAnimator == null) return;
                 mAnimator.animateDismissReleased();
+                if (mListener != null) {
+                    mListener.onStuckToDismissChanged(false /* stuck */);
+                }
             }
 
             @Override
@@ -206,4 +222,10 @@
             }
         });
     }
+
+    /** Interface to receive updates about the dismiss state */
+    public interface Listener {
+        /** Called when view is stuck or unstuck from dismiss target */
+        void onStuckToDismissChanged(boolean stuck);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
index 24dca5e..39440ba 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
@@ -30,9 +30,9 @@
 import androidx.dynamicanimation.animation.FloatPropertyCompat;
 
 import com.android.launcher3.R;
-import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.bubbles.DismissCircleView;
 import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.animation.PhysicsAnimator;
 
 /**
  * The animator performs the bubble animations while dragging and coordinates bubble and dismiss
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 08fd681..5ffc6d8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -28,7 +28,7 @@
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 
 /**
- * Controls bubble bar drag to dismiss interaction.
+ * Controls bubble bar drag interactions.
  * Interacts with {@link BubbleDismissController}, used by {@link BubbleBarViewController}.
  * Supported interactions:
  * - Drag a single bubble view into dismiss target to remove it.
@@ -39,6 +39,7 @@
     private final TaskbarActivityContext mActivity;
     private BubbleBarViewController mBubbleBarViewController;
     private BubbleDismissController mBubbleDismissController;
+    private BubbleBarPinController mBubbleBarPinController;
 
     public BubbleDragController(TaskbarActivityContext activity) {
         mActivity = activity;
@@ -52,6 +53,11 @@
     public void init(@NonNull BubbleControllers bubbleControllers) {
         mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
         mBubbleDismissController = bubbleControllers.bubbleDismissController;
+        mBubbleBarPinController = bubbleControllers.bubbleBarPinController;
+        mBubbleBarPinController.setListener(
+                bubbleControllers.bubbleBarController::updateBubbleBarLocation);
+        mBubbleDismissController.setListener(
+                stuck -> mBubbleBarPinController.setDropTargetHidden(stuck));
     }
 
     /**
@@ -89,6 +95,7 @@
     public void setupBubbleBarView(@NonNull BubbleBarView bubbleBarView) {
         PointF initialRelativePivot = new PointF();
         bubbleBarView.setOnTouchListener(new BubbleTouchListener() {
+
             @Override
             protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) {
                 if (bubbleBarView.isExpanded()) return false;
@@ -102,12 +109,38 @@
                 // By default the bubble bar view pivot is in bottom right corner, while dragging
                 // it should be centered in order to align it with the dismiss target view
                 bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f);
+                bubbleBarView.setIsDragging(true);
+                mBubbleBarPinController.onDragStart(
+                        bubbleBarView.getBubbleBarLocation().isOnLeft(bubbleBarView.isLayoutRtl()));
+            }
+
+            @Override
+            protected void onDragUpdate(float x, float y) {
+                mBubbleBarPinController.onDragUpdate(x, y);
+            }
+
+            @Override
+            protected void onDragRelease() {
+                mBubbleBarPinController.onDragEnd();
+            }
+
+            @Override
+            protected void onDragDismiss() {
+                mBubbleBarPinController.onDragEnd();
             }
 
             @Override
             void onDragEnd() {
                 // Restoring the initial pivot for the bubble bar view
                 bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
+                bubbleBarView.setIsDragging(false);
+            }
+
+            @Override
+            protected PointF getRestingPosition() {
+                PointF restingPosition = super.getRestingPosition();
+                bubbleBarView.adjustRelativeRestingPosition(restingPosition);
+                return restingPosition;
             }
         });
     }
@@ -170,6 +203,13 @@
         abstract void onDragStart();
 
         /**
+         * Called when bubble is dragged to new coordinates.
+         * Not called while bubble is stuck to the dismiss target.
+         */
+        protected void onDragUpdate(float x, float y) {
+        }
+
+        /**
          * Called when the dragging interaction has ended and all the animations have completed
          */
         abstract void onDragEnd();
@@ -188,6 +228,13 @@
         protected void onDragDismiss() {
         }
 
+        /**
+         * Get the resting position of the view when drag is released
+         */
+        protected PointF getRestingPosition() {
+            return mViewInitialPosition;
+        }
+
         @Override
         @SuppressLint("ClickableViewAccessibility")
         public boolean onTouch(@NonNull View view, @NonNull MotionEvent event) {
@@ -232,8 +279,10 @@
          * @param event the motion event
          */
         protected void onTouchMove(@NonNull View view, @NonNull MotionEvent event) {
-            final float dx = event.getRawX() - mTouchDownLocation.x;
-            final float dy = event.getRawY() - mTouchDownLocation.y;
+            float rawX = event.getRawX();
+            float rawY = event.getRawY();
+            final float dx = rawX - mTouchDownLocation.x;
+            final float dy = rawY - mTouchDownLocation.y;
             switch (mState) {
                 case TOUCHED:
                     final boolean movedOut = Math.hypot(dx, dy) > mTouchSlop;
@@ -244,7 +293,7 @@
                     }
                     break;
                 case DRAGGING:
-                    drag(view, event, dx, dy);
+                    drag(view, event, dx, dy, rawX, rawY);
                     break;
             }
         }
@@ -293,10 +342,12 @@
             mBubbleDismissController.showDismissView();
         }
 
-        private void drag(@NonNull View view, @NonNull MotionEvent event, float dx, float dy) {
+        private void drag(@NonNull View view, @NonNull MotionEvent event, float dx, float dy,
+                float x, float y) {
             if (mBubbleDismissController.handleTouchEvent(event)) return;
             view.setTranslationX(mViewInitialPosition.x + dx);
             view.setTranslationY(mViewInitialPosition.y + dy);
+            onDragUpdate(x, y);
         }
 
         private void stopDragging(@NonNull View view, @NonNull MotionEvent event) {
@@ -311,7 +362,7 @@
                 mAnimator.animateDismiss(mViewInitialPosition, onComplete);
             } else {
                 onDragRelease();
-                mAnimator.animateToInitialState(mViewInitialPosition, getCurrentVelocity(),
+                mAnimator.animateToInitialState(getRestingPosition(), getCurrentVelocity(),
                         onComplete);
             }
             mBubbleDismissController.hideDismissView();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 6549ad6..bcdc718 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -145,7 +145,7 @@
     }
 
     /** Sets the bubble being rendered in this view. */
-    void setBubble(BubbleBarBubble bubble) {
+    public void setBubble(BubbleBarBubble bubble) {
         mBubble = bubble;
         mBubbleIcon.setImageBitmap(bubble.getIcon());
         mAppIcon.setImageBitmap(bubble.getBadge());
@@ -159,7 +159,7 @@
      * the list of bubbles. It doesn't show an app icon because it is part of system UI / doesn't
      * come from an app.
      */
-    void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) {
+    public void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) {
         mBubble = overflow;
         mBubbleIcon.setImageBitmap(bitmap);
         mAppIcon.setVisibility(GONE); // Overflow doesn't show the app badge
@@ -168,7 +168,7 @@
 
     /** Returns the bubble being rendered in this view. */
     @Nullable
-    BubbleBarItem getBubble() {
+    public BubbleBarItem getBubble() {
         return mBubble;
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
new file mode 100644
index 0000000..bcb9f4d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.taskbar.bubbles.animation
+
+import android.view.View
+import android.view.View.VISIBLE
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.launcher3.taskbar.bubbles.BubbleBarBubble
+import com.android.launcher3.taskbar.bubbles.BubbleBarView
+import com.android.launcher3.taskbar.bubbles.BubbleStashController
+import com.android.launcher3.taskbar.bubbles.BubbleView
+import com.android.wm.shell.shared.animation.PhysicsAnimator
+
+/** Handles animations for bubble bar bubbles. */
+class BubbleBarViewAnimator
+@JvmOverloads
+constructor(
+    private val bubbleBarView: BubbleBarView,
+    private val bubbleStashController: BubbleStashController,
+    private val scheduler: Scheduler = HandlerScheduler(bubbleBarView)
+) {
+
+    private companion object {
+        /** The time to show the flyout. */
+        const val FLYOUT_DELAY_MS: Long = 2500
+        /** The translation Y the new bubble will animate to. */
+        const val BUBBLE_ANIMATION_TRANSLATION_Y = -50f
+    }
+
+    /** An interface for scheduling jobs. */
+    interface Scheduler {
+
+        /** Schedule the given [block] to run. */
+        fun post(block: () -> Unit)
+
+        /** Schedule the given [block] to start with a delay of [delayMillis]. */
+        fun postDelayed(delayMillis: Long, block: () -> Unit)
+    }
+
+    /** A [Scheduler] that uses a Handler to run jobs. */
+    private class HandlerScheduler(private val view: View) : Scheduler {
+
+        override fun post(block: () -> Unit) {
+            view.post(block)
+        }
+
+        override fun postDelayed(delayMillis: Long, block: () -> Unit) {
+            view.postDelayed(block, delayMillis)
+        }
+    }
+
+    private val springConfig =
+        PhysicsAnimator.SpringConfig(
+            stiffness = SpringForce.STIFFNESS_LOW,
+            dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
+        )
+
+    /** Animates a bubble for the state where the bubble bar is stashed. */
+    fun animateBubbleInForStashed(b: BubbleBarBubble) {
+        val bubbleView = b.view
+        val animator = PhysicsAnimator.getInstance(bubbleView)
+        if (animator.isRunning()) animator.cancel()
+        // the animation of a new bubble is divided into 2 parts. The first part shows the bubble
+        // and the second part hides it after a delay.
+        val showAnimation = buildShowAnimation(bubbleView, b.key, animator)
+        val hideAnimation = buildHideAnimation(animator)
+        scheduler.post(showAnimation)
+        scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation)
+    }
+
+    /** Returns a lambda that starts the animation that shows the new bubble. */
+    private fun buildShowAnimation(
+        bubbleView: BubbleView,
+        key: String,
+        animator: PhysicsAnimator<BubbleView>
+    ): () -> Unit = {
+        bubbleBarView.prepareForAnimatingBubbleWhileStashed(key)
+        animator.setDefaultSpringConfig(springConfig)
+        animator
+            .spring(DynamicAnimation.ALPHA, 1f)
+            .spring(DynamicAnimation.TRANSLATION_Y, BUBBLE_ANIMATION_TRANSLATION_Y)
+        bubbleView.alpha = 0f
+        bubbleView.visibility = VISIBLE
+        animator.start()
+    }
+
+    /** Returns a lambda that starts the animation that hides the new bubble. */
+    private fun buildHideAnimation(animator: PhysicsAnimator<BubbleView>): () -> Unit = {
+        animator.setDefaultSpringConfig(springConfig)
+        animator
+            .spring(DynamicAnimation.ALPHA, 0f)
+            .spring(DynamicAnimation.TRANSLATION_Y, 0f)
+            .addEndListener { _, _, _, canceled, _, _, allRelevantPropertyAnimsEnded ->
+                if (!canceled && allRelevantPropertyAnimsEnded) {
+                    if (bubbleStashController.isStashed) {
+                        bubbleBarView.alpha = 0f
+                    }
+                    bubbleBarView.onAnimatingBubbleCompleted()
+                }
+            }
+        animator.start()
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
deleted file mode 100644
index 8f1d319..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2020 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.uioverrides;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.launcher3.R;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-
-/** A util class that inflates a predicted app icon */
-public class PredictedAppIconInflater {
-    public static View inflate(LayoutInflater inflater, ViewGroup parent, WorkspaceItemInfo info) {
-        PredictedAppIcon icon = (PredictedAppIcon) inflater.inflate(
-                R.layout.predicted_app_icon, parent, false);
-        icon.applyFromWorkspaceItem(info);
-        return icon;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index b49c752..8b923ad 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -62,8 +62,8 @@
 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.window.flags.Flags.enableDesktopWindowingMode;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
 
 import android.animation.Animator;
@@ -105,7 +105,6 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Flags;
-import com.android.launcher3.HomeTransitionController;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -245,8 +244,6 @@
 
     private boolean mIsPredictiveBackToHomeInProgress;
 
-    private HomeTransitionController mHomeTransitionController;
-
     @Override
     protected void setupViews() {
         super.setupViews();
@@ -277,11 +274,6 @@
         mAppTransitionManager.registerRemoteAnimations();
         mAppTransitionManager.registerRemoteTransitions();
 
-        if (FeatureFlags.enableHomeTransitionListener()) {
-            mHomeTransitionController = new HomeTransitionController();
-            mHomeTransitionController.registerHomeTransitionListener(this);
-        }
-
         mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
         mDepthController = new DepthController(this);
         mDesktopVisibilityController = new DesktopVisibilityController(this);
@@ -523,10 +515,6 @@
             mLauncherUnfoldAnimationController.onDestroy();
         }
 
-        if (mHomeTransitionController != null) {
-            mHomeTransitionController.unregisterHomeTransitionListener();
-        }
-
         if (mDesktopVisibilityController != null) {
             mDesktopVisibilityController.unregisterSystemUiListener();
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index 23e2622..01d5ff0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
@@ -32,7 +33,6 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -101,7 +101,7 @@
                                     new ArrayList<>(h.mProviderChangedListeners).forEach(
                                     ProviderChangedListener::notifyWidgetProvidersChanged))),
                     UI_HELPER_EXECUTOR.getLooper());
-            if (!WidgetsModel.GO_DISABLE_WIDGETS) {
+            if (WIDGETS_ENABLED) {
                 sWidgetHost.startListening();
             }
         }
@@ -195,7 +195,7 @@
      */
     @Override
     public void stopListening() {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             return;
         }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index b6002e8..7875dae 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -32,6 +32,8 @@
 import com.android.quickstep.util.BaseDepthController;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Definition for AllApps state
  */
@@ -39,6 +41,8 @@
 
     private static final int STATE_FLAGS =
             FLAG_WORKSPACE_INACCESSIBLE | FLAG_CLOSE_POPUPS | FLAG_HOTSEAT_INACCESSIBLE;
+    private static final long BACK_CUJ_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
+
 
     public AllAppsState(int id) {
         super(id, LAUNCHER_STATE_ALLAPPS, STATE_FLAGS);
@@ -53,14 +57,33 @@
     }
 
     @Override
-    public void onBackPressed(Launcher launcher) {
+    public void onBackStarted(Launcher launcher) {
+        // Because the back gesture can take longer time depending on when user release the finger,
+        // we pass BACK_CUJ_TIMEOUT_MS as timeout to the jank monitor.
         InteractionJankMonitorWrapper.begin(launcher.getAppsView(),
-                Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
-        super.onBackPressed(launcher);
+                Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK, BACK_CUJ_TIMEOUT_MS);
+        super.onBackStarted(launcher);
     }
 
     @Override
-    protected void onBackPressCompleted(boolean success) {
+    public void onBackInvoked(Launcher launcher) {
+        // In predictive back swipe, onBackInvoked() will be called after onBackStarted().
+        // Because the 2nd InteractionJankMonitor.begin() will be ignore within timeout, it's safe
+        // to call InteractionJankMonitorWrapper.begin here.
+        InteractionJankMonitorWrapper.begin(launcher.getAppsView(),
+                Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
+        super.onBackInvoked(launcher);
+    }
+
+    /** Called when predictive back swipe is cancelled. */
+    @Override
+    public void onBackCancelled(Launcher launcher) {
+        super.onBackCancelled(launcher);
+        InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
+    }
+
+    @Override
+    protected void onBackAnimationCompleted(boolean success) {
         if (success) {
             // Animation was successful.
             InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 856b519..c63eaeb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -60,7 +60,7 @@
     }
 
     @Override
-    public void onBackPressed(Launcher launcher) {
+    public void onBackInvoked(Launcher launcher) {
         launcher.getStateManager().goToState(LauncherState.OVERVIEW);
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 8c2efc2..d0eef8e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -199,7 +199,7 @@
     }
 
     @Override
-    public void onBackPressed(Launcher launcher) {
+    public void onBackInvoked(Launcher launcher) {
         RecentsView recentsView = launcher.getOverviewPanel();
         TaskView taskView = recentsView.getRunningTaskView();
         if (taskView != null) {
@@ -209,7 +209,7 @@
                 recentsView.snapToPage(recentsView.indexOfChild(taskView));
             }
         } else {
-            super.onBackPressed(launcher);
+            super.onBackInvoked(launcher);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 0320f50..dc1c6a6 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1002,7 +1002,7 @@
         ActiveGestureLog.INSTANCE.addLog(
                 /* event= */ "cancelRecentsAnimation",
                 /* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
-        mActivityInitListener.unregister();
+        mActivityInitListener.unregister("AbsSwipeUpHandler.onRecentsAnimationCanceled");
         // Cache the recents animation controller so we can defer its cleanup to after having
         // properly cleaned up the screenshot without accidentally using it.
         mDeferredCleanupRecentsAnimationController = mRecentsAnimationController;
@@ -1964,7 +1964,7 @@
 
         // Cleanup when switching handlers
         mInputConsumerProxy.unregisterOnTouchDownCallback();
-        mActivityInitListener.unregister();
+        mActivityInitListener.unregister("AbsSwipeUpHandler.cancelCurrentAnimation");
         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
                 mActivityRestartListener);
         mTaskSnapshotCache.clear();
@@ -1982,7 +1982,7 @@
             mGestureEndCallback.run();
         }
 
-        mActivityInitListener.unregister();
+        mActivityInitListener.unregister("AbsSwipeUpHandler.invalidateHandler");
         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
                 mActivityRestartListener);
         mTaskSnapshotCache.clear();
@@ -2486,11 +2486,11 @@
     /**
      * Registers a callback to run when the activity is ready.
      */
-    public void initWhenReady() {
+    public void initWhenReady(String reasonString) {
         // Preload the plan
         RecentsModel.INSTANCE.get(mContext).getTasks(null);
 
-        mActivityInitListener.register();
+        mActivityInitListener.register(reasonString);
     }
 
     private boolean shouldFadeOutTargetsForKeyboardQuickSwitch(
diff --git a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
index fd2ed3a..6fd68d5 100644
--- a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
+++ b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
@@ -81,6 +81,7 @@
     }
 
     fun onDestroy() {
+        isActionRegistered = false
         context
             .getSystemService(AccessibilityManager::class.java)
             ?.unregisterSystemAction(
diff --git a/quickstep/src/com/android/quickstep/HomeVisibilityState.kt b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
new file mode 100644
index 0000000..241e16d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.quickstep
+
+import android.os.RemoteException
+import android.util.Log
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.util.Executors
+import com.android.wm.shell.shared.IHomeTransitionListener.Stub
+import com.android.wm.shell.shared.IShellTransitions
+
+/** Class to track visibility state of Launcher */
+class HomeVisibilityState {
+
+    var isHomeVisible = true
+        private set
+
+    private var listeners = mutableSetOf<VisibilityChangeListener>()
+
+    fun addListener(l: VisibilityChangeListener) = listeners.add(l)
+
+    fun removeListener(l: VisibilityChangeListener) = listeners.remove(l)
+
+    fun init(transitions: IShellTransitions?) {
+        if (!FeatureFlags.enableHomeTransitionListener()) return
+        try {
+            transitions?.setHomeTransitionListener(
+                object : Stub() {
+                    override fun onHomeVisibilityChanged(isVisible: Boolean) {
+                        Executors.MAIN_EXECUTOR.execute {
+                            isHomeVisible = isVisible
+                            listeners.forEach { it.onHomeVisibilityChanged(isVisible) }
+                        }
+                    }
+                }
+            )
+        } catch (e: RemoteException) {
+            Log.w(TAG, "Failed call setHomeTransitionListener", e)
+        }
+    }
+
+    interface VisibilityChangeListener {
+        fun onHomeVisibilityChanged(isVisible: Boolean)
+    }
+
+    override fun toString() = "{HomeVisibilityState isHomeVisible=$isHomeVisible}"
+
+    companion object {
+
+        private const val TAG = "HomeVisibilityState"
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 97c48e6..7c17e4e 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -35,6 +35,7 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherInitListener;
@@ -206,8 +207,17 @@
     @UiThread
     private Launcher getVisibleLauncher() {
         Launcher launcher = getCreatedActivity();
-        return (launcher != null) && launcher.isStarted()
-                && (isInLiveTileMode() || launcher.hasBeenResumed()) ? launcher : null;
+        if (launcher == null) {
+            return null;
+        }
+        if (launcher.isStarted() && (isInLiveTileMode() || launcher.hasBeenResumed())) {
+            return launcher;
+        }
+        if (Flags.useActivityOverlay()
+                && SystemUiProxy.INSTANCE.get(launcher).getHomeVisibilityState().isHomeVisible()) {
+            return launcher;
+        }
+        return null;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index cc5a923..2d25295 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -44,7 +44,6 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.View;
-import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.window.BackEvent;
@@ -84,7 +83,6 @@
  *
  */
 public class LauncherBackAnimationController {
-    private static final int CANCEL_TRANSITION_DURATION = 233;
     private static final int SCRIM_FADE_DURATION = 233;
     private static final float MIN_WINDOW_SCALE = 0.85f;
     private static final float MAX_SCRIM_ALPHA_DARK = 0.8f;
@@ -95,15 +93,12 @@
     private final Matrix mTransformMatrix = new Matrix();
     /** The window position at the beginning of the back animation. */
     private final Rect mStartRect = new Rect();
-    /** The window position when the back gesture is cancelled. */
-    private final RectF mCancelRect = new RectF();
     /** The current window position. */
     private final RectF mCurrentRect = new RectF();
     private final QuickstepLauncher mLauncher;
     private final int mWindowScaleMarginX;
     private float mWindowScaleEndCornerRadius;
     private float mWindowScaleStartCornerRadius;
-    private final Interpolator mCancelInterpolator;
     private final Interpolator mProgressInterpolator = Interpolators.STANDARD_DECELERATE;
     private final Interpolator mVerticalMoveInterpolator = new DecelerateInterpolator();
     private final PointF mInitialTouchPos = new PointF();
@@ -142,8 +137,6 @@
         loadCornerRadius();
         mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
                 R.dimen.swipe_back_window_scale_x_margin);
-        mCancelInterpolator =
-                AnimationUtils.loadInterpolator(mLauncher, R.interpolator.standard_interpolator);
     }
 
     /**
@@ -181,8 +174,7 @@
             mHandler.post(() -> {
                 LauncherBackAnimationController controller = mControllerRef.get();
                 if (controller != null) {
-                    mProgressAnimator.onBackCancelled(
-                            controller::resetPositionAnimated);
+                    mProgressAnimator.onBackCancelled(controller::onCancelFinished);
                 }
             });
         }
@@ -262,24 +254,9 @@
         public void onAnimationCancelled() {}
     }
 
-    private void resetPositionAnimated() {
-        ValueAnimator cancelAnimator = ValueAnimator.ofFloat(0, 1);
-        mCancelRect.set(mCurrentRect);
-        cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
-        cancelAnimator.setInterpolator(mCancelInterpolator);
-        cancelAnimator.addUpdateListener(
-                animation -> {
-                    updateCancelProgress((float) animation.getAnimatedValue());
-                });
-        cancelAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                // Refresh the status bar appearance to the original one.
-                customizeStatusBarAppearance(false);
-                finishAnimation();
-            }
-        });
-        cancelAnimator.start();
+    private void onCancelFinished() {
+        customizeStatusBarAppearance(false);
+        finishAnimation();
     }
 
     /** Unregisters the back to launcher callback in shell. */
@@ -292,6 +269,14 @@
     }
 
     private void startBack(BackMotionEvent backEvent) {
+        // in case we're still animating an onBackCancelled event, let's remove the finish-
+        // callback from the progress animator to prevent calling finishAnimation() before
+        // restarting a new animation
+        // Side note: startBack is never called during the post-commit phase if the back gesture
+        // was committed (not cancelled). BackAnimationController prevents that. Therefore we
+        // don't have to handle that case.
+        mProgressAnimator.removeOnBackCancelledFinishCallback();
+
         mBackInProgress = true;
         RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
 
@@ -314,7 +299,9 @@
                 new RemoteAnimationTarget[]{ mBackTarget });
         setLauncherTargetViewVisible(false);
         mCurrentRect.set(mStartRect);
-        addScrimLayer();
+        if (mScrimLayer == null) {
+            addScrimLayer();
+        }
         mTransaction.apply();
     }
 
@@ -397,23 +384,6 @@
         customizeStatusBarAppearance(progress > UPDATE_SYSUI_FLAGS_THRESHOLD);
     }
 
-    private void updateCancelProgress(float progress) {
-        if (mBackTarget == null) {
-            return;
-        }
-        mCurrentRect.set(
-                Utilities.mapRange(progress, mCancelRect.left, mStartRect.left),
-                Utilities.mapRange(progress, mCancelRect.top, mStartRect.top),
-                Utilities.mapRange(progress, mCancelRect.right, mStartRect.right),
-                Utilities.mapRange(progress, mCancelRect.bottom, mStartRect.bottom));
-
-        float endCornerRadius = Utilities.mapRange(
-                mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
-        float cornerRadius = Utilities.mapRange(
-                progress, endCornerRadius, mWindowScaleStartCornerRadius);
-        applyTransform(mCurrentRect, cornerRadius);
-    }
-
     /** Transform the target window to match the target rect. */
     private void applyTransform(RectF targetRect, float cornerRadius) {
         final float scale = targetRect.width() / mStartRect.width();
@@ -484,7 +454,6 @@
         mBackInProgress = false;
         mBackProgress = 0;
         mTransformMatrix.reset();
-        mCancelRect.setEmpty();
         mCurrentRect.setEmpty();
         mStartRect.setEmpty();
         mInitialTouchPos.set(0, 0);
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 56c9a00..0db50bf 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -310,7 +310,7 @@
                 .newHandler(gestureState, cmd.createTime);
         interactionHandler.setGestureEndCallback(
                 () -> onTransitionComplete(cmd, interactionHandler));
-        interactionHandler.initWhenReady();
+        interactionHandler.initWhenReady("OverviewCommandHelper: cmd.type=" + cmd.type);
 
         RecentsAnimationListener recentAnimListener = new RecentsAnimationListener() {
             @Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 72f67fc..4183842 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -63,7 +63,6 @@
 import com.android.internal.logging.InstanceId;
 import com.android.internal.util.ScreenshotRequest;
 import com.android.internal.view.AppearanceRegion;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
 import com.android.quickstep.util.ActiveGestureLog;
@@ -82,6 +81,7 @@
 import com.android.wm.shell.back.IBackAnimation;
 import com.android.wm.shell.bubbles.IBubbles;
 import com.android.wm.shell.bubbles.IBubblesListener;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
 import com.android.wm.shell.common.pip.IPip;
 import com.android.wm.shell.common.pip.IPipAnimationListener;
 import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
@@ -91,13 +91,12 @@
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.recents.IRecentTasks;
 import com.android.wm.shell.recents.IRecentTasksListener;
+import com.android.wm.shell.shared.IShellTransitions;
 import com.android.wm.shell.splitscreen.ISplitScreen;
 import com.android.wm.shell.splitscreen.ISplitScreenListener;
 import com.android.wm.shell.splitscreen.ISplitSelectListener;
 import com.android.wm.shell.startingsurface.IStartingWindow;
 import com.android.wm.shell.startingsurface.IStartingWindowListener;
-import com.android.wm.shell.transition.IHomeTransitionListener;
-import com.android.wm.shell.transition.IShellTransitions;
 import com.android.wm.shell.util.GroupedRecentTaskInfo;
 
 import java.io.PrintWriter;
@@ -157,7 +156,7 @@
     private IOnBackInvokedCallback mBackToLauncherCallback;
     private IRemoteAnimationRunner mBackToLauncherRunner;
     private IDragAndDrop mDragAndDrop;
-    private IHomeTransitionListener mHomeTransitionListener;
+    private final HomeVisibilityState mHomeVisibilityState = new HomeVisibilityState();
 
     // Used to dedupe calls to SystemUI
     private int mLastShelfHeight;
@@ -269,7 +268,7 @@
         setBubblesListener(mBubblesListener);
         registerSplitScreenListener(mSplitScreenListener);
         registerSplitSelectListener(mSplitSelectListener);
-        setHomeTransitionListener(mHomeTransitionListener);
+        mHomeVisibilityState.init(mShellTransitions);
         setStartingWindowListener(mStartingWindowListener);
         setLauncherUnlockAnimationController(
                 mLauncherActivityClass, mLauncherUnlockAnimationController);
@@ -808,6 +807,18 @@
         }
     }
 
+    /**
+     * Tells SysUI to update the bubble bar location to the new location.
+     * @param location new location for the bubble bar
+     */
+    public void setBubbleBarLocation(BubbleBarLocation location) {
+        try {
+            mBubbles.setBubbleBarLocation(location);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed call setBubbleBarLocation");
+        }
+    }
+
     //
     // Splitscreen
     //
@@ -1102,22 +1113,8 @@
         mRemoteTransitions.remove(remoteTransition);
     }
 
-    public void setHomeTransitionListener(IHomeTransitionListener listener) {
-        if (!FeatureFlags.enableHomeTransitionListener()) {
-            return;
-        }
-
-        mHomeTransitionListener = listener;
-
-        if (mShellTransitions != null) {
-            try {
-                mShellTransitions.setHomeTransitionListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setHomeTransitionListener", e);
-            }
-        } else  {
-            Log.w(TAG, "Unable to call setHomeTransitionListener because ShellTransitions is null");
-        }
+    public HomeVisibilityState getHomeVisibilityState() {
+        return mHomeVisibilityState;
     }
 
     /**
@@ -1558,7 +1555,7 @@
         pw.println("\tmSplitSelectListener=" + mSplitSelectListener);
         pw.println("\tmOneHanded=" + mOneHanded);
         pw.println("\tmShellTransitions=" + mShellTransitions);
-        pw.println("\tmHomeTransitionListener=" + mHomeTransitionListener);
+        pw.println("\tmHomeVisibilityState=" + mHomeVisibilityState);
         pw.println("\tmStartingWindow=" + mStartingWindow);
         pw.println("\tmStartingWindowListener=" + mStartingWindowListener);
         pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController);
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index c97e62a..147a3e2 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -136,8 +136,9 @@
     class SaveAppPairSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
         private final GroupedTaskView mTaskView;
 
-        public SaveAppPairSystemShortcut(BaseDraggingActivity activity, GroupedTaskView taskView) {
-            super(R.drawable.ic_save_app_pair, R.string.save_app_pair, activity,
+        public SaveAppPairSystemShortcut(BaseDraggingActivity activity, GroupedTaskView taskView,
+                int iconResId) {
+            super(iconResId, R.string.save_app_pair, activity,
                     taskView.getItemInfo(), taskView);
             mTaskView = taskView;
         }
@@ -332,16 +333,22 @@
             // - the task in question is a single task
             // - at least one app in app pair is unpinnable
             // - the Overview Actions Button should be visible
+            // - the task is not a GroupedTaskView
             if (!FeatureFlags.enableAppPairs()
                     || !recentsView.supportsAppPairs()
                     || !taskView.containsMultipleTasks()
                     || hasUnpinnableApp
-                    || shouldShowActionsButtonInstead) {
+                    || shouldShowActionsButtonInstead
+                    || !(taskView instanceof GroupedTaskView)) {
                 return null;
             }
 
+            int iconResId = deviceProfile.isLeftRightSplit
+                    ? R.drawable.ic_save_app_pair_left_right
+                    : R.drawable.ic_save_app_pair_up_down;
+
             return Collections.singletonList(
-                    new SaveAppPairSystemShortcut(activity, (GroupedTaskView) taskView));
+                    new SaveAppPairSystemShortcut(activity, (GroupedTaskView) taskView, iconResId));
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index b43c520..66d7144 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -135,9 +135,9 @@
 import com.android.wm.shell.draganddrop.IDragAndDrop;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.recents.IRecentTasks;
+import com.android.wm.shell.shared.IShellTransitions;
 import com.android.wm.shell.splitscreen.ISplitScreen;
 import com.android.wm.shell.startingsurface.IStartingWindow;
-import com.android.wm.shell.transition.IShellTransitions;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -442,8 +442,10 @@
 
         /** Refreshes the current overview target. */
         public void refreshOverviewTarget() {
-            executeForTouchInteractionService(tis -> tis.onOverviewTargetChange(
-                    tis.mOverviewComponentObserver.isHomeAndOverviewSame()));
+            executeForTouchInteractionService(tis -> {
+                tis.mAllAppsActionManager.onDestroy();
+                tis.onOverviewTargetChange(tis.mOverviewComponentObserver.isHomeAndOverviewSame());
+            });
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 0f8ceba..fbbfc16 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -394,7 +394,8 @@
         mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
         mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
         mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
-        mInteractionHandler.initWhenReady();
+        mInteractionHandler.initWhenReady(
+                "OtherActivityInputConsumer.startTouchTrackingForWindowAnimation");
 
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
             mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index d265918..e078a49 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -61,7 +61,7 @@
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.LogConfig;
@@ -375,7 +375,7 @@
                 Executors.MODEL_EXECUTOR.execute(
                         () -> write(event, applyOverwrites(mItemInfo.buildProto())));
             } else {
-                // Item is inside the folder, fetch folder info in a BG thread
+                // Item is inside a collection, fetch collection info in a BG thread
                 // and then write to StatsLog.
                 appState.getModel().enqueueModelUpdateTask(
                         new BaseModelUpdateTask() {
@@ -383,8 +383,9 @@
                             public void execute(@NonNull final LauncherAppState app,
                                     @NonNull final BgDataModel dataModel,
                                     @NonNull final AllAppsList apps) {
-                                FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
-                                write(event, applyOverwrites(mItemInfo.buildProto(folderInfo)));
+                                CollectionInfo collectionInfo =
+                                        dataModel.collections.get(mItemInfo.container);
+                                write(event, applyOverwrites(mItemInfo.buildProto(collectionInfo)));
                             }
                         });
             }
diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
index aeec36f..5efbb40 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -57,16 +57,16 @@
      * Registers the activity-created listener. If the activity is already created, then the
      * callback provided in the constructor will be called synchronously.
      */
-    public void register() {
+    public void register(String reasonString) {
         mIsRegistered = true;
-        mActivityTracker.registerCallback(this);
+        mActivityTracker.registerCallback(this, reasonString);
     }
 
     /**
      * After calling this, we won't {@link #init} even when the activity is ready.
      */
-    public void unregister() {
-        mActivityTracker.unregisterCallback(this);
+    public void unregister(String reasonString) {
+        mActivityTracker.unregisterCallback(this, reasonString);
         mIsRegistered = false;
         mOnInitListener = null;
     }
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index ecb6118..f4da867 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -53,7 +53,7 @@
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.AppPairInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
@@ -149,25 +149,17 @@
 
         app1.rank = encodeRank(SPLIT_POSITION_TOP_OR_LEFT, snapPosition);
         app2.rank = encodeRank(SPLIT_POSITION_BOTTOM_OR_RIGHT, snapPosition);
-        FolderInfo newAppPair = FolderInfo.createAppPair(app1, app2);
-
-        if (newAppPair.contents.size() != 2) {
-            // if app pair doesn't have exactly 2 members, log an error and do not create the app
-            // pair.
-            Log.wtf(TAG,
-                    "tried to save an app pair with " + newAppPair.contents.size() + " members");
-            return;
-        }
+        AppPairInfo newAppPair = new AppPairInfo(app1, app2);
 
         IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
         MODEL_EXECUTOR.execute(() -> {
-            newAppPair.contents.forEach(member -> {
+            newAppPair.getContents().forEach(member -> {
                 member.title = "";
                 member.bitmap = iconCache.getDefaultIcon(newAppPair.user);
                 iconCache.getTitleAndIcon(member, member.usingLowResIcon());
             });
-            newAppPair.title = getDefaultTitle(newAppPair.contents.get(0).title,
-                    newAppPair.contents.get(1).title);
+            newAppPair.title = getDefaultTitle(newAppPair.getFirstApp().title,
+                    newAppPair.getSecondApp().title);
             MAIN_EXECUTOR.execute(() -> {
                 LauncherAccessibilityDelegate delegate =
                         Launcher.getLauncher(mContext).getAccessibilityDelegate();
@@ -194,8 +186,8 @@
      *            monitoring
      */
     public void launchAppPair(AppPairIcon appPairIcon, int cuj) {
-        WorkspaceItemInfo app1 = appPairIcon.getInfo().contents.get(0);
-        WorkspaceItemInfo app2 = appPairIcon.getInfo().contents.get(1);
+        WorkspaceItemInfo app1 = appPairIcon.getInfo().getFirstApp();
+        WorkspaceItemInfo app2 = appPairIcon.getInfo().getSecondApp();
         ComponentKey app1Key = new ComponentKey(app1.getTargetComponent(), app1.user);
         ComponentKey app2Key = new ComponentKey(app2.getTargetComponent(), app2.user);
         mSplitSelectStateController.setLaunchingCuj(cuj);
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 8f5c9c1..6b27004 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -659,8 +659,8 @@
 
         // Create a new floating view in Launcher, positioned above the launching icon
         val drawableArea = launchingIconView.iconDrawableArea
-        val appIcon1 = launchingIconView.info.contents[0].newIcon(launchingIconView.context)
-        val appIcon2 = launchingIconView.info.contents[1].newIcon(launchingIconView.context)
+        val appIcon1 = launchingIconView.info.getFirstApp().newIcon(launchingIconView.context)
+        val appIcon2 = launchingIconView.info.getSecondApp().newIcon(launchingIconView.context)
         appIcon1.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx)
         appIcon2.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx)
         val floatingView =
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index 87be091..2282c46 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -16,7 +16,6 @@
 
 package com.android.quickstep.util;
 
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.window.flags.Flags.enableDesktopWindowingMode;
 
@@ -44,7 +43,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.AppPairInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -126,7 +125,7 @@
             intent = appInfo.intent;
             user = appInfo.user;
             bitmapInfo = appInfo.bitmap;
-        } else if (tag instanceof FolderInfo fi && fi.itemType == ITEM_TYPE_APP_PAIR) {
+        } else if (tag instanceof AppPairInfo) {
             // Prompt the user to select something else by wiggling the instructions view
             mController.getSplitInstructionsView().goBoing();
             return true;
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index e0091a5..384a8d8 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -20,7 +20,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
@@ -43,8 +42,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-import java.util.stream.Collectors;
 
 /**
  * View for showing action buttons in Overview
@@ -297,17 +294,6 @@
         int desiredVisibility = mSplitButtonHiddenFlags == 0 ? VISIBLE : GONE;
         mSplitButton.setVisibility(desiredVisibility);
         findViewById(R.id.action_split_space).setVisibility(desiredVisibility);
-
-        String callStack = Arrays.stream(
-                        Log.getStackTraceString(new Exception("thread stacktrace"))
-                                .split("\\n"))
-                .limit(5)
-                .skip(1) // Removes the line "java.lang.Exception: thread stacktrace"
-                .collect(Collectors.joining("\n"));
-        Log.d("b/321291049", "updateSplitButtonHiddenFlags called with flag: " + flag
-                + " enabled: " + enable
-                + " visibility: " + desiredVisibility
-                + " partial trace: \n" + callStack);
     }
 
     /**
@@ -407,7 +393,11 @@
                 ? R.drawable.ic_split_horizontal
                 : R.drawable.ic_split_vertical;
         mSplitButton.setCompoundDrawablesRelativeWithIntrinsicBounds(splitIconRes, 0, 0, 0);
+
+        int appPairIconRes = dp.isLeftRightSplit
+                ? R.drawable.ic_save_app_pair_left_right
+                : R.drawable.ic_save_app_pair_up_down;
         mSaveAppPairButton.setCompoundDrawablesRelativeWithIntrinsicBounds(
-                R.drawable.ic_save_app_pair, 0, 0, 0);
+                appPairIconRes, 0, 0, 0);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index abd4ec4..79bd107 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -135,8 +135,6 @@
 public class TaskView extends FrameLayout implements Reusable {
 
     private static final String TAG = TaskView.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
     public static final int FLAG_UPDATE_ICON = 1;
     public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1;
     public static final int FLAG_UPDATE_CORNER_RADIUS = FLAG_UPDATE_THUMBNAIL << 1;
@@ -184,7 +182,7 @@
             Collections.singletonList(new Rect());
 
     public static final FloatProperty<TaskView> FOCUS_TRANSITION =
-            new FloatProperty<TaskView>("focusTransition") {
+            new FloatProperty<>("focusTransition") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setIconsAndBannersTransitionProgress(v, false /* invert */);
@@ -197,7 +195,7 @@
             };
 
     private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_X =
-            new FloatProperty<TaskView>("splitSelectTranslationX") {
+            new FloatProperty<>("splitSelectTranslationX") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setSplitSelectTranslationX(v);
@@ -210,7 +208,7 @@
             };
 
     private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_Y =
-            new FloatProperty<TaskView>("splitSelectTranslationY") {
+            new FloatProperty<>("splitSelectTranslationY") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setSplitSelectTranslationY(v);
@@ -223,7 +221,7 @@
             };
 
     private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
-            new FloatProperty<TaskView>("dismissTranslationX") {
+            new FloatProperty<>("dismissTranslationX") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setDismissTranslationX(v);
@@ -236,7 +234,7 @@
             };
 
     private static final FloatProperty<TaskView> DISMISS_TRANSLATION_Y =
-            new FloatProperty<TaskView>("dismissTranslationY") {
+            new FloatProperty<>("dismissTranslationY") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setDismissTranslationY(v);
@@ -249,7 +247,7 @@
             };
 
     private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_X =
-            new FloatProperty<TaskView>("taskOffsetTranslationX") {
+            new FloatProperty<>("taskOffsetTranslationX") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setTaskOffsetTranslationX(v);
@@ -262,7 +260,7 @@
             };
 
     private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_Y =
-            new FloatProperty<TaskView>("taskOffsetTranslationY") {
+            new FloatProperty<>("taskOffsetTranslationY") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setTaskOffsetTranslationY(v);
@@ -275,7 +273,7 @@
             };
 
     private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_X =
-            new FloatProperty<TaskView>("taskResistanceTranslationX") {
+            new FloatProperty<>("taskResistanceTranslationX") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setTaskResistanceTranslationX(v);
@@ -288,7 +286,7 @@
             };
 
     private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_Y =
-            new FloatProperty<TaskView>("taskResistanceTranslationY") {
+            new FloatProperty<>("taskResistanceTranslationY") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setTaskResistanceTranslationY(v);
@@ -301,7 +299,7 @@
             };
 
     public static final FloatProperty<TaskView> GRID_END_TRANSLATION_X =
-            new FloatProperty<TaskView>("gridEndTranslationX") {
+            new FloatProperty<>("gridEndTranslationX") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setGridEndTranslationX(v);
@@ -314,7 +312,7 @@
             };
 
     public static final FloatProperty<TaskView> SNAPSHOT_SCALE =
-            new FloatProperty<TaskView>("snapshotScale") {
+            new FloatProperty<>("snapshotScale") {
                 @Override
                 public void setValue(TaskView taskView, float v) {
                     taskView.setSnapshotScale(v);
@@ -602,10 +600,7 @@
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
             computeAndSetIconTouchDelegate(mIconView, mIconCenterCoords, mIconTouchDelegate);
         }
-        if (mIconTouchDelegate != null && mIconTouchDelegate.onTouchEvent(event)) {
-            return true;
-        }
-        return false;
+        return mIconTouchDelegate != null && mIconTouchDelegate.onTouchEvent(event);
     }
 
     protected void computeAndSetIconTouchDelegate(TaskViewIcon view, float[] tempCenterCoords,
@@ -647,9 +642,6 @@
 
     /**
      * Updates this task view to the given {@param task}.
-     *
-     * TODO(b/142282126) Re-evaluate if we need to pass in isMultiWindowMode after
-     *   that issue is fixed
      */
     public void bind(Task task, RecentsOrientedState orientedState) {
         cancelPendingLoadTasks();
@@ -1594,19 +1586,6 @@
         mEndQuickswitchCuj = endQuickswitchCuj;
     }
 
-    private int getExpectedViewHeight(View view) {
-        int expectedHeight;
-        int h = view.getLayoutParams().height;
-        if (h > 0) {
-            expectedHeight = h;
-        } else {
-            int m = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY - 1, MeasureSpec.AT_MOST);
-            view.measure(m, m);
-            expectedHeight = view.getMeasuredHeight();
-        }
-        return expectedHeight;
-    }
-
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
new file mode 100644
index 0000000..c17aeaa
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.taskbar.bubbles.animation
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Path
+import android.graphics.drawable.ColorDrawable
+import android.view.LayoutInflater
+import android.view.View.INVISIBLE
+import android.view.View.VISIBLE
+import android.widget.FrameLayout
+import androidx.core.graphics.drawable.toBitmap
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.bubbles.BubbleBarBubble
+import com.android.launcher3.taskbar.bubbles.BubbleBarOverflow
+import com.android.launcher3.taskbar.bubbles.BubbleBarView
+import com.android.launcher3.taskbar.bubbles.BubbleStashController
+import com.android.launcher3.taskbar.bubbles.BubbleView
+import com.android.wm.shell.common.bubbles.BubbleInfo
+import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleBarViewAnimatorTest {
+
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+    private val animatorScheduler = TestBubbleBarViewAnimatorScheduler()
+
+    @Before
+    fun setUp() {
+        PhysicsAnimatorTestUtils.prepareForTest()
+    }
+
+    @Test
+    fun animateBubbleInForStashed() {
+        lateinit var overflowView: BubbleView
+        lateinit var bubbleView: BubbleView
+        lateinit var bubble: BubbleBarBubble
+        val bubbleBarView = BubbleBarView(context)
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubbleBarView.layoutParams = FrameLayout.LayoutParams(0, 0)
+            val inflater = LayoutInflater.from(context)
+
+            val bitmap = ColorDrawable(Color.WHITE).toBitmap(width = 20, height = 20)
+            overflowView =
+                inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView
+            overflowView.setOverflow(BubbleBarOverflow(overflowView), bitmap)
+            bubbleBarView.addView(overflowView)
+
+            val bubbleInfo = BubbleInfo("key", 0, null, null, 0, context.packageName, null, false)
+            bubbleView =
+                inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView
+            bubble =
+                BubbleBarBubble(bubbleInfo, bubbleView, bitmap, bitmap, Color.WHITE, Path(), "")
+            bubbleView.setBubble(bubble)
+            bubbleBarView.addView(bubbleView)
+        }
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+        val bubbleStashController = mock<BubbleStashController>()
+        whenever(bubbleStashController.isStashed).thenReturn(true)
+
+        val animator =
+            BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            animator.animateBubbleInForStashed(bubble)
+        }
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+        assertThat(overflowView.visibility).isEqualTo(INVISIBLE)
+        assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
+        assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
+
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
+            DynamicAnimation.ALPHA,
+            DynamicAnimation.TRANSLATION_Y
+        )
+
+        assertThat(bubbleView.alpha).isEqualTo(1)
+        assertThat(bubbleView.translationY).isEqualTo(-50)
+
+        assertThat(animatorScheduler.delayedBlock).isNotNull()
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(animatorScheduler.delayedBlock!!)
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
+            DynamicAnimation.ALPHA,
+            DynamicAnimation.TRANSLATION_Y
+        )
+
+        assertThat(bubbleView.alpha).isEqualTo(1)
+        assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
+        assertThat(bubbleView.translationY).isEqualTo(0)
+        assertThat(bubbleBarView.alpha).isEqualTo(0)
+        assertThat(overflowView.alpha).isEqualTo(1)
+        assertThat(overflowView.visibility).isEqualTo(VISIBLE)
+    }
+
+    private class TestBubbleBarViewAnimatorScheduler : BubbleBarViewAnimator.Scheduler {
+
+        var delayedBlock: (() -> Unit)? = null
+            private set
+
+        override fun post(block: () -> Unit) {
+            block.invoke()
+        }
+
+        override fun postDelayed(delayMillis: Long, block: () -> Unit) {
+            check(delayedBlock == null) { "there is already a pending block waiting to run" }
+            delayedBlock = block
+        }
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 3444dc2..e4d8e92 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
 import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
 import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
+import static com.android.launcher3.testing.shared.TestProtocol.UPDATE_OVERVIEW_TARGETS_RUNNING_LATE;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_ACTIVITY_TIMEOUT;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_BROADCAST_TIMEOUT_SECS;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT;
@@ -44,6 +45,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.os.RemoteException;
+import android.util.Log;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -133,6 +135,13 @@
                         getLauncherCommand(mOtherLauncherActivity));
                 updateHandler.mChangeCounter
                         .await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
+                Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+                        "AFTER AWAIT: mObserver home intent package name="
+                                + updateHandler.mObserver.getHomeIntent()
+                                        .getComponent().getPackageName());
+                Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+                        "AFTER AWAIT: mOtherLauncherActivity package name="
+                                + mOtherLauncherActivity.packageName);
                 try {
                     base.evaluate();
                 } finally {
@@ -344,12 +353,25 @@
             mRads = new RecentsAnimationDeviceState(ctx);
             mObserver = new OverviewComponentObserver(ctx, mRads);
             mChangeCounter = new CountDownLatch(1);
+            Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+                    "OverviewUpdateHandler(Constructor): mObserver home intent package name="
+                            + mObserver.getHomeIntent().getComponent().getPackageName());
+            Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+                    "OverviewUpdateHandler(Constructor): mOtherLauncherActivity package name="
+                            + mOtherLauncherActivity.packageName);
             if (mObserver.getHomeIntent().getComponent()
                     .getPackageName().equals(mOtherLauncherActivity.packageName)) {
                 // Home already same
                 mChangeCounter.countDown();
             } else {
-                mObserver.setOverviewChangeListener(b -> mChangeCounter.countDown());
+                mObserver.setOverviewChangeListener(b -> {
+                    Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+                            "OverviewChangeListener(Callback): isHomeAndOverviewSame=" + b);
+                    Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+                            "OverviewChangeListener(Callback): mObserver home intent package name="
+                                    + mObserver.getHomeIntent().getComponent().getPackageName());
+                    mChangeCounter.countDown();
+                });
             }
         }
 
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index 82b0b8d..d14dcd5 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -21,4 +21,6 @@
     android:layout_height="match_parent"
     android:theme="@style/HomeScreenElementTheme"
     android:importantForAccessibility="no"
+    android:clickable="false"
+    android:longClickable="false"
     launcher:containerType="hotseat" />
\ No newline at end of file
diff --git a/res/layout/predicted_app_icon.xml b/res/layout/predicted_app_icon.xml
new file mode 100644
index 0000000..52df694
--- /dev/null
+++ b/res/layout/predicted_app_icon.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace" />
diff --git a/res/layout/private_space_header.xml b/res/layout/private_space_header.xml
index 2b5db48..185207b 100644
--- a/res/layout/private_space_header.xml
+++ b/res/layout/private_space_header.xml
@@ -33,7 +33,7 @@
         android:layout_centerVertical="true"
         android:gravity="center_vertical"
         android:layout_alignParentEnd="true"
-        android:animateLayoutChanges="true">
+        android:animateLayoutChanges="false">
         <ImageButton
             android:id="@+id/ps_settings_button"
             android:layout_width="@dimen/ps_header_image_height"
diff --git a/res/layout/snackbar.xml b/res/layout/snackbar.xml
index b818943..6bc1729 100644
--- a/res/layout/snackbar.xml
+++ b/res/layout/snackbar.xml
@@ -39,6 +39,7 @@
         android:paddingLeft="8dp"
         android:paddingRight="8dp"
         android:background="?android:attr/selectableItemBackground"
+        android:longClickable="false"
         android:textStyle="bold"
         android:textSize="@dimen/snackbar_max_text_size"
         android:textColor="?android:attr/colorAccent"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 8902033..9718479 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -87,7 +87,7 @@
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lys werkprogramme"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Verwyder"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleer"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Programinligting"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Appinligting"</string>
     <string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Installeer privaat"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Installeer"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Moenie voorstel nie"</string>
@@ -188,7 +188,8 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Misluk: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privaat ruimte"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Hou privaat apps gesluit en versteek"</string>
+    <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
+    <skip />
     <string name="ps_container_title" msgid="4391796149519594205">"Privaat"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Privaat Ruimte-instellings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Sluit/ontsluit Privaat Ruimte"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 0b94a4c..2121e40 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"አጣራ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"አልተሳካም፦ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"የግል ቦታ"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"የግል መተግበሪያዎች እንደተቆለፉ እና እንደተበቁ እንዲቆዩ ያድርጉ"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"ለማዋቀር ወይም ለመክፈት መታ ያድርጉ"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"የግል"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"የግል ቦታ ቅንብሮች"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"የግል ቦታን ቆልፍ/ክፈት"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 7ae39e3..30284b0 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"الشاشة الرئيسية"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"تقسيم الشاشة"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"‏معلومات تطبيق %1$s"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"حفظ إعدادات ميزة \"استخدام تطبيقين في الوقت نفسه\""</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"حفظ استخدام التطبيقين معًا"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | ‏<xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"لا يمكن استخدام هذين التطبيقَين في الوقت نفسه على هذا الجهاز"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"افتح الجهاز لاستخدام هذين التطبيقَين في الوقت نفسه"</string>
@@ -39,7 +39,7 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏العرض %1$d الطول %2$d"</string>
     <string name="widget_preview_context_description" msgid="9045841361655787574">"أداة <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
     <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"‏التطبيق المصغّرة \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\"، بعرض ‎%2$d وارتفاع ‎%3$d"</string>
-    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"انقر مع الاستمرار على التطبيق المصغّر لنقله إلى الشاشة الرئيسية."</string>
+    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"انقر مع الاستمرار على التطبيق المصغّر لنقله إلى الشاشة الرئيسية"</string>
     <string name="add_to_home_screen" msgid="9168649446635919791">"إضافة إلى الشاشة الرئيسية"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"تمت إضافة الأداة <xliff:g id="WIDGET_NAME">%1$s</xliff:g> إلى الشاشة الرئيسية."</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"اقتراحات"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"فلتر"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"تعذَّر <xliff:g id="WHAT">%1$s</xliff:g>."</string>
     <string name="private_space_label" msgid="2359721649407947001">"مساحة خاصة"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"إبقاء التطبيقات الخاصة مقفلة ومخفية"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"النقر للإعداد أو الفتح"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"المساحة الخاصة"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"إعدادات المساحة الخاصة"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"قفل المساحة الخاصة أو فتح قفلها"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 47586d9..f7775ab 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -187,11 +187,11 @@
     <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"আনপজ কৰক"</string>
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ফিল্টাৰ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"বিফল: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
-    <string name="private_space_label" msgid="2359721649407947001">"ব্যক্তিগত স্পে’চ"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"ব্যক্তিগত এপ্‌সমূহ লক কৰি লুকুৱাই ৰাখক"</string>
+    <string name="private_space_label" msgid="2359721649407947001">"প্ৰাইভেট স্পে\'চ"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"ছেট আপ কৰিবলৈ টিপক অথবা খোলক"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ব্যক্তিগত"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"ব্যক্তিগত স্পে’চৰ ছেটিং"</string>
-    <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ব্যক্তিগত স্পে’চ লক/আনলক কৰক"</string>
+    <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"প্ৰাইভেট স্পে\'চ লক/আনলক কৰক"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"লক কৰক"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"ব্যক্তিগত স্পে’চৰ স্থানান্তৰণ"</string>
     <string name="ps_add_button_label" msgid="8611055839242385935">"এপ্‌ ইনষ্টল কৰক"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 4631f61..d9f4acd 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtr"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Alınmadı: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Şəxsi yer"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Şəxsi tətbiqləri kilidli və gizli saxlayın"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Toxunaraq ayarlayın və ya açın"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Şəxsi"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Şəxsi məkan ayarları"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Şəxsi məkanı kilidləyin/kiliddən çıxarın"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index d96d660..2ae1c59 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -43,7 +43,7 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na početni ekran"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Dodali ste vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> na početni ekran"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Predlozi"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovne aplikacije"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Neophodne aplikacije"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Novosti i časopisi"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona za opuštanje"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privatni prostor"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Neka privatne aplikacije budu zaključane i sakrivene"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Dodirnite da biste podesili ili otvorili"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privatno"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Podešavanja privatnog prostora"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaključaj/otključaj privatni prostor"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 7c33ff6..c5147ed 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Фільтр"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Не ўдалося: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Прыватная вобласць"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Схавайце прыватныя праграмы ў асобную прастору і закрыйце доступ да яе"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Націсніце, каб наладзіць або адкрыць"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Прыватная"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Налады прыватнай вобласці"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заблакіраваць (разблакіраваць) прыватную вобласць"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 2a9ac1b..ae94d14 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтър"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Неуспешно: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Лично пространство"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Заключване и скриване на частните приложения"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Докоснете за настройване или отваряне"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Лично"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Настройки за личното пространство"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заключване/отключване на личното пространство"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index ca6ee84..d25b9c5 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ফিল্টার"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"কাজটি করা যায়নি: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"ব্যক্তিগত স্পেস"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"ব্যক্তিগত অ্যাপ লক করে লুকিয়ে রাখুন"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"সেট-আপ করতে বা খুলতে ট্যাপ করুন"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ব্যক্তিগত"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"ব্যক্তিগত স্পেসের সেটিংস"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ব্যক্তিগত স্পেস লক/আনলক করুন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 96f45c1..42365c1 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -39,7 +39,7 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
     <string name="widget_preview_context_description" msgid="9045841361655787574">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
     <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, širina %2$d, visina %3$d"</string>
-    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Dodirnite i držite vidžet da ga pomjerate po početnom ekranu"</string>
+    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Dodirnite i zadržite vidžet da ga pomjerate po početnom ekranu"</string>
     <string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na početni ekran"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> je dodan na početni ekran"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Prijedlozi"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privatan prostor"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Ostavite privatne aplikacije zaključane i sakrivene"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Dodirnite da biste postavili ili otvorili"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privatno"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Postavke privatnog prostora"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaključavanje/otključavanje privatnog prostora"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 666cb15..933084f 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -118,7 +118,7 @@
     <string name="app_pair_name_format" msgid="8134106404716224054">"Parella d\'aplicacions: <xliff:g id="APP1">%1$s</xliff:g> i <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Estil i fons de pantalla"</string>
     <string name="edit_home_screen" msgid="8947858375782098427">"Edita la pantalla d\'inici"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Config. pantalla d\'inici"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Configuració de la pantalla d\'inici"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
     <string name="allow_rotation_title" msgid="7222049633713050106">"Permet la rotació de la pantalla d\'inici"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"En girar el telèfon"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Espai privat"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Mantén les aplicacions privades bloquejades i amagades"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Toca per configurar o obrir"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Configuració d\'Espai privat"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloqueja o desbloqueja Espai privat"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 1d02668..267f40b 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"Domů"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdělit obrazovku"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informace o aplikaci %1$s"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"Uložit pár aplikací"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"Uložit dvojici aplikací"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Tento pár aplikací není na tomto zařízení podporován"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"Pokud chcete použít tento pár aplikací, rozložte zařízení"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtr"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Selhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Soukromý prostor"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Mějte soukromé aplikace uzamknuté a skryté"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Klepnutím nastavíte nebo otevřete"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Soukromé"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Nastavení soukromého prostoru"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zamknout/odemknout soukromý prostor"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 2f1aa65..65347e0 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Mislykket: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privat område"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Hold private apps låste og skjulte"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Tryk for at konfigurere eller åbne"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Indstillinger for privat rum"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås/oplås det private område"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index e19a9b0..f6fd609 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -188,7 +188,8 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Fehler: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privates Profil"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Hier kannst du deine privaten Apps verstecken und sperren"</string>
+    <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
+    <skip />
     <string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Einstellungen für privaten Bereich"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privaten Bereich sperren/entsperren"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 55575e7..838ccc7 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Φίλτρο"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Αποτυχία: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Ιδιωτικός χώρος"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Διατηρήστε τις ιδιωτικές εφαρμογές κλειδωμένες και κρυφές"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Πάτημα για ρύθμιση ή άνοιγμα"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Ιδιωτικό"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Ρυθμίσεις Ιδιωτικού χώρου"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Κλείδωμα/Ξεκλείδωμα Ιδιωτικού χώρου"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index d86770a..6a7fbb7 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Private space"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Keep private apps locked and hidden"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Tap to set up or open"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Private"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space Settings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lock/Unlock Private Space"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 2967941..53c0074 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Private space"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Keep private apps locked and hidden"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Tap to set up or open"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Private"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space Settings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lock/Unlock Private Space"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index d86770a..6a7fbb7 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Private space"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Keep private apps locked and hidden"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Tap to set up or open"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Private"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space Settings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lock/Unlock Private Space"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index d86770a..6a7fbb7 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Private space"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Keep private apps locked and hidden"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Tap to set up or open"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Private"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space Settings"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lock/Unlock Private Space"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index dab6052..f95149f 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎Filter‎‏‎‎‏‎"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎Failed: ‎‏‎‎‏‏‎<xliff:g id="WHAT">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="private_space_label" msgid="2359721649407947001">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‎‏‎Private space‎‏‎‎‏‎"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‎Keep private apps locked and hidden‎‏‎‎‏‎"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎Tap to set up or open‎‏‎‎‏‎"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎Private‎‏‎‎‏‎"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‏‎Private Space Settings‎‏‎‎‏‎"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‎‏‎‏‏‎Lock/Unlock Private Space‎‏‎‎‏‎"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 21325b9..02335ab 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"Pantalla principal"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"Información de la app de %1$s"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"Guardar vinculación de apps"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"Guardar vinculación"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"No se admite esta vinculación de apps en este dispositivo"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"Abre el dispositivo para usar esta vinculación de apps"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Espacio privado"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Mantén las apps privadas bloqueadas y ocultas"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Presiona para configurar o abrir"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privado"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Configuración de Espacio privado"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear o desbloquear Espacio privado"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 1858290..9e494ad 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Se ha producido un error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Espacio privado"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Bloquea y oculta tus aplicaciones privadas"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Toca para configurarlo o abrirlo"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privado"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Ajustes del espacio privado"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear/Desbloquear espacio privado"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 26110ae..4cbf751 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nurjus: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privaatne ruum"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Hoidke privaatsed rakendused lukustatud ja peidetuna"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Seadistamiseks või avamiseks puudutage"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privaatne"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Privaatse ruumi seaded"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privaatse ruumi lukustamine/avamine"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index a722a58..71ef150 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Iragazi"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Huts egin du: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Eremu pribatua"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Mantendu aplikazio pribatuak blokeatuta eta ezkutatuta"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Sakatu konfiguratzeko edo irekitzeko"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Pribatua"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Eremu pribatuaren ezarpenak"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Blokeatu/Desblokeatu eremu pribatua"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index d69a590..bf9263b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"فیلتر"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ناموفق بود: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"فضای خصوصی"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"برنامه‌های خصوصی قفل و پنهان نگه داشته می‌شود"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"برای راه‌اندازی یا باز کردن، ضربه بزنید"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"خصوصی"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"تنظیمات «فضای خصوصی»"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"قفل/ باز کردن «فضای خصوصی»"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 81790b9..0e8573d 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Suodatin"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Epäonnistui: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Yksityinen tila"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Pidä yksityiset sovellukset lukittuna ja piilossa"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Ota käyttöön tai avaa napauttamalla"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Yksityinen"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Yksityisen tilan asetukset"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lukitse yksityinen tila / avaa sen lukitus"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index d21fef4..d47e63e 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"Accueil"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran divisé"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"Renseignements sur l\'appli pour %1$s"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"Enregistrer la paire d\'applications"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"Enr. paire d\'applis"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Cette paire d\'applications n\'est pas prise en charge sur cet appareil"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"Déplier l\'appareil pour utiliser cette paire d\'applications"</string>
@@ -188,7 +188,8 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrer"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Espace privé"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Verrouiller et cacher les applications privées"</string>
+    <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
+    <skip />
     <string name="ps_container_title" msgid="4391796149519594205">"Privé"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Paramètres de l\'Espace privé"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Verrouiller/Déverrouiller l\'Espace privé"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 4244f23..627e996 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -123,8 +123,8 @@
     <string name="allow_rotation_title" msgid="7222049633713050106">"Autoriser la rotation de l\'écran d\'accueil"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"Pastilles de notification"</string>
-    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activé"</string>
-    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivé"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activées"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivées"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Accès aux notifications requis"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Pour afficher les pastilles de notification, activez les notifications de l\'application <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Modifier les paramètres"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtre"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Espace privé"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Gardez les applications privées verrouillées et masquées."</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Appuyer pour ouvrir ou configurer"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privé"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Paramètres d\'Espace privé"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Verrouiller/Déverrouiller Espace privé"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 1089ecf..e330b05 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"Inicio"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"Información da aplicación para %1$s"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"Gardar emparellamento de aplicacións"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"Gardar parella de aplicacións"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"O dispositivo non admite este emparellamento de aplicacións"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"Desprega o dispositivo para usar este emparellamento de aplicacións"</string>
@@ -87,7 +87,7 @@
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicacións de traballo"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicación"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Información da app"</string>
     <string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalar en privado"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suxerir aplicación"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Erro: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Espazo privado"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Manter bloqueadas e ocultas as aplicacións privadas"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Toca para configuralo ou abrilo"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privado"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Configuración do espazo privado"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear ou desbloquear o espazo privado"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index a30af7e..912d798 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ફિલ્ટર કરો"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"નિષ્ફળ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"ખાનગી સ્પેસ"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"ખાનગી ઍપને લૉક કરેલી અને છુપાવેલી રાખો"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"સેટઅપ કરવા કે ખોલવા માટે ટૅપ કરો"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ખાનગી"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"ખાનગી સ્પેસના સેટિંગ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ખાનગી સ્પેસને લૉક/અનલૉક કરો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index f65584b..0c4da56 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"होम स्क्रीन"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रीन"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s के लिए ऐप्लिकेशन की जानकारी"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"साथ में इस्तेमाल किए जा सकने वाले ऐप्लिकेशन की जानकारी सेव करें"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"ऐप पेयर सेव करें"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"साथ में इस्तेमाल किए जा सकने वाले ये ऐप्लिकेशन, इस डिवाइस पर काम नहीं कर सकते"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"साथ में इस्तेमाल किए जा सकने वाले ये ऐप्लिकेशन इस्तेमाल करने के लिए डिवाइस को अनफ़ोल्ड करें"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"फ़िल्टर"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"पूरा नहीं हुआ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"प्राइवेट स्पेस"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"निजी ऐप्लिकेशन, लॉक करें और छिपाकर रखें"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"सेट अप करने या खोलने के लिए टैप करें"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"निजी"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"प्राइवेट स्पेस सेटिंग"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"प्राइवेट स्पेस को लॉक करें/अनलॉक करें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 5ae78a3..b754e95 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privatni prostor"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Neka privatne aplikacije ostanu zaključane i skrivene"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Dodirnite da biste postavili ili otvorili"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privatno"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Postavke privatnog prostora"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaključavanje/otključavanje privatnog prostora"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 0a05ce4..626a79e 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Szűrő"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Sikertelen: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privát terület"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Privát alkalmazások zárolásának és rejtve tartásának fenntartása"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Koppintson a beállításhoz vagy a megnyitáshoz"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privát"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Privát terület beállításai"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privát terület zárolása/zárolásának feloldása"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 7bb79e5..e1aaad2 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"Հիմնական էկրան"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Տրոհել էկրանը"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"Տեղեկություններ %1$s հավելվածի մասին"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"Պահել հավելվածների զույգը"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"Պահել հավելվ. զույգը"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Հավելվածների զույգը չի աջակցվում այս սարքում"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"Բացեք սարքը՝ այս հավելվածների զույգն օգտագործելու համար"</string>
@@ -188,12 +188,12 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Զտեք"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Չհաջողվեց կատարել գործողությունը (<xliff:g id="WHAT">%1$s</xliff:g>)"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Անձնական տարածք"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Անձնական հավելվածները պահեք կողպված և թաքցված"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Հպեք կարգավորելու կամ բացելու համար"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Անձնական"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Անձնական տարածքի կարգավորումներ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Կողպել/ապակողպել անձնական տարածքը"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Կողպում"</string>
-    <string name="ps_container_transition" msgid="8667331812048014412">"Անցում անձնական տարածք"</string>
+    <string name="ps_container_transition" msgid="8667331812048014412">"Անցում մասնավոր տարածք"</string>
     <string name="ps_add_button_label" msgid="8611055839242385935">"Հավելվածների տեղադրում"</string>
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Հավելվածների տեղադրում անձնական տարածքում"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Լրացուցիչ ընտրացանկ"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 246367d..d0b0964 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Ruang pribadi"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Tetap kunci dan sembunyikan aplikasi pribadi"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Ketuk untuk menyiapkan atau membuka"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Pribadi"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Setelan Ruang Pribadi"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Kunci/Buka Kunci Ruang Pribadi"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 23899ad..506d68e 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Sía"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Mistókst: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Einkarými"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Haltu einkaforritum læstum og földum"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Ýttu til að setja upp eða opna"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Lokað"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Stillingar einkarýmis"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Læsaeinkarými/taka einkarými úr lás"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index e45f9dd..ed122fe 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Operazione non riuscita: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Spazio privato"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Mantieni le app private bloccate e nascoste"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Tocca per configurare o aprire"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privato"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Impostazioni dello Spazio privato"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Blocca/sblocca Spazio privato"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 41c08e2..b25150a 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"בית"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"מסך מפוצל"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"‏פרטים על האפליקציה %1$s"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"שמירה של צמד אפליקציות"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"שמירת צמד אפליקציות"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"צמד האפליקציות הזה לא נתמך במכשיר הזה"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"צריך לפתוח את המכשיר כדי להשתמש בצמד האפליקציות הזה"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"סינון"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"הפעולה נכשלה: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"מרחב פרטי"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"נעילה והסתרה של אפליקציות פרטיות"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"אפשר להקיש כדי להגדיר או לפתוח"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"פרטי"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"הגדרות המרחב הפרטי"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"נעילה או ביטול הנעילה של המרחב הפרטי"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index d467f2c..b8977be 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"フィルタ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"失敗: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"プライベート スペース"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"限定公開アプリをロックして非表示"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"設定したり開いたりするにはタップしてください"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"プライベート"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"プライベート スペースの設定"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"プライベート スペースをロック / ロック解除する"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index e473052..6f7bd9b 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ფილტრი"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ვერ მოხერხდა: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"პირადი სივრცე"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"პირადი აპების ჩაკეტვა და დამალვა"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"დასაყენებლად ან გასახსნელად შეეხეთ"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"პირადი"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"პირადი სივრცის პარამეტრები"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"პირადი სივრცის ჩაკეტვა/განბლოკვა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index fce1090..4156253 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Сүзгі"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Қате шықты: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Жеке бөлме"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Құпия кеңістіктегі қолданбаларды құлыптаулы және жасырын күйде қалдырыңыз."</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Реттеу немесе ашу үшін түртіңіз"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Жеке"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Жеке бөлме параметрлері"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Жеке бөлмені құлыптау/оның құлпын ашу"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 817bb35..3aca213 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"តម្រង"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"បានបរាជ័យ៖ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"បន្ទប់​ឯកជន"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"រក្សាកម្មវិធីឯកជនឱ្យនៅជាប់សោ និងលាក់"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"ចុចដើម្បីរៀបចំ ឬបើក"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ឯកជន"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"ការកំណត់ Private Space"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ចាក់សោ/ដោះសោ Private Space"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index f0e61cf..93c63ff 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"ಹೋಮ್"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ಗಾಗಿ ಆ್ಯಪ್ ಮಾಹಿತಿ"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"ಆ್ಯಪ್ ಜೋಡಿ ಉಳಿಸಿ"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"ಆ್ಯಪ್ ಪೇರ್ ಸೇವ್ ಮಾಡಿ"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ಈ ಆ್ಯಪ್ ಜೋಡಿಯು ಈ ಸಾಧನದಲ್ಲಿ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"ಈ ಆ್ಯಪ್ ಜೋಡಿಯನ್ನು ಬಳಸಲು ಸಾಧನವನ್ನು ಅನ್‌ಫೋಲ್ಡ್ ಮಾಡಿ"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ಫಿಲ್ಟರ್‌"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ವಿಫಲವಾಗಿದೆ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"ಖಾಸಗಿ ಸ್ಪೇಸ್"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"ಖಾಸಗಿ ಆ್ಯಪ್‌ಗಳನ್ನು ಲಾಕ್ ಮಾಡಿ ಮತ್ತು ಮರೆಮಾಡಿ"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"ಸೆಟಪ್ ಮಾಡಲು ಅಥವಾ ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ಖಾಸಗಿ"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಅನ್ನು ಲಾಕ್/ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index e916cef..5457674 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"필터"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"실패: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"비공개 스페이스"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"비공개 앱을 잠그고 숨겨진 상태로 유지"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"탭하여 설정 또는 열기"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"비공개"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"비공개 스페이스 설정"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"비공개 스페이스 잠금/잠금 해제"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 47eae5b..a0a605e 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"Башкы экран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Экранды бөлүү"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s колдонмосу жөнүндө маалымат"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"Эки колдонмону бир маалда пайдаланууну сактоо"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"Колдонмолорду сактап коюу"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Бул эки колдонмону бул түзмөктө бир маалда пайдаланууга болбойт"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"Бул эки колдонмону бир маалда пайдалануу үчүн түзмөктү ачыңыз"</string>
@@ -187,11 +187,11 @@
     <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Улантуу"</string>
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Чыпкалоо"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Аткарылган жок: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
-    <string name="private_space_label" msgid="2359721649407947001">"Жеке чөйрө"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Жеке колдонмолорду кулпулап жана жашырып коюңуз"</string>
+    <string name="private_space_label" msgid="2359721649407947001">"Жеке мейкиндик"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Тууралоо же ачуу үчүн таптап коюңуз"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Жеке"</string>
-    <string name="ps_container_settings" msgid="6059734123353320479">"Жеке чөйрөнүн параметрлери"</string>
-    <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Жеке чөйрөнү кулпулоо/кулпусун ачуу"</string>
+    <string name="ps_container_settings" msgid="6059734123353320479">"Жеке мейкиндиктин параметрлери"</string>
+    <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Жеке мейкиндикти кулпулоо/кулпусун ачуу"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Кулпулоо"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Жеке чөйрөгө өтүү"</string>
     <string name="ps_add_button_label" msgid="8611055839242385935">"Колдонмолорду орнотуу"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 658e8e3..e3142da 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ກັ່ນຕອງ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ບໍ່ສຳເລັດ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"ພື້ນທີ່ສ່ວນຕົວ"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"ລັອກ ແລະ ເຊື່ອງແອັບສ່ວນຕົວໄວ້"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"ແຕະເພື່ອຕັ້ງຄ່າ ຫຼື ເປີດ"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ສ່ວນຕົວ"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"ການຕັ້ງຄ່າພື້ນທີ່ສ່ວນຕົວ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ລັອກ/ປົດລັອກພື້ນທີ່ສ່ວນຕົວ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 4e8c453..f0573a9 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtruoti"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nepavyko: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privati erdvė"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Privačių programų užrakinimas ir slėpimas"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Palieskite, kad nustatytumėte arba atidarytumėte"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privatus"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Privačios erdvės nustatymai"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Užrakinti ir (arba) atrakinti privačią erdvę"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 3e1fcdb..8a4ab47 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrs"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Neizdevās: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privātā telpa"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Paslēpiet privātās lietotnes un bloķējiet piekļuvi tām"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Pieskarieties, lai iestatītu vai atvērtu"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privātā mape"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Privātās mapes iestatījumi"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloķēt/atbloķēt privāto mapi"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index cf9c954..051ab2e 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -188,13 +188,13 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Не успеа: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Приватен простор"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Чувајте ги приватните апликации заклучени и скриени"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Допрете за да поставите или отворите"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Приватен"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Поставки за „Приватен простор“"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заклучување/отклучување на „Приватен простор“"</string>
     <string name="ps_container_lock_title" msgid="2640257399982364682">"Брава"</string>
     <string name="ps_container_transition" msgid="8667331812048014412">"Префрлање на „Приватен простор“"</string>
-    <string name="ps_add_button_label" msgid="8611055839242385935">"Инсталирање апликации"</string>
+    <string name="ps_add_button_label" msgid="8611055839242385935">"Инсталирајте апликации"</string>
     <string name="ps_add_button_content_description" msgid="3254274107740952556">"Инсталирање апликации во „Приватен простор“"</string>
     <string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Проширено балонче"</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 0b66a36..ce5ccc6 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ഫിൽട്ടർ ചെയ്യുക"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"പരാജയപ്പെട്ടു: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"സ്വകാര്യ സ്പേസ്"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"സ്വകാര്യ ആപ്പുകൾ ലോക്ക് ചെയ്ത് മറയ്‌ക്കുക"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"സജ്ജീകരിക്കാനോ തുറക്കാനോ ടാപ്പ് ചെയ്യുക"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"സ്വകാര്യം"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"സ്വകാര്യ സ്‌പേസ് ക്രമീകരണം"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"സ്വകാര്യ സ്‌പേസ് ലോക്ക് ചെയ്യുക/അൺലോക്ക് ചെയ്യുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 0feeb10..254947a 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Шүүлтүүр"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Амжилтгүй болсон: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Хувийн орон зай"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Хувийн аппуудыг түгжээтэй бөгөөд нуугдсан байлгана уу"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Тохируулах эсвэл нээхийн тулд товших"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Хувийн"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Private Space-н тохиргоо"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Private Space-г түгжих/түгжээг тайлах"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 58f4f0e..9e524ac 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -122,7 +122,7 @@
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
     <string name="allow_rotation_title" msgid="7222049633713050106">"होम स्क्रीन फिरवण्‍याची अनुमती द्या"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"फोन फिरवला जातो तेव्हा"</string>
-    <string name="notification_dots_title" msgid="9062440428204120317">"सूचना बिंदू"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"नोटिफिकेशन डॉट"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"सुरू"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"बंद"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"सूचनांच्या अ‍ॅक्सेसची आवश्यकता आहे"</string>
@@ -150,8 +150,8 @@
     <string name="action_add_to_workspace" msgid="215894119683164916">"होम स्क्रीनवर जोडा"</string>
     <string name="action_move_here" msgid="2170188780612570250">"आयटम येथे हलवा"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"आयटम मुख्य स्क्रीनवर जोडला"</string>
-    <string name="item_removed" msgid="851119963877842327">"आयटम काढला"</string>
-    <string name="undo" msgid="4151576204245173321">"पूर्ववत करा"</string>
+    <string name="item_removed" msgid="851119963877842327">"आयटम काढून टाकला"</string>
+    <string name="undo" msgid="4151576204245173321">"पहिल्यासारखे करा"</string>
     <string name="action_move" msgid="4339390619886385032">"आयटम हलवा"</string>
     <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> मधील <xliff:g id="NUMBER_0">%1$s</xliff:g> पंक्ती <xliff:g id="NUMBER_1">%2$s</xliff:g> स्तंभ यावर हलवा"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> स्थानावर हलवा"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"फिल्टर"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"हे करता आले नाही: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"खाजगी स्पेस"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"खाजगी अ‍ॅप्स लॉक करून आणि लपवून ठेवा"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"सेट करण्यासाठी किंवा उघडण्यासाठी टॅप करा"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"खाजगी"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"खाजगी स्पेस ची सेटिंग्ज"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"खाजगी स्पेस लॉक/अनलॉक करा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 7832705..d2a8732 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -187,8 +187,8 @@
     <string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Nyahjeda"</string>
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Tapis"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
-    <string name="private_space_label" msgid="2359721649407947001">"Ruang peribadi"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Pastikan apl peribadi kekal dikunci dan disembunyikan"</string>
+    <string name="private_space_label" msgid="2359721649407947001">"Ruang privasi"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Ketik untuk menyediakan atau membuka"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Peribadi"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Tetapan Ruang Peribadi"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Kunci/Buka kunci Ruang Peribadi"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 80fef4b..d32e6f1 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -39,7 +39,7 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"အလျား %1$d နှင့် အမြင့် %2$d"</string>
     <string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်"</string>
     <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်၊ အကျယ် %2$d နှင့် အမြင့် %3$d"</string>
-    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"ပင်မစာမျက်နှာတွင်ရွှေ့ရန် ဝိဂျက်ကို တို့ထိ၍ ဖိထားပါ"</string>
+    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"ဝိဂျက်ကို တို့ထိ၍ ဖိထားပြီး ပင်မစာမျက်နှာပေါ်တွင် နေရာအမျိုးမျိုးသို့ ရွှေ့နိုင်သည်"</string>
     <string name="add_to_home_screen" msgid="9168649446635919791">"ပင်မစာမျက်နှာတွင် ထည့်ရန်"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်ကို ပင်မစာမျက်နှာတွင် ထည့်လိုက်ပြီ"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"အကြံပြုချက်"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"စစ်ထုတ်ရန်"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"မအောင်မြင်ပါ− <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"သီးသန့်ချတ်ခန်း"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"သီးသန့်အက်ပ်များကို လော့ခ်ချပြီး ဖျောက်ထားပါ"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"စနစ်ထည့်သွင်းရန် (သို့) ဖွင့်ရန် တို့ပါ"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"သီးသန့်"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"သီးသန့်ချတ်ခန်း ဆက်တင်များ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"သီးသန့်ချတ်ခန်း လော့ခ်ချ/ဖွင့်ရန်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index f0c947b..22002e8 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Mislyktes: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privat område"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Hold private apper låst og skjult"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Trykk for å konfigurere eller åpne"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Innstillinger for Private Space"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås / lås opp Private Space"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index e18dce4..15bc8e4 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"फिल्टर"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"कार्य पूरा गर्न सकिएन: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"निजी स्पेस"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"निजी एपहरू लक गरिराख्नुहोस् र लुकाइराख्नुहोस्"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"सेटअप गर्न वा खोल्न ट्याप गर्नुहोस्"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"निजी"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"निजी स्पेससम्बन्धी सेटिङ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"निजी स्पेस लक/अनलक गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index c16fb3c..b2e106c 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -39,7 +39,7 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed en %2$d hoog"</string>
     <string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
     <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d breed bij %3$d hoog"</string>
-    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Tik op de widget en houd vast om deze te verplaatsen op het startscherm"</string>
+    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Houd je vinger op de widget om deze te verplaatsen op het startscherm"</string>
     <string name="add_to_home_screen" msgid="9168649446635919791">"Toevoegen aan startscherm"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> toegevoegd aan startscherm"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggesties"</string>
@@ -177,7 +177,7 @@
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
     <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Werk-apps hebben badges en zijn zichtbaar voor je IT-beheerder"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
-    <string name="work_apps_paused_title" msgid="3040901117349444598">"Werk-apps zijn onderbroken"</string>
+    <string name="work_apps_paused_title" msgid="3040901117349444598">"Werk-apps zijn gepauzeerd"</string>
     <string name="work_apps_paused_info_body" msgid="1687828929959237477">"Je krijgt geen meldingen van je werk-apps"</string>
     <string name="work_apps_paused_body" msgid="261634750995824906">"Je werk-apps kunnen je geen meldingen sturen, je batterij niet gebruiken en geen toegang krijgen tot je locatie"</string>
     <string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"Je krijgt geen telefoongesprekken, tekstberichten of meldingen van je werk-apps"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filteren"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Mislukt: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privéruimte"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Privé-apps vergrendeld en verborgen houden"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Tik om in te stellen of te openen"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privé"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Instellingen voor privéruimte"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privéruimte vergrendelen/ontgrendelen"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 21b5f03..bdd39e1 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -87,7 +87,7 @@
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"କାର୍ଯ୍ୟକାରୀ ଆପ୍‌ ତାଲିକା"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ଅନଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"ଆପ୍‌ ସୂଚନା"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ଆପ ସୂଚନା"</string>
     <string name="install_private_system_shortcut_label" msgid="1616889277073184841">"ପ୍ରାଇଭେଟରେ ଇନଷ୍ଟଲ କର"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"ଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ଆପ ପରାମର୍ଶ ଦିଅନ୍ତୁ ନାହିଁ"</string>
@@ -151,7 +151,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"ଆଇଟମ୍‌କୁ ଏଠାକୁ ଘୁଞ୍ଚାନ୍ତୁ"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"ହୋମ ସ୍କ୍ରିନରେ ଆଇଟମ ଯୋଗ କରାଗଲା"</string>
     <string name="item_removed" msgid="851119963877842327">"ଆଇଟମକୁ କାଢ଼ି ଦିଆଯାଇଛି"</string>
-    <string name="undo" msgid="4151576204245173321">"ପୂର୍ବବତ୍‍"</string>
+    <string name="undo" msgid="4151576204245173321">"ପୂର୍ବବତ କରନ୍ତୁ"</string>
     <string name="action_move" msgid="4339390619886385032">"ଆଇଟମ୍‌ ଘୁଞ୍ଚାନ୍ତୁ"</string>
     <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>ରେ ଧାଡି <xliff:g id="NUMBER_0">%1$s</xliff:g> ସ୍ତମ୍ଭ <xliff:g id="NUMBER_1">%2$s</xliff:g>କୁ ମୁଭ କରନ୍ତୁ"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ସ୍ଥିତିକୁ ନିଅନ୍ତୁ"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ଫିଲ୍ଟର୍"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ବିଫଳ ହୋଇଛି: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"ପ୍ରାଇଭେଟ ଆପ୍ସକୁ ଲକ ଏବଂ ଲୁକ୍କାୟିତ ରଖନ୍ତୁ"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"ସେଟ ଅପ କରିବା କିମ୍ବା ଖୋଲିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ପ୍ରାଇଭେଟ"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"ପ୍ରାଇଭେଟ ସ୍ପେସ ସେଟିଂସ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ପ୍ରାଇଭେଟ ସ୍ପେସକୁ ଲକ/ଅନଲକ କରନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 9ae784f..2b89ebf 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ਫਿਲਟਰ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ਇਹ ਕਾਰਵਾਈ ਅਸਫਲ ਹੋਈ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"ਨਿੱਜੀ ਸਪੇਸ"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"ਨਿੱਜੀ ਐਪਾਂ ਨੂੰ ਲਾਕ ਕਰ ਕੇ ਅਦਿੱਖ ਰੱਖੋ"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"ਸੈੱਟਅੱਪ ਕਰਨ ਜਾਂ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ਨਿੱਜੀ"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"ਨਿੱਜੀ ਸਪੇਸ ਸੰਬੰਧੀ ਸੈਟਿੰਗਾਂ"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ਨਿੱਜੀ ਸਪੇਸ ਨੂੰ ਲਾਕ/ਅਣਲਾਕ ਕਰੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index a765377..2c7908c 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtruj"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Niepowodzenie: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Obszar prywatny"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Pozostaw aplikacje prywatne zablokowane i ukryte"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Kliknij, aby skonfigurować lub otworzyć"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Prywatne"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Ustawienia obszaru prywatnego"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zablokuj/odblokuj obszar prywatny"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 946ed1b..25a6562 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrar"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Falhou: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Espaço privado"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Mantenha as apps privadas bloqueadas e ocultas"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Tocar para configurar ou abrir"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privado"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Definições do espaço privado"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear/desbloquear espaço privado"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 04688d1..2e10d80 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrar"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Falha: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Espaço particular"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Mantenha apps particulares bloqueados e ocultos"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Toque para configurar ou abrir"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Particular"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Configurações do Espaço particular"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear/desbloquear o Espaço particular"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 4605871..81f7250 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtru"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Eșuare: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Spațiu privat"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Păstrează aplicațiile private blocate și ascunse"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Atinge pentru a configura sau a deschide"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Setări spațiu privat"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Blochează / deblochează spațiul privat"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 9a40b84..9c35b9d 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"Главный экран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Разделить экран"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"Сведения о приложении \"%1$s\""</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"Сохранить настройки одновременного использования двух приложений"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"Сохранить приложения"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Одновременно использовать эти два приложения на устройстве нельзя."</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"Чтобы одновременно использовать эти два приложения, разложите устройство."</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Фильтр"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Не удалось выполнить действие (<xliff:g id="WHAT">%1$s</xliff:g>)."</string>
     <string name="private_space_label" msgid="2359721649407947001">"Личное пространство"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Приложения в личном пространстве скрыты и доступны только вам"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Нажмите, чтобы настроить или открыть"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Доступно только вам"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Настройки личного пространства"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Блокировка и разблокировка личного пространства"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 8780527..506c634 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"පෙරහන"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"අසාර්ථකයි: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"පෞද්ගලික ඉඩ"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"පෞද්ගලික යෙදුම් අගුලු දමා සඟවා තබා ගන්න"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"පිහිටුවීමට හෝ විවෘත කිරීමට තට්ටු කරන්න"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"පෞද්ගලික"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"පෞද්ගලික අවකාශ සැකසීම්"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"පෞද්ගලික අවකාශය අගුළු දමන්න/අගුළු හරින්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index e513497..58e280c 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrujte"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Zlyhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Súkromný priestor"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Ponechajte súkromné aplikácie uzamknuté a skryté"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Klepnutím nastavte alebo otvorte"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Súkromné"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Nastavenia súkromného priestoru"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Súkromný priestor zamykania a odomykania"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 70a4c08..8b67004 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtriranje"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Ni uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Zasebni prostor"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Naj vaše zasebne aplikacije ostanejo zaklenjene in skrite"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Dotaknite se, da nastavite ali odprete"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Zasebno"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Nastavitve zasebnega prostora"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaklepanje/odklepanje zasebnega prostora"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 51fcf13..fe3650a 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Dështoi: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Hapësira private"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Mbaji të kyçura dhe të fshehura aplikacionet private"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Trokit për të konfiguruar ose për të hapur"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Private"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Cilësimet e \"Hapësirës private\""</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Kyç/Shkyç \"Hapësirën private\""</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 7a32a54..5bae891 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -43,7 +43,7 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Додај на почетни екран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Додали сте виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> на почетни екран"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предлози"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основне апликације"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Неопходне апликације"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новости и часописи"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зона за опуштање"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забава"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Није успело: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Приватни простор"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Нека приватне апликације буду закључане и сакривене"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Додирните да бисте подесили или отворили"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Приватно"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Подешавања приватног простора"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Закључај/откључај приватни простор"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index df2539f..5bec2a9 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"Startskärm"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Delad skärm"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinformation för %1$s"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"Spara appar som ska användas tillsammans"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"Spara app-par"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"De här apparna som ska användas tillsammans stöds inte på den här enheten"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"Vik upp enheten för att använda de här apparna som ska användas tillsammans"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Misslyckades: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Privat rum"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Håll privata appar låsta och dolda"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Tryck för att ställa in eller öppna"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Inställningar för privat rum"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås eller lås upp ditt privata rum"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 4d3b22f..f0c28cd 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Kichujio"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Hitilafu: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Nafasi ya faragha"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Funga na ufiche programu za faragha"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Gusa ili uweke mipangilio au ufungue"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Faragha"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Mipangilio ya Nafasi ya Faragha"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Funga/Fungua Nafasi ya Faragha"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 9e82389..edca769 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"வடிப்பான்"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"தோல்வி: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"தனிப்பட்ட சேமிப்பிடம்"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"தனிப்பட்ட ஆப்ஸை லாக் செய்தும் மறைத்தும் வைக்கலாம்"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"அமைக்கவோ திறக்கவோ தட்டுங்கள்"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"தனிப்பட்டது"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"தனிப்பட்ட சேமிப்பிட அமைப்புகள்"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"தனிப்பட்ட சேமிப்பிடத்தை லாக்/அன்லாக் செய்யும்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 9ad29c6..20649ad 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ఫిల్టర్ చేయి"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"విఫలమైంది: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"ప్రైవేట్ స్పేస్"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"ప్రైవేట్ యాప్‌లను లాక్ చేసి దాచి ఉంచండి"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"సెటప్ చేయడానికి లేదా తెరవడానికి ట్యాప్ చేయండి"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ప్రైవేట్"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"ప్రైవేట్ స్పేస్ సెట్టింగ్‌లు"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ప్రైవేట్ స్పేస్‌ను లాక్/అన్‌లాక్ చేయండి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index dc92980..05b3910 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"ตัวกรอง"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ไม่สำเร็จ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"พื้นที่ส่วนตัว"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"ล็อกและซ่อนแอปส่วนตัวไว้"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"แตะเพื่อตั้งค่าหรือเปิด"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"ส่วนตัว"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"การตั้งค่าพื้นที่ส่วนตัว"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ล็อก/ปลดล็อกพื้นที่ส่วนตัว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 36a7b5c..0ea7e16 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -29,7 +29,7 @@
     <string name="home_screen" msgid="5629429142036709174">"Home"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"Impormasyon ng app para sa %1$s"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"I-save ang pares ng app"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"I-save ang app pair"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Hindi sinusuportahan sa device na ito ang pares ng app na ito"</string>
     <string name="app_pair_needs_unfold" msgid="4588897528143807002">"I-unfold ang device para magamit ang pares ng app na ito"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Hindi nagawa: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Pribadong space"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Panatilihing naka-lock at nakatago ang mga pribadong app"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"I-tap para i-set up o buksan"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Pribado"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Mga Setting ng Pribadong Space"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"I-lock/I-unlock ang Pribadong Space"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 8c00ec3..d509343 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtre"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Başarısız: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Gizli alan"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Özel uygulamaları kilitli ve gizli tutun"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Kurmak veya açmak için dokunun"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Gizli"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Gizli Alan Ayarları"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Gizli Alanı Kilitleyin/Kilidini Açın"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index a8211f5..7f586ba 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Фільтр"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Не вдалося <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Приватний простір"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Зберігайте приватні додатки в прихованому й заблокованому сховищі"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Натисніть, щоб налаштувати або відкрити"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Приватні"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Налаштування приватного простору"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заблокувати/розблокувати приватний простір"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 430c835..57cb87f 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"فلٹر"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ناکام ہو گيا: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"نجی اسپیس"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"نجی ایپس کو مقفل اور پوشیدہ رکھیں"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"سیٹ اپ کرنے یا کھولنے کے لیے تھپتھپائیں"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"نجی"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"نجی اسپیس کی ترتیبات"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"نجی اسپیس کو مقفل کریں/غیر مقفل کریں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 8e03a13..eacf845 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Saralash"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Xato: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Shaxsiy xona"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Maxfiy ilovalar qulflangan va yashirin saqlansin"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Sozlash yoki ochish uchun bosing"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Yopiq"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Shaxsiy xona sozlamalari"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Shaxsiy xonani ochish/qulflash"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 17817ac..5e76aed 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Bộ lọc"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Không thực hiện được thao tác: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Không gian riêng tư"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Luôn khoá và ẩn các ứng dụng riêng tư"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Nhấn để thiết lập hoặc mở"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Riêng tư"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Cài đặt không gian riêng tư"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Khoá/mở khoá không gian riêng tư"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 4e77075..3b1d0c7 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -29,17 +29,17 @@
     <string name="home_screen" msgid="5629429142036709174">"主屏幕"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"分屏"</string>
     <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 的应用信息"</string>
-    <string name="save_app_pair" msgid="5647523853662686243">"保存应用对"</string>
+    <string name="save_app_pair" msgid="5647523853662686243">"保存应用组合"</string>
     <string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
     <string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"在该设备上无法使用此应用对"</string>
-    <string name="app_pair_needs_unfold" msgid="4588897528143807002">"展开设备即可使用此应用对"</string>
+    <string name="app_pair_needs_unfold" msgid="4588897528143807002">"展开设备即可使用此应用组合"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"轻触并按住即可移动微件。"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"点按两次并按住微件即可移动该微件或使用自定义操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"宽 %1$d,高 %2$d"</string>
     <string name="widget_preview_context_description" msgid="9045841361655787574">"“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件"</string>
     <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件,宽 %2$d,高 %3$d"</string>
-    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"轻触并按住此微件即可在主屏幕上随意移动它"</string>
+    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"轻触并按住此微件即可在主屏幕上随意移动"</string>
     <string name="add_to_home_screen" msgid="9168649446635919791">"添加到主屏幕"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已将“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件添加到主屏幕"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"建议"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"过滤器"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"失败:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"私密空间"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"让专用应用保持锁定状态并隐藏"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"点按即可设置或打开"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"私密"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"私密空间设置"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"锁定/解锁私密空间"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 735ff2f..da8b547 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"篩選器"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"操作失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"私人空間"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"讓私人應用程式保持鎖定及隱藏"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"輕按即可設定或開啟"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"私人"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"「私人空間」設定"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"鎖定/解鎖「私人空間」"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index b74bde2..6a7313f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -39,7 +39,7 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"寬度為 %1$d,高度為 %2$d"</string>
     <string name="widget_preview_context_description" msgid="9045841361655787574">"「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具"</string>
     <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具 (寬 %2$d,高 %3$d)"</string>
-    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"按住小工具即可將它移到主畫面上的任何位置"</string>
+    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"按住小工具即可拖曳到主畫面的任何位置"</string>
     <string name="add_to_home_screen" msgid="9168649446635919791">"新增至主畫面"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已將「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具新增到主畫面"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"建議"</string>
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"篩選器"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"私人空間"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"讓私人應用程式保持鎖定及隱藏的狀態"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"輕觸即可設定或開啟"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"私人"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"私人空間設定"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"鎖定/取消鎖定私人空間"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 3fdb6a7..3db9e5d 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -188,7 +188,7 @@
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Hlunga"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Yehlulekile: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
     <string name="private_space_label" msgid="2359721649407947001">"Isikhala esiyimfihlo"</string>
-    <string name="private_space_secondary_label" msgid="611902414159280263">"Gcina ama-app angasese ekhiyiwe futhi efihliwe"</string>
+    <string name="private_space_secondary_label" msgid="9203933341714508907">"Thepha ukuze usethe noma uvule"</string>
     <string name="ps_container_title" msgid="4391796149519594205">"Okuyimfihlo"</string>
     <string name="ps_container_settings" msgid="6059734123353320479">"Amasethingi Esikhala Esiyimfihlo"</string>
     <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Khiya/Vula Isikhala Esiyimfihlo"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 9b4460a..8c58931 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -492,7 +492,7 @@
     <dimen name="ps_header_image_height">48dp</dimen>
     <dimen name="ps_header_text_height">24dp</dimen>
     <dimen name="ps_header_layout_margin">16dp</dimen>
-    <dimen name="ps_header_settings_icon_margin_end">8dp</dimen>
+    <dimen name="ps_header_settings_icon_margin_end">4dp</dimen>
     <dimen name="ps_header_text_size">16sp</dimen>
     <dimen name="ps_button_height">40dp</dimen>
     <dimen name="ps_button_width">40dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index eb3441e..c442195 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -480,7 +480,7 @@
     <!-- Private space tile label -->
     <string name="private_space_label">Private space</string>
     <!-- Private space tile secondary label -->
-    <string name="private_space_secondary_label">Keep private apps locked and hidden</string>
+    <string name="private_space_secondary_label">Tap to set up or open</string>
     <!-- Title for Private Space Container shown at the bottom of all apps drawer -->
     <string name="ps_container_title">Private</string>
     <!-- Description for Private Space Settings button -->
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index ce13bfb..0000000
--- a/settings.gradle
+++ /dev/null
@@ -1,5 +0,0 @@
-include ':IconLoader'
-project(':IconLoader').projectDir = new File(rootDir, 'iconloaderlib')
-
-include ':SharedLibWrapper'
-project(':SharedLibWrapper').projectDir = new File(rootDir, 'SharedLibWrapper')
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3ee1c61..4c8ed15 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -58,7 +58,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.dragndrop.DragOptions.PreDragCondition;
 import com.android.launcher3.dragndrop.DraggableView;
@@ -96,10 +95,10 @@
 public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
         IconLabelDotView, DraggableView, Reorderable {
 
-    private static final int DISPLAY_WORKSPACE = 0;
+    public static final int DISPLAY_WORKSPACE = 0;
     public static final int DISPLAY_ALL_APPS = 1;
-    private static final int DISPLAY_FOLDER = 2;
-    protected static final int DISPLAY_TASKBAR = 5;
+    public static final int DISPLAY_FOLDER = 2;
+    public static final int DISPLAY_TASKBAR = 5;
     public static final int DISPLAY_SEARCH_RESULT = 6;
     public static final int DISPLAY_SEARCH_RESULT_SMALL = 7;
     public static final int DISPLAY_PREDICTION_ROW = 8;
@@ -238,7 +237,7 @@
             defaultIconSize = getResources().getDimensionPixelSize(
                     R.dimen.search_row_small_icon_size);
         } else if (mDisplay == DISPLAY_TASKBAR) {
-            defaultIconSize = mDeviceProfile.iconSizePx;
+            defaultIconSize = mDeviceProfile.taskbarIconSize;
         } else {
             // widget_selection or shortcut_popup
             defaultIconSize = mDeviceProfile.iconSizePx;
@@ -436,8 +435,7 @@
     }
 
     @UiThread
-    @VisibleForTesting
-    public void applyLabel(ItemInfoWithIcon info) {
+    public void applyLabel(ItemInfo info) {
         CharSequence label = info.title;
         if (label != null) {
             mLastOriginalText = label;
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 9a5627a..58789fd 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -27,7 +27,7 @@
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -75,7 +75,7 @@
         }
 
         return (info instanceof LauncherAppWidgetInfo)
-                || (info instanceof FolderInfo);
+                || (info instanceof CollectionInfo);
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index e7d2843..cfa8967 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -202,6 +202,8 @@
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.StringCache;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.AppPairInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -661,6 +663,11 @@
         // #5 state handler
         return new OnBackAnimationCallback() {
             @Override
+            public void onBackStarted(BackEvent backEvent) {
+                Launcher.this.onBackStarted();
+            }
+
+            @Override
             public void onBackInvoked() {
                 onStateBack();
             }
@@ -798,13 +805,19 @@
     @Override
     public void invalidateParent(ItemInfo info) {
         if (info.container >= 0) {
-            View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container);
-            if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) {
+            View collectionIcon = getWorkspace().getHomescreenIconByItemId(info.container);
+            if (collectionIcon instanceof FolderIcon folderIcon
+                    && collectionIcon.getTag() instanceof FolderInfo) {
                 if (new FolderGridOrganizer(getDeviceProfile())
                         .setFolderInfo((FolderInfo) folderIcon.getTag())
                         .isItemInPreview(info.rank)) {
                     folderIcon.invalidate();
                 }
+            } else if (collectionIcon instanceof AppPairIcon appPairIcon
+                    && collectionIcon.getTag() instanceof AppPairInfo appPairInfo) {
+                if (appPairInfo.getContents().contains(info)) {
+                    appPairIcon.getIconDrawableArea().redraw();
+                }
             }
         }
     }
@@ -2003,24 +2016,26 @@
     public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb,
             @Nullable final String reason) {
         if (itemInfo instanceof WorkspaceItemInfo) {
-            // Remove the shortcut from the folder before removing it from launcher
-            View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
-            if (folderIcon instanceof FolderIcon) {
-                ((FolderInfo) folderIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true);
+            View collectionIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
+            if (collectionIcon instanceof FolderIcon) {
+                // Remove the shortcut from the folder before removing it from launcher
+                ((FolderInfo) collectionIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true);
+            } else if (collectionIcon instanceof AppPairIcon appPairIcon) {
+                removeItem(appPairIcon, appPairIcon.getInfo(), deleteFromDb,
+                        "removing app pair because one of its member apps was removed");
             } else {
                 mWorkspace.removeWorkspaceItem(v);
             }
             if (deleteFromDb) {
                 getModelWriter().deleteItemFromDatabase(itemInfo, reason);
             }
-        } else if (itemInfo instanceof FolderInfo) {
-            final FolderInfo folderInfo = (FolderInfo) itemInfo;
+        } else if (itemInfo instanceof CollectionInfo ci) {
             if (v instanceof FolderIcon) {
                 ((FolderIcon) v).removeListeners();
             }
             mWorkspace.removeWorkspaceItem(v);
             if (deleteFromDb) {
-                getModelWriter().deleteFolderAndContentsFromDatabase(folderInfo);
+                getModelWriter().deleteCollectionAndContentsFromDatabase(ci);
             }
         } else if (itemInfo instanceof LauncherAppWidgetInfo) {
             final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
@@ -2063,8 +2078,12 @@
         getOnBackAnimationCallback().onBackInvoked();
     }
 
+    protected void onBackStarted() {
+        mStateManager.getState().onBackStarted(this);
+    }
+
     protected void onStateBack() {
-        mStateManager.getState().onBackPressed(this);
+        mStateManager.getState().onBackInvoked(this);
     }
 
     protected void onScreenOnChanged(boolean isOn) {
@@ -2651,6 +2670,7 @@
 
         mModel.dumpState(prefix, fd, writer, args);
         mOverlayManager.dump(prefix, writer);
+        ACTIVITY_TRACKER.dump(prefix, writer);
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index be01d63..7fdfd72 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -47,12 +47,12 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
 import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseLauncherBinder;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.CacheDataUpdatedTask;
 import com.android.launcher3.model.ItemInstallQueue;
-import com.android.launcher3.model.LauncherBinder;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.model.ModelDelegate;
@@ -365,7 +365,7 @@
                     MAIN_EXECUTOR.execute(cb::clearPendingBinds);
                 }
 
-                LauncherBinder launcherBinder = new LauncherBinder(
+                BaseLauncherBinder launcherBinder = new BaseLauncherBinder(
                         mApp, mBgDataModel, mBgAllAppsList, callbacksList);
                 if (bindDirectly) {
                     // Divide the set of loaded items into those that we are binding synchronously,
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 27e084c..cb19b14 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -20,7 +20,6 @@
 import android.content.SharedPreferences
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener
 import android.util.Log
-import android.view.ViewConfiguration
 import androidx.annotation.VisibleForTesting
 import com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN
 import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
@@ -319,7 +318,7 @@
         val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
             nonRestorableItem(
                 "LPNH_TIMEOUT_MS",
-                ViewConfiguration.getLongPressTimeout(),
+                450,
                 EncryptionType.MOVE_TO_DEVICE_PROTECTED
             )
         @JvmField
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 6e66c14..3bdd863 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -424,20 +424,29 @@
         return TestProtocol.stateOrdinalToString(ordinal);
     }
 
-    public void onBackPressed(Launcher launcher) {
+    /** Called when predictive back gesture is started. */
+    public void onBackStarted(Launcher launcher) {}
+
+    /**
+     * Called when back action is invoked. This can happen when:
+     * 1. back button is pressed in 3-button navigation.
+     * 2. when back is committed during back swiped (predictive or non-predictive).
+     * 3. when we programmatically perform back action.
+     */
+    public void onBackInvoked(Launcher launcher) {
         if (this != NORMAL) {
             StateManager<LauncherState> lsm = launcher.getStateManager();
             LauncherState lastState = lsm.getLastState();
-            lsm.goToState(lastState, forEndCallback(this::onBackPressCompleted));
+            lsm.goToState(lastState, forEndCallback(this::onBackAnimationCompleted));
         }
     }
 
     /**
-     * To be called if back press is completed in a launcher state.
+     * To be called if back animation is completed in a launcher state.
      *
-     * @param success whether back press animation was successful or canceled.
+     * @param success whether back animation was successful or canceled.
      */
-    protected void onBackPressCompleted(boolean success) {
+    protected void onBackAnimationCompleted(boolean success) {
         // Do nothing. To be overridden by child class.
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index ca34dd1..ce3c55a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -95,6 +95,7 @@
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
+import com.android.launcher3.model.data.AppPairInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -3313,7 +3314,7 @@
                     }
                 } else if (child instanceof FolderIcon) {
                     FolderInfo folderInfo = (FolderInfo) info;
-                    List<WorkspaceItemInfo> matches = folderInfo.contents.stream()
+                    List<WorkspaceItemInfo> matches = folderInfo.getContents().stream()
                             .filter(matcher)
                             .collect(Collectors.toList());
                     if (!matches.isEmpty()) {
@@ -3322,6 +3323,11 @@
                             ((FolderIcon) child).getFolder().close(false /* animate */);
                         }
                     }
+                } else if (info instanceof AppPairInfo api) {
+                    // If an app pair's member apps are being removed, delete the whole app pair.
+                    if (api.anyMatch(matcher)) {
+                        mLauncher.removeItem(child, info, true);
+                    }
                 }
             }
         }
@@ -3373,9 +3379,9 @@
                 }
             } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
                 FolderInfo fi = (FolderInfo) info;
-                if (fi.contents.stream().anyMatch(matcher)) {
+                if (fi.anyMatch(matcher)) {
                     FolderDotInfo folderDotInfo = new FolderDotInfo();
-                    for (WorkspaceItemInfo si : fi.contents) {
+                    for (WorkspaceItemInfo si : fi.getContents()) {
                         folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si));
                     }
                     ((FolderIcon) v).setDotInfo(folderDotInfo);
diff --git a/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java
index 19d0421..29862ae 100644
--- a/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java
@@ -28,7 +28,7 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -45,6 +45,7 @@
     public enum DragType {
         ICON,
         FOLDER,
+        APP_PAIR,
         WIDGET
     }
 
@@ -103,7 +104,7 @@
                     && item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
         }
         return (item instanceof LauncherAppWidgetInfo)
-                || (item instanceof FolderInfo);
+                || (item instanceof CollectionInfo);
     }
 
     @Override
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 66b8216..bb25b6d 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -39,6 +39,8 @@
 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.AppPairInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -317,6 +319,8 @@
         mDragInfo.dragType = DragType.ICON;
         if (info instanceof FolderInfo) {
             mDragInfo.dragType = DragType.FOLDER;
+        } else if (info instanceof AppPairInfo) {
+            mDragInfo.dragType = DragType.APP_PAIR;
         } else if (info instanceof LauncherAppWidgetInfo) {
             mDragInfo.dragType = DragType.WIDGET;
         }
@@ -430,16 +434,16 @@
                         LauncherSettings.Favorites.CONTAINER_DESKTOP,
                         screenId, coordinates[0], coordinates[1]);
                 bindItem(info, accessibility, finishCallback);
-            } else if (item instanceof FolderInfo fi) {
+            } else if (item instanceof CollectionInfo ci) {
                 Workspace<?> workspace = mContext.getWorkspace();
                 workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
-                mContext.getModelWriter().addItemToDatabase(fi,
+                mContext.getModelWriter().addItemToDatabase(ci,
                         LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, coordinates[0],
                         coordinates[1]);
-                fi.contents.forEach(member -> {
-                    mContext.getModelWriter().addItemToDatabase(member, fi.id, -1, -1, -1);
-                });
-                bindItem(fi, accessibility, finishCallback);
+                ci.getContents().forEach(member ->
+                        mContext.getModelWriter()
+                                .addItemToDatabase(member, ci.id, -1, -1, -1));
+                bindItem(ci, accessibility, finishCallback);
             }
         }));
         return true;
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index a8624dd..52073cc 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -149,7 +149,7 @@
                 // Find the first item in the folder.
                 FolderInfo folder = (FolderInfo) info;
                 WorkspaceItemInfo firstItem = null;
-                for (WorkspaceItemInfo shortcut : folder.contents) {
+                for (WorkspaceItemInfo shortcut : folder.getContents()) {
                     if (firstItem == null || firstItem.rank > shortcut.rank) {
                         firstItem = shortcut;
                     }
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index fbeab4e..4b65b73 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -70,7 +70,6 @@
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.Flags;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.R;
@@ -127,7 +126,6 @@
     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;
@@ -192,8 +190,6 @@
     private float mTotalHeaderProtectionHeight;
     @Nullable private AllAppsTransitionController mAllAppsTransitionController;
 
-    private PrivateSpaceHeaderViewController mPrivateSpaceHeaderViewController;
-
     public ActivityAllAppsContainerView(Context context) {
         this(context, null);
     }
@@ -261,10 +257,6 @@
      */
     protected void initContent() {
         mMainAdapterProvider = mSearchUiDelegate.createMainAdapterProvider();
-        if (Flags.enablePrivateSpace()) {
-            mPrivateSpaceHeaderViewController =
-                    new PrivateSpaceHeaderViewController(this, mPrivateProfileManager);
-        }
 
         mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN,
                 new AlphabeticalAppsList<>(mActivityContext,
@@ -398,7 +390,7 @@
         mAllAppsTransitionController = allAppsTransitionController;
     }
 
-    private void animateToSearchState(boolean goingToSearch, long durationMs) {
+    void animateToSearchState(boolean goingToSearch, long durationMs) {
         if (!mSearchTransitionController.isRunning() && goingToSearch == isSearching()) {
             return;
         }
@@ -499,9 +491,9 @@
     }
 
     /**
-     * Exits search and returns to A-Z apps list. Scroll to the bottom.
+     * Exits search and returns to A-Z apps list. Scroll to the private space header.
      */
-    public void resetAndScrollToBottom() {
+    public void resetAndScrollToPrivateSpaceHeader() {
         if (mTouchHandler != null) {
             mTouchHandler.endFastScrolling();
         }
@@ -518,7 +510,13 @@
             // Switch to the main tab
             switchToTab(ActivityAllAppsContainerView.AdapterHolder.MAIN);
             // Scroll to bottom
-            getActiveRecyclerView().scrollToBottomWithMotion(SCROLL_TO_BOTTOM_DURATION);
+            if (mPrivateProfileManager != null) {
+                mPrivateProfileManager.scrollForViewToBeVisibleInContainer(
+                        getActiveAppsRecyclerView(),
+                        getPersonalAppList().getAdapterItems(),
+                        mPrivateProfileManager.getPsHeaderHeight(),
+                        mActivityContext.getDeviceProfile().allAppsCellHeightPx);
+            }
         });
     }
 
@@ -906,7 +904,7 @@
 
     protected BaseAllAppsAdapter<T> createAdapter(AlphabeticalAppsList<T> appsList) {
         return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
-                mMainAdapterProvider, mPrivateSpaceHeaderViewController);
+                mMainAdapterProvider);
     }
 
     // TODO(b/216683257): Remove when Taskbar All Apps supports search.
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 5f002b5..df383bf 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -73,9 +73,8 @@
 
 
     public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
-            AlphabeticalAppsList apps, SearchAdapterProvider<?> adapterProvider,
-            PrivateSpaceHeaderViewController privateSpaceHeaderViewController) {
-        super(activityContext, inflater, apps, adapterProvider, privateSpaceHeaderViewController);
+            AlphabeticalAppsList apps, SearchAdapterProvider<?> adapterProvider) {
+        super(activityContext, inflater, apps, adapterProvider);
         mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext);
         mGridLayoutMgr.setSpanSizeLookup(new GridSpanSizer());
         setAppsPerRow(activityContext.getDeviceProfile().numShownAllAppsColumns);
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 3678109..63f6227 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -295,9 +295,6 @@
         mLauncher.getScrimView().setScrimHeaderScale(scaleProgress);
 
         AllAppsRecyclerView rv = mLauncher.getAppsView().getActiveRecyclerView();
-        if (rv != null && rv.getScrollbar() != null) {
-            rv.getScrollbar().setVisibility(scaleProgress < 1f ? View.INVISIBLE : View.VISIBLE);
-        }
 
         // Disable view clipping from all apps' RecyclerView up to all apps view during scale
         // animation, and vice versa. The goal is to display extra roll(s) app icons (rendered in
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 81cfa86..4d4b8d2 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -206,7 +206,10 @@
      */
     @Override
     public void onAppsUpdated() {
-        if (mAllAppsStore == null) {
+        // Don't update apps when the private profile animations are running, otherwise the motion
+        // is canceled.
+        if (mAllAppsStore == null || (mPrivateProviderManager != null &&
+                mPrivateProviderManager.getAnimationRunning())) {
             return;
         }
         // Sort the list of apps
@@ -444,6 +447,10 @@
         return roundRegion;
     }
 
+    public PrivateProfileManager getPrivateProfileManager() {
+        return mPrivateProviderManager;
+    }
+
     private static class MyDiffCallback extends DiffUtil.Callback {
 
         private final List<AdapterItem> mOldList;
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 5e5795d..2190e1a 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -169,16 +169,9 @@
     protected final OnClickListener mOnIconClickListener;
     protected final OnLongClickListener mOnIconLongClickListener;
     protected OnFocusChangeListener mIconFocusListener;
-    private final PrivateSpaceHeaderViewController mPrivateSpaceHeaderViewController;
 
     public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
             AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider) {
-        this(activityContext, inflater, apps, adapterProvider, null);
-    }
-
-    public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
-            AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider,
-            PrivateSpaceHeaderViewController privateSpaceHeaderViewController) {
         mActivityContext = activityContext;
         mApps = apps;
         mLayoutInflater = inflater;
@@ -187,7 +180,6 @@
         mOnIconLongClickListener = mActivityContext.getAllAppsItemLongClickListener();
 
         mAdapterProvider = adapterProvider;
-        mPrivateSpaceHeaderViewController = privateSpaceHeaderViewController;
     }
 
     /** Checks if the passed viewType represents all apps divider. */
@@ -283,13 +275,10 @@
             case VIEW_TYPE_PRIVATE_SPACE_HEADER:
                 RelativeLayout psHeaderLayout = holder.itemView.findViewById(
                         R.id.ps_header_layout);
-                assert mPrivateSpaceHeaderViewController != null;
-                assert psHeaderLayout != null;
-                mPrivateSpaceHeaderViewController.addPrivateSpaceHeaderViewElements(psHeaderLayout);
+                mApps.getPrivateProfileManager().addPrivateSpaceHeaderViewElements(psHeaderLayout);
                 AdapterItem adapterItem = mApps.getAdapterItems().get(position);
                 int roundRegions = ROUND_TOP_LEFT | ROUND_TOP_RIGHT;
-                if (mPrivateSpaceHeaderViewController.getPrivateProfileManager().getCurrentState()
-                        == STATE_DISABLED) {
+                if (mApps.getPrivateProfileManager().getCurrentState() == STATE_DISABLED) {
                     roundRegions |= (ROUND_BOTTOM_LEFT | ROUND_BOTTOM_RIGHT);
                 }
                 adapterItem.decorationInfo =
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index e7bb1d0..eb967bc 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -16,26 +16,54 @@
 
 package com.android.launcher3.allapps;
 
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 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.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SettingsCache.PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.LayoutTransition;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.recyclerview.widget.LinearSmoothScroller;
+import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.app.animation.Interpolators;
 import com.android.launcher3.BuildConfig;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatedPropertySetter;
+import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.StatsLogManager;
@@ -45,6 +73,8 @@
 import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.RecyclerViewFastScroller;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -57,14 +87,17 @@
  * logic in the Personal tab.
  */
 public class PrivateProfileManager extends UserProfileManager {
-
+    private static final int EXPAND_COLLAPSE_DURATION = 800;
+    private static final int SETTINGS_OPACITY_DURATION = 160;
     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;
+    private boolean mIsAnimationRunning;
+    private int mHeaderHeight;
+    private boolean mAnimate;
 
     public PrivateProfileManager(UserManager userManager,
             ActivityAllAppsContainerView<?> allApps,
@@ -118,20 +151,19 @@
 
     /**
      * Disables quiet mode for Private Space User Profile.
-     * The runnable passed will be executed in the {@link #reset()} method,
-     * when Launcher receives update about profile availability.
-     * The runnable passed is only executed once, and reset after execution.
+     * When called from search, a runnable is set and executed in the {@link #reset()} method, when
+     * Launcher receives update about profile availability.
+     * The runnable is only executed once, and reset after execution.
      * In case the method is called again, before the previously set runnable was executed,
      * the runnable will be updated.
      */
-    public void unlockPrivateProfile(Runnable runnable) {
-        enableQuietMode(false);
-        mUnlockRunnable = runnable;
+    public void unlockPrivateProfile() {
+        setQuietMode(false);
     }
 
     /** Enables quiet mode for Private Space User Profile. */
-    public void lockPrivateProfile() {
-        enableQuietMode(true);
+    void lockPrivateProfile() {
+        setQuietMode(true);
     }
 
     /** Whether private profile should be hidden on Launcher. */
@@ -149,7 +181,9 @@
         setCurrentState(updatedState);
         resetPrivateSpaceDecorator(updatedState);
         if (transitioningFromLockedToUnlocked(previousState, updatedState)) {
-            applyUnlockRunnable();
+            postUnlock();
+        } else if (transitioningFromUnlockedToLocked(previousState, updatedState)){
+            executeLock();
         }
     }
 
@@ -235,23 +269,45 @@
         }
     }
 
-    /** Posts quiet mode enable/disable call for private profile. */
-    private void enableQuietMode(boolean enable) {
-        setQuietMode(enable);
+    @Override
+    public void setQuietMode(boolean enable) {
+        super.setQuietMode(enable);
+        mAnimate = true;
     }
 
-    void applyUnlockRunnable() {
-        if (mUnlockRunnable != null) {
-            // reset the runnable to prevent re-execution.
-            MAIN_EXECUTOR.post(mUnlockRunnable);
-            mUnlockRunnable = null;
+    /**
+     * Expand the private space after the app list has been added and updated from
+     * {@link AlphabeticalAppsList#onAppsUpdated()}
+     */
+    void postUnlock() {
+        if (mAllApps.isSearching()) {
+            MAIN_EXECUTOR.post(this::exitSearchAndExpand);
+        } else {
+            MAIN_EXECUTOR.post(this::expandPrivateSpace);
         }
     }
 
+    /** Collapses the private space before the app list has been updated. */
+    void executeLock() {
+        MAIN_EXECUTOR.execute(this::collapsePrivateSpace);
+    }
+
+    void setAnimationRunning(boolean isAnimationRunning) {
+        mIsAnimationRunning = isAnimationRunning;
+    }
+
+    boolean getAnimationRunning() {
+        return mIsAnimationRunning;
+    }
+
     private boolean transitioningFromLockedToUnlocked(int previousState, int updatedState) {
         return previousState == STATE_DISABLED && updatedState == STATE_ENABLED;
     }
 
+    private boolean transitioningFromUnlockedToLocked(int previousState, int updatedState) {
+        return previousState == STATE_ENABLED && updatedState == STATE_DISABLED;
+    }
+
     @Override
     public Predicate<UserHandle> getUserMatcher() {
         return mPrivateProfileMatcher;
@@ -266,4 +322,349 @@
                 && (appInfo.componentName == null
                 || !(mPreInstalledSystemPackages.contains(appInfo.componentName.getPackageName())));
     }
+
+    /** Add Private Space Header view elements based upon {@link UserProfileState} */
+    public void addPrivateSpaceHeaderViewElements(RelativeLayout parent) {
+        // Set the transition duration for the settings and lock button to animate.
+        ViewGroup settingAndLockGroup = parent.findViewById(R.id.settingsAndLockGroup);
+        if (mAnimate) {
+            enableLayoutTransition(settingAndLockGroup);
+        } else {
+            // Ensure any unwanted animations to not happen.
+            settingAndLockGroup.setLayoutTransition(null);
+        }
+
+        //Add quietMode image and action for lock/unlock button
+        ViewGroup lockButton =
+                parent.findViewById(R.id.ps_lock_unlock_button);
+        assert lockButton != null;
+        addLockButton(lockButton);
+
+        //Trigger lock/unlock action from header.
+        addHeaderOnClickListener(parent);
+
+        //Add image and action for private space settings button
+        ImageButton settingsButton = parent.findViewById(R.id.ps_settings_button);
+        assert settingsButton != null;
+        addPrivateSpaceSettingsButton(settingsButton);
+
+        //Add image for private space transitioning view
+        ImageView transitionView = parent.findViewById(R.id.ps_transition_image);
+        assert transitionView != null;
+        addTransitionImage(transitionView);
+        mHeaderHeight = parent.getHeight();
+    }
+
+    /**
+     *  Adds the quietModeButton and attach onClickListener for the header to animate different
+     *  states when clicked.
+     */
+    private void addLockButton(ViewGroup lockButton) {
+        TextView lockText = lockButton.findViewById(R.id.lock_text);
+        switch (getCurrentState()) {
+            case STATE_ENABLED -> {
+                lockText.setVisibility(VISIBLE);
+                lockButton.setVisibility(VISIBLE);
+                lockButton.setOnClickListener(view -> lockingAction(/* lock */ true));
+            }
+            case STATE_DISABLED -> {
+                lockText.setVisibility(GONE);
+                lockButton.setVisibility(VISIBLE);
+                lockButton.setOnClickListener(view -> lockingAction(/* lock */ false));
+            }
+            default -> lockButton.setVisibility(GONE);
+        }
+    }
+
+    private void addHeaderOnClickListener(RelativeLayout header) {
+        if (getCurrentState() == STATE_DISABLED) {
+            header.setOnClickListener(view -> lockingAction(/* lock */ false));
+        } else {
+            header.setOnClickListener(null);
+        }
+    }
+
+    /** Sets the enablement of the profile when header or button is clicked. */
+    private void lockingAction(boolean lock) {
+        logEvents(lock ? LAUNCHER_PRIVATE_SPACE_LOCK_TAP : LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
+        if (lock) {
+            lockPrivateProfile();
+        } else {
+            unlockPrivateProfile();
+        }
+    }
+
+    private void addPrivateSpaceSettingsButton(ImageButton settingsButton) {
+        if (getCurrentState() == STATE_ENABLED
+                && isPrivateSpaceSettingsAvailable()) {
+            settingsButton.setVisibility(VISIBLE);
+            settingsButton.setAlpha(1f);
+            settingsButton.setOnClickListener(
+                    view -> {
+                        logEvents(LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP);
+                        openPrivateSpaceSettings();
+                    });
+        } else {
+            settingsButton.setVisibility(GONE);
+        }
+    }
+
+    private void addTransitionImage(ImageView transitionImage) {
+        if (getCurrentState() == STATE_TRANSITION) {
+            transitionImage.setVisibility(VISIBLE);
+        } else {
+            transitionImage.setVisibility(GONE);
+        }
+    }
+
+    /** Finds the private space header to scroll to and set the private space icons to GONE. */
+    private void collapse() {
+        AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
+        List<BaseAllAppsAdapter.AdapterItem> appListAdapterItems =
+                allAppsRecyclerView.getApps().getAdapterItems();
+        for (int i = appListAdapterItems.size() - 1; i > 0; i--) {
+            BaseAllAppsAdapter.AdapterItem currentItem = appListAdapterItems.get(i);
+            // Scroll to the private space header.
+            if (currentItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
+                // Note: SmoothScroller is meant to be used once.
+                RecyclerView.SmoothScroller smoothScroller =
+                        new LinearSmoothScroller(mAllApps.getContext()) {
+                            @Override protected int getVerticalSnapPreference() {
+                                return LinearSmoothScroller.SNAP_TO_END;
+                            }
+                        };
+                smoothScroller.setTargetPosition(i);
+                RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
+                if (layoutManager != null) {
+                    layoutManager.startSmoothScroll(smoothScroller);
+                }
+                break;
+            }
+            // Make the private space apps gone to "collapse".
+            if (currentItem.decorationInfo != null) {
+                RecyclerView.ViewHolder viewHolder =
+                        allAppsRecyclerView.findViewHolderForAdapterPosition(i);
+                if (viewHolder != null) {
+                    viewHolder.itemView.setVisibility(GONE);
+                }
+            }
+        }
+    }
+
+    /**
+     * Upon expanding, only scroll to the item position in the adapter that allows the header to be
+     * visible.
+     */
+    public int scrollForViewToBeVisibleInContainer(
+            AllAppsRecyclerView allAppsRecyclerView,
+            List<BaseAllAppsAdapter.AdapterItem> appListAdapterItems,
+            int psHeaderHeight,
+            int allAppsCellHeight) {
+        int rowToExpandToWithRespectToHeader = -1;
+        int itemToScrollTo = -1;
+        // Looks for the item in the app list to scroll to so that the header is visible.
+        for (int i = 0; i < appListAdapterItems.size(); i++) {
+            BaseAllAppsAdapter.AdapterItem currentItem = appListAdapterItems.get(i);
+            if (currentItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
+                itemToScrollTo = i;
+                continue;
+            }
+            if (itemToScrollTo != -1) {
+                itemToScrollTo = i;
+                if (rowToExpandToWithRespectToHeader == -1) {
+                    rowToExpandToWithRespectToHeader = currentItem.rowIndex;
+                }
+                int rowToScrollTo =
+                        (int) Math.floor((double) (mAllApps.getHeight() - psHeaderHeight
+                                - mAllApps.getHeaderProtectionHeight()) / allAppsCellHeight);
+                int currentRowDistance = currentItem.rowIndex - rowToExpandToWithRespectToHeader;
+                // rowToScrollTo - 1 since the item to scroll to is 0 indexed.
+                if (currentRowDistance == rowToScrollTo - 1) {
+                    break;
+                }
+            }
+        }
+        if (itemToScrollTo != -1) {
+            // Note: SmoothScroller is meant to be used once.
+            RecyclerView.SmoothScroller smoothScroller =
+                    new LinearSmoothScroller(mAllApps.getContext()) {
+                        @Override protected int getVerticalSnapPreference() {
+                            return LinearSmoothScroller.SNAP_TO_ANY;
+                        }
+                    };
+            smoothScroller.setTargetPosition(itemToScrollTo);
+            RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
+            if (layoutManager != null) {
+                layoutManager.startSmoothScroll(smoothScroller);
+            }
+        }
+        return itemToScrollTo;
+    }
+
+    /**
+     * Scrolls up to the private space header and animates the collapsing of the text.
+     */
+    private ValueAnimator animateCollapseAnimation() {
+        float from = 1;
+        float to = 0;
+        RecyclerViewFastScroller scrollBar = mAllApps.getActiveRecyclerView().getScrollbar();
+        ValueAnimator collapseAnim = ValueAnimator.ofFloat(from, to);
+        collapseAnim.setDuration(EXPAND_COLLAPSE_DURATION);
+        collapseAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                if (scrollBar != null) {
+                    scrollBar.setVisibility(INVISIBLE);
+                }
+                // Scroll up to header.
+                collapse();
+            }
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                if (scrollBar != null) {
+                    scrollBar.setThumbOffsetY(-1);
+                    scrollBar.setVisibility(VISIBLE);
+                }
+            }
+        });
+        return collapseAnim;
+    }
+
+    /**
+     * Using PropertySetter{@link PropertySetter}, we can update the view's attributes within an
+     * animation. At the moment, collapsing, setting alpha changes, and animating the text is done
+     * here.
+     */
+    private void updatePrivateStateAnimator(boolean expand, @Nullable ViewGroup psHeader) {
+        if (psHeader == null) {
+            return;
+        }
+        ViewGroup settingsAndLockGroup = psHeader.findViewById(R.id.settingsAndLockGroup);
+        ViewGroup lockButton = psHeader.findViewById(R.id.ps_lock_unlock_button);
+        if (settingsAndLockGroup.getLayoutTransition() == null) {
+            // Set a new transition if the current ViewGroup does not already contain one as each
+            // transition should only happen once when applied.
+            enableLayoutTransition(settingsAndLockGroup);
+        }
+
+        PropertySetter setter = new AnimatedPropertySetter();
+        ImageButton settingsButton = psHeader.findViewById(R.id.ps_settings_button);
+        updateSettingsGearAlpha(settingsButton, expand, setter);
+        AnimatorSet animatorSet = setter.buildAnim();
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                // Animate the collapsing of the text at the same time while updating lock button.
+                lockButton.findViewById(R.id.lock_text).setVisibility(expand ? VISIBLE : GONE);
+                setAnimationRunning(true);
+            }
+        });
+        animatorSet.addListener(forEndCallback(() -> {
+            setAnimationRunning(false);
+            mAnimate = false;
+            if (!expand) {
+                // Call onAppsUpdated() because it may be canceled when this animation occurs.
+                mAllApps.getPersonalAppList().onAppsUpdated();
+            }
+        }));
+        // Play the collapsing together of the stateAnimator to avoid being unable to scroll to the
+        // header. Otherwise the smooth scrolling will scroll higher when played with the state
+        // animator.
+        if (!expand) {
+            animatorSet.playTogether(animateCollapseAnimation());
+        }
+        animatorSet.setDuration(EXPAND_COLLAPSE_DURATION);
+        animatorSet.start();
+    }
+
+    /** Animates the layout changes when the text of the button becomes visible/gone. */
+    private void enableLayoutTransition(ViewGroup settingsAndLockGroup) {
+        LayoutTransition settingsAndLockTransition = new LayoutTransition();
+        settingsAndLockTransition.enableTransitionType(LayoutTransition.CHANGING);
+        settingsAndLockTransition.setDuration(EXPAND_COLLAPSE_DURATION);
+        settingsAndLockTransition.addTransitionListener(new LayoutTransition.TransitionListener() {
+            @Override
+            public void startTransition(LayoutTransition transition, ViewGroup viewGroup,
+                    View view, int i) {
+            }
+            @Override
+            public void endTransition(LayoutTransition transition, ViewGroup viewGroup,
+                    View view, int i) {
+                settingsAndLockGroup.setLayoutTransition(null);
+                mAnimate = false;
+            }
+        });
+        settingsAndLockGroup.setLayoutTransition(settingsAndLockTransition);
+    }
+
+    /** Change the settings gear alpha when expanded or collapsed. */
+    private void updateSettingsGearAlpha(ImageButton settingsButton, boolean expand,
+            PropertySetter setter) {
+        float toAlpha = expand ? 1 : 0;
+        setter.setFloat(settingsButton, VIEW_ALPHA, toAlpha, Interpolators.LINEAR)
+                .setDuration(SETTINGS_OPACITY_DURATION).setStartDelay(0);
+    }
+
+    void expandPrivateSpace() {
+        // If we are on main adapter view, we apply the PS Container expansion animation and
+        // scroll down to load the entire container, making animation visible.
+        ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder = mAllApps.mAH.get(MAIN);
+        List<BaseAllAppsAdapter.AdapterItem> adapterItems =
+                mainAdapterHolder.mAppsList.getAdapterItems();
+        if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()
+                && mAllApps.isPersonalTab()) {
+            // Animate the text and settings icon.
+            DeviceProfile deviceProfile =
+                    ActivityContext.lookupContext(mAllApps.getContext()).getDeviceProfile();
+            scrollForViewToBeVisibleInContainer(mainAdapterHolder.mRecyclerView, adapterItems,
+                    getPsHeaderHeight(), deviceProfile.allAppsCellHeightPx);
+            ViewGroup psHeader = getPsHeader(mainAdapterHolder.mRecyclerView, adapterItems);
+            updatePrivateStateAnimator(true, psHeader);
+        }
+    }
+
+    private void exitSearchAndExpand() {
+        mAllApps.updateHeaderScroll(0);
+        // Animate to A-Z with 0 time to reset the animation with proper state management.
+        mAllApps.animateToSearchState(false, 0);
+        MAIN_EXECUTOR.post(() -> {
+            mAllApps.mSearchUiManager.resetSearch();
+            mAllApps.switchToTab(ActivityAllAppsContainerView.AdapterHolder.MAIN);
+            expandPrivateSpace();
+        });
+    }
+
+    private void collapsePrivateSpace() {
+        AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
+        AlphabeticalAppsList<?> appList = allAppsRecyclerView.getApps();
+        if (appList == null) {
+            return;
+        }
+        ViewGroup psHeader = getPsHeader(allAppsRecyclerView, appList.getAdapterItems());
+        assert psHeader != null;
+        updatePrivateStateAnimator(false, psHeader);
+    }
+
+    int getPsHeaderHeight() {
+        return mHeaderHeight;
+    }
+
+    /** Get the private space header from the adapter items. */
+    @Nullable
+    private ViewGroup getPsHeader(AllAppsRecyclerView allAppsRecyclerView,
+            List<BaseAllAppsAdapter.AdapterItem> adapterItems){
+        ViewGroup psHeader = null;
+        for (int i = 0; i < adapterItems.size(); i++) {
+            BaseAllAppsAdapter.AdapterItem currentItem = adapterItems.get(i);
+            if (currentItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
+                RecyclerView.ViewHolder viewHolder =
+                        allAppsRecyclerView.findViewHolderForAdapterPosition(i);
+                if (viewHolder != null) {
+                    psHeader = (ViewGroup) viewHolder.itemView;
+                }
+            }
+        }
+        return psHeader;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
deleted file mode 100644
index fdc035e..0000000
--- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.allapps;
-
-import static android.view.View.GONE;
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
-
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
-import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
-import static com.android.launcher3.allapps.PrivateProfileManager.STATE_DISABLED;
-import static com.android.launcher3.allapps.PrivateProfileManager.STATE_ENABLED;
-import static com.android.launcher3.allapps.PrivateProfileManager.STATE_TRANSITION;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_TAP;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.LayoutTransition;
-import android.animation.ValueAnimator;
-import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.recyclerview.widget.LinearSmoothScroller;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.app.animation.Interpolators;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Flags;
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.UserProfileManager.UserProfileState;
-import com.android.launcher3.anim.AnimatedPropertySetter;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.RecyclerViewFastScroller;
-
-import java.util.List;
-
-/**
- * Controller which returns views to be added to Private Space Header based upon
- * {@link UserProfileState}
- */
-public class PrivateSpaceHeaderViewController {
-    private static final int EXPAND_COLLAPSE_DURATION = 800;
-    private static final int SETTINGS_OPACITY_DURATION = 160;
-    private final ActivityAllAppsContainerView mAllApps;
-    private final PrivateProfileManager mPrivateProfileManager;
-
-    public PrivateSpaceHeaderViewController(ActivityAllAppsContainerView allApps,
-            PrivateProfileManager privateProfileManager) {
-        this.mAllApps = allApps;
-        this.mPrivateProfileManager = privateProfileManager;
-    }
-
-    /** Add Private Space Header view elements based upon {@link UserProfileState} */
-    public void addPrivateSpaceHeaderViewElements(RelativeLayout parent) {
-        // Set the transition duration for the settings and lock button to animate.
-        ViewGroup settingsAndLockGroup = parent.findViewById(R.id.settingsAndLockGroup);
-        LayoutTransition settingsAndLockTransition = settingsAndLockGroup.getLayoutTransition();
-        settingsAndLockTransition.enableTransitionType(LayoutTransition.CHANGING);
-        settingsAndLockTransition.setDuration(EXPAND_COLLAPSE_DURATION);
-
-        //Add quietMode image and action for lock/unlock button
-        ViewGroup lockButton =
-                parent.findViewById(R.id.ps_lock_unlock_button);
-        assert lockButton != null;
-        addLockButton(parent, lockButton);
-
-        //Trigger lock/unlock action from header.
-        addHeaderOnClickListener(parent);
-
-        //Add image and action for private space settings button
-        ImageButton settingsButton = parent.findViewById(R.id.ps_settings_button);
-        assert settingsButton != null;
-        addPrivateSpaceSettingsButton(settingsButton);
-
-        //Add image for private space transitioning view
-        ImageView transitionView = parent.findViewById(R.id.ps_transition_image);
-        assert transitionView != null;
-        addTransitionImage(transitionView);
-    }
-
-    /**
-     *  Adds the quietModeButton and attach onClickListener for the header to animate different
-     *  states when clicked.
-     */
-    private void addLockButton(ViewGroup psHeader, ViewGroup lockButton) {
-        TextView lockText = lockButton.findViewById(R.id.lock_text);
-        switch (mPrivateProfileManager.getCurrentState()) {
-            case STATE_ENABLED -> {
-                lockText.setVisibility(VISIBLE);
-                lockButton.setVisibility(VISIBLE);
-                lockButton.setOnClickListener(view -> lockAction(psHeader));
-            }
-            case STATE_DISABLED -> {
-                lockText.setVisibility(GONE);
-                lockButton.setVisibility(VISIBLE);
-                lockButton.setOnClickListener(view -> unlockAction(psHeader));
-            }
-            default -> lockButton.setVisibility(GONE);
-        }
-    }
-
-    private void addHeaderOnClickListener(RelativeLayout header) {
-        if (mPrivateProfileManager.getCurrentState() == STATE_DISABLED) {
-            header.setOnClickListener(view -> unlockAction(header));
-        } else {
-            header.setOnClickListener(null);
-        }
-    }
-
-    private void unlockAction(ViewGroup psHeader) {
-        mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
-        mPrivateProfileManager.unlockPrivateProfile((() -> onPrivateProfileUnlocked(psHeader)));
-    }
-
-    private void lockAction(ViewGroup psHeader) {
-        mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_LOCK_TAP);
-        if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()) {
-            updatePrivateStateAnimator(false, psHeader);
-        } else {
-            mPrivateProfileManager.lockPrivateProfile();
-        }
-    }
-
-    private void addPrivateSpaceSettingsButton(ImageButton settingsButton) {
-        if (mPrivateProfileManager.getCurrentState() == STATE_ENABLED
-                && mPrivateProfileManager.isPrivateSpaceSettingsAvailable()) {
-            settingsButton.setVisibility(VISIBLE);
-            settingsButton.setAlpha(1f);
-            settingsButton.setOnClickListener(
-                    view -> {
-                        mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP);
-                        mPrivateProfileManager.openPrivateSpaceSettings();
-                    });
-        } else {
-            settingsButton.setVisibility(GONE);
-        }
-    }
-
-    private void addTransitionImage(ImageView transitionImage) {
-        if (mPrivateProfileManager.getCurrentState() == STATE_TRANSITION) {
-            transitionImage.setVisibility(VISIBLE);
-        } else {
-            transitionImage.setVisibility(GONE);
-        }
-    }
-
-    private void onPrivateProfileUnlocked(ViewGroup header) {
-        // If we are on main adapter view, we apply the PS Container expansion animation and
-        // then scroll down to load the entire container, making animation visible.
-        ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder =
-                (ActivityAllAppsContainerView<?>.AdapterHolder) mAllApps.mAH.get(MAIN);
-        if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()
-                && mAllApps.getActiveRecyclerView() == mainAdapterHolder.mRecyclerView) {
-            // Animate the text and settings icon.
-            updatePrivateStateAnimator(true, header);
-            DeviceProfile deviceProfile =
-                    ActivityContext.lookupContext(mAllApps.getContext()).getDeviceProfile();
-            AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
-            scrollForViewToBeVisibleInContainer(allAppsRecyclerView,
-                    allAppsRecyclerView.getApps().getAdapterItems(),
-                    header.getHeight(), deviceProfile.allAppsCellHeightPx);
-        }
-    }
-
-    /** Finds the private space header to scroll to and set the private space icons to GONE. */
-    private void collapse() {
-        AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
-        List<BaseAllAppsAdapter.AdapterItem> appListAdapterItems =
-                allAppsRecyclerView.getApps().getAdapterItems();
-        for (int i = appListAdapterItems.size() - 1; i > 0; i--) {
-            BaseAllAppsAdapter.AdapterItem currentItem = appListAdapterItems.get(i);
-            // Scroll to the private space header.
-            if (currentItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
-                // Note: SmoothScroller is meant to be used once.
-                RecyclerView.SmoothScroller smoothScroller =
-                        new LinearSmoothScroller(mAllApps.getContext()) {
-                            @Override protected int getVerticalSnapPreference() {
-                                return LinearSmoothScroller.SNAP_TO_END;
-                            }
-                        };
-                smoothScroller.setTargetPosition(i);
-                RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
-                if (layoutManager != null) {
-                    layoutManager.startSmoothScroll(smoothScroller);
-                }
-                break;
-            }
-            // Make the private space apps gone to "collapse".
-            if (currentItem.decorationInfo != null) {
-                RecyclerView.ViewHolder viewHolder =
-                        allAppsRecyclerView.findViewHolderForAdapterPosition(i);
-                if (viewHolder != null) {
-                    viewHolder.itemView.setVisibility(GONE);
-                }
-            }
-        }
-    }
-
-    /**
-     * Upon expanding, only scroll to the item position in the adapter that allows the header to be
-     * visible.
-     */
-    @VisibleForTesting
-    public int scrollForViewToBeVisibleInContainer(
-            AllAppsRecyclerView allAppsRecyclerView,
-            List<BaseAllAppsAdapter.AdapterItem> appListAdapterItems,
-            int psHeaderHeight,
-            int allAppsCellHeight) {
-        int rowToExpandToWithRespectToHeader = -1;
-        int itemToScrollTo = -1;
-        // Looks for the item in the app list to scroll to so that the header is visible.
-        for (int i = 0; i < appListAdapterItems.size(); i++) {
-            BaseAllAppsAdapter.AdapterItem currentItem = appListAdapterItems.get(i);
-            if (currentItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
-                itemToScrollTo = i;
-                continue;
-            }
-            if (itemToScrollTo != -1) {
-                if (rowToExpandToWithRespectToHeader == -1) {
-                    rowToExpandToWithRespectToHeader = currentItem.rowIndex;
-                }
-                int rowToScrollTo =
-                        (int) Math.floor((double) (mAllApps.getHeight() - psHeaderHeight
-                                - mAllApps.getHeaderProtectionHeight()) / allAppsCellHeight);
-                int currentRowDistance = currentItem.rowIndex - rowToExpandToWithRespectToHeader;
-                // rowToScrollTo - 1 since the item to scroll to is 0 indexed.
-                if (currentRowDistance == rowToScrollTo - 1) {
-                    itemToScrollTo = i;
-                    break;
-                }
-            }
-        }
-        if (itemToScrollTo != -1) {
-            // Note: SmoothScroller is meant to be used once.
-            RecyclerView.SmoothScroller smoothScroller =
-                    new LinearSmoothScroller(mAllApps.getContext()) {
-                        @Override protected int getVerticalSnapPreference() {
-                            return LinearSmoothScroller.SNAP_TO_ANY;
-                        }
-                    };
-            smoothScroller.setTargetPosition(itemToScrollTo);
-            RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
-            if (layoutManager != null) {
-                layoutManager.startSmoothScroll(smoothScroller);
-            }
-        }
-        return itemToScrollTo;
-    }
-
-    PrivateProfileManager getPrivateProfileManager() {
-        return mPrivateProfileManager;
-    }
-
-    /**
-     * Scrolls up to the private space header and animates the collapsing of the text.
-     */
-    private ValueAnimator animateCollapseAnimation(ViewGroup lockButton) {
-        float from = 1;
-        float to = 0;
-        RecyclerViewFastScroller scrollBar = mAllApps.getActiveRecyclerView().getScrollbar();
-        ValueAnimator collapseAnim = ValueAnimator.ofFloat(from, to);
-        collapseAnim.setDuration(EXPAND_COLLAPSE_DURATION);
-        collapseAnim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                if (scrollBar != null) {
-                    scrollBar.setVisibility(INVISIBLE);
-                }
-                // scroll up
-                collapse();
-                // Animate the collapsing of the text.
-                lockButton.findViewById(R.id.lock_text).setVisibility(GONE);
-            }
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                if (scrollBar != null) {
-                    scrollBar.setThumbOffsetY(-1);
-                    scrollBar.setVisibility(VISIBLE);
-                }
-                mPrivateProfileManager.lockPrivateProfile();
-            }
-        });
-        return collapseAnim;
-    }
-
-    /**
-     * Using PropertySetter{@link PropertySetter}, we can update the view's attributes within an
-     * animation. At the moment, collapsing, setting alpha changes, and animating the text is done
-     * here.
-     */
-    private void updatePrivateStateAnimator(boolean expand, ViewGroup psHeader) {
-        PropertySetter setter = new AnimatedPropertySetter();
-        ViewGroup lockButton = psHeader.findViewById(R.id.ps_lock_unlock_button);
-        ImageButton settingsButton = psHeader.findViewById(R.id.ps_settings_button);
-        updateSettingsGearAlpha(settingsButton, expand, setter);
-        AnimatorSet animatorSet = setter.buildAnim();
-        animatorSet.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                // Animate the collapsing of the text at the same time while updating lock button.
-                lockButton.findViewById(R.id.lock_text).setVisibility(expand ? VISIBLE : GONE);
-            }
-        });
-        // Play the collapsing together of the stateAnimator to avoid being unable to scroll to the
-        // header. Otherwise the smooth scrolling will scroll higher when played with the state
-        // animator.
-        if (!expand) {
-            animatorSet.playTogether(animateCollapseAnimation(lockButton));
-        }
-        animatorSet.setDuration(EXPAND_COLLAPSE_DURATION);
-        animatorSet.start();
-     }
-
-    /** Change the settings gear alpha when expanded or collapsed. */
-     private void updateSettingsGearAlpha(ImageButton settingsButton, boolean expand,
-            PropertySetter setter) {
-        float toAlpha = expand ? 1 : 0;
-        setter.setFloat(settingsButton, VIEW_ALPHA, toAlpha, Interpolators.LINEAR)
-                .setDuration(SETTINGS_OPACITY_DURATION).setStartDelay(0);
-    }
-}
diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java
index 13fefc4..9010f82 100644
--- a/src/com/android/launcher3/apppairs/AppPairIcon.java
+++ b/src/com/android/launcher3/apppairs/AppPairIcon.java
@@ -16,12 +16,13 @@
 
 package com.android.launcher3.apppairs;
 
+import static com.android.launcher3.BubbleTextView.DISPLAY_FOLDER;
+
 import android.content.Context;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.LayoutInflater;
-import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
@@ -32,12 +33,11 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Reorderable;
 import com.android.launcher3.dragndrop.DraggableView;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.AppPairInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.views.ActivityContext;
 
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.function.Predicate;
 
@@ -50,17 +50,15 @@
 public class AppPairIcon extends FrameLayout implements DraggableView, Reorderable {
     private static final String TAG = "AppPairIcon";
 
-    /**
-     * Indicates that the app pair is currently launchable on the current screen.
-     */
-    private boolean mIsLaunchableAtScreenSize = true;
-
     // A view that holds the app pair icon graphic.
     private AppPairIconGraphic mIconGraphic;
     // A view that holds the app pair's title.
     private BubbleTextView mAppPairName;
     // The underlying ItemInfo that stores info about the app pair members, etc.
-    private FolderInfo mInfo;
+    private AppPairInfo mInfo;
+    // The containing element that holds this icon: workspace, taskbar, folder, etc. Affects certain
+    // aspects of how the icon is drawn.
+    private int mContainer;
 
     // Required for Reorderable -- handles translation and bouncing movements
     private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
@@ -78,7 +76,7 @@
      * Builds an AppPairIcon to be added to the Launcher.
      */
     public static AppPairIcon inflateIcon(int resId, ActivityContext activity,
-            @Nullable ViewGroup group, FolderInfo appPairInfo) {
+            @Nullable ViewGroup group, AppPairInfo appPairInfo, int container) {
         DeviceProfile grid = activity.getDeviceProfile();
         LayoutInflater inflater = (group != null)
                 ? LayoutInflater.from(group.getContext())
@@ -86,32 +84,31 @@
         AppPairIcon icon = (AppPairIcon) inflater.inflate(resId, group, false);
 
         // Sort contents, so that left-hand app comes first
-        Collections.sort(appPairInfo.contents, Comparator.comparingInt(a -> a.rank));
+        appPairInfo.getContents().sort(Comparator.comparingInt(a -> a.rank));
 
-        icon.setClipToPadding(false);
         icon.setTag(appPairInfo);
         icon.setOnClickListener(activity.getItemOnClickListener());
         icon.mInfo = appPairInfo;
-
-        // TODO (b/326664798): Delete this check, instead check at launcher load time
-        if (icon.mInfo.contents.size() != 2) {
-            Log.wtf(TAG, "AppPair contents not 2, size: " + icon.mInfo.contents.size());
-            return icon;
-        }
+        icon.mContainer = container;
 
         // Set up icon drawable area
         icon.mIconGraphic = icon.findViewById(R.id.app_pair_icon_graphic);
-        icon.mIconGraphic.init(activity, icon);
-
-        icon.checkDisabledState();
+        icon.mIconGraphic.init(icon, container);
 
         // Set up app pair title
         icon.mAppPairName = icon.findViewById(R.id.app_pair_icon_name);
-        icon.mAppPairName.setCompoundDrawablePadding(0);
         FrameLayout.LayoutParams lp =
                 (FrameLayout.LayoutParams) icon.mAppPairName.getLayoutParams();
-        lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
-        icon.mAppPairName.setText(appPairInfo.title);
+        // Shift the title text down to leave room for the icon graphic. Since the icon graphic is
+        // a separate element (and not set as a CompoundDrawable on the BubbleTextView), we need to
+        // shift the text down manually.
+        lp.topMargin = container == DISPLAY_FOLDER
+                ? grid.folderChildIconSizePx + grid.folderChildDrawablePaddingPx
+                : grid.iconSizePx + grid.iconDrawablePaddingPx;
+        // For some reason, app icons have setIncludeFontPadding(false) inside folders, so we set it
+        // here to match that.
+        icon.mAppPairName.setIncludeFontPadding(container != DISPLAY_FOLDER);
+        icon.mAppPairName.applyLabel(appPairInfo);
 
         // Set up accessibility
         icon.setContentDescription(icon.getAccessibilityTitle(appPairInfo));
@@ -123,9 +120,9 @@
     /**
      * Returns a formatted accessibility title for app pairs.
      */
-    public String getAccessibilityTitle(FolderInfo appPairInfo) {
-        CharSequence app1 = appPairInfo.contents.get(0).title;
-        CharSequence app2 = appPairInfo.contents.get(1).title;
+    public String getAccessibilityTitle(AppPairInfo appPairInfo) {
+        CharSequence app1 = appPairInfo.getFirstApp().title;
+        CharSequence app2 = appPairInfo.getSecondApp().title;
         return getContext().getString(R.string.app_pair_name_format, app1, app2);
     }
 
@@ -170,44 +167,45 @@
         return mScaleForReorderBounce;
     }
 
-    public FolderInfo getInfo() {
+    public AppPairInfo getInfo() {
         return mInfo;
     }
 
-    public View getIconDrawableArea() {
+    public BubbleTextView getTitleTextView() {
+        return mAppPairName;
+    }
+
+    public AppPairIconGraphic getIconDrawableArea() {
         return mIconGraphic;
     }
 
-    public boolean isLaunchableAtScreenSize() {
-        return mIsLaunchableAtScreenSize;
-    }
-
-    /**
-     * Updates the "disabled" state of the app pair in the current device configuration.
-     * App pairs can be "disabled" in two ways:
-     * 1) One of the member WorkspaceItemInfos is disabled (i.e. the app software itself is paused
-     * by the user or can't be launched for some other reason).
-     * 2) This specific instance of an app pair can't be launched due to screen size requirements.
-     */
-    public void checkDisabledState() {
-        DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
-        // If user is on a small screen, we can't launch if either of the apps is non-resizeable
-        mIsLaunchableAtScreenSize =
-                dp.isTablet || getInfo().contents.stream().noneMatch(
-                        wii -> wii.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE));
-        // Call applyIcons to check and update icons
-        mIconGraphic.applyIcons();
-    }
-
     /**
      * Called when WorkspaceItemInfos get updated, and the app pair icon may need to be redrawn.
      */
     public void maybeRedrawForWorkspaceUpdate(Predicate<WorkspaceItemInfo> itemCheck) {
         // If either of the app pair icons return true on the predicate (i.e. in the list of
         // updated apps), redraw the icon graphic (icon background and both icons).
-        if (getInfo().contents.stream().anyMatch(itemCheck)) {
-            checkDisabledState();
-            mIconGraphic.invalidate();
+        if (getInfo().anyMatch(itemCheck)) {
+            mIconGraphic.redraw();
         }
     }
+
+    /**
+     * Inside folders, icons are vertically centered in their rows. See
+     * {@link BubbleTextView} for comparison.
+     */
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mContainer == DISPLAY_FOLDER) {
+            int height = MeasureSpec.getSize(heightMeasureSpec);
+            ActivityContext activity = ActivityContext.lookupContext(getContext());
+            Paint.FontMetrics fm = mAppPairName.getPaint().getFontMetrics();
+            int cellHeightPx = activity.getDeviceProfile().folderChildIconSizePx
+                    + activity.getDeviceProfile().folderChildDrawablePaddingPx
+                    + (int) Math.ceil(fm.bottom - fm.top);
+            setPadding(getPaddingLeft(), (height - cellHeightPx) / 2, getPaddingRight(),
+                    getPaddingBottom());
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
 }
diff --git a/src/com/android/launcher3/apppairs/AppPairIconBackground.java b/src/com/android/launcher3/apppairs/AppPairIconBackground.java
deleted file mode 100644
index 187541f..0000000
--- a/src/com/android/launcher3/apppairs/AppPairIconBackground.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.apppairs;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-
-import com.android.launcher3.R;
-
-/**
- * A Drawable for the background behind the twin app icons (looks like two rectangles).
- */
-class AppPairIconBackground extends Drawable {
-    // The underlying view that we are drawing this background on.
-    private final AppPairIconGraphic icon;
-    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
-    /**
-     * Null values to use with
-     * {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}, since there
-     * doesn't seem to be any other API for drawing rectangles with 4 different corner radii.
-     */
-    private static final RectF EMPTY_RECT = new RectF();
-    private static final float[] ARRAY_OF_ZEROES = new float[8];
-
-    AppPairIconBackground(Context context, AppPairIconGraphic iconGraphic) {
-        icon = iconGraphic;
-        // Set up background paint color
-        TypedArray ta = context.getTheme().obtainStyledAttributes(R.styleable.FolderIconPreview);
-        mBackgroundPaint.setStyle(Paint.Style.FILL);
-        mBackgroundPaint.setColor(
-                ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0));
-        ta.recycle();
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (icon.isLeftRightSplit()) {
-            drawLeftRightSplit(canvas);
-        } else {
-            drawTopBottomSplit(canvas);
-        }
-    }
-
-    /**
-     * When device is in landscape, we draw the rectangles with a left-right split.
-     */
-    private void drawLeftRightSplit(Canvas canvas) {
-        // Get the bounds where we will draw the background image
-        int width = getBounds().width();
-        int height = getBounds().height();
-
-        // The left half of the background image, excluding center channel
-        RectF leftSide = new RectF(
-                0,
-                0,
-                (width / 2f) - (icon.getCenterChannelSize() / 2f),
-                height
-        );
-        // The right half of the background image, excluding center channel
-        RectF rightSide = new RectF(
-                (width / 2f) + (icon.getCenterChannelSize() / 2f),
-                0,
-                width,
-                height
-        );
-
-        drawCustomRoundedRect(canvas, leftSide, new float[]{
-                icon.getBigRadius(), icon.getBigRadius(),
-                icon.getSmallRadius(), icon.getSmallRadius(),
-                icon.getSmallRadius(), icon.getSmallRadius(),
-                icon.getBigRadius(), icon.getBigRadius()});
-        drawCustomRoundedRect(canvas, rightSide, new float[]{
-                icon.getSmallRadius(), icon.getSmallRadius(),
-                icon.getBigRadius(), icon.getBigRadius(),
-                icon.getBigRadius(), icon.getBigRadius(),
-                icon.getSmallRadius(), icon.getSmallRadius()});
-    }
-
-    /**
-     * When device is in portrait, we draw the rectangles with a top-bottom split.
-     */
-    private void drawTopBottomSplit(Canvas canvas) {
-        // Get the bounds where we will draw the background image
-        int width = getBounds().width();
-        int height = getBounds().height();
-
-        // The top half of the background image, excluding center channel
-        RectF topSide = new RectF(
-                0,
-                0,
-                width,
-                (height / 2f) - (icon.getCenterChannelSize() / 2f)
-        );
-        // The bottom half of the background image, excluding center channel
-        RectF bottomSide = new RectF(
-                0,
-                (height / 2f) + (icon.getCenterChannelSize() / 2f),
-                width,
-                height
-        );
-
-        drawCustomRoundedRect(canvas, topSide, new float[]{
-                icon.getBigRadius(), icon.getBigRadius(),
-                icon.getBigRadius(), icon.getBigRadius(),
-                icon.getSmallRadius(), icon.getSmallRadius(),
-                icon.getSmallRadius(), icon.getSmallRadius()});
-        drawCustomRoundedRect(canvas, bottomSide, new float[]{
-                icon.getSmallRadius(), icon.getSmallRadius(),
-                icon.getSmallRadius(), icon.getSmallRadius(),
-                icon.getBigRadius(), icon.getBigRadius(),
-                icon.getBigRadius(), icon.getBigRadius()});
-    }
-
-    /**
-     * Draws a rectangle with custom rounded corners.
-     * @param c The Canvas to draw on.
-     * @param rect The bounds of the rectangle.
-     * @param radii An array of 8 radii for the corners: top left x, top left y, top right x, top
-     *              right y, bottom right x, and so on.
-     */
-    private void drawCustomRoundedRect(Canvas c, RectF rect, float[] radii) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-            // Canvas.drawDoubleRoundRect is supported from Q onward
-            c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, mBackgroundPaint);
-        } else {
-            // Fallback rectangle with uniform rounded corners
-            c.drawRoundRect(rect, icon.getBigRadius(), icon.getBigRadius(), mBackgroundPaint);
-        }
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.OPAQUE;
-    }
-
-    @Override
-    public void setAlpha(int i) {
-        mBackgroundPaint.setAlpha(i);
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        mBackgroundPaint.setColorFilter(colorFilter);
-    }
-}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconDrawable.java b/src/com/android/launcher3/apppairs/AppPairIconDrawable.java
new file mode 100644
index 0000000..c0ac11a
--- /dev/null
+++ b/src/com/android/launcher3/apppairs/AppPairIconDrawable.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.apppairs;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.icons.FastBitmapDrawable;
+
+/**
+ * A composed Drawable consisting of the two app pair icons and the background behind them (looks
+ * like two rectangles).
+ */
+class AppPairIconDrawable extends Drawable {
+    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final AppPairIconDrawingParams mP;
+    private final FastBitmapDrawable mIcon1;
+    private final FastBitmapDrawable mIcon2;
+
+    /**
+     * Null values to use with
+     * {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}, since there
+     * doesn't seem to be any other API for drawing rectangles with 4 different corner radii.
+     */
+    private static final RectF EMPTY_RECT = new RectF();
+    private static final float[] ARRAY_OF_ZEROES = new float[8];
+
+    AppPairIconDrawable(
+            AppPairIconDrawingParams p, FastBitmapDrawable icon1, FastBitmapDrawable icon2) {
+        mP = p;
+        mBackgroundPaint.setStyle(Paint.Style.FILL);
+        mBackgroundPaint.setColor(p.getBgColor());
+        mIcon1 = icon1;
+        mIcon2 = icon2;
+    }
+
+    @Override
+    public void draw(@NonNull Canvas canvas) {
+        if (mP.isLeftRightSplit()) {
+            drawLeftRightSplit(canvas);
+        } else {
+            drawTopBottomSplit(canvas);
+        }
+
+        canvas.translate(
+                mP.getStandardIconPadding() + mP.getOuterPadding(),
+                mP.getStandardIconPadding() + mP.getOuterPadding()
+        );
+
+        // Draw first icon.
+        canvas.save();
+        // The app icons are placed differently depending on device orientation.
+        if (mP.isLeftRightSplit()) {
+            canvas.translate(
+                    mP.getInnerPadding(),
+                    mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f
+            );
+        } else {
+            canvas.translate(
+                    mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f,
+                    mP.getInnerPadding()
+            );
+        }
+
+        mIcon1.draw(canvas);
+        canvas.restore();
+
+        // Draw second icon.
+        canvas.save();
+        // The app icons are placed differently depending on device orientation.
+        if (mP.isLeftRightSplit()) {
+            canvas.translate(
+                    mP.getBackgroundSize() - (mP.getInnerPadding() + mP.getMemberIconSize()),
+                    mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f
+            );
+        } else {
+            canvas.translate(
+                    mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f,
+                    mP.getBackgroundSize() - (mP.getInnerPadding() + mP.getMemberIconSize())
+            );
+        }
+
+        mIcon2.draw(canvas);
+    }
+
+    /**
+     * When device is in landscape, we draw the rectangles with a left-right split.
+     */
+    private void drawLeftRightSplit(Canvas canvas) {
+        // Get the bounds where we will draw the background image
+        int width = mP.getIconSize();
+        int height = mP.getIconSize();
+
+        // The left half of the background image, excluding center channel
+        RectF leftSide = new RectF(
+                mP.getStandardIconPadding() + mP.getOuterPadding(),
+                mP.getStandardIconPadding() + mP.getOuterPadding(),
+                (width / 2f) - (mP.getCenterChannelSize() / 2f),
+                height - (mP.getStandardIconPadding() + mP.getOuterPadding())
+        );
+        // The right half of the background image, excluding center channel
+        RectF rightSide = new RectF(
+                (width / 2f) + (mP.getCenterChannelSize() / 2f),
+                (mP.getStandardIconPadding() + mP.getOuterPadding()),
+                width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
+                height - (mP.getStandardIconPadding() + mP.getOuterPadding())
+        );
+
+        drawCustomRoundedRect(canvas, leftSide, new float[]{
+                mP.getBigRadius(), mP.getBigRadius(),
+                mP.getSmallRadius(), mP.getSmallRadius(),
+                mP.getSmallRadius(), mP.getSmallRadius(),
+                mP.getBigRadius(), mP.getBigRadius()});
+        drawCustomRoundedRect(canvas, rightSide, new float[]{
+                mP.getSmallRadius(), mP.getSmallRadius(),
+                mP.getBigRadius(), mP.getBigRadius(),
+                mP.getBigRadius(), mP.getBigRadius(),
+                mP.getSmallRadius(), mP.getSmallRadius()});
+    }
+
+    /**
+     * When device is in portrait, we draw the rectangles with a top-bottom split.
+     */
+    private void drawTopBottomSplit(Canvas canvas) {
+        // Get the bounds where we will draw the background image
+        int width = mP.getIconSize();
+        int height = mP.getIconSize();
+
+        // The top half of the background image, excluding center channel
+        RectF topSide = new RectF(
+                (mP.getStandardIconPadding() + mP.getOuterPadding()),
+                (mP.getStandardIconPadding() + mP.getOuterPadding()),
+                width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
+                (height / 2f) - (mP.getCenterChannelSize() / 2f)
+        );
+        // The bottom half of the background image, excluding center channel
+        RectF bottomSide = new RectF(
+                (mP.getStandardIconPadding() + mP.getOuterPadding()),
+                (height / 2f) + (mP.getCenterChannelSize() / 2f),
+                width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
+                height - (mP.getStandardIconPadding() + mP.getOuterPadding())
+        );
+
+        drawCustomRoundedRect(canvas, topSide, new float[]{
+                mP.getBigRadius(), mP.getBigRadius(),
+                mP.getBigRadius(), mP.getBigRadius(),
+                mP.getSmallRadius(), mP.getSmallRadius(),
+                mP.getSmallRadius(), mP.getSmallRadius()});
+        drawCustomRoundedRect(canvas, bottomSide, new float[]{
+                mP.getSmallRadius(), mP.getSmallRadius(),
+                mP.getSmallRadius(), mP.getSmallRadius(),
+                mP.getBigRadius(), mP.getBigRadius(),
+                mP.getBigRadius(), mP.getBigRadius()});
+    }
+
+    /**
+     * Draws a rectangle with custom rounded corners.
+     * @param c The Canvas to draw on.
+     * @param rect The bounds of the rectangle.
+     * @param radii An array of 8 radii for the corners: top left x, top left y, top right x, top
+     *              right y, bottom right x, and so on.
+     */
+    private void drawCustomRoundedRect(Canvas c, RectF rect, float[] radii) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            // Canvas.drawDoubleRoundRect is supported from Q onward
+            c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, mBackgroundPaint);
+        } else {
+            // Fallback rectangle with uniform rounded corners
+            c.drawRoundRect(rect, mP.getBigRadius(), mP.getBigRadius(), mBackgroundPaint);
+        }
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.OPAQUE;
+    }
+
+    @Override
+    public void setAlpha(int i) {
+        mBackgroundPaint.setAlpha(i);
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mBackgroundPaint.setColorFilter(colorFilter);
+    }
+}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt b/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt
new file mode 100644
index 0000000..62e5771
--- /dev/null
+++ b/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.apppairs
+
+import android.content.Context
+import com.android.launcher3.BubbleTextView.DISPLAY_FOLDER
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.views.ActivityContext
+
+class AppPairIconDrawingParams(val context: Context, container: Int) {
+    companion object {
+        // Design specs -- the below ratios are in relation to the size of a standard app icon.
+        // Note: The standard app icon has two sizes. One is the full size of the drawable (returned
+        // by dp.iconSizePx), and one is the visual size of the icon on-screen (11/12 of that).
+        // Hence the calculations below.
+        const val STANDARD_ICON_PADDING = 1 / 24f
+        const val STANDARD_ICON_SHRINK = 1 - STANDARD_ICON_PADDING * 2
+        // App pairs are slightly smaller than the *visual* size of a standard icon, so all ratios
+        // are calculated with that in mind.
+        const val OUTER_PADDING_SCALE = 1 / 30f * STANDARD_ICON_SHRINK
+        const val INNER_PADDING_SCALE = 1 / 24f * STANDARD_ICON_SHRINK
+        const val CENTER_CHANNEL_SCALE = 1 / 30f * STANDARD_ICON_SHRINK
+        const val BIG_RADIUS_SCALE = 1 / 5f * STANDARD_ICON_SHRINK
+        const val SMALL_RADIUS_SCALE = 1 / 15f * STANDARD_ICON_SHRINK
+        const val MEMBER_ICON_SCALE = 11 / 30f * STANDARD_ICON_SHRINK
+    }
+
+    // The size at which this graphic will be drawn.
+    val iconSize: Int
+    // Standard app icons are padded by this amount on each side.
+    val standardIconPadding: Float
+    // App pair icons are slightly smaller than regular icons, so we pad the icon by this much on
+    // each side.
+    val outerPadding: Float
+    // The colored background (two rectangles in a square area) is this big.
+    val backgroundSize: Float
+    // The size of the channel between the two halves of the app pair icon.
+    val centerChannelSize: Float
+    // The corner radius of the outside corners.
+    val bigRadius: Float
+    // The corner radius of the inside corners, touching the center channel.
+    val smallRadius: Float
+    // Inside of the icon, the two member apps are padded by this much.
+    val innerPadding: Float
+    // The two member apps have icons that are this big (in diameter).
+    val memberIconSize: Float
+    // The app pair icon appears differently in portrait and landscape.
+    var isLeftRightSplit: Boolean = true
+    // The background paint color (based on container).
+    val bgColor: Int
+
+    init {
+        val activity: ActivityContext = ActivityContext.lookupContext(context)
+        val dp = activity.deviceProfile
+        iconSize = if (container == DISPLAY_FOLDER) dp.folderChildIconSizePx else dp.iconSizePx
+        standardIconPadding = iconSize * STANDARD_ICON_PADDING
+        outerPadding = iconSize * OUTER_PADDING_SCALE
+        backgroundSize = iconSize * STANDARD_ICON_SHRINK - (outerPadding * 2)
+        centerChannelSize = iconSize * CENTER_CHANNEL_SCALE
+        bigRadius = iconSize * BIG_RADIUS_SCALE
+        smallRadius = iconSize * SMALL_RADIUS_SCALE
+        innerPadding = iconSize * INNER_PADDING_SCALE
+        memberIconSize = iconSize * MEMBER_ICON_SCALE
+        updateOrientation(dp)
+        if (container == DISPLAY_FOLDER) {
+            val ta =
+                context.theme.obtainStyledAttributes(
+                    intArrayOf(R.attr.materialColorSurfaceContainerLowest)
+                )
+            bgColor = ta.getColor(0, 0)
+            ta.recycle()
+        } else {
+            val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview)
+            bgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0)
+            ta.recycle()
+        }
+    }
+
+    /** Checks the device orientation and updates isLeftRightSplit accordingly. */
+    fun updateOrientation(dp: DeviceProfile) {
+        isLeftRightSplit = dp.isLeftRightSplit
+    }
+}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
index 777831b..a3a1cfc 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
+++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
@@ -21,14 +21,14 @@
 import android.graphics.Rect
 import android.graphics.drawable.Drawable
 import android.util.AttributeSet
-import android.util.Log
 import android.view.Gravity
 import android.widget.FrameLayout
+import androidx.annotation.OpenForTesting
 import com.android.launcher3.DeviceProfile
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
 import com.android.launcher3.icons.BitmapInfo
-import com.android.launcher3.icons.FastBitmapDrawable
 import com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter
+import com.android.launcher3.model.data.AppPairInfo
 import com.android.launcher3.util.Themes
 import com.android.launcher3.views.ActivityContext
 
@@ -36,166 +36,108 @@
  * A FrameLayout marking the area on an [AppPairIcon] where the visual icon will be drawn. One of
  * two child UI elements on an [AppPairIcon], along with a BubbleTextView holding the text title.
  */
-class AppPairIconGraphic @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+@OpenForTesting
+open class AppPairIconGraphic
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null) :
     FrameLayout(context, attrs), OnDeviceProfileChangeListener {
     private val TAG = "AppPairIconGraphic"
 
     companion object {
-        // Design specs -- the below ratios are in relation to the size of a standard app icon.
-        private const val OUTER_PADDING_SCALE = 1 / 30f
-        private const val INNER_PADDING_SCALE = 1 / 24f
-        private const val MEMBER_ICON_SCALE = 11 / 30f
-        private const val CENTER_CHANNEL_SCALE = 1 / 30f
-        private const val BIG_RADIUS_SCALE = 1 / 5f
-        private const val SMALL_RADIUS_SCALE = 1 / 15f
+        /**
+         * Composes a drawable for this icon, consisting of a background and 2 app icons. The app
+         * pair will draw as "disabled" if either of the following is true:
+         * 1) One of the member WorkspaceItemInfos is disabled (i.e. the app software itself is
+         *    paused or can't be launched for some other reason).
+         * 2) One of the member apps can't be launched due to screen size requirements.
+         */
+        @JvmStatic
+        fun composeDrawable(appPairInfo: AppPairInfo, p: AppPairIconDrawingParams): Drawable {
+            // Generate new icons, using themed flag if needed.
+            val flags = if (Themes.isThemedIconEnabled(p.context)) BitmapInfo.FLAG_THEMED else 0
+            val appIcon1 = appPairInfo.getFirstApp().newIcon(p.context, flags)
+            val appIcon2 = appPairInfo.getSecondApp().newIcon(p.context, flags)
+            appIcon1.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())
+            appIcon2.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())
+
+            val shouldDrawAsDisabled =
+                appPairInfo.isDisabled || !appPairInfo.isLaunchable(p.context)
+
+            // Set disabled status on icons.
+            appIcon1.setIsDisabled(shouldDrawAsDisabled)
+            appIcon2.setIsDisabled(shouldDrawAsDisabled)
+
+            // Create icon drawable.
+            val fullIconDrawable = AppPairIconDrawable(p, appIcon1, appIcon2)
+            fullIconDrawable.setBounds(0, 0, p.iconSize, p.iconSize)
+
+            // Set disabled color filter on background paint.
+            fullIconDrawable.colorFilter =
+                if (shouldDrawAsDisabled) getDisabledColorFilter() else null
+
+            return fullIconDrawable
+        }
     }
 
-    // App pair icons are slightly smaller than regular icons, so we pad the icon by this much on
-    // each side.
-    private var outerPadding = 0f
-    // Inside of the icon, the two member apps are padded by this much.
-    private var innerPadding = 0f
-    // The colored background (two rectangles in a square area) is this big.
-    private var backgroundSize = 0f
-    // The two member apps have icons that are this big (in diameter).
-    private var memberIconSize = 0f
-    // The size of the center channel.
-    var centerChannelSize = 0f
-    // The large outer radius of the background rectangles.
-    var bigRadius = 0f
-    // The small inner radius of the background rectangles.
-    var smallRadius = 0f
-    // The app pairs icon appears differently in portrait and landscape.
-    var isLeftRightSplit = false
-
-    private lateinit var activityContext: ActivityContext
     private lateinit var parentIcon: AppPairIcon
-    private lateinit var appPairBackground: Drawable
-    private lateinit var appIcon1: FastBitmapDrawable
-    private lateinit var appIcon2: FastBitmapDrawable
+    private lateinit var drawParams: AppPairIconDrawingParams
+    private lateinit var drawable: Drawable
 
-    fun init(activity: ActivityContext, icon: AppPairIcon) {
-        activityContext = activity
-
-        // Calculate device-specific measurements
-        val defaultIconSize = activity.deviceProfile.iconSizePx
-        outerPadding = OUTER_PADDING_SCALE * defaultIconSize
-        innerPadding = INNER_PADDING_SCALE * defaultIconSize
-        backgroundSize = defaultIconSize - outerPadding * 2
-        memberIconSize = MEMBER_ICON_SCALE * defaultIconSize
-        centerChannelSize = CENTER_CHANNEL_SCALE * defaultIconSize
-        bigRadius = BIG_RADIUS_SCALE * defaultIconSize
-        smallRadius = SMALL_RADIUS_SCALE * defaultIconSize
+    fun init(icon: AppPairIcon, container: Int) {
         parentIcon = icon
-        updateOrientation()
-
-        appPairBackground = AppPairIconBackground(context, this)
-        appPairBackground.setBounds(0, 0, backgroundSize.toInt(), backgroundSize.toInt())
-
-        applyIcons()
+        drawParams = AppPairIconDrawingParams(context, container)
+        drawable = composeDrawable(icon.info, drawParams)
 
         // Center the drawable area in the larger icon canvas
         val lp: LayoutParams = layoutParams as LayoutParams
         lp.gravity = Gravity.CENTER_HORIZONTAL
-        lp.topMargin = outerPadding.toInt()
-        lp.height = backgroundSize.toInt()
-        lp.width = backgroundSize.toInt()
+        lp.height = drawParams.iconSize
+        lp.width = drawParams.iconSize
         layoutParams = lp
     }
 
     override fun onAttachedToWindow() {
         super.onAttachedToWindow()
-        activityContext.addOnDeviceProfileChangeListener(this)
+        getActivityContext().addOnDeviceProfileChangeListener(this)
     }
 
     override fun onDetachedFromWindow() {
         super.onDetachedFromWindow()
-        activityContext.removeOnDeviceProfileChangeListener(this)
+        getActivityContext().removeOnDeviceProfileChangeListener(this)
     }
 
-    /** Checks the device orientation and updates isLeftRightSplit accordingly. */
-    private fun updateOrientation() {
-        val activity: ActivityContext = ActivityContext.lookupContext(context)
-        isLeftRightSplit = activity.deviceProfile.isLeftRightSplit
+    private fun getActivityContext(): ActivityContext {
+        return ActivityContext.lookupContext(context)
     }
 
     /** When device profile changes, update orientation */
-    override fun onDeviceProfileChanged(dp: DeviceProfile?) {
-        updateOrientation()
+    override fun onDeviceProfileChanged(dp: DeviceProfile) {
+        drawParams.updateOrientation(dp)
+        redraw()
+    }
+
+    /** Updates the icon drawable and redraws it */
+    fun redraw() {
+        drawable = composeDrawable(parentIcon.info, drawParams)
         invalidate()
     }
 
-    /** Sets up app pair member icons for drawing. */
-    fun applyIcons() {
-        val apps = parentIcon.info.contents
-
-        // TODO (b/326664798): Delete this check, instead check at launcher load time
-        if (apps.size != 2) {
-            Log.wtf(TAG, "AppPair contents not 2, size: " + apps.size, Throwable())
-            return
-        }
-
-        // Generate new icons, using themed flag if needed
-        val flags = if (Themes.isThemedIconEnabled(context)) BitmapInfo.FLAG_THEMED else 0
-        appIcon1 = apps[0].newIcon(context, flags)
-        appIcon2 = apps[1].newIcon(context, flags)
-        appIcon1.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
-        appIcon2.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
-
-        // Check disabled state
-        val shouldDrawAsDisabled =
-            parentIcon.info.isDisabled || !parentIcon.isLaunchableAtScreenSize
-
-        appPairBackground.colorFilter = if (shouldDrawAsDisabled) getDisabledColorFilter() else null
-        appIcon1.setIsDisabled(shouldDrawAsDisabled)
-        appIcon2.setIsDisabled(shouldDrawAsDisabled)
-    }
-
-    /** Gets this icon graphic's bounds, with respect to the parent icon's coordinate system. */
+    /**
+     * Gets this icon graphic's visual bounds, with respect to the parent icon's coordinate system.
+     */
     fun getIconBounds(outBounds: Rect) {
-        outBounds.set(0, 0, backgroundSize.toInt(), backgroundSize.toInt())
+        outBounds.set(0, 0, drawParams.backgroundSize.toInt(), drawParams.backgroundSize.toInt())
         outBounds.offset(
             // x-coordinate in parent's coordinate system
-            ((parentIcon.width - backgroundSize) / 2).toInt(),
+            ((parentIcon.width - drawParams.backgroundSize) / 2).toInt(),
             // y-coordinate in parent's coordinate system
-            parentIcon.paddingTop + outerPadding.toInt()
+            (parentIcon.paddingTop + drawParams.standardIconPadding + drawParams.outerPadding)
+                .toInt()
         )
     }
 
     override fun dispatchDraw(canvas: Canvas) {
         super.dispatchDraw(canvas)
-
-        // Draw background
-        appPairBackground.draw(canvas)
-
-        // Draw first icon
-        canvas.save()
-        // The app icons are placed differently depending on device orientation.
-        if (isLeftRightSplit) {
-            canvas.translate(innerPadding, height / 2f - memberIconSize / 2f)
-        } else {
-            canvas.translate(width / 2f - memberIconSize / 2f, innerPadding)
-        }
-
-        appIcon1.draw(canvas)
-        canvas.restore()
-
-        // Draw second icon
-        canvas.save()
-        // The app icons are placed differently depending on device orientation.
-        if (isLeftRightSplit) {
-            canvas.translate(
-                width - (innerPadding + memberIconSize),
-                height / 2f - memberIconSize / 2f
-            )
-        } else {
-            canvas.translate(
-                width / 2f - memberIconSize / 2f,
-                height - (innerPadding + memberIconSize)
-            )
-        }
-
-        appIcon2.draw(canvas)
-        canvas.restore()
+        drawable.draw(canvas)
     }
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 6d64c22..b7c9161 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -33,7 +33,6 @@
 import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
 
 import android.content.res.Resources;
-import android.view.ViewConfiguration;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -132,7 +131,7 @@
                     "Allow entering All Apps from Overview (e.g. long swipe up from app)");
 
     public static final BooleanFlag CUSTOM_LPNH_THRESHOLDS =
-            getReleaseFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", DISABLED,
+            getReleaseFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", ENABLED,
                     "Add dev options to customize the LPNH trigger slop and milliseconds");
 
     public static final BooleanFlag ANIMATE_LPNH =
@@ -155,8 +154,7 @@
                     LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP);
 
     public static final IntFlag LPNH_TIMEOUT_MS =
-            FlagsFactory.getIntFlag(301680992, "LPNH_TIMEOUT_MS",
-                    ViewConfiguration.getLongPressTimeout(),
+            FlagsFactory.getIntFlag(301680992, "LPNH_TIMEOUT_MS", 450,
                     "Controls lpnh timeout in milliseconds", LONG_PRESS_NAV_HANDLE_TIMEOUT_MS);
 
     public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag(
@@ -397,11 +395,6 @@
                     + "waiting for SystemUI and then merging the SystemUI progress whenever we "
                     + "start receiving the events");
 
-    // TODO(Block 24): Clean up flags
-    public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(270393455,
-            "ENABLE_NEW_MIGRATION_LOGIC", ENABLED,
-            "Enable the new grid migration logic, keeping pages when src < dest");
-
     // TODO(Block 25): Clean up flags
     public static final BooleanFlag ENABLE_NEW_GESTURE_NAV_TUTORIAL = getDebugFlag(270396257,
             "ENABLE_NEW_GESTURE_NAV_TUTORIAL", ENABLED,
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 213c458..29aa216 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -258,7 +258,7 @@
                         .addCategory(Intent.CATEGORY_HOME)
                         .setPackage(getPackageName())
                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        Launcher.ACTIVITY_TRACKER.registerCallback(listener);
+        Launcher.ACTIVITY_TRACKER.registerCallback(listener, "AddItemActivity.onLongClick");
         startActivity(homeIntent, ApiWrapper.createFadeOutAnimOptions(this).toBundle());
         logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED);
         mFinishOnPause = true;
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index b6e5977..bc5a164 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Flags;
 import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.model.data.AppPairInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -289,7 +290,8 @@
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
             ItemInfo dragInfo = mDragObject.dragInfo;
-            if (dragInfo instanceof WorkspaceItemInfo && matcher.test(dragInfo)) {
+            if ((dragInfo instanceof WorkspaceItemInfo && matcher.test(dragInfo))
+                    || (dragInfo instanceof AppPairInfo api && api.anyMatch(matcher))) {
                 cancelDrag();
             }
         }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index df01a65..f01e7e4 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -493,7 +493,7 @@
         mInfo = info;
         mFromTitle = info.title;
         mFromLabelState = info.getFromLabelState();
-        ArrayList<WorkspaceItemInfo> children = info.contents;
+        ArrayList<WorkspaceItemInfo> children = info.getContents();
         Collections.sort(children, ITEM_POS_COMPARATOR);
         updateItemLocationsInDatabaseBatch(true);
 
@@ -626,7 +626,7 @@
         // onDropComplete. Perform cleanup once drag-n-drop ends.
         mDragController.addDragListener(this);
 
-        ArrayList<WorkspaceItemInfo> items = new ArrayList<>(mInfo.contents);
+        ArrayList<WorkspaceItemInfo> items = new ArrayList<>(mInfo.getContents());
         mEmptyCellRank = items.size();
         items.add(null);    // Add an empty spot at the end
 
@@ -639,7 +639,7 @@
      * is played.
      */
     public void animateOpen() {
-        animateOpen(mInfo.contents, 0);
+        animateOpen(mInfo.getContents(), 0);
     }
 
     /**
@@ -1097,9 +1097,9 @@
                 mActivityContext.getDeviceProfile()).setFolderInfo(mInfo);
 
         ArrayList<ItemInfo> items = new ArrayList<>();
-        int total = mInfo.contents.size();
+        int total = mInfo.getContents().size();
         for (int i = 0; i < total; i++) {
-            WorkspaceItemInfo itemInfo = mInfo.contents.get(i);
+            WorkspaceItemInfo itemInfo = mInfo.getContents().get(i);
             if (verifier.updateRankAndPos(itemInfo, i)) {
                 items.add(itemInfo);
             }
@@ -1113,7 +1113,7 @@
                 FolderNameInfos nameInfos = new FolderNameInfos();
                 FolderNameProvider fnp = FolderNameProvider.newInstance(getContext());
                 fnp.getSuggestedFolderName(
-                        getContext(), mInfo.contents, nameInfos);
+                        getContext(), mInfo.getContents(), nameInfos);
                 mInfo.suggestedFolderNames = nameInfos;
             });
         }
@@ -1217,7 +1217,7 @@
     }
 
     public int getItemCount() {
-        return mInfo.contents.size();
+        return mInfo.getContents().size();
     }
 
     void replaceFolderWithFinalItem() {
diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java
index cc24761..593673d 100644
--- a/src/com/android/launcher3/folder/FolderGridOrganizer.java
+++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java
@@ -57,7 +57,7 @@
      * Updates the organizer with the provided folder info
      */
     public FolderGridOrganizer setFolderInfo(FolderInfo info) {
-        return setContentSize(info.contents.size());
+        return setContentSize(info.getContents().size());
     }
 
     /**
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index cf984bb..175452e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -215,7 +215,7 @@
 
         // Keep the notification dot up to date with the sum of all the content's dots.
         FolderDotInfo folderDotInfo = new FolderDotInfo();
-        for (WorkspaceItemInfo si : folderInfo.contents) {
+        for (WorkspaceItemInfo si : folderInfo.getContents()) {
             folderDotInfo.addDotInfo(activity.getDotInfoForItem(si));
         }
         icon.setDotInfo(folderDotInfo);
@@ -422,7 +422,7 @@
             FolderNameInfos nameInfos = new FolderNameInfos();
             Executors.MODEL_EXECUTOR.post(() -> {
                 d.folderNameProvider.getSuggestedFolderName(
-                        getContext(), mInfo.contents, nameInfos);
+                        getContext(), mInfo.getContents(), nameInfos);
                 postDelayed(() -> {
                     setLabelSuggestion(nameInfos, d.logInstanceId);
                     invalidate();
@@ -487,7 +487,7 @@
         }
         mFolder.notifyDrop();
         onDrop(item, d, null, 1.0f,
-                itemReturnedOnFailedDrop ? item.rank : mInfo.contents.size(),
+                itemReturnedOnFailedDrop ? item.rank : mInfo.getContents().size(),
                 itemReturnedOnFailedDrop
         );
     }
@@ -666,7 +666,7 @@
      * Returns the list of items which should be visible in the preview
      */
     public List<WorkspaceItemInfo> getPreviewItemsOnPage(int page) {
-        return mPreviewVerifier.setFolderInfo(mInfo).previewItemsForPage(page, mInfo.contents);
+        return mPreviewVerifier.setFolderInfo(mInfo).previewItemsForPage(page, mInfo.getContents());
     }
 
     @Override
@@ -810,7 +810,7 @@
      * Returns a formatted accessibility title for folder
      */
     public String getAccessiblityTitle(CharSequence title) {
-        int size = mInfo.contents.size();
+        int size = mInfo.getContents().size();
         if (size < MAX_NUM_ITEMS_IN_PREVIEW) {
             return getContext().getString(R.string.folder_name_format_exact, title, size);
         } else {
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index bf59594..5d2bb3a 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -35,7 +35,7 @@
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.StringCache;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.Preconditions;
@@ -62,7 +62,7 @@
      * name edit box can also be used to provide suggestion.
      */
     public static final int SUGGEST_MAX = 4;
-    protected IntSparseArrayMap<FolderInfo> mFolderInfos;
+    protected IntSparseArrayMap<CollectionInfo> mCollectionInfos;
     protected List<AppInfo> mAppInfos;
 
     /**
@@ -79,7 +79,7 @@
     }
 
     public static FolderNameProvider newInstance(Context context, List<AppInfo> appInfos,
-            IntSparseArrayMap<FolderInfo> folderInfos) {
+            IntSparseArrayMap<CollectionInfo> folderInfos) {
         Preconditions.assertWorkerThread();
         FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
                 context.getApplicationContext(), R.string.folder_name_provider_class);
@@ -93,9 +93,9 @@
                 new FolderNameWorker());
     }
 
-    private void load(List<AppInfo> appInfos, IntSparseArrayMap<FolderInfo> folderInfos) {
+    private void load(List<AppInfo> appInfos, IntSparseArrayMap<CollectionInfo> folderInfos) {
         mAppInfos = appInfos;
-        mFolderInfos = folderInfos;
+        mCollectionInfos = folderInfos;
     }
 
     /**
@@ -195,7 +195,7 @@
         @Override
         public void execute(@NonNull final LauncherAppState app,
                 @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
-            mFolderInfos = dataModel.folders.clone();
+            mCollectionInfos = dataModel.collections.clone();
             mAppInfos = Arrays.asList(apps.copyData());
         }
     }
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
index 78298b3..33bcf21 100644
--- a/src/com/android/launcher3/folder/LauncherDelegate.java
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -93,7 +93,7 @@
                         // folder
                         CellLayout cellLayout = mLauncher.getCellLayout(info.container,
                                 mLauncher.getCellPosMapper().mapModelToPresenter(info).screenId);
-                        finalItem =  info.contents.remove(0);
+                        finalItem =  info.getContents().remove(0);
                         newIcon = mLauncher.getItemInflater().inflateItem(
                                 finalItem, mLauncher.getModelWriter(), cellLayout);
                         mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index e0a6627..6b3bb51 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -20,6 +20,8 @@
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 import static android.view.View.VISIBLE;
 
+import static com.android.launcher3.BubbleTextView.DISPLAY_TASKBAR;
+import static com.android.launcher3.BubbleTextView.DISPLAY_WORKSPACE;
 import static com.android.launcher3.DeviceProfile.DEFAULT_SCALE;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 import static com.android.launcher3.config.FeatureFlags.shouldShowFirstPageWidget;
@@ -80,13 +82,14 @@
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.AppPairInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 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.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.uioverrides.PredictedAppIconInflater;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
@@ -387,14 +390,16 @@
         addInScreenFromBind(icon, info);
     }
 
-    private void inflateAndAddCollectionIcon(FolderInfo info) {
-        CellLayout screen = info.container == Favorites.CONTAINER_DESKTOP
+    private void inflateAndAddCollectionIcon(CollectionInfo info) {
+        boolean isOnDesktop = info.container == Favorites.CONTAINER_DESKTOP;
+        CellLayout screen = isOnDesktop
                 ? mWorkspaceScreens.get(info.screenId)
                 : mHotseat;
-        FrameLayout folderIcon = info.itemType == Favorites.ITEM_TYPE_FOLDER
-                ? FolderIcon.inflateIcon(R.layout.folder_icon, this, screen, info)
-                : AppPairIcon.inflateIcon(R.layout.app_pair_icon, this, screen, info);
-        addInScreenFromBind(folderIcon, info);
+        FrameLayout collectionIcon = info.itemType == Favorites.ITEM_TYPE_FOLDER
+                ? FolderIcon.inflateIcon(R.layout.folder_icon, this, screen, (FolderInfo) info)
+                : AppPairIcon.inflateIcon(R.layout.app_pair_icon, this, screen, (AppPairInfo) info,
+                        isOnDesktop ? DISPLAY_WORKSPACE : DISPLAY_TASKBAR);
+        addInScreenFromBind(collectionIcon, info);
     }
 
     private void inflateAndAddWidgets(
@@ -453,10 +458,10 @@
 
     private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
         CellLayout screen = mWorkspaceScreens.get(info.screenId);
-        View view = PredictedAppIconInflater.inflate(mHomeElementInflater, screen, info);
-        if (view != null) {
-            addInScreenFromBind(view, info);
-        }
+        BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
+                R.layout.predicted_app_icon, screen, false);
+        icon.applyFromWorkspaceItem(info);
+        addInScreenFromBind(icon, info);
     }
 
     private void dispatchVisibilityAggregated(View view, boolean isVisible) {
@@ -498,7 +503,7 @@
                     break;
                 case Favorites.ITEM_TYPE_FOLDER:
                 case Favorites.ITEM_TYPE_APP_PAIR:
-                    inflateAndAddCollectionIcon((FolderInfo) itemInfo);
+                    inflateAndAddCollectionIcon((CollectionInfo) itemInfo);
                     break;
                 default:
                     break;
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index fe327d0..addd072 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -50,10 +50,10 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
+import com.android.launcher3.model.BaseLauncherBinder;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.GridSizeMigrationUtil;
-import com.android.launcher3.model.LauncherBinder;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.provider.LauncherDbUtils;
@@ -244,7 +244,7 @@
                     /* bgAllAppsList= */ null,
                     bgModel,
                     LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
-                    new LauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
+                    new BaseLauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
                             /* bgAllAppsList= */ null, new Callbacks[0])) {
 
                 @Override
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index 1791539..f40eda6 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.icons;
 
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -101,7 +101,7 @@
      * Launcher specific checks
      */
     public static Drawable getIcon(Context context, ShortcutInfo shortcutInfo, int density) {
-        if (GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             return null;
         }
         try {
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 96a8da9..ce563b7 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -31,7 +31,7 @@
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -131,8 +131,8 @@
                 int screenId = coords[0];
 
                 ItemInfo itemInfo;
-                if (item instanceof WorkspaceItemInfo || item instanceof FolderInfo ||
-                        item instanceof LauncherAppWidgetInfo) {
+                if (item instanceof WorkspaceItemInfo || item instanceof CollectionInfo
+                        || item instanceof LauncherAppWidgetInfo) {
                     itemInfo = item;
                 } else if (item instanceof WorkspaceItemFactory) {
                     itemInfo = ((WorkspaceItemFactory) item).makeWorkspaceItem(app.getContext());
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index 966b6a6..41dbe4e 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.model;
 
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 import static com.android.launcher3.Flags.enableWorkspaceInflation;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
@@ -43,6 +44,7 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInflater;
@@ -50,10 +52,12 @@
 import com.android.launcher3.util.LooperIdleLock;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -65,7 +69,7 @@
 /**
  * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
  */
-public abstract class BaseLauncherBinder {
+public class BaseLauncherBinder {
 
     protected static final String TAG = "LauncherBinder";
     private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
@@ -81,8 +85,8 @@
     private int mMyBindingId;
 
     public BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel,
-            AllAppsList allAppsList, Callbacks[] callbacksList, LooperExecutor uiExecutor) {
-        mUiExecutor = uiExecutor;
+            AllAppsList allAppsList, Callbacks[] callbacksList) {
+        mUiExecutor = MAIN_EXECUTOR;
         mApp = app;
         mBgDataModel = dataModel;
         mBgAllAppsList = allAppsList;
@@ -158,7 +162,16 @@
     /**
      * BindDeepShortcuts is abstract because it is a no-op for the go launcher.
      */
-    public abstract void bindDeepShortcuts();
+    public void bindDeepShortcuts() {
+        if (!WIDGETS_ENABLED) {
+            return;
+        }
+        final HashMap<ComponentKey, Integer> shortcutMapCopy;
+        synchronized (mBgDataModel) {
+            shortcutMapCopy = new HashMap<>(mBgDataModel.deepShortcutMap);
+        }
+        executeCallbacksTask(c -> c.bindDeepShortcutMap(shortcutMapCopy), mUiExecutor);
+    }
 
     /**
      * Binds the all apps results from LoaderTask to the callbacks UX.
@@ -178,12 +191,24 @@
     /**
      * bindWidgets is abstract because it is a no-op for the go launcher.
      */
-    public abstract void bindWidgets();
+    public void bindWidgets() {
+        if (!WIDGETS_ENABLED) {
+            return;
+        }
+        final List<WidgetsListBaseEntry> widgets =
+                mBgDataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
+        executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
+    }
 
     /**
      * bindWidgets is abstract because it is a no-op for the go launcher.
      */
-    public abstract void bindSmartspaceWidget();
+    public void bindSmartspaceWidget() {
+        if (!WIDGETS_ENABLED) {
+            return;
+        }
+        executeCallbacksTask(c -> c.bindSmartspaceWidget(), mUiExecutor);
+    }
 
     /**
      * Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to right)
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 8579d1d..44e45da 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -18,9 +18,9 @@
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY;
 
 import static com.android.launcher3.BuildConfig.QSB_ON_FIRST_SCREEN;
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL;
 import static com.android.launcher3.config.FeatureFlags.shouldShowFirstPageWidget;
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
 import static com.android.launcher3.shortcuts.ShortcutRequest.PINNED;
 
 import static java.util.stream.Collectors.groupingBy;
@@ -44,6 +44,7 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -102,9 +103,9 @@
     public final ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
 
     /**
-     * Map of id to FolderInfos of all the folders created by LauncherModel
+     * Map of id to CollectionInfos of all the folders or app pairs created by LauncherModel
      */
-    public final IntSparseArrayMap<FolderInfo> folders = new IntSparseArrayMap<>();
+    public final IntSparseArrayMap<CollectionInfo> collections = new IntSparseArrayMap<>();
 
     /**
      * Extra container based items
@@ -144,7 +145,7 @@
     public synchronized void clear() {
         workspaceItems.clear();
         appWidgets.clear();
-        folders.clear();
+        collections.clear();
         itemsIdMap.clear();
         deepShortcutMap.clear();
         extraItems.clear();
@@ -179,9 +180,9 @@
         for (int i = 0; i < appWidgets.size(); i++) {
             writer.println(prefix + '\t' + appWidgets.get(i).toString());
         }
-        writer.println(prefix + " ---- folder items ");
-        for (int i = 0; i < folders.size(); i++) {
-            writer.println(prefix + '\t' + folders.valueAt(i).toString());
+        writer.println(prefix + " ---- collection items ");
+        for (int i = 0; i < collections.size(); i++) {
+            writer.println(prefix + '\t' + collections.valueAt(i).toString());
         }
         writer.println(prefix + " ---- extra items ");
         for (int i = 0; i < extraItems.size(); i++) {
@@ -211,12 +212,12 @@
             switch (item.itemType) {
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                 case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
-                    folders.remove(item.id);
+                    collections.remove(item.id);
                     if (FeatureFlags.IS_STUDIO_BUILD) {
                         for (ItemInfo info : itemsIdMap) {
                             if (info.container == item.id) {
-                                // We are deleting a folder which still contains items that
-                                // think they are contained by that folder.
+                                // We are deleting a collection which still contains items that
+                                // think they are contained by that collection.
                                 String msg = "deleting a collection (" + item + ") which still "
                                         + "contains items (" + info + ")";
                                 Log.e(TAG, msg);
@@ -259,7 +260,7 @@
         switch (item.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
             case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
-                folders.put(item.id, (FolderInfo) item);
+                collections.put(item.id, (CollectionInfo) item);
                 workspaceItems.add(item);
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
@@ -269,14 +270,14 @@
                     workspaceItems.add(item);
                 } else {
                     if (newItem) {
-                        if (!folders.containsKey(item.container)) {
+                        if (!collections.containsKey(item.container)) {
                             // Adding an item to a nonexistent collection.
                             String msg = "attempted to add item: " + item + " to a nonexistent app"
                                     + " collection";
                             Log.e(TAG, msg);
                         }
                     } else {
-                        findOrMakeFolder(item.container).add((WorkspaceItemInfo) item, false);
+                        findOrMakeFolder(item.container).add((WorkspaceItemInfo) item);
                     }
                 }
                 break;
@@ -305,7 +306,7 @@
      * shortcuts and unpinning any extra shortcuts.
      */
     public synchronized void updateShortcutPinnedState(Context context, UserHandle user) {
-        if (GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             return;
         }
 
@@ -371,15 +372,18 @@
      * Return an existing FolderInfo object if we have encountered this ID previously,
      * or make a new one.
      */
-    public synchronized FolderInfo findOrMakeFolder(int id) {
+    public synchronized CollectionInfo findOrMakeFolder(int id) {
         // See if a placeholder was created for us already
-        FolderInfo folderInfo = folders.get(id);
-        if (folderInfo == null) {
-            // No placeholder -- create a new instance
-            folderInfo = new FolderInfo();
-            folders.put(id, folderInfo);
+        CollectionInfo collectionInfo = collections.get(id);
+        if (collectionInfo == null) {
+            // No placeholder -- create a new blank folder instance. At this point, we don't know
+            // if the desired container is supposed to be a folder or an app pair. In the case that
+            // it is an app pair, the blank folder will be replaced by a blank app pair when the app
+            // pair is getting processed, in WorkspaceItemProcessor.processFolderOrAppPair().
+            collectionInfo = new FolderInfo();
+            collections.put(id, collectionInfo);
         }
-        return folderInfo;
+        return collectionInfo;
     }
 
     /**
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index 8c68eb8..729b381 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -156,11 +156,11 @@
     }
 
     public Integer getColumns() {
-        return Integer.parseInt(String.valueOf(mGridSizeString.charAt(0)));
+        return Integer.parseInt(String.valueOf(mGridSizeString.split(",")[0]));
     }
 
     public Integer getRows() {
-        return Integer.parseInt(String.valueOf(mGridSizeString.charAt(2)));
+        return Integer.parseInt(String.valueOf(mGridSizeString.split(",")[1]));
     }
 
     @Override
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index 9e91b9d..1deb665 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -36,6 +36,7 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -67,7 +68,8 @@
     private static final String ACTION_FIRST_SCREEN_ACTIVE_INSTALLS
             = "com.android.launcher3.action.FIRST_SCREEN_ACTIVE_INSTALLS";
 
-    private static final String FOLDER_ITEM_EXTRA = "folderItem";
+    // String retained as "folderItem" for back-compatibility reasons.
+    private static final String COLLECTION_ITEM_EXTRA = "folderItem";
     private static final String WORKSPACE_ITEM_EXTRA = "workspaceItem";
     private static final String HOTSEAT_ITEM_EXTRA = "hotseatItem";
     private static final String WIDGET_ITEM_EXTRA = "widgetItem";
@@ -105,20 +107,19 @@
     @WorkerThread
     private void sendBroadcastToInstaller(Context context, String installerPackageName,
             Set<String> packages, List<ItemInfo> firstScreenItems) {
-        Set<String> folderItems = new HashSet<>();
+        Set<String> collectionItems = new HashSet<>();
         Set<String> workspaceItems = new HashSet<>();
         Set<String> hotseatItems = new HashSet<>();
         Set<String> widgetItems = new HashSet<>();
 
         for (ItemInfo info : firstScreenItems) {
-            if (info instanceof FolderInfo) {
-                FolderInfo folderInfo = (FolderInfo) info;
-                String folderItemInfoPackage;
-                for (ItemInfo folderItemInfo : cloneOnMainThread(folderInfo.contents)) {
-                    folderItemInfoPackage = getPackageName(folderItemInfo);
-                    if (folderItemInfoPackage != null
-                            && packages.contains(folderItemInfoPackage)) {
-                        folderItems.add(folderItemInfoPackage);
+            if (info instanceof CollectionInfo ci) {
+                String collectionItemInfoPackage;
+                for (ItemInfo collectionItemInfo : cloneOnMainThread(ci.getContents())) {
+                    collectionItemInfoPackage = getPackageName(collectionItemInfo);
+                    if (collectionItemInfoPackage != null
+                            && packages.contains(collectionItemInfoPackage)) {
+                        collectionItems.add(collectionItemInfoPackage);
                     }
                 }
             }
@@ -137,13 +138,13 @@
         }
 
         if (DEBUG) {
-            printList(installerPackageName, "Folder item", folderItems);
+            printList(installerPackageName, "Collection item", collectionItems);
             printList(installerPackageName, "Workspace item", workspaceItems);
             printList(installerPackageName, "Hotseat item", hotseatItems);
             printList(installerPackageName, "Widget item", widgetItems);
         }
 
-        if (folderItems.isEmpty()
+        if (collectionItems.isEmpty()
                 && workspaceItems.isEmpty()
                 && hotseatItems.isEmpty()
                 && widgetItems.isEmpty()) {
@@ -152,7 +153,7 @@
         }
         context.sendBroadcast(new Intent(ACTION_FIRST_SCREEN_ACTIVE_INSTALLS)
                 .setPackage(installerPackageName)
-                .putStringArrayListExtra(FOLDER_ITEM_EXTRA, new ArrayList<>(folderItems))
+                .putStringArrayListExtra(COLLECTION_ITEM_EXTRA, new ArrayList<>(collectionItems))
                 .putStringArrayListExtra(WORKSPACE_ITEM_EXTRA, new ArrayList<>(workspaceItems))
                 .putStringArrayListExtra(HOTSEAT_ITEM_EXTRA, new ArrayList<>(hotseatItems))
                 .putStringArrayListExtra(WIDGET_ITEM_EXTRA, new ArrayList<>(widgetItems))
@@ -180,7 +181,7 @@
     }
 
     /**
-     * Clone the provided list on UI thread. This is used for {@link FolderInfo#contents} which
+     * Clone the provided list on UI thread. This is used for {@link FolderInfo#getContents()} which
      * is always modified on UI thread.
      */
     @AnyThread
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index 15190c7..f24a7c1 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -49,6 +49,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
+import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -223,19 +224,13 @@
             screens.add(screenId);
         }
 
-        boolean preservePages = false;
-        if (screens.isEmpty() && FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.get()) {
-            preservePages = destDeviceState.compareTo(srcDeviceState) >= 0
-                    && destDeviceState.getColumns() - srcDeviceState.getColumns() <= 2;
-        }
-
         // Then we place the items on the screens
         for (int screenId : screens) {
             if (DEBUG) {
                 Log.d(TAG, "Migrating " + screenId);
             }
             solveGridPlacement(helper, srcReader,
-                    destReader, screenId, trgX, trgY, workspaceToBeAdded, false);
+                    destReader, screenId, trgX, trgY, workspaceToBeAdded);
             if (workspaceToBeAdded.isEmpty()) {
                 break;
             }
@@ -245,8 +240,8 @@
         // any of the screens, in this case we add them to new screens until all of them are placed.
         int screenId = destReader.mLastScreenId + 1;
         while (!workspaceToBeAdded.isEmpty()) {
-            solveGridPlacement(helper, srcReader,
-                    destReader, screenId, trgX, trgY, workspaceToBeAdded, preservePages);
+            solveGridPlacement(helper, srcReader, destReader, screenId, trgX, trgY,
+                    workspaceToBeAdded);
             screenId++;
         }
 
@@ -348,7 +343,7 @@
     private static void solveGridPlacement(@NonNull final DatabaseHelper helper,
             @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
             final int screenId, final int trgX, final int trgY,
-            @NonNull final List<DbEntry> sortedItemsToPlace, final boolean matchingScreenIdOnly) {
+            @NonNull final List<DbEntry> sortedItemsToPlace) {
         final GridOccupancy occupied = new GridOccupancy(trgX, trgY);
         final Point trg = new Point(trgX, trgY);
         final Point next = new Point(0, screenId == 0
@@ -366,8 +361,6 @@
         Iterator<DbEntry> iterator = sortedItemsToPlace.iterator();
         while (iterator.hasNext()) {
             final DbEntry entry = iterator.next();
-            if (matchingScreenIdOnly && entry.screenId < screenId) continue;
-            if (matchingScreenIdOnly && entry.screenId > screenId) break;
             if (entry.minSpanX > trgX || entry.minSpanY > trgY) {
                 iterator.remove();
                 continue;
@@ -435,7 +428,8 @@
         }
     }
 
-    protected static class DbReader {
+    @VisibleForTesting
+    public static class DbReader {
 
         private final SQLiteDatabase mDb;
         private final String mTableName;
@@ -446,7 +440,7 @@
         private final Map<Integer, ArrayList<DbEntry>> mWorkspaceEntriesByScreenId =
                 new ArrayMap<>();
 
-        DbReader(SQLiteDatabase db, String tableName, Context context,
+        public DbReader(SQLiteDatabase db, String tableName, Context context,
                 Set<String> validPackages) {
             mDb = db;
             mTableName = tableName;
@@ -681,6 +675,11 @@
         private String mProvider;
         private Map<String, Set<Integer>> mFolderItems = new HashMap<>();
 
+        /**
+         * Id of the specific widget.
+         */
+        public int appWidgetId = NO_ID;
+
         /** Comparator according to the reading order */
         @Override
         public int compareTo(DbEntry another) {
@@ -714,6 +713,18 @@
             values.put(LauncherSettings.Favorites.SPANY, spanY);
         }
 
+        @Override
+        public void writeToValues(@NonNull ContentWriter writer) {
+            super.writeToValues(writer);
+            writer.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+        }
+
+        @Override
+        public void readFromValues(@NonNull ContentValues values) {
+            super.readFromValues(values);
+            appWidgetId = values.getAsInteger(LauncherSettings.Favorites.APPWIDGET_ID);
+        }
+
         /** This id is not used in the DB is only used while doing the migration and it identifies
          * an entry on each workspace. For example two calculator icons would have the same
          * migration id even thought they have different database ids.
@@ -724,7 +735,10 @@
                 case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
                     return getFolderMigrationId();
                 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-                    return mProvider;
+                    // mProvider is the app the widget belongs to and appWidgetId it's the unique
+                    // is of the widget, we need both because if you remove a widget and then add it
+                    // again, then it can change and the WidgetProvider would not know the widget.
+                    return mProvider + appWidgetId;
                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                     final String intentStr = cleanIntentString(mIntent);
                     try {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 17cef90..30cccd5 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -20,7 +20,6 @@
 import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
 import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
 import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL;
 import static com.android.launcher3.config.FeatureFlags.SMARTSPACE_AS_A_WIDGET;
@@ -77,9 +76,12 @@
 import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.AppPairInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.IconRequestInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
@@ -99,7 +101,6 @@
 import com.android.launcher3.widget.WidgetInflater;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -132,7 +133,7 @@
     private FirstScreenBroadcast mFirstScreenBroadcast;
 
     @NonNull
-    private final LauncherBinder mLauncherBinder;
+    private final BaseLauncherBinder mLauncherBinder;
 
     private final LauncherApps mLauncherApps;
     private final UserManager mUserManager;
@@ -153,13 +154,13 @@
     private String mDbName;
 
     public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
-            ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder) {
+            ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder) {
         this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, new UserManagerState());
     }
 
     @VisibleForTesting
     LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
-            ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder,
+            ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
             UserManagerState userManagerState) {
         mApp = app;
         mBgAllAppsList = bgAllAppsList;
@@ -234,6 +235,7 @@
             if (Objects.equals(mApp.getInvariantDeviceProfile().dbFile, mDbName)) {
                 verifyNotStopped();
                 sanitizeFolders(mItemsDeleted);
+                sanitizeAppPairs();
                 sanitizeWidgetsShortcutsAndPackages();
                 logASplit("sanitizeData");
             }
@@ -482,14 +484,20 @@
     }
 
     /**
-     * After all items have been processed and added to the BgDataModel, this method requests
-     * high-res icons for the items that are part of an app pair
+     * After all items have been processed and added to the BgDataModel, this method sorts and
+     * requests high-res icons for the items that are part of an app pair.
      */
     private void processAppPairItems() {
-        mBgDataModel.workspaceItems.stream()
-                .filter((itemInfo -> itemInfo.itemType == ITEM_TYPE_APP_PAIR))
-                .forEach(fi -> ((FolderInfo) fi).contents.forEach(item ->
-                        mIconCache.getTitleAndIcon(item, false /*useLowResIcon*/)));
+        for (CollectionInfo collection : mBgDataModel.collections) {
+            if (!(collection instanceof AppPairInfo appPair)) {
+                continue;
+            }
+
+            appPair.getContents().sort(Folder.ITEM_POS_COMPARATOR);
+            // Fetch hi-res icons if needed.
+            appPair.getContents().stream().filter(ItemInfoWithIcon::usingLowResIcon)
+                    .forEach(member -> mIconCache.getTitleAndIcon(member, false));
+        }
     }
 
     /**
@@ -545,20 +553,21 @@
         // Sort the folder items, update ranks, and make sure all preview items are high res.
         List<FolderGridOrganizer> verifiers = mApp.getInvariantDeviceProfile().supportedProfiles
                 .stream().map(FolderGridOrganizer::new).toList();
-        for (FolderInfo folder : mBgDataModel.folders) {
-            Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
+        for (CollectionInfo collection : mBgDataModel.collections) {
+            if (!(collection instanceof FolderInfo folder)) {
+                continue;
+            }
+
+            folder.getContents().sort(Folder.ITEM_POS_COMPARATOR);
             verifiers.forEach(verifier -> verifier.setFolderInfo(folder));
-            int size = folder.contents.size();
+            int size = folder.getContents().size();
 
             // Update ranks here to ensure there are no gaps caused by removed folder items.
             // Ranks are the source of truth for folder items, so cellX and cellY can be
             // ignored for now. Database will be updated once user manually modifies folder.
             for (int rank = 0; rank < size; ++rank) {
-                WorkspaceItemInfo info = folder.contents.get(rank);
-                // rank is used differently in app pairs, so don't reset
-                if (folder.itemType != ITEM_TYPE_APP_PAIR) {
-                    info.rank = rank;
-                }
+                WorkspaceItemInfo info = folder.getContents().get(rank);
+                info.rank = rank;
 
                 if (info.usingLowResIcon() && info.itemType == Favorites.ITEM_TYPE_APPLICATION
                         && verifiers.stream().anyMatch(it -> it.isItemInPreview(info.rank))) {
@@ -611,14 +620,32 @@
             IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders();
             synchronized (mBgDataModel) {
                 for (int folderId : deletedFolderIds) {
-                    mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
-                    mBgDataModel.folders.remove(folderId);
+                    mBgDataModel.workspaceItems.remove(mBgDataModel.collections.get(folderId));
+                    mBgDataModel.collections.remove(folderId);
                     mBgDataModel.itemsIdMap.remove(folderId);
                 }
             }
         }
     }
 
+    /** Cleans up app pairs if they don't have the right number of member apps (2). */
+    private void sanitizeAppPairs() {
+        IntArray deletedAppPairIds = mApp.getModel().getModelDbController().deleteBadAppPairs();
+        IntArray deletedAppIds = mApp.getModel().getModelDbController().deleteUnparentedApps();
+
+        IntArray deleted = new IntArray();
+        deleted.addAll(deletedAppPairIds);
+        deleted.addAll(deletedAppIds);
+
+        synchronized (mBgDataModel) {
+            for (int id : deleted) {
+                mBgDataModel.workspaceItems.remove(mBgDataModel.collections.get(id));
+                mBgDataModel.collections.remove(id);
+                mBgDataModel.itemsIdMap.remove(id);
+            }
+        }
+    }
+
     private void sanitizeWidgetsShortcutsAndPackages() {
         Context context = mApp.getContext();
 
@@ -754,16 +781,16 @@
 
     private void loadFolderNames() {
         FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
-                mBgAllAppsList.data, mBgDataModel.folders);
+                mBgAllAppsList.data, mBgDataModel.collections);
 
         synchronized (mBgDataModel) {
-            for (int i = 0; i < mBgDataModel.folders.size(); i++) {
+            for (int i = 0; i < mBgDataModel.collections.size(); i++) {
                 FolderNameInfos suggestionInfos = new FolderNameInfos();
-                FolderInfo info = mBgDataModel.folders.valueAt(i);
-                if (info.suggestedFolderNames == null) {
-                    provider.getSuggestedFolderName(mApp.getContext(), info.contents,
+                CollectionInfo info = mBgDataModel.collections.valueAt(i);
+                if (info instanceof FolderInfo fi && fi.suggestedFolderNames == null) {
+                    provider.getSuggestedFolderName(mApp.getContext(), fi.getContents(),
                             suggestionInfos);
-                    info.suggestedFolderNames = suggestionInfos;
+                    fi.suggestedFolderNames = suggestionInfos;
                 }
             }
         }
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 8ed554a..7e1d40d 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -15,11 +15,15 @@
  */
 package com.android.launcher3.model;
 
+import static android.provider.BaseColumns._ID;
 import static android.util.Base64.NO_PADDING;
 import static android.util.Base64.NO_WRAP;
 
 import static com.android.launcher3.DefaultLayoutParser.RES_PARTNER_DEFAULT_LAYOUT;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
 import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb;
 import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
 import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
@@ -391,6 +395,68 @@
         }
     }
 
+    /**
+     * Deletes any app pair that doesn't contain 2 member apps from the DB.
+     * @return Ids of deleted app pairs.
+     */
+    @WorkerThread
+    public IntArray deleteBadAppPairs() {
+        createDbIfNotExists();
+
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        try (SQLiteTransaction t = new SQLiteTransaction(db)) {
+            // Select all entries with ITEM_TYPE = ITEM_TYPE_APP_PAIR whose id does not appear
+            // exactly twice in the CONTAINER column.
+            String selection =
+                    ITEM_TYPE + " = " + ITEM_TYPE_APP_PAIR
+                            + " AND " + _ID +  " NOT IN"
+                            + " (SELECT " + CONTAINER + " FROM " + TABLE_NAME
+                            + " GROUP BY " + CONTAINER + " HAVING COUNT(*) = 2)";
+
+            IntArray appPairIds = LauncherDbUtils.queryIntArray(false, db, TABLE_NAME,
+                    _ID, selection, null, null);
+            if (!appPairIds.isEmpty()) {
+                db.delete(TABLE_NAME, Utilities.createDbSelectionQuery(
+                        _ID, appPairIds), null);
+            }
+            t.commit();
+            return appPairIds;
+        } catch (SQLException ex) {
+            Log.e(TAG, ex.getMessage(), ex);
+            return new IntArray();
+        }
+    }
+
+    /**
+     * Deletes any app with a container id that doesn't exist.
+     * @return Ids of deleted apps.
+     */
+    @WorkerThread
+    public IntArray deleteUnparentedApps() {
+        createDbIfNotExists();
+
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        try (SQLiteTransaction t = new SQLiteTransaction(db)) {
+            // Select all entries whose container id does not appear in the database.
+            String selection =
+                    CONTAINER + " >= 0"
+                            + " AND " + CONTAINER + " NOT IN"
+                            + " (SELECT " + _ID + " FROM " + TABLE_NAME + ")";
+
+            IntArray appIds = LauncherDbUtils.queryIntArray(false, db, TABLE_NAME,
+                    _ID, selection, null, null);
+            if (!appIds.isEmpty()) {
+                db.delete(TABLE_NAME, Utilities.createDbSelectionQuery(
+                        _ID, appIds), null);
+            }
+            t.commit();
+            return appIds;
+        } catch (SQLException ex) {
+            Log.e(TAG, ex.getMessage(), ex);
+            return new IntArray();
+        }
+    }
+
     private static void addModifiedTime(ContentValues values) {
         values.put(LauncherSettings.Favorites.MODIFIED, System.currentTimeMillis());
     }
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 55093a3..b477cb1 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -37,7 +37,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.BgDataModel.Callbacks;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -275,7 +275,7 @@
     public void deleteItemsFromDatabase(@NonNull final Predicate<ItemInfo> matcher,
             @Nullable final String reason) {
         deleteItemsFromDatabase(StreamSupport.stream(mBgDataModel.itemsIdMap.spliterator(), false)
-                        .filter(matcher).collect(Collectors.toList()), reason);
+                .filter(matcher).collect(Collectors.toList()), reason);
     }
 
     /**
@@ -302,15 +302,15 @@
     /**
      * Remove the specified folder and all its contents from the database.
      */
-    public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
+    public void deleteCollectionAndContentsFromDatabase(final CollectionInfo info) {
         ModelVerifier verifier = new ModelVerifier();
         notifyDelete(Collections.singleton(info));
 
         enqueueDeleteRunnable(newModelTask(() -> {
             mModel.getModelDbController().delete(Favorites.TABLE_NAME,
                     Favorites.CONTAINER + "=" + info.id, null);
-            mBgDataModel.removeItem(mContext, info.contents);
-            info.contents.clear();
+            mBgDataModel.removeItem(mContext, info.getContents());
+            info.getContents().clear();
 
             mModel.getModelDbController().delete(Favorites.TABLE_NAME,
                     Favorites._ID + "=" + info.id, null);
@@ -458,12 +458,12 @@
 
                 if (item.container != Favorites.CONTAINER_DESKTOP &&
                         item.container != Favorites.CONTAINER_HOTSEAT) {
-                    // Item is in a folder, make sure this folder exists
-                    if (!mBgDataModel.folders.containsKey(item.container)) {
+                    // Item is in a collection, make sure this collection exists
+                    if (!mBgDataModel.collections.containsKey(item.container)) {
                         // An items container is being set to a that of an item which is not in
                         // the list of Folders.
                         String msg = "item: " + item + " container being set to: " +
-                                item.container + ", not in the list of folders";
+                                item.container + ", not in the list of collections";
                         Log.e(TAG, msg);
                     }
                 }
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 0ba468d..ea1ae2e 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -361,17 +361,10 @@
         }
 
         if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
-            // This predicate is used to mark an ItemInfo for removal if its package or component
-            // is marked for removal.
-            Predicate<ItemInfo> removeAppMatch =
+            Predicate<ItemInfo> removeMatch =
                     ItemInfoMatcher.ofPackages(removedPackages, mUser)
                             .or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
                             .and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate());
-            // This predicate is used to mark an app pair for removal if it contains an app marked
-            // for removal.
-            Predicate<ItemInfo> removeAppPairMatch =
-                    ItemInfoMatcher.forAppPairMatch(removeAppMatch);
-            Predicate<ItemInfo> removeMatch = removeAppMatch.or(removeAppPairMatch);
             deleteAndBindComponentsRemoved(removeMatch,
                     "removed because the corresponding package or component is removed. "
                             + "mOp=" + mOp + " removedPackages=" + removedPackages.stream().collect(
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
similarity index 96%
rename from src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
rename to src/com/android/launcher3/model/WidgetsModel.java
index 8b983fc..91ce5ea 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -3,6 +3,7 @@
 
 import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER;
 
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
 import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
 
@@ -44,6 +45,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -59,10 +61,6 @@
  */
 public class WidgetsModel {
 
-    // True is the widget support is disabled.
-    public static final boolean GO_DISABLE_WIDGETS = false;
-    public static final boolean GO_DISABLE_NOTIFICATION_DOTS = false;
-
     private static final String TAG = "WidgetsModel";
     private static final boolean DEBUG = false;
 
@@ -79,6 +77,9 @@
     public synchronized ArrayList<WidgetsListBaseEntry> getFilteredWidgetsListForPicker(
             Context context,
             Predicate<WidgetItem> widgetItemFilter) {
+        if (!WIDGETS_ENABLED) {
+            return new ArrayList<>();
+        }
         ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
         AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
 
@@ -111,6 +112,9 @@
 
     /** Returns a mapping of packages to their widgets without static shortcuts. */
     public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
+        if (!WIDGETS_ENABLED) {
+            return Collections.emptyMap();
+        }
         Map<PackageUserKey, List<WidgetItem>> packagesToWidgets = new HashMap<>();
         mWidgetsList.forEach((packageItemInfo, widgetsAndShortcuts) -> {
             List<WidgetItem> widgets = widgetsAndShortcuts.stream()
@@ -131,6 +135,9 @@
      */
     public List<ComponentWithLabelAndIcon> update(
             LauncherAppState app, @Nullable PackageUserKey packageUser) {
+        if (!WIDGETS_ENABLED) {
+            return Collections.emptyList();
+        }
         Preconditions.assertWorkerThread();
 
         Context context = app.getContext();
@@ -207,6 +214,9 @@
 
     public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
             LauncherAppState app) {
+        if (!WIDGETS_ENABLED) {
+            return;
+        }
         WidgetManagerHelper widgetManager = new WidgetManagerHelper(app.getContext());
         for (Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
             if (packageNames.contains(entry.getKey().packageName)) {
@@ -231,6 +241,9 @@
 
     public WidgetItem getWidgetProviderInfoByProviderName(
             ComponentName providerName, UserHandle user) {
+        if (!WIDGETS_ENABLED) {
+            return null;
+        }
         List<WidgetItem> widgetsList = mWidgetsList.get(
                 new PackageItemInfo(providerName.getPackageName(), user));
         if (widgetsList == null) {
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 22e5eb4..aa29290 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -32,6 +32,8 @@
 import com.android.launcher3.Utilities
 import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
 import com.android.launcher3.logging.FileLog
+import com.android.launcher3.model.data.AppPairInfo
+import com.android.launcher3.model.data.FolderInfo
 import com.android.launcher3.model.data.IconRequestInfo
 import com.android.launcher3.model.data.ItemInfoWithIcon
 import com.android.launcher3.model.data.LauncherAppWidgetInfo
@@ -360,25 +362,40 @@
     }
 
     /**
-     * Loads the folder information from the database and formats it into a FolderInfo. Some of the
-     * processing for folder content items is done in LoaderTask after all the items in the
-     * workspace have been loaded. The loaded FolderInfos are stored in the BgDataModel.
+     * Loads CollectionInfo information from the database and formats it. This function runs while
+     * LoaderTask is still active; some of the processing for folder content items is done after all
+     * the items in the workspace have been loaded. The loaded and formatted CollectionInfo is then
+     * stored in the BgDataModel.
      */
     private fun processFolderOrAppPair() {
-        val folderInfo =
-            bgDataModel.findOrMakeFolder(c.id).apply {
-                c.applyCommonProperties(this)
-                itemType = c.itemType
-                // Do not trim the folder label, as is was set by the user.
-                title = c.getString(c.mTitleIndex)
-                spanX = 1
-                spanY = 1
-                options = c.options
-            }
+        var collection = bgDataModel.findOrMakeFolder(c.id)
+        // If we generated a placeholder Folder before this point, it may need to be replaced with
+        // an app pair.
+        if (c.itemType == Favorites.ITEM_TYPE_APP_PAIR && collection is FolderInfo) {
+            val folderInfo: FolderInfo = collection
+            val newAppPair = AppPairInfo()
+            // Move the placeholder's contents over to the new app pair.
+            folderInfo.contents.forEach(newAppPair::add)
+            collection = newAppPair
+            // Remove the placeholder and add the app pair into the data model.
+            bgDataModel.collections.remove(c.id)
+            bgDataModel.collections.put(c.id, collection)
+        }
 
-        // no special handling required for restored folders
+        c.applyCommonProperties(collection)
+        // Do not trim the folder label, as is was set by the user.
+        collection.title = c.getString(c.mTitleIndex)
+        collection.spanX = 1
+        collection.spanY = 1
+        if (collection is FolderInfo) {
+            collection.options = c.options
+        } else {
+            // An app pair may be inside another folder, so it needs to preserve rank information.
+            collection.rank = c.rank
+        }
+
         c.markRestored()
-        c.checkAndAddItem(folderInfo, bgDataModel, memoryLogger)
+        c.checkAndAddItem(collection, bgDataModel, memoryLogger)
     }
 
     /**
diff --git a/src/com/android/launcher3/model/data/AppPairInfo.kt b/src/com/android/launcher3/model/data/AppPairInfo.kt
new file mode 100644
index 0000000..4081316
--- /dev/null
+++ b/src/com/android/launcher3/model/data/AppPairInfo.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.model.data
+
+import android.content.Context
+import com.android.launcher3.LauncherSettings
+import com.android.launcher3.logger.LauncherAtom
+import com.android.launcher3.views.ActivityContext
+
+/** A type of app collection that launches multiple apps into split screen. */
+class AppPairInfo() : CollectionInfo() {
+    init {
+        itemType = LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR
+    }
+
+    /** Convenience constructor, calls primary constructor and init block */
+    constructor(app1: WorkspaceItemInfo, app2: WorkspaceItemInfo) : this() {
+        add(app1)
+        add(app2)
+    }
+
+    /** Adds an element to the contents array. */
+    override fun add(item: WorkspaceItemInfo) {
+        contents.add(item)
+    }
+
+    /** Returns the first app in the pair. */
+    fun getFirstApp() = contents[0]
+
+    /** Returns the second app in the pair. */
+    fun getSecondApp() = contents[1]
+
+    /** Returns if either of the app pair members is currently disabled. */
+    override fun isDisabled() = anyMatch { it.isDisabled }
+
+    /** Checks if the app pair is launchable at the current screen size. */
+    fun isLaunchable(context: Context) =
+        (ActivityContext.lookupContext(context) as ActivityContext).getDeviceProfile().isTablet ||
+            noneMatch { it.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE) }
+
+    /** Generates an ItemInfo for logging. */
+    override fun buildProto(cInfo: CollectionInfo?): LauncherAtom.ItemInfo {
+        val appPairIcon = LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size)
+        appPairIcon.setLabelInfo(title.toString())
+        return getDefaultItemInfoBuilder()
+            .setFolderIcon(appPairIcon)
+            .setRank(rank)
+            .setContainerInfo(getContainerInfo())
+            .build()
+    }
+}
diff --git a/src/com/android/launcher3/model/data/CollectionInfo.kt b/src/com/android/launcher3/model/data/CollectionInfo.kt
new file mode 100644
index 0000000..2b865a5
--- /dev/null
+++ b/src/com/android/launcher3/model/data/CollectionInfo.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.model.data
+
+import com.android.launcher3.LauncherSettings
+import com.android.launcher3.logger.LauncherAtom
+import com.android.launcher3.util.ContentWriter
+import java.util.function.Predicate
+
+abstract class CollectionInfo : ItemInfo() {
+    var contents: ArrayList<WorkspaceItemInfo> = ArrayList()
+
+    abstract fun add(item: WorkspaceItemInfo)
+
+    /** Convenience function. Checks contents to see if any match a given predicate. */
+    fun anyMatch(matcher: Predicate<in WorkspaceItemInfo>): Boolean {
+        return contents.stream().anyMatch(matcher)
+    }
+
+    /** Convenience function. Returns true if none of the contents match a given predicate. */
+    fun noneMatch(matcher: Predicate<in WorkspaceItemInfo>): Boolean {
+        return contents.stream().noneMatch(matcher)
+    }
+
+    override fun onAddToDatabase(writer: ContentWriter) {
+        super.onAddToDatabase(writer)
+        writer.put(LauncherSettings.Favorites.TITLE, title)
+    }
+
+    /** Returns the collection wrapped as {@link LauncherAtom.ItemInfo} for logging. */
+    override fun buildProto(): LauncherAtom.ItemInfo {
+        return buildProto(null)
+    }
+}
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 4a1d744..39d9cd9 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -24,8 +24,6 @@
 import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL;
 import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL;
 
-import android.os.Process;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
@@ -49,7 +47,7 @@
 /**
  * Represents a folder containing shortcuts or apps.
  */
-public class FolderInfo extends ItemInfo {
+public class FolderInfo extends CollectionInfo {
 
     public static final int NO_FLAGS = 0x00000000;
 
@@ -100,27 +98,15 @@
 
     public FolderNameInfos suggestedFolderNames;
 
-    /**
-     * The apps and shortcuts
-     */
-    public ArrayList<WorkspaceItemInfo> contents = new ArrayList<>();
-
     private ArrayList<FolderListener> mListeners = new ArrayList<>();
 
     public FolderInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
-        user = Process.myUserHandle();
     }
 
-    /**
-     * Create an app pair, a type of app collection that launches multiple apps into split screen
-     */
-    public static FolderInfo createAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) {
-        FolderInfo newAppPair = new FolderInfo();
-        newAppPair.itemType = LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
-        newAppPair.add(app1, /* animate */ false);
-        newAppPair.add(app2, /* animate */ false);
-        return newAppPair;
+    /** Adds a app or shortcut to the contents array without animation. */
+    public void add(@NonNull WorkspaceItemInfo item) {
+        add(item, false /* animate */);
     }
 
     /**
@@ -129,15 +115,15 @@
      * @param item
      */
     public void add(WorkspaceItemInfo item, boolean animate) {
-        add(item, contents.size(), animate);
+        add(item, getContents().size(), animate);
     }
 
     /**
      * Add an app or shortcut for a specified rank.
      */
     public void add(WorkspaceItemInfo item, int rank, boolean animate) {
-        rank = Utilities.boundToRange(rank, 0, contents.size());
-        contents.add(rank, item);
+        rank = Utilities.boundToRange(rank, 0, getContents().size());
+        getContents().add(rank, item);
         for (int i = 0; i < mListeners.size(); i++) {
             mListeners.get(i).onAdd(item, rank);
         }
@@ -157,7 +143,7 @@
      * Remove all matching app or shortcut. Does not change the DB.
      */
     public void removeAll(List<WorkspaceItemInfo> items, boolean animate) {
-        contents.removeAll(items);
+        getContents().removeAll(items);
         for (int i = 0; i < mListeners.size(); i++) {
             mListeners.get(i).onRemove(items);
         }
@@ -167,8 +153,7 @@
     @Override
     public void onAddToDatabase(@NonNull ContentWriter writer) {
         super.onAddToDatabase(writer);
-        writer.put(LauncherSettings.Favorites.TITLE, title)
-                .put(LauncherSettings.Favorites.OPTIONS, options);
+        writer.put(LauncherSettings.Favorites.OPTIONS, options);
     }
 
     public void addListener(FolderListener listener) {
@@ -221,9 +206,9 @@
 
     @NonNull
     @Override
-    public LauncherAtom.ItemInfo buildProto(@Nullable FolderInfo fInfo) {
+    public LauncherAtom.ItemInfo buildProto(@Nullable CollectionInfo cInfo) {
         FolderIcon.Builder folderIcon = FolderIcon.newBuilder()
-                .setCardinality(contents.size());
+                .setCardinality(getContents().size());
         if (LabelState.SUGGESTED.equals(getLabelState())) {
             folderIcon.setLabelInfo(title.toString());
         }
@@ -284,20 +269,11 @@
     public ItemInfo makeShallowCopy() {
         FolderInfo folderInfo = new FolderInfo();
         folderInfo.copyFrom(this);
-        folderInfo.contents = this.contents;
+        folderInfo.setContents(this.getContents());
         return folderInfo;
     }
 
     /**
-     * Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging.
-     */
-    @NonNull
-    @Override
-    public LauncherAtom.ItemInfo buildProto() {
-        return buildProto(null);
-    }
-
-    /**
      * Returns index of the accepted suggestion.
      */
     public OptionalInt getAcceptedSuggestionIndex() {
@@ -377,13 +353,4 @@
         }
         return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
     }
-
-    @Override
-    public boolean isDisabled() {
-        if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR) {
-            return contents.stream().anyMatch((WorkspaceItemInfo::isDisabled));
-        }
-
-        return super.isDisabled();
-    }
 }
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index f7cff78..8c3efd7 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -349,10 +349,9 @@
 
     /**
      * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
-     * @param fInfo
      */
     @NonNull
-    public LauncherAtom.ItemInfo buildProto(@Nullable final FolderInfo fInfo) {
+    public LauncherAtom.ItemInfo buildProto(@Nullable final CollectionInfo cInfo) {
         LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder();
         Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent());
         switch (itemType) {
@@ -398,21 +397,21 @@
             default:
                 break;
         }
-        if (fInfo != null) {
+        if (cInfo != null) {
             LauncherAtom.FolderContainer.Builder folderBuilder =
                     LauncherAtom.FolderContainer.newBuilder();
             folderBuilder.setGridX(cellX).setGridY(cellY).setPageIndex(screenId);
 
-            switch (fInfo.container) {
+            switch (cInfo.container) {
                 case CONTAINER_HOTSEAT:
                 case CONTAINER_HOTSEAT_PREDICTION:
                     folderBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder()
-                            .setIndex(fInfo.screenId));
+                            .setIndex(cInfo.screenId));
                     break;
                 case CONTAINER_DESKTOP:
                     folderBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder()
-                            .setPageIndex(fInfo.screenId)
-                            .setGridX(fInfo.cellX).setGridY(fInfo.cellY));
+                            .setPageIndex(cInfo.screenId)
+                            .setGridX(cInfo.cellX).setGridY(cInfo.cellY));
                     break;
             }
             itemBuilder.setContainerInfo(ContainerInfo.newBuilder().setFolder(folderBuilder));
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index 6fa8c54..f4dda55 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -271,8 +271,8 @@
 
     @NonNull
     @Override
-    public LauncherAtom.ItemInfo buildProto(@Nullable FolderInfo folderInfo) {
-        LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
+    public LauncherAtom.ItemInfo buildProto(@Nullable CollectionInfo collectionInfo) {
+        LauncherAtom.ItemInfo info = super.buildProto(collectionInfo);
         return info.toBuilder()
                 .setWidget(info.getWidget().toBuilder().setWidgetFeatures(widgetFeatures))
                 .addItemAttributes(getAttribute(sourceContainer))
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 756153e..9f2b10f 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -18,6 +18,7 @@
 
 import static android.os.Process.myUserHandle;
 
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
 import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
 import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
@@ -64,7 +65,6 @@
 import com.android.launcher3.model.DeviceGridState;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDbController;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -428,7 +428,7 @@
     private void restoreAppWidgetIds(Context context, ModelDbController controller,
             LauncherRestoreEventLogger launcherRestoreEventLogger, int[] oldWidgetIds,
             int[] newWidgetIds, @NonNull AppWidgetHost host) {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             FileLog.e(TAG, "Skipping widget ID remap as widgets not supported");
             host.deleteHost();
             launcherRestoreEventLogger.logFavoritesItemsRestoreFailed(Favorites.ITEM_TYPE_APPWIDGET,
diff --git a/src/com/android/launcher3/search/StringMatcherUtility.java b/src/com/android/launcher3/search/StringMatcherUtility.java
index 28fc4f0..7446314 100644
--- a/src/com/android/launcher3/search/StringMatcherUtility.java
+++ b/src/com/android/launcher3/search/StringMatcherUtility.java
@@ -18,6 +18,8 @@
 
 import android.text.TextUtils;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.util.IntArray;
 
 import java.text.Collator;
@@ -120,7 +122,11 @@
         /**
          * Returns true if {@param query} is a prefix of {@param target}
          */
-        public boolean matches(String query, String target) {
+        public boolean matches(@Nullable String query, @Nullable String target) {
+            // `mCollator.compare` requires non-null inputs, so return false earlier (not a match)
+            if (query == null || target == null) {
+                return false;
+            }
             switch (mCollator.compare(query, target)) {
                 case 0:
                     return true;
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index a5f9c2a..c10d85e 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.model.WidgetsModel;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.uioverrides.flags.DeveloperOptionsUI;
 import com.android.launcher3.util.DisplayController;
@@ -238,7 +237,7 @@
         protected boolean initPreference(Preference preference) {
             switch (preference.getKey()) {
                 case NOTIFICATION_DOTS_PREFERENCE_KEY:
-                    return !WidgetsModel.GO_DISABLE_NOTIFICATION_DOTS;
+                    return BuildConfig.NOTIFICATION_DOTS_ENABLED;
 
                 case ALLOW_ROTATION_PREFERENCE_KEY:
                     DisplayController.Info info =
diff --git a/src/com/android/launcher3/shortcuts/ShortcutRequest.java b/src/com/android/launcher3/shortcuts/ShortcutRequest.java
index 21efceb..015d54d 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutRequest.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutRequest.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.shortcuts;
 
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -47,7 +47,7 @@
             | ShortcutQuery.FLAG_MATCH_MANIFEST;
     public static final int PINNED = ShortcutQuery.FLAG_MATCH_PINNED;
 
-    private final ShortcutQuery mQuery = GO_DISABLE_WIDGETS ? null : new ShortcutQuery();
+    private final ShortcutQuery mQuery = !WIDGETS_ENABLED ? null : new ShortcutQuery();
 
     private final Context mContext;
     private final UserHandle mUserHandle;
@@ -74,7 +74,7 @@
      * @return A list of ShortcutInfo's associated with the given package.
      */
     public ShortcutRequest forPackage(String packageName, @Nullable List<String> shortcutIds) {
-        if (!GO_DISABLE_WIDGETS && packageName != null) {
+        if (WIDGETS_ENABLED && packageName != null) {
             mQuery.setPackage(packageName);
             mQuery.setShortcutIds(shortcutIds);
         }
@@ -82,7 +82,7 @@
     }
 
     public ShortcutRequest withContainer(@Nullable ComponentName activity) {
-        if (!GO_DISABLE_WIDGETS) {
+        if (WIDGETS_ENABLED) {
             if (activity == null) {
                 mFailed = true;
             } else {
@@ -93,7 +93,7 @@
     }
 
     public QueryResult query(int flags) {
-        if (GO_DISABLE_WIDGETS || mFailed) {
+        if (!WIDGETS_ENABLED || mFailed) {
             return QueryResult.DEFAULT;
         }
         mQuery.setQueryFlags(flags);
@@ -109,7 +109,7 @@
 
     public static class QueryResult extends ArrayList<ShortcutInfo> {
 
-        static final QueryResult DEFAULT = new QueryResult(GO_DISABLE_WIDGETS);
+        static final QueryResult DEFAULT = new QueryResult(!WIDGETS_ENABLED);
 
         private final boolean mWasSuccess;
 
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 911568c..0c25e96 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -53,6 +53,7 @@
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.AppPairInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -101,11 +102,9 @@
         if (tag instanceof WorkspaceItemInfo) {
             onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
         } else if (tag instanceof FolderInfo) {
-            if (v instanceof FolderIcon) {
-                onClickFolderIcon(v);
-            } else if (v instanceof AppPairIcon) {
-                onClickAppPairIcon(v);
-            }
+            onClickFolderIcon(v);
+        } else if (tag instanceof AppPairInfo) {
+            onClickAppPairIcon(v);
         } else if (tag instanceof AppInfo) {
             startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
         } else if (tag instanceof LauncherAppWidgetInfo) {
@@ -150,7 +149,7 @@
     private static void onClickAppPairIcon(View v) {
         Launcher launcher = Launcher.getLauncher(v.getContext());
         AppPairIcon appPairIcon = (AppPairIcon) v;
-        if (!appPairIcon.isLaunchableAtScreenSize()) {
+        if (!appPairIcon.getInfo().isLaunchable(launcher)) {
             // Display a message for app pairs that are disabled due to screen size
             boolean isFoldable = InvariantDeviceProfile.INSTANCE.get(launcher)
                     .supportedProfiles.stream().anyMatch(dp -> dp.isTwoPanels);
@@ -159,8 +158,8 @@
                             : R.string.app_pair_unlaunchable_at_screen_size,
                     Toast.LENGTH_SHORT).show();
         } else if (appPairIcon.getInfo().isDisabled()) {
-            WorkspaceItemInfo app1 = appPairIcon.getInfo().contents.get(0);
-            WorkspaceItemInfo app2 = appPairIcon.getInfo().contents.get(1);
+            WorkspaceItemInfo app1 = appPairIcon.getInfo().getFirstApp();
+            WorkspaceItemInfo app2 = appPairIcon.getInfo().getSecondApp();
             // Show the user why the app pair is disabled.
             if (app1.isDisabled() && !handleDisabledItemClicked(app1, launcher)) {
                 // If handleDisabledItemClicked() did not handle the error message, we initiate an
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
index 405d2bb..95a0511 100644
--- a/src/com/android/launcher3/util/ActivityTracker.java
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -18,10 +18,13 @@
 import static com.android.launcher3.testing.shared.TestProtocol.GET_FROM_RECENTS_FAILURE;
 import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
 
+import android.util.Log;
+
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseActivity;
 
+import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -31,6 +34,8 @@
  */
 public final class ActivityTracker<T extends BaseActivity> {
 
+    private static final String TAG = "ActivityTracker";
+
     private WeakReference<T> mCurrentActivity = new WeakReference<>(null);
     private CopyOnWriteArrayList<SchedulerCallback<T>> mCallbacks = new CopyOnWriteArrayList<>();
 
@@ -57,12 +62,13 @@
      *
      * @param callback The callback to call init() on when the activity is ready.
      */
-    public void registerCallback(SchedulerCallback<T> callback) {
+    public void registerCallback(SchedulerCallback<T> callback, String reasonString) {
+        Log.d(TAG, "Registering callback: " + callback + ", reason=" + reasonString);
         T activity = mCurrentActivity.get();
         mCallbacks.add(callback);
         if (activity != null) {
             if (!callback.init(activity, activity.isStarted())) {
-                unregisterCallback(callback);
+                unregisterCallback(callback, "ActivityTracker.registerCallback: Intent handled");
             }
         }
     }
@@ -70,7 +76,8 @@
     /**
      * Unregisters a registered callback.
      */
-    public void unregisterCallback(SchedulerCallback<T> callback) {
+    public void unregisterCallback(SchedulerCallback<T> callback, String reasonString) {
+        Log.d(TAG, "Unregistering callback: " + callback + ", reason=" + reasonString);
         mCallbacks.remove(callback);
     }
 
@@ -87,16 +94,25 @@
 
     private boolean handleIntent(T activity, boolean alreadyOnHome) {
         boolean handled = false;
+        if (!mCallbacks.isEmpty()) {
+            Log.d(TAG, "handleIntent: mCallbacks=" + mCallbacks);
+        }
         for (SchedulerCallback<T> cb : mCallbacks) {
             if (!cb.init(activity, alreadyOnHome)) {
                 // Callback doesn't want any more updates
-                unregisterCallback(cb);
+                unregisterCallback(cb, "ActivityTracker.handleIntent: Intent handled");
             }
             handled = true;
         }
         return handled;
     }
 
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "ActivityTracker:");
+        writer.println(prefix + "\tmCurrentActivity=" + mCurrentActivity.get());
+        writer.println(prefix + "\tmCallbacks=" + mCallbacks);
+    }
+
     public interface SchedulerCallback<T extends BaseActivity> {
 
         /**
diff --git a/src/com/android/launcher3/util/GridOccupancy.java b/src/com/android/launcher3/util/GridOccupancy.java
index 43e486c..f248e58 100644
--- a/src/com/android/launcher3/util/GridOccupancy.java
+++ b/src/com/android/launcher3/util/GridOccupancy.java
@@ -7,7 +7,7 @@
 /**
  * Utility object to manage the occupancy in a grid.
  */
-public class GridOccupancy extends AbsGridOccupancy {
+public class GridOccupancy {
 
     private final int mCountX;
     private final int mCountY;
@@ -30,7 +30,24 @@
      * @return true if a vacant cell was found
      */
     public boolean findVacantCell(int[] vacantOut, int spanX, int spanY) {
-        return super.findVacantCell(vacantOut, cells, mCountX, mCountY, spanX, spanY);
+        for (int y = 0; (y + spanY) <= mCountY; y++) {
+            for (int x = 0; (x + spanX) <= mCountX; x++) {
+                boolean available = !cells[x][y];
+                out:
+                for (int i = x; i < x + spanX; i++) {
+                    for (int j = y; j < y + spanY; j++) {
+                        available = available && !cells[i][j];
+                        if (!available) break out;
+                    }
+                }
+                if (available) {
+                    vacantOut[0] = x;
+                    vacantOut[1] = y;
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     public void copyTo(GridOccupancy dest) {
diff --git a/src/com/android/launcher3/util/ItemInflater.kt b/src/com/android/launcher3/util/ItemInflater.kt
index cc66af1..ebf4656 100644
--- a/src/com/android/launcher3/util/ItemInflater.kt
+++ b/src/com/android/launcher3/util/ItemInflater.kt
@@ -29,6 +29,7 @@
 import com.android.launcher3.apppairs.AppPairIcon
 import com.android.launcher3.folder.FolderIcon
 import com.android.launcher3.model.ModelWriter
+import com.android.launcher3.model.data.AppPairInfo
 import com.android.launcher3.model.data.FolderInfo
 import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.model.data.LauncherAppWidgetInfo
@@ -81,7 +82,8 @@
                     R.layout.app_pair_icon,
                     context,
                     parent,
-                    item as FolderInfo
+                    item as AppPairInfo,
+                    BubbleTextView.DISPLAY_WORKSPACE
                 )
             Favorites.ITEM_TYPE_APPWIDGET,
             Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ->
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 3074111..063313a 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -65,20 +65,11 @@
      * Returns a matcher for items within folders.
      */
     public static Predicate<ItemInfo> forFolderMatch(Predicate<ItemInfo> childOperator) {
-        return info -> info instanceof FolderInfo && ((FolderInfo) info).contents.stream()
+        return info -> info instanceof FolderInfo && ((FolderInfo) info).getContents().stream()
                 .anyMatch(childOperator);
     }
 
     /**
-     * Returns a matcher for items within app pairs.
-     */
-    public static Predicate<ItemInfo> forAppPairMatch(Predicate<ItemInfo> childOperator) {
-        Predicate<ItemInfo> isAppPair = info ->
-                info instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_APP_PAIR;
-        return isAppPair.and(forFolderMatch(childOperator));
-    }
-
-    /**
      * Returns a matcher for items with provided ids
      */
     public static Predicate<ItemInfo> ofItemIds(IntSet ids) {
diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
index 69786bb..02779ce 100644
--- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
+++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.model.data.AppPairInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -59,7 +60,7 @@
                                 : null);
             } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
                 ((FolderIcon) v).updatePreviewItems(updates::contains);
-            } else if (info instanceof FolderInfo && v instanceof AppPairIcon appPairIcon) {
+            } else if (info instanceof AppPairInfo && v instanceof AppPairIcon appPairIcon) {
                 appPairIcon.maybeRedrawForWorkspaceUpdate(updates::contains);
             }
 
@@ -89,7 +90,7 @@
                 ((PendingAppWidgetHostView) v).applyState();
             } else if (v instanceof FolderIcon && info instanceof FolderInfo) {
                 ((FolderIcon) v).updatePreviewItems(updates::contains);
-            } else if (info instanceof FolderInfo && v instanceof AppPairIcon appPairIcon) {
+            } else if (info instanceof AppPairInfo && v instanceof AppPairIcon appPairIcon) {
                 appPairIcon.maybeRedrawForWorkspaceUpdate(updates::contains);
             }
             // process all the shortcuts
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index 91cf835..07b7941 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -15,9 +15,10 @@
  */
 package com.android.launcher3.util;
 
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
+
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
@@ -34,7 +35,7 @@
      * Returns true when we should show depp shortcuts in shortcut menu for the item.
      */
     public static boolean supportsDeepShortcuts(ItemInfo info) {
-        return isActive(info) && isApp(info) && !WidgetsModel.GO_DISABLE_WIDGETS;
+        return isActive(info) && isApp(info) && !!WIDGETS_ENABLED;
     }
 
     /**
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 31f5d65..bf43a22 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -17,12 +17,12 @@
 
 import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR;
 
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
 import static com.android.launcher3.Utilities.allowBGLaunch;
 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;
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
@@ -389,7 +389,7 @@
         boolean isShortcut = (item instanceof WorkspaceItemInfo)
                 && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
                 && !((WorkspaceItemInfo) item).isPromise();
-        if (isShortcut && GO_DISABLE_WIDGETS) {
+        if (isShortcut && !WIDGETS_ENABLED) {
             return null;
         }
         ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 5cdad03..62eed5c 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -17,6 +17,7 @@
 
 import static androidx.core.content.ContextCompat.getColorStateList;
 
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
@@ -47,7 +48,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.StatsLogManager.EventEnum;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.ArrowPopup;
 import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -207,7 +207,7 @@
                 R.drawable.ic_palette,
                 IGNORE,
                 OptionsPopupView::startWallpaperPicker));
-        if (!WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (WIDGETS_ENABLED) {
             options.add(new OptionItem(launcher,
                     R.string.widget_button_text,
                     R.drawable.ic_widget,
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 5266448..76ffbbd 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -164,11 +164,11 @@
      * Click handler for tap to add button.
      */
     public void addWidget(PendingAddItemInfo info) {
-        mActivityContext.getStatsLogManager().logger().withItemInfo(info).log(
-                StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP);
         handleClose(true);
         Launcher.getLauncher(mActivityContext).getAccessibilityDelegate()
                 .addToWorkspace(info, /*accessibility=*/ false, /*finishCallback=*/ null);
+        mActivityContext.getStatsLogManager().logger().withItemInfo(info).log(
+                StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP);
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index 0fb4e09..a5e22c5 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -17,6 +17,7 @@
 
 import static android.app.Activity.RESULT_CANCELED;
 
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
 import static com.android.launcher3.Flags.enableWorkspaceInflation;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.fromProviderInfo;
@@ -40,7 +41,6 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -100,7 +100,7 @@
      * Starts listening to the widget updates from the server side
      */
     public void startListening() {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             return;
         }
 
@@ -177,7 +177,7 @@
      * @return The allocated app widget id if allocation is successful, returns -1 otherwise
      */
     public int allocateAppWidgetId() {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             return AppWidgetManager.INVALID_APPWIDGET_ID;
         }
 
@@ -210,7 +210,7 @@
      */
     public void startConfigActivity(@NonNull BaseDraggingActivity activity, int widgetId,
             int requestCode) {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             sendActionCancelled(activity, requestCode);
             return;
         }
@@ -261,7 +261,7 @@
      */
     public void startBindFlow(@NonNull BaseActivity activity,
             int appWidgetId, @NonNull AppWidgetProviderInfo info, int requestCode) {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             sendActionCancelled(activity, requestCode);
             return;
         }
@@ -279,7 +279,7 @@
      * Stop the host from listening to the widget updates
      */
     public void stopListening() {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             return;
         }
         mWidgetHost.stopListening();
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index a501960..a916252 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -25,7 +25,7 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.logger.LauncherAtom;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.widget.picker.WidgetRecommendationCategory;
 import com.android.launcher3.widget.util.WidgetSizes;
@@ -82,8 +82,8 @@
 
     @NonNull
     @Override
-    public LauncherAtom.ItemInfo buildProto(@Nullable FolderInfo folderInfo) {
-        LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
+    public LauncherAtom.ItemInfo buildProto(@Nullable CollectionInfo collectionInfo) {
+        LauncherAtom.ItemInfo info = super.buildProto(collectionInfo);
         return info.toBuilder()
                 .addItemAttributes(LauncherAppWidgetInfo.getAttribute(sourceContainer))
                 .build();
diff --git a/src/com/android/launcher3/widget/WidgetInflater.kt b/src/com/android/launcher3/widget/WidgetInflater.kt
index dd50b71..271c9c2 100644
--- a/src/com/android/launcher3/widget/WidgetInflater.kt
+++ b/src/com/android/launcher3/widget/WidgetInflater.kt
@@ -17,11 +17,11 @@
 package com.android.launcher3.widget
 
 import android.content.Context
+import com.android.launcher3.BuildConfig
 import com.android.launcher3.Launcher
 import com.android.launcher3.LauncherAppState
 import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
 import com.android.launcher3.logging.FileLog
-import com.android.launcher3.model.WidgetsModel
 import com.android.launcher3.model.data.LauncherAppWidgetInfo
 import com.android.launcher3.qsb.QsbContainerView
 
@@ -55,7 +55,7 @@
             // The widget id is not valid. Try to find the widget based on the provider info.
             appWidgetInfo = widgetHelper.findProvider(item.providerName, item.user)
             if (appWidgetInfo == null) {
-                if (WidgetsModel.GO_DISABLE_WIDGETS) {
+                if (!BuildConfig.WIDGETS_ENABLED) {
                     removalReason = "widgets are disabled on go device."
                     logReason = RestoreError.WIDGETS_DISABLED
                 } else {
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index ba7e075..d293d15 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.widget;
 
+import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
+
 import android.annotation.TargetApi;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -31,7 +33,6 @@
 import androidx.annotation.RequiresApi;
 
 import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.PackageUserKey;
@@ -87,7 +88,7 @@
      */
     @TargetApi(Build.VERSION_CODES.O)
     public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             return Collections.emptyList();
         }
 
@@ -112,7 +113,7 @@
      */
     public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
             Bundle options) {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             return false;
         }
         if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
@@ -123,7 +124,7 @@
     }
 
     public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandle user) {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+        if (!WIDGETS_ENABLED) {
             return null;
         }
         for (AppWidgetProviderInfo info :
@@ -139,8 +140,8 @@
      * Returns if a AppWidgetProvider has marked a widget restored
      */
     public boolean isAppWidgetRestored(int appWidgetId) {
-        return !WidgetsModel.GO_DISABLE_WIDGETS && mAppWidgetManager.getAppWidgetOptions(
-                appWidgetId).getBoolean(WIDGET_OPTION_RESTORE_COMPLETED);
+        return WIDGETS_ENABLED && mAppWidgetManager.getAppWidgetOptions(appWidgetId)
+                .getBoolean(WIDGET_OPTION_RESTORE_COMPLETED);
     }
 
 
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index ba6c4cf..5cf8203 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -27,7 +27,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.Process;
@@ -47,12 +46,9 @@
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.window.BackEvent;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.Px;
-import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.RecyclerView;
@@ -552,6 +548,14 @@
     public void exitSearchMode() {
         if (!mIsInSearchMode) return;
         onSearchResults(new ArrayList<>());
+        WidgetsRecyclerView searchRecyclerView = mAdapters.get(
+                AdapterHolder.SEARCH).mWidgetsRecyclerView;
+        // Remove all views when exiting the search mode; this prevents animating from stale results
+        // to new ones the next time we enter search mode. By the time recycler view is hidden,
+        // layout may not have happened to clear up existing results. So, instead of waiting for it
+        // to happen, we clear the views here.
+        searchRecyclerView.swapAdapter(
+                searchRecyclerView.getAdapter(), /*removeAndRecycleExistingViews=*/ true);
         setViewVisibilityBasedOnSearch(/*isInSearchMode=*/ false);
         if (mHasWorkProfile) {
             mViewPager.snapToPage(AdapterHolder.PRIMARY);
@@ -904,17 +908,6 @@
     }
 
     @Override
-    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
-    public void onBackProgressed(@NonNull BackEvent backEvent) {
-        super.onBackProgressed(backEvent);
-        // In two pane picker, scroll bar is always hidden.
-        if (!isTwoPane()) {
-            mFastScroller.setVisibility(
-                    backEvent.getProgress() > 0 ? View.INVISIBLE : View.VISIBLE);
-        }
-    }
-
-    @Override
     public void onBackInvoked() {
         if (mIsInSearchMode) {
             mSearchBar.reset();
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 7a2b4ef..563894d 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -109,7 +109,6 @@
                 WidgetCell widgetCell = addItemCell(tableRow);
                 widgetCell.applyFromCellItem(widgetItem);
                 widgetCell.showAppIconInWidgetTitle(true);
-                widgetCell.showBadge();
                 if (enableCategorizedWidgetSuggestions()) {
                     widgetCell.showDescription(false);
                     widgetCell.showDimensions(false);
diff --git a/src_build_config/com/android/launcher3/BuildConfig.java b/src_build_config/com/android/launcher3/BuildConfig.java
index 3841969..6d4f56d 100644
--- a/src_build_config/com/android/launcher3/BuildConfig.java
+++ b/src_build_config/com/android/launcher3/BuildConfig.java
@@ -35,4 +35,9 @@
      * Flag to control various developer centric features
      */
     public static final boolean IS_DEBUG_DEVICE = false;
+
+    // Flag to control widgets support in Launcher
+    public static final boolean WIDGETS_ENABLED = true;
+    // Flag to control notification dots support in Launcher
+    public static final boolean NOTIFICATION_DOTS_ENABLED = true;
 }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_no_quickstep/com/android/launcher3/uioverrides/ApiWrapper.java
similarity index 100%
rename from src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
rename to src_no_quickstep/com/android/launcher3/uioverrides/ApiWrapper.java
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/PredictedAppIconInflater.java b/src_no_quickstep/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
similarity index 100%
rename from src_ui_overrides/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
rename to src_no_quickstep/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java b/src_no_quickstep/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
similarity index 100%
rename from src_ui_overrides/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
rename to src_no_quickstep/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java
similarity index 100%
rename from src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java
rename to src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/src_no_quickstep/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
similarity index 100%
rename from src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
rename to src_no_quickstep/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
similarity index 100%
rename from src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
rename to src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java b/src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java
similarity index 100%
rename from src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
rename to src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java b/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
deleted file mode 100644
index 7e73ab5..0000000
--- a/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2017 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.model;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.model.BgDataModel.Callbacks;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
- */
-public class LauncherBinder extends BaseLauncherBinder {
-
-    public LauncherBinder(LauncherAppState app, BgDataModel dataModel,
-            AllAppsList allAppsList, Callbacks[] callbacks) {
-        super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
-    }
-
-    @Override
-    public void bindDeepShortcuts() {
-        final HashMap<ComponentKey, Integer> shortcutMapCopy;
-        synchronized (mBgDataModel) {
-            shortcutMapCopy = new HashMap<>(mBgDataModel.deepShortcutMap);
-        }
-        executeCallbacksTask(c -> c.bindDeepShortcutMap(shortcutMapCopy), mUiExecutor);
-    }
-
-    @Override
-    public void bindWidgets() {
-        final List<WidgetsListBaseEntry> widgets =
-                mBgDataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
-        executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
-    }
-
-    @Override
-    public void bindSmartspaceWidget() {
-        executeCallbacksTask(c -> c.bindSmartspaceWidget(), mUiExecutor);
-    }
-}
diff --git a/src_shortcuts_overrides/com/android/launcher3/util/AbsGridOccupancy.java b/src_shortcuts_overrides/com/android/launcher3/util/AbsGridOccupancy.java
deleted file mode 100644
index 968b281..0000000
--- a/src_shortcuts_overrides/com/android/launcher3/util/AbsGridOccupancy.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-/**
- * Defines method to find the next vacant cell on a grid.
- * This uses the default top-down, left-right approach and can be over-written through
- * code swaps in different launchers.
- */
-public abstract class AbsGridOccupancy {
-    /**
-     * Find the first vacant cell, if there is one.
-     *
-     * @param vacantOut Holds the x and y coordinate of the vacant cell
-     * @param spanX Horizontal cell span.
-     * @param spanY Vertical cell span.
-     *
-     * @return true if a vacant cell was found
-     */
-    protected boolean findVacantCell(int[] vacantOut, boolean[][] cells, int countX, int countY,
-            int spanX, int spanY) {
-        for (int y = 0; (y + spanY) <= countY; y++) {
-            for (int x = 0; (x + spanX) <= countX; x++) {
-                boolean available = !cells[x][y];
-                out:
-                for (int i = x; i < x + spanX; i++) {
-                    for (int j = y; j < y + spanY; j++) {
-                        available = available && !cells[i][j];
-                        if (!available) break out;
-                    }
-                }
-                if (available) {
-                    vacantOut[0] = x;
-                    vacantOut[1] = y;
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-}
diff --git a/tests/Android.bp b/tests/Android.bp
index eeafdba..13a1cbb 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -86,6 +86,7 @@
         "mockito-target-extended-minus-junit4",
         "launcher_log_protos_lite",
         "truth",
+        "kotlinx_coroutines_test",
         "platform-test-rules",
         "testables",
         "com_android_launcher3_flags_lib",
@@ -168,20 +169,6 @@
     ],
 }
 
-android_library {
-    name: "Launcher3Lib",
-    srcs: [
-        ":launcher-src",
-        ":launcher-src_shortcuts_overrides",
-        ":launcher-src_ui_overrides",
-    ],
-    static_libs: [
-        "Launcher3CommonDepsLib",
-    ],
-    // TODO(b/319712088): re-enable use_resource_processor
-    use_resource_processor: false,
-}
-
 android_robolectric_test {
     enabled: true,
     name: "Launcher3RoboTests",
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index e0fafcc..fea0330 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -180,6 +180,7 @@
     public static final String TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE = "b/326073471";
     public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
     public static final String ACTIVITY_NOT_RESUMED_AFTER_BACK = "b/322823209";
+    public static final String UPDATE_OVERVIEW_TARGETS_RUNNING_LATE = "b/321775748";
 
     public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
     public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
index b86333c..0c3081f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
@@ -26,7 +26,7 @@
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.model.ModelDbController;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.util.ContentWriter;
@@ -73,9 +73,8 @@
                     ContentWriter writer = new ContentWriter(mContext);
                     ItemInfo item = mItemsToSubmit.get(i).get();
 
-                    if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
-                        FolderInfo folderInfo = (FolderInfo) item;
-                        for (ItemInfo itemInfo : folderInfo.contents) {
+                    if (item instanceof CollectionInfo ci) {
+                        for (ItemInfo itemInfo : ci.getContents()) {
                             itemInfo.container = i;
                             containerItems.add(itemInfo);
                         }
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index eea4fe5..476bda5 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -26,7 +26,7 @@
 import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doNothing;
@@ -80,7 +80,7 @@
 
     private PrivateProfileManager mPrivateProfileManager;
     @Mock
-    private ActivityAllAppsContainerView mActivityAllAppsContainerView;
+    private ActivityAllAppsContainerView mAllApps;
     @Mock
     private StatsLogManager mStatsLogManager;
     @Mock
@@ -90,13 +90,13 @@
     @Mock
     private Context mContext;
     @Mock
-    private AllAppsStore mAllAppsStore;
+    private AllAppsStore<?> mAllAppsStore;
     @Mock
     private PackageManager mPackageManager;
     @Mock
     private LauncherApps mLauncherApps;
-
-    private boolean mRunnableCalled = false;
+    @Mock
+    private AllAppsRecyclerView mAllAppsRecyclerView;
 
     @Before
     public void setUp() {
@@ -105,8 +105,9 @@
                 .thenReturn(Arrays.asList(MAIN_HANDLE, PRIVATE_HANDLE));
         when(mUserCache.getUserInfo(Process.myUserHandle())).thenReturn(MAIN_ICON_INFO);
         when(mUserCache.getUserInfo(PRIVATE_HANDLE)).thenReturn(PRIVATE_ICON_INFO);
-        when(mActivityAllAppsContainerView.getContext()).thenReturn(mContext);
-        when(mActivityAllAppsContainerView.getAppsStore()).thenReturn(mAllAppsStore);
+        when(mAllApps.getContext()).thenReturn(mContext);
+        when(mAllApps.getAppsStore()).thenReturn(mAllAppsStore);
+        when(mAllApps.getActiveRecyclerView()).thenReturn(mAllAppsRecyclerView);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.resolveActivity(any(), any())).thenReturn(new ResolveInfo());
         when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps);
@@ -117,7 +118,7 @@
                 .thenReturn("com.android.launcher3.tests.privateProfileManager");
         when(mLauncherApps.getPreInstalledSystemPackages(any())).thenReturn(new ArrayList<>());
         mPrivateProfileManager = new PrivateProfileManager(mUserManager,
-                mActivityAllAppsContainerView, mStatsLogManager, mUserCache);
+                mAllApps, mStatsLogManager, mUserCache);
     }
 
     @Test
@@ -134,7 +135,7 @@
     public void unlockPrivateProfile_requestsQuietModeAsFalse() throws Exception {
         when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(true);
 
-        mPrivateProfileManager.unlockPrivateProfile(() -> {});
+        mPrivateProfileManager.unlockPrivateProfile();
 
         awaitTasksCompleted();
         Mockito.verify(mUserManager).requestQuietModeEnabled(false, PRIVATE_HANDLE);
@@ -160,20 +161,36 @@
 
     @Test
     @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/320703862
-    public void transitioningToUnlocked_resetCallsPendingRunnable() throws Exception {
+    public void transitioningToUnlocked_resetCallsPostUnlock() throws Exception {
         PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
         doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
         when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
                 .thenReturn(false);
+        doNothing().when(privateProfileManager).expandPrivateSpace();
         when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
-        mRunnableCalled = false;
 
-        privateProfileManager.unlockPrivateProfile(this::testRunnable);
+        privateProfileManager.unlockPrivateProfile();
         privateProfileManager.reset();
 
         awaitTasksCompleted();
-        Mockito.verify(privateProfileManager).applyUnlockRunnable();
-        assertTrue("Unlock Runnable not Invoked", mRunnableCalled);
+        Mockito.verify(privateProfileManager).postUnlock();
+    }
+
+    @Test
+    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
+    public void transitioningToLocked_resetCallsExecuteLock() throws Exception {
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
+        when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
+                .thenReturn(true);
+        doNothing().when(privateProfileManager).expandPrivateSpace();
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+
+        privateProfileManager.lockPrivateProfile();
+        privateProfileManager.reset();
+
+        awaitTasksCompleted();
+        Mockito.verify(privateProfileManager).executeLock();
     }
 
     @Test
@@ -191,8 +208,4 @@
     private static void awaitTasksCompleted() throws Exception {
         UI_HELPER_EXECUTOR.submit(() -> null).get();
     }
-
-    private void testRunnable() {
-        mRunnableCalled = true;
-    }
 }
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
similarity index 75%
rename from tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
rename to tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
index 043461d..7ac276a 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
@@ -28,6 +28,10 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.AdditionalAnswers.answer;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
@@ -39,6 +43,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Process;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageButton;
@@ -51,8 +56,11 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.ActivityContextWrapper;
+import com.android.launcher3.util.UserIconInfo;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,14 +69,20 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class PrivateSpaceHeaderViewControllerTest {
+public class PrivateSpaceHeaderViewTest {
 
     private static final UserHandle MAIN_HANDLE = Process.myUserHandle();
     private static final UserHandle PRIVATE_HANDLE = new UserHandle(11);
+    private static final UserIconInfo MAIN_ICON_INFO =
+            new UserIconInfo(MAIN_HANDLE, UserIconInfo.TYPE_MAIN);
+    private static final UserIconInfo PRIVATE_ICON_INFO =
+            new UserIconInfo(PRIVATE_HANDLE, UserIconInfo.TYPE_PRIVATE);
+    private static final String CAMERA_PACKAGE_NAME = "com.android.launcher3.tests.camera";
     private static final int CONTAINER_HEADER_ELEMENT_COUNT = 1;
     private static final int LOCK_UNLOCK_BUTTON_COUNT = 1;
     private static final int PS_SETTINGS_BUTTON_COUNT_VISIBLE = 1;
@@ -84,37 +98,41 @@
     private static final float HEADER_PROTECTION_HEIGHT = 1F;
 
     private Context mContext;
-    private PrivateSpaceHeaderViewController mPsHeaderViewController;
     private RelativeLayout mPsHeaderLayout;
     private AlphabeticalAppsList<?> mAlphabeticalAppsList;
-    @Mock
     private PrivateProfileManager mPrivateProfileManager;
     @Mock
     private ActivityAllAppsContainerView mAllApps;
     @Mock
     private AllAppsStore<?> mAllAppsStore;
+    @Mock
+    private UserCache mUserCache;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private StatsLogManager mStatsLogManager;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = new ActivityContextWrapper(getApplicationContext());
-        when(mPrivateProfileManager.getItemInfoMatcher()).thenReturn(info ->
-                info != null && info.user.equals(PRIVATE_HANDLE));
-        mPsHeaderViewController = new PrivateSpaceHeaderViewController(mAllApps,
-                mPrivateProfileManager);
+        when(mAllApps.getContext()).thenReturn(mContext);
+        when(mUserCache.getUserInfo(PRIVATE_HANDLE)).thenReturn(PRIVATE_ICON_INFO);
+        when(mUserCache.getUserProfiles())
+                .thenReturn(Arrays.asList(MAIN_HANDLE, PRIVATE_HANDLE));
+        when(mUserCache.getUserInfo(Process.myUserHandle())).thenReturn(MAIN_ICON_INFO);
+        mPrivateProfileManager = new PrivateProfileManager(mUserManager,
+                mAllApps, mStatsLogManager, mUserCache);
         mPsHeaderLayout = (RelativeLayout) LayoutInflater.from(mContext).inflate(
                 R.layout.private_space_header, null);
-        mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
-                null, mPrivateProfileManager);
-        mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
     }
 
     @Test
     public void privateProfileDisabled_psHeaderContainsLockedView() throws Exception {
         Bitmap unlockButton = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
-        when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
-
-        mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
+        privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
         awaitTasksCompleted();
 
         int totalContainerHeaderView = 0;
@@ -137,6 +155,7 @@
                 assertEquals(View.GONE, view.getVisibility());
             }
         }
+
         assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView);
         assertEquals(LOCK_UNLOCK_BUTTON_COUNT, totalLockUnlockButtonView);
     }
@@ -145,10 +164,10 @@
     public void privateProfileEnabled_psHeaderContainsUnlockedView() throws Exception {
         Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
         Bitmap settingsImage = getBitmap(mContext.getDrawable(R.drawable.ic_ps_settings));
-        when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
-        when(mPrivateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(true);
-
-        mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+        when(privateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(true);
+        privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
         awaitTasksCompleted();
 
         int totalContainerHeaderView = 0;
@@ -177,6 +196,7 @@
                 assertEquals(View.GONE, view.getVisibility());
             }
         }
+
         assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView);
         assertEquals(LOCK_UNLOCK_BUTTON_COUNT, totalLockUnlockButtonView);
         assertEquals(PS_SETTINGS_BUTTON_COUNT_VISIBLE, totalSettingsImageView);
@@ -186,10 +206,10 @@
     public void privateProfileEnabledAndNoSettingsIntent_psHeaderContainsUnlockedView()
             throws Exception {
         Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
-        when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
-        when(mPrivateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(false);
-
-        mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+        when(privateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(false);
+        privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
         awaitTasksCompleted();
 
         int totalContainerHeaderView = 0;
@@ -216,6 +236,7 @@
                 assertEquals(View.GONE, view.getVisibility());
             }
         }
+
         assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView);
         assertEquals(LOCK_UNLOCK_BUTTON_COUNT, totalLockUnlockButtonView);
         assertEquals(PS_SETTINGS_BUTTON_COUNT_INVISIBLE, totalSettingsImageView);
@@ -224,9 +245,9 @@
     @Test
     public void privateProfileTransitioning_psHeaderContainsTransitionView() throws Exception {
         Bitmap transitionImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_transition_image));
-        when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_TRANSITION);
-
-        mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_TRANSITION);
+        privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
         awaitTasksCompleted();
 
         int totalContainerHeaderView = 0;
@@ -248,6 +269,7 @@
                 assertEquals(View.GONE, view.getVisibility());
             }
         }
+
         assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView);
         assertEquals(PS_TRANSITION_IMAGE_COUNT, totalLockUnlockButtonView);
     }
@@ -255,18 +277,25 @@
     @Test
     public void scrollForViewToBeVisibleInContainer_withHeader() {
         when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
-        when(mPrivateProfileManager.addPrivateSpaceHeader(any()))
-                .thenAnswer(answer(this::addPrivateSpaceHeader));
-        when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
-        when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps())
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+        when(privateProfileManager.splitIntoUserInstalledAndSystemApps())
                 .thenReturn(iteminfo -> iteminfo.componentName == null
                         || !iteminfo.componentName.getPackageName()
-                        .equals("com.android.launcher3.tests.camera"));
-        when(mAllApps.getContext()).thenReturn(mContext);
-        mAlphabeticalAppsList.updateItemFilter(info -> info != null
-                && info.user.equals(MAIN_HANDLE));
+                        .equals(CAMERA_PACKAGE_NAME));
+        doReturn(0).when(privateProfileManager).addPrivateSpaceHeader(any());
+        doAnswer(answer(this::addPrivateSpaceHeader)).when(privateProfileManager)
+                .addPrivateSpaceHeader(any());
+        doNothing().when(privateProfileManager).addPrivateSpaceInstallAppButton(any());
+        doReturn(0).when(privateProfileManager).addSystemAppsDivider(any());
         when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
         when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
+        mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
+                null, privateProfileManager);
+        mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
+        mAlphabeticalAppsList.updateItemFilter(info -> info != null
+                && info.user.equals(MAIN_HANDLE));
+
         int rows = (int) (ALL_APPS_HEIGHT - PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
         int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
 
@@ -274,7 +303,7 @@
         assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
                 mAlphabeticalAppsList.getAdapterItems().size());
         assertEquals(position,
-                mPsHeaderViewController.scrollForViewToBeVisibleInContainer(
+                privateProfileManager.scrollForViewToBeVisibleInContainer(
                         new AllAppsRecyclerView(mContext),
                         mAlphabeticalAppsList.getAdapterItems(),
                         PS_HEADER_HEIGHT,
@@ -284,18 +313,25 @@
     @Test
     public void scrollForViewToBeVisibleInContainer_withHeaderAndLessAppRowSpace() {
         when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
-        when(mPrivateProfileManager.addPrivateSpaceHeader(any()))
-                .thenAnswer(answer(this::addPrivateSpaceHeader));
-        when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
-        when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps())
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+        when(privateProfileManager.splitIntoUserInstalledAndSystemApps())
                 .thenReturn(iteminfo -> iteminfo.componentName == null
                         || !iteminfo.componentName.getPackageName()
-                        .equals("com.android.launcher3.tests.camera"));
-        when(mAllApps.getContext()).thenReturn(mContext);
-        mAlphabeticalAppsList.updateItemFilter(info -> info != null
-                && info.user.equals(MAIN_HANDLE));
+                        .equals(CAMERA_PACKAGE_NAME));
+        doReturn(0).when(privateProfileManager).addPrivateSpaceHeader(any());
+        doAnswer(answer(this::addPrivateSpaceHeader)).when(privateProfileManager)
+                .addPrivateSpaceHeader(any());
+        doNothing().when(privateProfileManager).addPrivateSpaceInstallAppButton(any());
+        doReturn(0).when(privateProfileManager).addSystemAppsDivider(any());
         when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
         when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
+        mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
+                null, privateProfileManager);
+        mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
+        mAlphabeticalAppsList.updateItemFilter(info -> info != null
+                && info.user.equals(MAIN_HANDLE));
+
         int rows = (int) (ALL_APPS_HEIGHT - BIGGER_PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
         int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
 
@@ -303,7 +339,7 @@
         assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
                 mAlphabeticalAppsList.getAdapterItems().size());
         assertEquals(position,
-                mPsHeaderViewController.scrollForViewToBeVisibleInContainer(
+                privateProfileManager.scrollForViewToBeVisibleInContainer(
                         new AllAppsRecyclerView(mContext),
                         mAlphabeticalAppsList.getAdapterItems(),
                         BIGGER_PS_HEADER_HEIGHT,
@@ -313,21 +349,27 @@
     @Test
     public void scrollForViewToBeVisibleInContainer_withNoHeader() {
         when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
-        when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
-        when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps())
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+        when(privateProfileManager.splitIntoUserInstalledAndSystemApps())
                 .thenReturn(iteminfo -> iteminfo.componentName == null
                         || !iteminfo.componentName.getPackageName()
-                        .equals("com.android.launcher3.tests.camera"));
-        when(mAllApps.getContext()).thenReturn(mContext);
-        mAlphabeticalAppsList.updateItemFilter(info -> info != null
-                && info.user.equals(MAIN_HANDLE));
+                        .equals(CAMERA_PACKAGE_NAME));
+        doReturn(0).when(privateProfileManager).addPrivateSpaceHeader(any());
+        doNothing().when(privateProfileManager).addPrivateSpaceInstallAppButton(any());
+        doReturn(0).when(privateProfileManager).addSystemAppsDivider(any());
         when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
         when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
+        mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
+                null, privateProfileManager);
+        mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
+        mAlphabeticalAppsList.updateItemFilter(info -> info != null
+                && info.user.equals(MAIN_HANDLE));
 
         // The number of adapterItems should be the private space apps + one main app.
         assertEquals(NUM_PRIVATE_SPACE_APPS + 1,
                 mAlphabeticalAppsList.getAdapterItems().size());
-        assertEquals(SCROLL_NO_WHERE, mPsHeaderViewController.scrollForViewToBeVisibleInContainer(
+        assertEquals(SCROLL_NO_WHERE, privateProfileManager.scrollForViewToBeVisibleInContainer(
                 new AllAppsRecyclerView(mContext),
                 mAlphabeticalAppsList.getAdapterItems(),
                 BIGGER_PS_HEADER_HEIGHT,
@@ -376,7 +418,7 @@
                 AppInfo(gmailComponentName, "Gmail", MAIN_HANDLE, new Intent());
         appInfos.add(gmailAppInfo);
         ComponentName privateCameraComponentName = new ComponentName(
-                "com.android.launcher3.tests.camera", "CameraActivity");
+                CAMERA_PACKAGE_NAME, "CameraActivity");
         for (int i = 0; i < NUM_PRIVATE_SPACE_APPS; i++) {
             AppInfo privateCameraAppInfo = new AppInfo(privateCameraComponentName,
                     "Private Camera " + i, PRIVATE_HANDLE, new Intent());
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt b/tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
index c5dbce4..ff46987 100644
--- a/tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
+++ b/tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
@@ -21,6 +21,13 @@
 
 /** Generates a random CellLayoutBoard. */
 open class RandomBoardGenerator(generator: Random) : DeterministicRandomGenerator(generator) {
+
+    companion object {
+        // This is the max number of widgets because we encode the widgets as letters A-Z and we
+        // already have some of those letter used by other things so 22 is a safe number
+        val MAX_NUMBER_OF_WIDGETS = 22
+    }
+
     /**
      * @param remainingEmptySpaces the maximum number of spaces we will fill with icons and widgets
      *   meaning that if the number is 100 we will try to fill the board with at most 100 spaces
@@ -33,9 +40,9 @@
     }
 
     protected fun fillBoard(
-            board: CellLayoutBoard,
-            area: Rect,
-            remainingEmptySpacesArg: Int
+        board: CellLayoutBoard,
+        area: Rect,
+        remainingEmptySpacesArg: Int
     ): CellLayoutBoard {
         var remainingEmptySpaces = remainingEmptySpacesArg
         if (area.height() * area.width() <= 0) return board
@@ -45,11 +52,18 @@
         val y = area.top + getRandom(0, area.height() - height)
         if (remainingEmptySpaces > 0) {
             remainingEmptySpaces -= width * height
-        } else if (board.widgets.size <= 22 && width * height > 1) {
+        }
+
+        if (board.widgets.size <= MAX_NUMBER_OF_WIDGETS && width * height > 1) {
             board.addWidget(x, y, width, height)
         } else {
             board.addIcon(x, y)
         }
+
+        if (remainingEmptySpaces < 0) {
+            // optimization, no need to keep going
+            return board
+        }
         fillBoard(board, Rect(area.left, area.top, area.right, y), remainingEmptySpaces)
         fillBoard(board, Rect(area.left, y, x, area.bottom), remainingEmptySpaces)
         fillBoard(board, Rect(x, y + height, area.right, area.bottom), remainingEmptySpaces)
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt b/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt
new file mode 100644
index 0000000..a006fd7
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.testgenerator
+
+import android.graphics.Point
+import com.android.launcher3.LauncherSettings
+import com.android.launcher3.celllayout.board.CellLayoutBoard
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.model.gridmigration.WorkspaceItem
+import java.util.Random
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * Generate a list of WorkspaceItem's for the given test case.
+ *
+ * @param repeatAfter a number after which we would repeat the same number of icons and widgets to
+ *   account for cases where the user have the same item multiple times.
+ */
+fun generateItemsForTest(
+    boards: List<CellLayoutBoard>,
+    repeatAfterRange: Point
+): List<WorkspaceItem> {
+    val id = AtomicInteger(0)
+    val widgetId = AtomicInteger(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - 1)
+    // Repeat the same appWidgetProvider and intent to have repeating widgets and icons and test
+    // that case too
+    val getIntent = { i: Int -> "Intent ${(i + repeatAfterRange.x) % repeatAfterRange.y}" }
+    val getProvider = { i: Int ->
+        "com.test/test.Provider${(i + repeatAfterRange.x) % repeatAfterRange.y }"
+    }
+    val hotseatEntries =
+        (0 until boards[0].width).map {
+            WorkspaceItem(
+                x = it,
+                y = 0,
+                spanX = 1,
+                spanY = 1,
+                id = id.getAndAdd(1),
+                screenId = it,
+                title = "Hotseat ${id.get()}",
+                appWidgetId = -1,
+                appWidgetProvider = "Hotseat icons don't have a provider",
+                intent = getIntent(id.get()),
+                type = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION,
+                container = LauncherSettings.Favorites.CONTAINER_HOTSEAT
+            )
+        }
+    var widgetEntries =
+        boards
+            .flatMapIndexed { i, board -> board.widgets.map { Pair(i, it) } }
+            .map {
+                WorkspaceItem(
+                    x = it.second.cellX,
+                    y = it.second.cellY,
+                    spanX = it.second.spanX,
+                    spanY = it.second.spanY,
+                    id = id.getAndAdd(1),
+                    screenId = it.first,
+                    title = "Title Widget ${id.get()}",
+                    appWidgetId = widgetId.getAndAdd(-1),
+                    appWidgetProvider = getProvider(id.get()),
+                    intent = "Widgets don't have intent",
+                    type = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET,
+                    container = LauncherSettings.Favorites.CONTAINER_DESKTOP
+                )
+            }
+    widgetEntries = widgetEntries.filter { it.appWidgetProvider.contains("Provider4") }
+    val iconEntries =
+        boards
+            .flatMapIndexed { i, board -> board.icons.map { Pair(i, it) } }
+            .map {
+                WorkspaceItem(
+                    x = it.second.coord.x,
+                    y = it.second.coord.y,
+                    spanX = 1,
+                    spanY = 1,
+                    id = id.getAndAdd(1),
+                    screenId = it.first,
+                    title = "Title Icon ${id.get()}",
+                    appWidgetId = -1,
+                    appWidgetProvider = "Icons don't have providers",
+                    intent = getIntent(id.get()),
+                    type = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION,
+                    container = LauncherSettings.Favorites.CONTAINER_DESKTOP
+                )
+            }
+    return widgetEntries + hotseatEntries + iconEntries
+}
+
+data class GridMigrationUnitTestCase(
+    val boards: List<CellLayoutBoard>,
+    val destBoards: List<CellLayoutBoard>,
+    val srcSize: Point,
+    val targetSize: Point,
+    val seed: Long
+)
+
+class ValidGridMigrationTestCaseGenerator(private val generator: Random) :
+    DeterministicRandomGenerator(generator) {
+
+    companion object {
+        const val MAX_BOARD_SIZE = 12
+        const val MAX_BOARD_COUNT = 10
+        const val SEED = 10342
+    }
+
+    private fun generateBoards(
+        boardGenerator: RandomBoardGenerator,
+        width: Int,
+        height: Int,
+        boardCount: Int
+    ): List<CellLayoutBoard> {
+        val boards = mutableListOf<CellLayoutBoard>()
+        for (i in 0 until boardCount) {
+            boards.add(
+                boardGenerator.generateBoard(
+                    width,
+                    height,
+                    boardGenerator.getRandom(0, width * height)
+                )
+            )
+        }
+        return boards
+    }
+
+    fun generateTestCase(isDestEmpty: Boolean): GridMigrationUnitTestCase {
+        val seed = generator.nextLong()
+        val randomBoardGenerator = RandomBoardGenerator(Random(seed))
+        val width = randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
+        val height = randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
+        val targetSize =
+            Point(
+                randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE),
+                randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
+            )
+        val destBoards =
+            if (isDestEmpty) {
+                listOf()
+            } else {
+                generateBoards(
+                    boardGenerator = randomBoardGenerator,
+                    width = targetSize.x,
+                    height = targetSize.y,
+                    boardCount = randomBoardGenerator.getRandom(3, MAX_BOARD_COUNT)
+                )
+            }
+        return GridMigrationUnitTestCase(
+            boards =
+                generateBoards(
+                    boardGenerator = randomBoardGenerator,
+                    width = width,
+                    height = height,
+                    boardCount = randomBoardGenerator.getRandom(3, MAX_BOARD_COUNT)
+                ),
+            destBoards = destBoards,
+            srcSize = Point(width, height),
+            targetSize = targetSize,
+            seed = seed
+        )
+    }
+}
diff --git a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
index 4ec5b0e..3a1883c 100644
--- a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
+++ b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
@@ -28,6 +28,7 @@
 import com.android.launcher3.icons.BaseIconFactory
 import com.android.launcher3.icons.FastBitmapDrawable
 import com.android.launcher3.icons.UserBadgeDrawable
+import com.android.launcher3.model.data.FolderInfo
 import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.launcher3.util.ActivityContextWrapper
 import com.android.launcher3.util.FlagOp
@@ -71,8 +72,8 @@
                     .build()
             )
             .loadModelSync()
-        folderItems = modelHelper.bgDataModel.folders.valueAt(0).contents
-        folderIcon.mInfo = modelHelper.bgDataModel.folders.valueAt(0)
+        folderItems = modelHelper.bgDataModel.collections.valueAt(0).contents
+        folderIcon.mInfo = modelHelper.bgDataModel.collections.valueAt(0) as FolderInfo
         folderIcon.mInfo.contents = folderItems
 
         // Set first icon to be themed.
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 7a0b60a..abb0c39 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -160,6 +160,6 @@
     }
 
     private List<WorkspaceItemInfo> allItems() {
-        return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).contents;
+        return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).getContents();
     }
 }
diff --git a/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index 2b89321..10785f7 100644
--- a/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -87,7 +87,7 @@
         assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
         ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
         assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
-        assertEquals(3, ((FolderInfo) info).contents.size());
+        assertEquals(3, ((FolderInfo) info).getContents().size());
     }
 
     @Test
@@ -102,7 +102,7 @@
         assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
         ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
         assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
-        assertEquals(3, ((FolderInfo) info).contents.size());
+        assertEquals(3, ((FolderInfo) info).getContents().size());
         assertEquals("CustomFolder", info.title.toString());
     }
 
@@ -154,11 +154,11 @@
         // Verify folder
         assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
         FolderInfo info = (FolderInfo) mModelHelper.getBgDataModel().workspaceItems.get(0);
-        assertEquals(3, info.contents.size());
+        assertEquals(3, info.getContents().size());
 
         // Verify last icon
         assertEquals(LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT,
-                info.contents.get(info.contents.size() - 1).itemType);
+                info.getContents().get(info.getContents().size() - 1).itemType);
     }
 
     private void writeLayoutAndLoad(LauncherLayoutBuilder builder) throws Exception {
diff --git a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
index 2118ed6..ed587a1 100644
--- a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
+++ b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
@@ -165,11 +165,11 @@
         // Reload again with correct icon state
         app.model.forceReload()
         modelHelper.loadModelSync()
-        val folders = modelHelper.getBgDataModel().folders
+        val collections = modelHelper.getBgDataModel().collections
 
-        assertThat(folders.size()).isEqualTo(1)
-        assertThat(folders.valueAt(0).contents.size).isEqualTo(itemCount)
-        return folders.valueAt(0).contents
+        assertThat(collections.size()).isEqualTo(1)
+        assertThat(collections.valueAt(0).contents.size).isEqualTo(itemCount)
+        return collections.valueAt(0).contents
     }
 
     private fun verifyHighRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) {
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index 04735f2..761f06d 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -28,7 +28,6 @@
 import com.android.launcher3.LauncherPrefs
 import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
 import com.android.launcher3.LauncherSettings.Favorites.*
-import com.android.launcher3.config.FeatureFlags
 import com.android.launcher3.model.GridSizeMigrationUtil.DbReader
 import com.android.launcher3.pm.UserCache
 import com.android.launcher3.provider.LauncherDbUtils
@@ -98,10 +97,7 @@
         modelHelper.destroy()
     }
 
-    /**
-     * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not
-     * needed anymore
-     */
+    /** Old migration logic, should be modified once is not needed anymore */
     @Test
     @Throws(Exception::class)
     fun testMigration() {
@@ -208,10 +204,7 @@
         assertThat(locMap[testPackage9]).isEqualTo(Point(0, 2))
     }
 
-    /**
-     * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not
-     * needed anymore
-     */
+    /** Old migration logic, should be modified once is not needed anymore */
     @Test
     @Throws(Exception::class)
     fun testMigrationBackAndForth() {
@@ -606,68 +599,6 @@
     }
 
     /**
-     * Migrating from a smaller grid to a large one should keep the pages if the column difference
-     * is less than 2
-     */
-    @Test
-    @Throws(Exception::class)
-    fun migrateFromSmallerGridSmallDifference() {
-        enableNewMigrationLogic("4,4")
-
-        // Setup src grid
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage1, 5, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage2, 6, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 3, 1, testPackage3, 7, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 3, 2, testPackage4, 8, TMP_TABLE)
-        addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 3, 3, testPackage5, 9, TMP_TABLE)
-
-        idp.numDatabaseHotseatIcons = 4
-        idp.numColumns = 6
-        idp.numRows = 5
-
-        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
-        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
-        GridSizeMigrationUtil.migrate(
-            dbHelper,
-            srcReader,
-            destReader,
-            idp.numDatabaseHotseatIcons,
-            Point(idp.numColumns, idp.numRows),
-            DeviceGridState(context),
-            DeviceGridState(idp)
-        )
-
-        // Get workspace items
-        val c =
-            db.query(
-                TABLE_NAME,
-                arrayOf(INTENT, SCREEN),
-                "container=$CONTAINER_DESKTOP",
-                null,
-                null,
-                null,
-                null
-            )
-                ?: throw IllegalStateException()
-        val intentIndex = c.getColumnIndex(INTENT)
-        val screenIndex = c.getColumnIndex(SCREEN)
-
-        // Get in which screen the icon is
-        val locMap = HashMap<String?, Int>()
-        while (c.moveToNext()) {
-            locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
-                c.getInt(screenIndex)
-        }
-        c.close()
-        assertThat(locMap.size).isEqualTo(5)
-        assertThat(locMap[testPackage1]).isEqualTo(0)
-        assertThat(locMap[testPackage2]).isEqualTo(0)
-        assertThat(locMap[testPackage3]).isEqualTo(1)
-        assertThat(locMap[testPackage4]).isEqualTo(1)
-        assertThat(locMap[testPackage5]).isEqualTo(2)
-    }
-
-    /**
      * Migrating from a smaller grid to a large one should reflow the pages if the column difference
      * is more than 2
      */
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index e2ca31f..5731e2a 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -46,7 +46,7 @@
     @Mock private lateinit var app: LauncherAppState
     @Mock private lateinit var bgAllAppsList: AllAppsList
     @Mock private lateinit var modelDelegate: ModelDelegate
-    @Mock private lateinit var launcherBinder: LauncherBinder
+    @Mock private lateinit var launcherBinder: BaseLauncherBinder
     @Mock private lateinit var launcherModel: LauncherModel
     @Mock private lateinit var transaction: LoaderTransaction
     @Mock private lateinit var iconCache: IconCache
@@ -103,7 +103,7 @@
                 .runSyncOnBackgroundThread()
             Truth.assertThat(workspaceItems.size).isAtLeast(25)
             Truth.assertThat(appWidgets.size).isAtLeast(7)
-            Truth.assertThat(folders.size()).isAtLeast(8)
+            Truth.assertThat(collections.size()).isAtLeast(8)
             Truth.assertThat(itemsIdMap.size()).isAtLeast(40)
         }
 
diff --git a/tests/src/com/android/launcher3/model/gridmigration/GridMigrationUtils.kt b/tests/src/com/android/launcher3/model/gridmigration/GridMigrationUtils.kt
new file mode 100644
index 0000000..cc8e61d
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/gridmigration/GridMigrationUtils.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.model.gridmigration
+
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase
+import android.graphics.Point
+import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.celllayout.board.CellLayoutBoard
+
+class MockSet(override val size: Int) : Set<String> {
+    override fun contains(element: String): Boolean = true
+    override fun containsAll(elements: Collection<String>): Boolean = true
+    override fun isEmpty(): Boolean = false
+    override fun iterator(): Iterator<String> = listOf<String>().iterator()
+}
+
+fun itemListToBoard(itemsArg: List<WorkspaceItem>, boardSize: Point): List<CellLayoutBoard> {
+    val items = itemsArg.filter { it.container != Favorites.CONTAINER_HOTSEAT }
+    val boardList =
+        List(items.maxOf { it.screenId + 1 }) { CellLayoutBoard(boardSize.x, boardSize.y) }
+    items.forEach {
+        when (it.type) {
+            Favorites.ITEM_TYPE_FOLDER,
+            Favorites.ITEM_TYPE_APP_PAIR -> throw Exception("Not implemented")
+            Favorites.ITEM_TYPE_APPWIDGET ->
+                boardList[it.screenId].addWidget(it.x, it.y, it.spanX, it.spanY)
+            Favorites.ITEM_TYPE_APPLICATION -> boardList[it.screenId].addIcon(it.x, it.y)
+        }
+    }
+    return boardList
+}
+
+fun insertIntoDb(tableName: String, entry: WorkspaceItem, db: SQLiteDatabase) {
+    val values = ContentValues()
+    values.put(Favorites.SCREEN, entry.screenId)
+    values.put(Favorites.CELLX, entry.x)
+    values.put(Favorites.CELLY, entry.y)
+    values.put(Favorites.SPANX, entry.spanX)
+    values.put(Favorites.SPANY, entry.spanY)
+    values.put(Favorites.TITLE, entry.title)
+    values.put(Favorites.INTENT, entry.intent)
+    values.put(Favorites.APPWIDGET_PROVIDER, entry.appWidgetProvider)
+    values.put(Favorites.APPWIDGET_ID, entry.appWidgetId)
+    values.put(Favorites.CONTAINER, entry.container)
+    values.put(Favorites.ITEM_TYPE, entry.type)
+    values.put(Favorites._ID, entry.id)
+    db.insert(tableName, null, values)
+}
+
+fun readDb(tableName: String, db: SQLiteDatabase): List<WorkspaceItem> {
+    val result = mutableListOf<WorkspaceItem>()
+    val cursor = db.query(tableName, null, null, null, null, null, null)
+    val indexCellX: Int = cursor.getColumnIndexOrThrow(Favorites.CELLX)
+    val indexCellY: Int = cursor.getColumnIndexOrThrow(Favorites.CELLY)
+    val indexSpanX: Int = cursor.getColumnIndexOrThrow(Favorites.SPANX)
+    val indexSpanY: Int = cursor.getColumnIndexOrThrow(Favorites.SPANY)
+    val indexId: Int = cursor.getColumnIndexOrThrow(Favorites._ID)
+    val indexScreen: Int = cursor.getColumnIndexOrThrow(Favorites.SCREEN)
+    val indexTitle: Int = cursor.getColumnIndexOrThrow(Favorites.TITLE)
+    val indexAppWidgetId: Int = cursor.getColumnIndexOrThrow(Favorites.APPWIDGET_ID)
+    val indexWidgetProvider: Int = cursor.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER)
+    val indexIntent: Int = cursor.getColumnIndexOrThrow(Favorites.INTENT)
+    val indexItemType: Int = cursor.getColumnIndexOrThrow(Favorites.ITEM_TYPE)
+    val container: Int = cursor.getColumnIndexOrThrow(Favorites.CONTAINER)
+    while (cursor.moveToNext()) {
+        result.add(
+            WorkspaceItem(
+                x = cursor.getInt(indexCellX),
+                y = cursor.getInt(indexCellY),
+                spanX = cursor.getInt(indexSpanX),
+                spanY = cursor.getInt(indexSpanY),
+                id = cursor.getInt(indexId),
+                screenId = cursor.getInt(indexScreen),
+                title = cursor.getString(indexTitle),
+                appWidgetId = cursor.getInt(indexAppWidgetId),
+                appWidgetProvider = cursor.getString(indexWidgetProvider),
+                intent = cursor.getString(indexIntent),
+                type = cursor.getInt(indexItemType),
+                container = cursor.getInt(container)
+            )
+        )
+    }
+    return result
+}
+
+data class WorkspaceItem(
+    val x: Int,
+    val y: Int,
+    val spanX: Int,
+    val spanY: Int,
+    val id: Int,
+    val screenId: Int,
+    val title: String,
+    val appWidgetId: Int,
+    val appWidgetProvider: String,
+    val intent: String,
+    val type: Int,
+    val container: Int,
+)
diff --git a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
new file mode 100644
index 0000000..58b915f
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model.gridmigration
+
+import android.content.Context
+import android.database.sqlite.SQLiteDatabase
+import android.graphics.Point
+import android.os.Process
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.celllayout.testgenerator.ValidGridMigrationTestCaseGenerator
+import com.android.launcher3.celllayout.testgenerator.generateItemsForTest
+import com.android.launcher3.model.DatabaseHelper
+import com.android.launcher3.model.DeviceGridState
+import com.android.launcher3.model.GridSizeMigrationUtil
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.provider.LauncherDbUtils
+import com.android.launcher3.util.rule.TestStabilityRule
+import com.android.launcher3.util.rule.TestStabilityRule.Stability
+import java.util.Random
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private data class Grid(val tableName: String, val size: Point, val items: List<WorkspaceItem>) {
+    fun toGridState(): DeviceGridState =
+        DeviceGridState(size.x, size.y, size.x, InvariantDeviceProfile.TYPE_PHONE, tableName)
+}
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ValidGridMigrationUnitTest {
+
+    companion object {
+        const val SEED = 1044542
+        val REPEAT_AFTER = Point(0, 10)
+        val REPEAT_AFTER_DST = Point(6, 15)
+        const val TAG = "ValidGridMigrationUnitTest"
+        const val SMALL_TEST_SIZE = 60
+        const val LARGE_TEST_SIZE = 1000
+    }
+
+    private lateinit var context: Context
+
+    @Before
+    fun setUp() {
+        context = InstrumentationRegistry.getInstrumentation().targetContext
+    }
+
+    private fun validate(srcGrid: Grid, dstGrid: Grid, resultItems: List<WorkspaceItem>) {
+        // This returns a map with the number of repeated elements
+        // ex { calculatorIcon : 6, weatherWidget : 2 }
+        val itemsToMap = { it: List<WorkspaceItem> ->
+            it.filter { it.container != Favorites.CONTAINER_HOTSEAT }
+                .groupingBy {
+                    when (it.type) {
+                        Favorites.ITEM_TYPE_FOLDER,
+                        Favorites.ITEM_TYPE_APP_PAIR -> throw Exception("Not implemented")
+                        Favorites.ITEM_TYPE_APPWIDGET -> it.appWidgetProvider
+                        Favorites.ITEM_TYPE_APPLICATION -> it.intent
+                        else -> it.title
+                    }
+                }
+                .eachCount()
+        }
+        resultItems.forEach {
+            assert((it.x in 0..dstGrid.size.x) && (it.y in 0..dstGrid.size.y)) {
+                "Item outside of the board size. Size = ${dstGrid.size} Item = $it"
+            }
+            assert(
+                (it.x + it.spanX in 0..dstGrid.size.x) && (it.y + it.spanY in 0..dstGrid.size.y)
+            ) {
+                "Item doesn't fit in the grid. Size = ${dstGrid.size} Item = $it"
+            }
+        }
+
+        val srcCountMap = itemsToMap(srcGrid.items)
+        val resultCountMap = itemsToMap(resultItems)
+        val diff = resultCountMap - srcCountMap
+
+        diff.forEach { (k, count) ->
+            assert(count >= 0) { "Source item $k not present on the result" }
+        }
+    }
+
+    private fun addItemsToDb(db: SQLiteDatabase, grid: Grid) {
+        LauncherDbUtils.SQLiteTransaction(db).use { transaction ->
+            grid.items.forEach { insertIntoDb(grid.tableName, it, transaction.db) }
+            transaction.commit()
+        }
+    }
+
+    private fun migrate(
+        srcGrid: Grid,
+        dstGrid: Grid,
+    ): List<WorkspaceItem> {
+        val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
+        val dbHelper =
+            DatabaseHelper(
+                context,
+                null,
+                { UserCache.INSTANCE.get(context).getSerialNumberForUser(it) },
+                {}
+            )
+
+        Favorites.addTableToDb(dbHelper.writableDatabase, userSerial, false, srcGrid.tableName)
+
+        addItemsToDb(dbHelper.writableDatabase, srcGrid)
+        addItemsToDb(dbHelper.writableDatabase, dstGrid)
+
+        LauncherDbUtils.SQLiteTransaction(dbHelper.writableDatabase).use {
+            GridSizeMigrationUtil.migrate(
+                dbHelper,
+                GridSizeMigrationUtil.DbReader(it.db, srcGrid.tableName, context, MockSet(1)),
+                GridSizeMigrationUtil.DbReader(it.db, dstGrid.tableName, context, MockSet(1)),
+                dstGrid.size.x,
+                dstGrid.size,
+                srcGrid.toGridState(),
+                dstGrid.toGridState()
+            )
+            it.commit()
+        }
+        return readDb(dstGrid.tableName, dbHelper.readableDatabase)
+    }
+
+    @Test
+    fun runTestCase() {
+        val caseGenerator = ValidGridMigrationTestCaseGenerator(Random(SEED.toLong()))
+        for (i in 0..SMALL_TEST_SIZE) {
+            val testCase = caseGenerator.generateTestCase(isDestEmpty = true)
+            Log.d(TAG, "Test case = $testCase")
+            val srcGrid =
+                Grid(
+                    tableName = Favorites.TMP_TABLE,
+                    size = testCase.srcSize,
+                    items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+                )
+            val dstGrid =
+                Grid(tableName = Favorites.TABLE_NAME, size = testCase.targetSize, items = listOf())
+            validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
+        }
+    }
+
+    @Test
+    fun mergeBoards() {
+        val caseGenerator = ValidGridMigrationTestCaseGenerator(Random(SEED.toLong()))
+        for (i in 0..SMALL_TEST_SIZE) {
+            val testCase = caseGenerator.generateTestCase(isDestEmpty = false)
+            Log.d(TAG, "Test case = $testCase")
+            val srcGrid =
+                Grid(
+                    tableName = Favorites.TMP_TABLE,
+                    size = testCase.srcSize,
+                    items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+                )
+            val dstGrid =
+                Grid(
+                    tableName = Favorites.TABLE_NAME,
+                    size = testCase.targetSize,
+                    items = generateItemsForTest(testCase.destBoards, REPEAT_AFTER_DST)
+                )
+            validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
+        }
+    }
+
+    // This test takes about 4 minutes, there is no need to run it in presubmit.
+    @Stability(flavors = TestStabilityRule.LOCAL or TestStabilityRule.PLATFORM_POSTSUBMIT)
+    @Test
+    fun runExtensiveTestCases() {
+        val caseGenerator = ValidGridMigrationTestCaseGenerator(Random(SEED.toLong()))
+        for (i in 0..LARGE_TEST_SIZE) {
+            val testCase = caseGenerator.generateTestCase(isDestEmpty = true)
+            Log.d(TAG, "Test case = $testCase")
+            val srcGrid =
+                Grid(
+                    tableName = Favorites.TMP_TABLE,
+                    size = testCase.srcSize,
+                    items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+                )
+            val dstGrid =
+                Grid(tableName = Favorites.TABLE_NAME, size = testCase.targetSize, items = listOf())
+            validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
index 43fc8ff..913dfa2 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
@@ -37,6 +37,7 @@
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.ScreenRecordRule;
 
 import org.junit.After;
 import org.junit.Before;
@@ -240,6 +241,7 @@
         });
     }
 
+    @ScreenRecordRule.ScreenRecord // b/329935119
     @Test
     @PortraitLandscape
     public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {
diff --git a/tests/src/com/android/launcher3/util/ItemInflaterTest.kt b/tests/src/com/android/launcher3/util/ItemInflaterTest.kt
index efad899..0065527 100644
--- a/tests/src/com/android/launcher3/util/ItemInflaterTest.kt
+++ b/tests/src/com/android/launcher3/util/ItemInflaterTest.kt
@@ -36,6 +36,7 @@
 import com.android.launcher3.folder.FolderIcon
 import com.android.launcher3.model.ModelWriter
 import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.model.data.AppPairInfo
 import com.android.launcher3.model.data.FolderInfo
 import com.android.launcher3.model.data.LauncherAppWidgetInfo
 import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
@@ -170,7 +171,7 @@
 
     @Test
     fun test_app_pair_inflated_on_UI() {
-        val itemInfo = FolderInfo()
+        val itemInfo = AppPairInfo()
         itemInfo.itemType = ITEM_TYPE_APP_PAIR
         itemInfo.contents.add(workspaceItemInfo())
         itemInfo.contents.add(workspaceItemInfo())
@@ -186,7 +187,7 @@
     fun test_app_pair_inflated_on_BG() {
         setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION)
 
-        val itemInfo = FolderInfo()
+        val itemInfo = AppPairInfo()
         itemInfo.itemType = ITEM_TYPE_APP_PAIR
         itemInfo.contents.add(workspaceItemInfo())
         itemInfo.contents.add(workspaceItemInfo())
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index c7d3754..0a52955 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -24,6 +24,7 @@
 import static android.view.MotionEvent.ACTION_SCROLL;
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT;
+import static android.view.Surface.ROTATION_90;
 
 import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID;
 import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
@@ -474,12 +475,25 @@
         logShellCommand(cmd);
     }
 
+    /**
+     * Retrieves a resource value from context that defines if nav bar can change position or if it
+     * is fixed position regardless of device orientation.
+     */
+    private boolean getNavBarCanMove() {
+        final Context baseContext = mInstrumentation.getTargetContext();
+        try {
+            final Context ctx = getLauncherContext(baseContext);
+            return getNavBarCanMove(ctx);
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+        return false;
+    }
+
     public NavigationModel getNavigationModel() {
         final Context baseContext = mInstrumentation.getTargetContext();
         try {
-            // Workaround, use constructed context because both the instrumentation context and the
-            // app context are not constructed with resources that take overlays into account
-            final Context ctx = baseContext.createPackageContext(getLauncherPackageName(), 0);
+            final Context ctx = getLauncherContext(baseContext);
             for (int i = 0; i < 100; ++i) {
                 final int currentInteractionMode = getCurrentInteractionMode(ctx);
                 final NavigationModel model = getNavigationModel(currentInteractionMode);
@@ -1101,19 +1115,31 @@
     /**
      * Goes to home from immersive fullscreen app by first swiping up to bring navbar, and then
      * performing {@code goHome()} action.
-     * Currently only supports gesture navigation mode.
      *
      * @return the Workspace object.
      */
     public Workspace goHomeFromImmersiveFullscreenApp() {
-        assertTrue("expected gesture navigation mode",
-                getNavigationModel() == NavigationModel.ZERO_BUTTON);
-        final Point displaySize = getRealDisplaySize();
-        linearGesture(
-                displaySize.x / 2, displaySize.y - 1,
-                displaySize.x / 2, 0,
-                ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
-                false, GestureScope.EXPECT_PILFER);
+        final boolean navBarCanMove = getNavBarCanMove();
+        if (getNavigationModel() == NavigationModel.ZERO_BUTTON || !navBarCanMove) {
+            // in gesture nav we can swipe up at the bottom to bring the navbar handle
+            final Point displaySize = getRealDisplaySize();
+            linearGesture(
+                    displaySize.x / 2, displaySize.y - 1,
+                    displaySize.x / 2, 0,
+                    ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
+                    false, GestureScope.EXPECT_PILFER);
+        } else {
+            // in 3 button nav we swipe up on the side edge of the screen to bring the navbar
+            final boolean rotated90degrees = mDevice.getDisplayRotation() == ROTATION_90;
+            final Point displaySize = getRealDisplaySize();
+            final int startX = rotated90degrees ? displaySize.x : 0;
+            final int endX = rotated90degrees ? 0 : displaySize.x;
+            linearGesture(
+                    startX, displaySize.y / 2,
+                    endX, displaySize.y / 2,
+                    ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
+                    false, GestureScope.EXPECT_PILFER);
+        }
         return goHome();
     }
 
@@ -2126,6 +2152,13 @@
         return getSystemIntegerRes(context, "config_navBarInteractionMode");
     }
 
+    /**
+     * Retrieve the resource value that defines if nav bar can moved or if it is fixed position.
+     */
+    private static boolean getNavBarCanMove(Context context) {
+        return getSystemBooleanRes(context, "config_navBarCanMove");
+    }
+
     @NonNull
     UiObject2 clickAndGet(
             @NonNull final UiObject2 target, @NonNull String resName, Pattern longClickEvent) {
@@ -2166,6 +2199,18 @@
         }
     }
 
+    private static boolean getSystemBooleanRes(Context context, String resName) {
+        Resources res = context.getResources();
+        int resId = res.getIdentifier(resName, "bool", "android");
+
+        if (resId != 0) {
+            return res.getBoolean(resId);
+        } else {
+            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+            return false;
+        }
+    }
+
     private static int getSystemIntegerRes(Context context, String resName) {
         Resources res = context.getResources();
         int resId = res.getIdentifier(resName, "integer", "android");
@@ -2431,6 +2476,13 @@
         return Math.max(topRadius, bottomRadius) + tmpBuffer;
     }
 
+    private Context getLauncherContext(Context baseContext)
+            throws PackageManager.NameNotFoundException {
+        // Workaround, use constructed context because both the instrumentation context and the
+        // app context are not constructed with resources that take overlays into account
+        return baseContext.createPackageContext(getLauncherPackageName(), 0);
+    }
+
     private static boolean supportsRoundedCornersOnWindows(Resources resources) {
         return ResourceUtils.getBoolByName(
                 "config_supportsRoundedCornersOnWindows", resources, false);