Merge "Send bubble bar location update source to shell" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 5101851..878aa6e 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -311,6 +311,13 @@
 }
 
 flag {
+    name: "all_apps_sheet_for_handheld"
+    namespace: "launcher"
+    description: "All Apps will be presented on a bottom sheet in handheld mode"
+    bug: "374186088"
+}
+
+flag {
     name: "multiline_search_bar"
     namespace: "launcher"
     description: "Search bar can wrap to multi-line"
@@ -512,4 +519,11 @@
   namespace: "launcher"
   description: "Enable MSDL feedback for Launcher interactions"
   bug: "377496684"
+}
+
+flag {
+    name: "taskbar_recents_layout_transition"
+    namespace: "launcher"
+    description: "Enable Taskbar LayoutTransition for Recent Apps"
+    bug: "343521765"
 }
\ No newline at end of file
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index a87ed44..6c5748d 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -69,7 +69,7 @@
     <string name="home_gesture_intro_title" msgid="836590312858441830">"Əsas səhifəyə keçmək üçün sürüşdürün"</string>
     <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Ekranın aşağısından yuxarısına sürüşdürün. Bu jest həmişə Əsas səhifəyə aparır."</string>
     <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2 barmaqla ekranın aşağısından yuxarısına sürüşdürün. Bu jest həmişə Əsas səhifəyə aparır."</string>
-    <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Əsas səhifəyə qayıdın"</string>
+    <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Əsas səhifəyə keçin"</string>
     <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Ekranın aşağısından yuxarı sürüşdürün"</string>
     <string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Əla!"</string>
     <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Ekranın aşağı kənarından yuxarı sürüşdürün"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index eee46bb..dc16036 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -140,7 +140,7 @@
     <string name="always_show_taskbar" msgid="3608801276107751229">"始终显示任务栏"</string>
     <string name="change_navigation_mode" msgid="9088393078736808968">"更改导航模式"</string>
     <string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"任务栏分隔线"</string>
-    <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"溢出式任务栏"</string>
+    <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"任务栏溢出图标"</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="3679780650881041632">"{count,plural, =1{多个应用}other{多个应用}}"</string>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 5c80575..f3c9467 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -52,6 +52,11 @@
 
     <integer name="max_depth_blur_radius">23</integer>
 
+    <!-- If predicted widgets from prediction service are less than this number, additional
+    eligible widgets may be added locally by launcher. When set to 0, no widgets will be added
+    locally. -->
+    <integer name="widget_predictions_min_count">6</integer>
+
     <!-- Accessibility actions -->
     <item type="id" name="action_move_to_top_or_left" />
     <item type="id" name="action_move_to_bottom_or_right" />
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index bd2c7cc..dc0f899 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -55,6 +55,7 @@
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
+import com.android.systemui.animation.back.FlingOnBackAnimationCallback;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -374,12 +375,12 @@
     /**
      * Animation callback for different predictive back animation states for the widget picker.
      */
-    private class BackAnimationCallback implements OnBackAnimationCallback {
+    private class BackAnimationCallback extends FlingOnBackAnimationCallback {
         @Nullable
         OnBackAnimationCallback mActiveOnBackAnimationCallback;
 
         @Override
-        public void onBackStarted(@NonNull BackEvent backEvent) {
+        public void onBackStartedCompat(@NonNull BackEvent backEvent) {
             if (mActiveOnBackAnimationCallback != null) {
                 mActiveOnBackAnimationCallback.onBackCancelled();
             }
@@ -390,7 +391,7 @@
         }
 
         @Override
-        public void onBackInvoked() {
+        public void onBackInvokedCompat() {
             if (mActiveOnBackAnimationCallback == null) {
                 return;
             }
@@ -399,7 +400,7 @@
         }
 
         @Override
-        public void onBackProgressed(@NonNull BackEvent backEvent) {
+        public void onBackProgressedCompat(@NonNull BackEvent backEvent) {
             if (mActiveOnBackAnimationCallback == null) {
                 return;
             }
@@ -407,7 +408,7 @@
         }
 
         @Override
-        public void onBackCancelled() {
+        public void onBackCancelledCompat() {
             if (mActiveOnBackAnimationCallback == null) {
                 return;
             }
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
index 6916a1d..e160f82 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
@@ -20,6 +20,7 @@
 import android.animation.AnimatorSet
 import android.animation.ValueAnimator
 import android.content.Context
+import android.graphics.Rect
 import android.os.IBinder
 import android.view.SurfaceControl.Transaction
 import android.view.WindowManager.TRANSIT_OPEN
@@ -31,6 +32,7 @@
 import android.window.TransitionInfo.Change
 import androidx.core.animation.addListener
 import com.android.app.animation.Interpolators
+import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.quickstep.RemoteRunnable
 import com.android.wm.shell.shared.animation.MinimizeAnimator
 import com.android.wm.shell.shared.animation.WindowAnimator
@@ -43,8 +45,19 @@
  * ([android.view.WindowManager.TRANSIT_TO_BACK]) this transition will apply a minimize animation to
  * that window.
  */
-class DesktopAppLaunchTransition(private val context: Context, private val mainExecutor: Executor) :
-    RemoteTransitionStub() {
+class DesktopAppLaunchTransition(
+    private val context: Context,
+    private val mainExecutor: Executor,
+    private val launchType: AppLaunchType,
+) : RemoteTransitionStub() {
+
+    enum class AppLaunchType(
+        val boundsAnimationParams: WindowAnimator.BoundsAnimationParams,
+        val alphaDurationMs: Long,
+    ) {
+        LAUNCH(launchBoundsAnimationDef, /* alphaDurationMs= */ 200L),
+        UNMINIMIZE(unminimizeBoundsAnimationDef, /* alphaDurationMs= */ 100L),
+    }
 
     override fun startAnimation(
         token: IBinder,
@@ -105,18 +118,24 @@
         val boundsAnimator =
             WindowAnimator.createBoundsAnimator(
                 context.resources.displayMetrics,
-                launchBoundsAnimationDef,
+                launchType.boundsAnimationParams,
                 change,
                 transaction,
             )
         val alphaAnimator =
             ValueAnimator.ofFloat(0f, 1f).apply {
-                duration = LAUNCH_ANIM_ALPHA_DURATION_MS
+                duration = launchType.alphaDurationMs
                 interpolator = Interpolators.LINEAR
                 addUpdateListener { animation ->
                     transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
                 }
             }
+        val clipRect = Rect(change.endAbsBounds).apply { offsetTo(0, 0) }
+        transaction.setCrop(change.leash, clipRect)
+        transaction.setCornerRadius(
+            change.leash,
+            ScreenDecorationsUtils.getWindowCornerRadius(context),
+        )
         return AnimatorSet().apply {
             playTogether(boundsAnimator, alphaAnimator)
             addListener(onEnd = { animation -> onAnimFinish(animation) })
@@ -124,13 +143,18 @@
     }
 
     companion object {
-        private val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
-
-        private const val LAUNCH_ANIM_ALPHA_DURATION_MS = 100L
-        private const val MINIMIZE_ANIM_ALPHA_DURATION_MS = 100L
+        val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
 
         private val launchBoundsAnimationDef =
             WindowAnimator.BoundsAnimationParams(
+                durationMs = 600,
+                startOffsetYDp = 36f,
+                startScale = 0.95f,
+                interpolator = Interpolators.STANDARD_DECELERATE,
+            )
+
+        private val unminimizeBoundsAnimationDef =
+            WindowAnimator.BoundsAnimationParams(
                 durationMs = 300,
                 startOffsetYDp = 12f,
                 startScale = 0.97f,
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 0395d32..9d9054e 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -16,8 +16,12 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
+import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
 
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toMap;
+
 import android.app.prediction.AppTarget;
 import android.content.ComponentName;
 import android.content.Context;
@@ -26,6 +30,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
+import com.android.launcher3.R;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
 import com.android.launcher3.model.data.ItemInfo;
@@ -34,8 +39,10 @@
 import com.android.launcher3.widget.picker.WidgetRecommendationCategoryProvider;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -60,33 +67,72 @@
     @Override
     public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
             @NonNull AllAppsList apps) {
+        Predicate<WidgetItem> predictedWidgetsFilter = enableTieredWidgetsByDefaultInPicker()
+                ? dataModel.widgetsModel.getPredictedWidgetsFilter() : null;
         Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
                 widget -> new ComponentKey(widget.providerName, widget.user)).collect(
                 Collectors.toSet());
-        Predicate<WidgetItem> notOnWorkspace = w -> !widgetsInWorkspace.contains(w);
-        Map<ComponentKey, WidgetItem> allWidgets =
-                dataModel.widgetsModel.getWidgetsByComponentKey();
+
+        // Widgets (excluding shortcuts & already added widgets) that belong to apps eligible for
+        // being in predictions.
+        Map<ComponentKey, WidgetItem> allEligibleWidgets =
+                dataModel.widgetsModel.getWidgetsByComponentKey()
+                        .entrySet()
+                        .stream()
+                        .filter(entry -> entry.getValue().widgetInfo != null
+                                && !widgetsInWorkspace.contains(entry.getValue())
+                                && (predictedWidgetsFilter == null
+                                || predictedWidgetsFilter.test(entry.getValue()))
+                        ).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+        Context context = taskController.getApp().getContext();
 
         List<WidgetItem> servicePredictedItems = new ArrayList<>();
+        List<String> addedWidgetApps = new ArrayList<>();
 
         for (AppTarget app : mTargets) {
             ComponentKey componentKey = new ComponentKey(
                     new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
-            WidgetItem widget = allWidgets.get(componentKey);
-            if (widget == null) {
+            WidgetItem widget = allEligibleWidgets.get(componentKey);
+            if (widget == null) { // widget not eligible.
                 continue;
             }
             String className = app.getClassName();
             if (!TextUtils.isEmpty(className)) {
-                if (notOnWorkspace.test(widget)) {
-                    servicePredictedItems.add(widget);
-                }
+                servicePredictedItems.add(widget);
+                addedWidgetApps.add(componentKey.componentName.getPackageName());
+            }
+        }
+
+        int minPredictionCount = context.getResources().getInteger(
+                R.integer.widget_predictions_min_count);
+        if (enableTieredWidgetsByDefaultInPicker()
+                && servicePredictedItems.size() < minPredictionCount) {
+            // Eligible apps that aren't already part of predictions.
+            Map<String, List<WidgetItem>> eligibleWidgetsByApp =
+                    allEligibleWidgets.values().stream()
+                            .filter(w -> !addedWidgetApps.contains(
+                                    w.componentName.getPackageName()))
+                            .collect(groupingBy(w -> w.componentName.getPackageName()));
+
+            // Randomize available apps list
+            List<String> appPackages = new ArrayList<>(eligibleWidgetsByApp.keySet());
+            Collections.shuffle(appPackages);
+
+            int widgetsToAdd = minPredictionCount - servicePredictedItems.size();
+            for (String appPackage : appPackages) {
+                if (widgetsToAdd <= 0) break;
+
+                List<WidgetItem> widgetsForApp = eligibleWidgetsByApp.get(appPackage);
+                int index = new Random().nextInt(widgetsForApp.size());
+                // Add a random widget from the app.
+                servicePredictedItems.add(widgetsForApp.get(index));
+                widgetsToAdd--;
             }
         }
 
         List<ItemInfo> items;
         if (enableCategorizedWidgetSuggestions()) {
-            Context context = taskController.getApp().getContext();
             WidgetRecommendationCategoryProvider categoryProvider =
                     WidgetRecommendationCategoryProvider.newInstance(context);
             items = servicePredictedItems.stream()
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 711a49a..23a5a27 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -54,6 +54,8 @@
     public static final int MAX_TASKS = 6;
 
     @NonNull private final ControllerCallbacks mControllerCallbacks = new ControllerCallbacks();
+    // Callback used to notify when the KQS view is closed.
+    @Nullable private Runnable mOnClosed;
 
     // Initialized on init
     @Nullable private RecentsModel mModel;
@@ -112,8 +114,12 @@
      * Opens or closes the view in response to taskbar action. The view shows a filtered list of
      * tasks.
      * @param taskIdsToExclude A list of tasks to exclude in the opened view.
+     * @param onClosed A callback used to notify when the KQS view is closed.
      */
-    void toggleQuickSwitchViewForTaskbar(@NonNull Set<Integer> taskIdsToExclude) {
+    void toggleQuickSwitchViewForTaskbar(@NonNull Set<Integer> taskIdsToExclude,
+            @NonNull Runnable onClosed) {
+        mOnClosed = onClosed;
+
         // Close the view if its shown, and was opened from the taskbar.
         if (mQuickSwitchViewController != null
                 && !mQuickSwitchViewController.isCloseAnimationRunning()
@@ -264,6 +270,10 @@
             return;
         }
         mQuickSwitchViewController.closeQuickSwitchView(animate);
+        if (mOnClosed != null) {
+            mOnClosed.run();
+            mOnClosed = null;
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 7a63f74..390112e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.desktop.DesktopAppLaunchTransition;
+import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
 import com.android.launcher3.util.DisplayController;
@@ -47,7 +48,6 @@
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -247,11 +247,13 @@
             return -1;
         }
         RemoteTransition remoteTransition = slideInTransition;
-        if (mOnDesktop && task.task1.isMinimized
-                && Flags.enableDesktopAppLaunchAlttabTransitions()) {
+        if (mOnDesktop
+                && mControllers.taskbarActivityContext.canUnminimizeDesktopTask(task.task1.key.id)
+        ) {
             // This app is being unminimized - use our own transition runner.
             remoteTransition = new RemoteTransition(
-                    new DesktopAppLaunchTransition(context, MAIN_EXECUTOR));
+                    new DesktopAppLaunchTransition(
+                        context, MAIN_EXECUTOR, AppLaunchType.UNMINIMIZE));
         }
         mControllers.taskbarActivityContext.handleGroupTaskLaunch(
                 task,
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 09dbeb6..b1cb2c6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -372,6 +372,11 @@
                             * mTaskbarInAppDisplayProgress.value);
             mControllers.navbarButtonsViewController
                     .getOnTaskbarBackgroundNavButtonColorOverride().updateValue(progress);
+
+            if (isBubbleBarEnabled()) {
+                mControllers.bubbleControllers.ifPresent(
+                        c -> c.bubbleStashController.setInAppDisplayOverrideProgress(progress));
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 7d8e93c..fbd1b6e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -49,6 +49,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
+import static com.android.window.flags.Flags.predictiveBackThreeButtonNav;
 import static com.android.wm.shell.Flags.enableBubbleBarInPersistentTaskBar;
 
 import android.animation.Animator;
@@ -71,8 +72,10 @@
 import android.graphics.drawable.RotateDrawable;
 import android.inputmethodservice.InputMethodService;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.util.Property;
 import android.view.Gravity;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
@@ -843,12 +846,43 @@
         buttonView.setImageResource(drawableId);
         buttonView.setContentDescription(parent.getContext().getString(
                 navButtonController.getButtonContentDescription(buttonType)));
-        buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType, view));
-        buttonView.setOnLongClickListener(view ->
-                navButtonController.onButtonLongClick(buttonType, view));
+        if (predictiveBackThreeButtonNav() && buttonType == BUTTON_BACK) {
+            // set up special touch listener for back button to support predictive back
+            setBackButtonTouchListener(buttonView, navButtonController);
+        } else {
+            buttonView.setOnClickListener(view ->
+                    navButtonController.onButtonClick(buttonType, view));
+            buttonView.setOnLongClickListener(view ->
+                    navButtonController.onButtonLongClick(buttonType, view));
+        }
         return buttonView;
     }
 
+    private void setBackButtonTouchListener(View buttonView,
+            TaskbarNavButtonController navButtonController) {
+        buttonView.setOnTouchListener((v, event) -> {
+            if (event.getAction() == MotionEvent.ACTION_MOVE) return false;
+            long time = SystemClock.uptimeMillis();
+            int action = event.getAction();
+            KeyEvent keyEvent = new KeyEvent(time, time,
+                    action == MotionEvent.ACTION_DOWN ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
+                    KeyEvent.KEYCODE_BACK, 0);
+            if (event.getAction() == MotionEvent.ACTION_CANCEL) {
+                keyEvent.cancel();
+            }
+            navButtonController.executeBack(keyEvent);
+
+            if (action == MotionEvent.ACTION_UP) {
+                buttonView.performClick();
+            }
+            return false;
+        });
+        buttonView.setOnLongClickListener((view) ->  {
+            navButtonController.onButtonLongClick(BUTTON_BACK, view);
+            return false;
+        });
+    }
+
     private ImageView addButton(ViewGroup parent, @IdRes int id, @LayoutRes int layoutId) {
         ImageView buttonView = (ImageView) mContext.getLayoutInflater()
                 .inflate(layoutId, parent, false);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e22de06..82acc0c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -59,6 +59,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
+import android.os.Bundle;
 import android.os.IRemoteCallback;
 import android.os.Process;
 import android.os.Trace;
@@ -72,6 +73,7 @@
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.Toast;
+import android.window.DesktopModeFlags;
 import android.window.RemoteTransition;
 
 import androidx.annotation.NonNull;
@@ -89,6 +91,8 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.apppairs.AppPairIcon;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.desktop.DesktopAppLaunchTransition;
+import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
@@ -282,6 +286,7 @@
         BubbleBarController.onTaskbarRecreated();
         if (BubbleBarController.isBubbleBarEnabled()
                 && !mDeviceProfile.isPhone
+                && !mDeviceProfile.isVerticalBarLayout()
                 && bubbleBarView != null
         ) {
             Optional<BubbleStashedHandleViewController> bubbleHandleController = Optional.empty();
@@ -859,6 +864,33 @@
         return makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED);
     }
 
+    private ActivityOptionsWrapper getActivityLaunchDesktopOptions(ItemInfo info) {
+        if (!DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue()) {
+            return null;
+        }
+        if (!areDesktopTasksVisible()) {
+            return null;
+        }
+        BubbleTextView.RunningAppState appState =
+                mControllers.taskbarRecentAppsController.getDesktopItemState(info);
+        AppLaunchType launchType = null;
+        switch (appState) {
+            case RUNNING:
+                return null;
+            case MINIMIZED:
+                launchType = AppLaunchType.UNMINIMIZE;
+                break;
+            case NOT_RUNNING:
+                launchType = AppLaunchType.LAUNCH;
+                break;
+        }
+        ActivityOptions options = ActivityOptions.makeRemoteTransition(
+                new RemoteTransition(
+                        new DesktopAppLaunchTransition(
+                                /* context= */ this, getMainExecutor(), launchType)));
+        return new ActivityOptionsWrapper(options, new RunnableList());
+    }
+
     /**
      * Sets a new data-source for this taskbar instance
      */
@@ -1219,10 +1251,10 @@
         mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
 
         if (tag instanceof GroupTask groupTask) {
-            handleGroupTaskLaunch(
-                    groupTask,
-                    /* remoteTransition= */ null,
-                    areDesktopTasksVisible());
+            RemoteTransition remoteTransition =
+                    (areDesktopTasksVisible() && canUnminimizeDesktopTask(groupTask.task1.key.id))
+                            ? createUnminimizeRemoteTransition() : null;
+            handleGroupTaskLaunch(groupTask, remoteTransition, areDesktopTasksVisible());
             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
         } else if (tag instanceof FolderInfo) {
             // Tapping an expandable folder icon on Taskbar
@@ -1240,9 +1272,11 @@
                 mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
             }
         } else if (tag instanceof TaskItemInfo info) {
+            RemoteTransition remoteTransition = canUnminimizeDesktopTask(info.getTaskId())
+                    ? createUnminimizeRemoteTransition() : null;
             UI_HELPER_EXECUTOR.execute(() ->
                     SystemUiProxy.INSTANCE.get(this).showDesktopApp(
-                            info.getTaskId(), /* remoteTransition= */ null));
+                            info.getTaskId(), remoteTransition));
             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(
                     /* stash= */ true);
         } else if (tag instanceof WorkspaceItemInfo) {
@@ -1361,8 +1395,7 @@
             return;
         }
         if (onDesktop) {
-            boolean useRemoteTransition = task.task1.isMinimized
-                    && com.android.window.flags.Flags.enableDesktopAppLaunchAlttabTransitions();
+            boolean useRemoteTransition = canUnminimizeDesktopTask(task.task1.key.id);
             UI_HELPER_EXECUTOR.execute(() -> {
                 if (onStartCallback != null) {
                     onStartCallback.run();
@@ -1389,6 +1422,20 @@
         mControllers.uiController.launchSplitTasks(task, remoteTransition);
     }
 
+    /** Returns whether the given task is minimized and can be unminimized. */
+    public boolean canUnminimizeDesktopTask(int taskId) {
+        BubbleTextView.RunningAppState runningAppState =
+                mControllers.taskbarRecentAppsController.getRunningAppState(taskId);
+        return runningAppState == BubbleTextView.RunningAppState.MINIMIZED
+                && DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS.isTrue();
+    }
+
+    private RemoteTransition createUnminimizeRemoteTransition() {
+        return new RemoteTransition(
+                new DesktopAppLaunchTransition(
+                        this, getMainExecutor(), AppLaunchType.UNMINIMIZE));
+    }
+
     /**
      * Runs when the user taps a Taskbar icon in TaskbarActivityContext (Overview or inside an app),
      * and calls the appropriate method to animate and launch.
@@ -1487,25 +1534,31 @@
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         try {
             TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
-            if (info.user.equals(Process.myUserHandle())) {
-                // TODO(b/216683257): Use startActivityForResult for search results that require it.
-                if (taskInRecents != null) {
-                    // Re launch instance from recents
-                    ActivityOptionsWrapper opts = getActivityLaunchOptions(null, info);
-                    opts.options.setLaunchDisplayId(
-                            getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
-                    if (ActivityManagerWrapper.getInstance()
-                            .startActivityFromRecents(taskInRecents.key, opts.options)) {
-                        mControllers.uiController.getRecentsView()
-                                .addSideTaskLaunchCallback(opts.onEndCallback);
-                        return;
-                    }
-                }
-                startActivity(intent);
-            } else {
+            if (!info.user.equals(Process.myUserHandle())) {
+                // TODO b/376819104: support Desktop launch animations for apps in managed profiles
                 getSystemService(LauncherApps.class).startMainActivity(
                         intent.getComponent(), info.user, intent.getSourceBounds(), null);
+                return;
             }
+            // TODO(b/216683257): Use startActivityForResult for search results that require it.
+            if (taskInRecents != null) {
+                // Re launch instance from recents
+                ActivityOptionsWrapper opts = getActivityLaunchOptions(null, info);
+                opts.options.setLaunchDisplayId(
+                        getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
+                if (ActivityManagerWrapper.getInstance()
+                        .startActivityFromRecents(taskInRecents.key, opts.options)) {
+                    mControllers.uiController.getRecentsView()
+                            .addSideTaskLaunchCallback(opts.onEndCallback);
+                    return;
+                }
+            }
+            ActivityOptionsWrapper opts = null;
+            if (areDesktopTasksVisible()) {
+                opts = getActivityLaunchDesktopOptions(info);
+            }
+            Bundle optionsBundle = opts == null ? null : opts.options.toBundle();
+            startActivity(intent, optionsBundle);
         } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
                     .show();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index fa54f7c..0807ee9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -175,6 +175,9 @@
                                 + "onActivityDestroyed.");
                 mActivity.removeEventCallback(EVENT_DESTROYED, this);
             }
+            if (mActivity == mRecentsViewContainer) {
+                mRecentsViewContainer = null;
+            }
             mActivity = null;
             debugWhyTaskbarNotDestroyed("clearActivity");
             if (mTaskbarActivityContext != null) {
@@ -659,6 +662,7 @@
      * Called when the manager is no longer needed
      */
     public void destroy() {
+        mRecentsViewContainer = null;
         debugWhyTaskbarNotDestroyed("TaskbarManager#destroy()");
         removeActivityCallbacksAndListeners();
         mTaskbarBroadcastReceiver.unregisterReceiverSafely(mContext);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index bdefea6..c20617d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -42,7 +42,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.function.Predicate;
 
 /**
@@ -196,26 +195,21 @@
         final TaskbarRecentAppsController recentAppsController =
                 mControllers.taskbarRecentAppsController;
         hotseatItemInfos = recentAppsController.updateHotseatItemInfos(hotseatItemInfos);
-        Set<Integer> runningTaskIds = recentAppsController.getRunningTaskIds();
-        Set<Integer> minimizedTaskIds = recentAppsController.getMinimizedTaskIds();
 
         if (mDeferUpdatesForSUW) {
             ItemInfo[] finalHotseatItemInfos = hotseatItemInfos;
             mDeferredUpdates = () ->
                     commitHotseatItemUpdates(finalHotseatItemInfos,
-                            recentAppsController.getShownTasks(), runningTaskIds,
-                            minimizedTaskIds);
+                            recentAppsController.getShownTasks());
         } else {
-            commitHotseatItemUpdates(hotseatItemInfos,
-                    recentAppsController.getShownTasks(), runningTaskIds, minimizedTaskIds);
+            commitHotseatItemUpdates(hotseatItemInfos, recentAppsController.getShownTasks());
         }
     }
 
-    private void commitHotseatItemUpdates(ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks,
-            Set<Integer> runningTaskIds, Set<Integer> minimizedTaskIds) {
+    private void commitHotseatItemUpdates(
+            ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
         mContainer.updateHotseatItems(hotseatItemInfos, recentTasks);
-        mControllers.taskbarViewController.updateIconViewsRunningStates(
-                runningTaskIds, minimizedTaskIds);
+        mControllers.taskbarViewController.updateIconViewsRunningStates();
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 8947914..0f9ede9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.taskbar;
 
+import static android.view.MotionEvent.ACTION_UP;
+
 import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
 import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_A11Y_BUTTON_LONGPRESS;
@@ -31,12 +33,14 @@
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.window.flags.Flags.predictiveBackThreeButtonNav;
 
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
 import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.Flags;
 
@@ -141,10 +145,7 @@
         view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
         switch (buttonType) {
             case BUTTON_BACK:
-                logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
-                mContextualEduStatsManager.updateEduStats(/* isTrackpadGesture= */ false,
-                        GestureType.BACK);
-                executeBack();
+                executeBack(/* keyEvent */ null);
                 break;
             case BUTTON_HOME:
                 logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
@@ -182,7 +183,9 @@
 
         // Provide the same haptic feedback that the system offers for long press.
         // The haptic feedback from long pressing on the home button is handled by circle to search.
-        if (buttonType != BUTTON_HOME) {
+        // There are no haptics for long pressing the back button if predictive back is enabled
+        if (buttonType != BUTTON_HOME
+                && (!predictiveBackThreeButtonNav() || buttonType != BUTTON_BACK)) {
             view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         }
         switch (buttonType) {
@@ -320,8 +323,13 @@
         mCallbacks.onToggleOverview();
     }
 
-    private void executeBack() {
-        mSystemUiProxy.onBackPressed();
+    void executeBack(@Nullable KeyEvent keyEvent) {
+        if (keyEvent == null || (keyEvent.getAction() == ACTION_UP && !keyEvent.isCanceled())) {
+            logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
+            mContextualEduStatsManager.updateEduStats(/* isTrackpadGesture= */ false,
+                    GestureType.BACK);
+        }
+        mSystemUiProxy.onBackEvent(keyEvent);
     }
 
     private void onImeSwitcherPress() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
index fad5ca3..126e9bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
@@ -25,10 +25,13 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.R;
 import com.android.launcher3.Reorderable;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MultiTranslateDelegate;
+import com.android.launcher3.util.Themes;
 import com.android.systemui.shared.recents.model.Task;
 
 import java.util.ArrayList;
@@ -42,12 +45,22 @@
  * each other in counter clockwise manner (icons of tasks partially overlapping with each other).
  */
 public class TaskbarOverflowView extends FrameLayout implements Reorderable {
+    private static final int MAX_ITEMS_IN_PREVIEW = 4;
+
+    private boolean mIsRtlLayout;
     private final List<Task> mItems = new ArrayList<Task>();
     private int mIconSize;
     private int mPadding;
     private Paint mItemBackgroundPaint;
     private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
     private float mScaleForReorderBounce = 1f;
+    private int mItemBackgroundColor;
+    private int mLeaveBehindColor;
+    private float mItemPreviewStrokeWidth;
+
+    // Active means the overflow icon has been pressed, which replaces the app icons with the
+    // leave-behind circle and shows the KQS UI.
+    private boolean mIsActive = false;
 
     public TaskbarOverflowView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -78,22 +91,32 @@
     }
 
     private void init() {
+        mIsRtlLayout = Utilities.isRtl(getResources());
         mItemBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mItemBackgroundPaint.setColor(getContext().getColor(R.color.taskbar_background));
+        mItemBackgroundColor = getContext().getColor(R.color.taskbar_background);
+        mLeaveBehindColor = Themes.getAttrColor(getContext(), android.R.attr.textColorTertiary);
+        mItemPreviewStrokeWidth = getResources().getDimension(
+                R.dimen.taskbar_overflow_button_preview_stroke);
 
         setWillNotDraw(false);
     }
 
     @Override
-    protected void onDraw(Canvas canvas) {
+    protected void onDraw(@NonNull Canvas canvas) {
         super.onDraw(canvas);
 
-        boolean isRtlLayout = Utilities.isRtl(getResources());
-        float radius = mIconSize / 2.0f - mPadding;
-        float itemPreviewStrokeWidth =
-                getResources().getDimension(R.dimen.taskbar_overflow_button_preview_stroke);
+        if (mIsActive) {
+            drawLeaveBehindCircle(canvas);
+        } else {
+            drawAppIcons(canvas);
+        }
+    }
 
-        int itemsToShow = Math.min(mItems.size(), 4);
+    private void drawAppIcons(@NonNull Canvas canvas) {
+        mItemBackgroundPaint.setColor(mItemBackgroundColor);
+        float radius = mIconSize / 2f - mPadding;
+
+        int itemsToShow = Math.min(mItems.size(), MAX_ITEMS_IN_PREVIEW);
         for (int i = itemsToShow - 1; i >= 0; --i) {
             Drawable icon = mItems.get(mItems.size() - i - 1).icon;
             if (icon == null) {
@@ -103,11 +126,11 @@
             // Set the item icon size so two items fit within the overflow icon with stroke width
             // included, and overlap of 4 stroke width sizes between base item preview items.
             // 2 * strokeWidth + 2 * itemIconSize - 4 * strokeWidth = iconSize = 2 * radius.
-            float itemIconSize = radius + itemPreviewStrokeWidth;
-            // Offset item icon from center so item icon stroke edge mateches the parent icon edge.
-            float itemCenterOffset = radius - itemIconSize / 2 - itemPreviewStrokeWidth;
+            float itemIconSize = radius + mItemPreviewStrokeWidth;
+            // Offset item icon from center so item icon stroke edge matches the parent icon edge.
+            float itemCenterOffset = radius - itemIconSize / 2 - mItemPreviewStrokeWidth;
 
-            float itemCenterX = getItemXOffset(itemCenterOffset, isRtlLayout, i, itemsToShow);
+            float itemCenterX = getItemXOffset(itemCenterOffset, mIsRtlLayout, i, itemsToShow);
             float itemCenterY = getItemYOffset(itemCenterOffset, i, itemsToShow);
 
             Drawable iconCopy = icon.getConstantState().newDrawable().mutate();
@@ -119,12 +142,19 @@
                     mPadding + itemCenterX + radius - itemIconRadius,
                     mPadding + itemCenterY + radius - itemIconRadius);
             canvas.drawCircle(itemIconRadius, itemIconRadius,
-                    itemIconRadius + itemPreviewStrokeWidth, mItemBackgroundPaint);
+                    itemIconRadius + mItemPreviewStrokeWidth, mItemBackgroundPaint);
             iconCopy.draw(canvas);
             canvas.restore();
         }
     }
 
+    private void drawLeaveBehindCircle(@NonNull Canvas canvas) {
+        mItemBackgroundPaint.setColor(mLeaveBehindColor);
+
+        final var xyCenter = mIconSize / 2f;
+        canvas.drawCircle(xyCenter, xyCenter, mIconSize / 4f, mItemBackgroundPaint);
+    }
+
     /**
      * Clears the list of tasks tracked by the view.
      */
@@ -153,7 +183,7 @@
         for (int i = 0; i < mItems.size(); ++i) {
             if (mItems.get(i).key.id == task.key.id) {
                 mItems.set(i, task);
-                if (i >= mItems.size() - 4) {
+                if (i >= mItems.size() - MAX_ITEMS_IN_PREVIEW) {
                     invalidate();
                 }
                 break;
@@ -161,6 +191,24 @@
         }
     }
 
+    /**
+     * Returns the view's state (whether it shows a set of app icons or a leave-behind circle).
+     */
+    public boolean getIsActive() {
+        return mIsActive;
+    }
+
+    /**
+     * Updates the view's state to draw either a set of app icons or a leave-behind circle.
+     * @param isActive The next state of the view.
+     */
+    public void setIsActive(boolean isActive) {
+        if (mIsActive != isActive) {
+            mIsActive = isActive;
+            invalidate();
+        }
+    }
+
     @Override
     public MultiTranslateDelegate getTranslateDelegate() {
         return mTranslateDelegate;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index 3e8b615..3d57de4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -18,6 +18,7 @@
 import android.content.Context
 import android.window.DesktopModeFlags
 import androidx.annotation.VisibleForTesting
+import com.android.launcher3.BubbleTextView.RunningAppState
 import com.android.launcher3.Flags.enableRecentsInTaskbar
 import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.model.data.TaskItemInfo
@@ -72,6 +73,43 @@
     var shownTasks: List<GroupTask> = emptyList()
         private set
 
+    /**
+     * Returns the state of the most active Desktop task represented by the given [ItemInfo].
+     *
+     * If there are several tasks represented by the same [ItemInfo] we return the most active one,
+     * i.e. we return [DesktopAppState.RUNNING] over [DesktopAppState.MINIMIZED], and
+     * [DesktopAppState.MINIMIZED] over [DesktopAppState.NOT_RUNNING].
+     */
+    fun getDesktopItemState(itemInfo: ItemInfo?): RunningAppState {
+        val packageName = itemInfo?.getTargetPackage() ?: return RunningAppState.NOT_RUNNING
+        return getDesktopAppState(packageName, itemInfo.user.identifier)
+    }
+
+    private fun getDesktopAppState(packageName: String, userId: Int): RunningAppState {
+        val tasks = desktopTask?.tasks ?: return RunningAppState.NOT_RUNNING
+        val appTasks =
+            tasks.filter { task ->
+                packageName == task.key.packageName && task.key.userId == userId
+            }
+        if (appTasks.find { getRunningAppState(it.key.id) == RunningAppState.RUNNING } != null) {
+            return RunningAppState.RUNNING
+        }
+        if (appTasks.find { getRunningAppState(it.key.id) == RunningAppState.MINIMIZED } != null) {
+            return RunningAppState.MINIMIZED
+        }
+        return RunningAppState.NOT_RUNNING
+    }
+
+    /** Get the [RunningAppState] for the given task. */
+    fun getRunningAppState(taskId: Int): RunningAppState {
+        return when (taskId) {
+            in minimizedTaskIds -> RunningAppState.MINIMIZED
+            in runningTaskIds -> RunningAppState.RUNNING
+            else -> RunningAppState.NOT_RUNNING
+        }
+    }
+
+    @VisibleForTesting
     val runningTaskIds: Set<Int>
         /**
          * Returns the task IDs of apps that should be indicated as "running" to the user.
@@ -88,6 +126,7 @@
             return tasks.map { task -> task.key.id }.toSet()
         }
 
+    @VisibleForTesting
     val minimizedTaskIds: Set<Int>
         /**
          * Returns the task IDs for the tasks that should be indicated as "minimized" to the user.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index bf086b4..4a7e4f0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -155,7 +155,7 @@
     }
 
     private void onClick() {
-        SystemUiProxy.INSTANCE.get(mActivity).onBackPressed();
+        SystemUiProxy.INSTANCE.get(mActivity).onBackEvent(null);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index 5d769d2..834f92e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -148,8 +148,7 @@
         return new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                mControllers.keyboardQuickSwitchController.toggleQuickSwitchViewForTaskbar(
-                        mControllers.taskbarViewController.getTaskIdsForPinnedApps());
+                toggleKeyboardQuickSwitchView();
             }
         };
     }
@@ -159,13 +158,28 @@
         return new View.OnLongClickListener() {
             @Override
             public boolean onLongClick(View v) {
-                mControllers.keyboardQuickSwitchController.toggleQuickSwitchViewForTaskbar(
-                        mControllers.taskbarViewController.getTaskIdsForPinnedApps());
+                toggleKeyboardQuickSwitchView();
                 return true;
             }
         };
     }
 
+    private void toggleKeyboardQuickSwitchView() {
+        if (mTaskbarView.getTaskbarOverflowView() != null) {
+            mTaskbarView.getTaskbarOverflowView().setIsActive(
+                    !mTaskbarView.getTaskbarOverflowView().getIsActive());
+        }
+        mControllers.keyboardQuickSwitchController.toggleQuickSwitchViewForTaskbar(
+                mControllers.taskbarViewController.getTaskIdsForPinnedApps(),
+                this::onKeyboardQuickSwitchViewClosed);
+    }
+
+    private void onKeyboardQuickSwitchViewClosed() {
+        if (mTaskbarView.getTaskbarOverflowView() != null) {
+            mTaskbarView.getTaskbarOverflowView().setIsActive(false);
+        }
+    }
+
     private float getDividerCenterX() {
         View divider = mTaskbarView.getTaskbarDividerViewContainer();
         if (divider == null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 87e19be..494c472 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -623,12 +623,10 @@
      * Updates which icons are marked as running or minimized given the Sets of currently running
      * and minimized tasks.
      */
-    public void updateIconViewsRunningStates(Set<Integer> runningTaskIds,
-            Set<Integer> minimizedTaskIds) {
+    public void updateIconViewsRunningStates() {
         for (View iconView : getIconViews()) {
             if (iconView instanceof BubbleTextView btv) {
-                btv.updateRunningState(
-                        getRunningAppState(btv, runningTaskIds, minimizedTaskIds));
+                btv.updateRunningState(getRunningAppState(btv));
             }
         }
     }
@@ -651,26 +649,15 @@
         return pinnedAppsWithTasks;
     }
 
-    private BubbleTextView.RunningAppState getRunningAppState(
-            BubbleTextView btv,
-            Set<Integer> runningTaskIds,
-            Set<Integer> minimizedTaskIds) {
+    private BubbleTextView.RunningAppState getRunningAppState(BubbleTextView btv) {
         Object tag = btv.getTag();
         if (tag instanceof TaskItemInfo itemInfo) {
-            if (minimizedTaskIds.contains(itemInfo.getTaskId())) {
-                return BubbleTextView.RunningAppState.MINIMIZED;
-            }
-            if (runningTaskIds.contains(itemInfo.getTaskId())) {
-                return BubbleTextView.RunningAppState.RUNNING;
-            }
+            return mControllers.taskbarRecentAppsController.getRunningAppState(
+                    itemInfo.getTaskId());
         }
         if (tag instanceof GroupTask groupTask && !groupTask.hasMultipleTasks()) {
-            if (minimizedTaskIds.contains(groupTask.task1.key.id)) {
-                return BubbleTextView.RunningAppState.MINIMIZED;
-            }
-            if (runningTaskIds.contains(groupTask.task1.key.id)) {
-                return BubbleTextView.RunningAppState.RUNNING;
-            }
+            return mControllers.taskbarRecentAppsController.getRunningAppState(
+                    groupTask.task1.key.id);
         }
         return BubbleTextView.RunningAppState.NOT_RUNNING;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index f2aa2d2..e0814d3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -297,9 +297,7 @@
                 Log.e(TAG, "Could not instantiate BubbleBarBubble for " + bubbleInfos.get(i));
                 continue;
             }
-            addBubbleInternally(bubble,  /* showAppBadge = */
-                    mBubbleBarViewController.isExpanded() || i == 0,
-                    /* isExpanding = */ false,  /* suppressAnimation = */ true);
+            addBubbleInternally(bubble, /* isExpanding= */ false, /* suppressAnimation= */ true);
         }
     }
 
@@ -390,8 +388,7 @@
             for (int i = update.currentBubbles.size() - 1; i >= 0; i--) {
                 BubbleBarBubble bubble = update.currentBubbles.get(i);
                 if (bubble != null) {
-                    addBubbleInternally(bubble, /* showAppBadge = */ !isCollapsed || i == 0,
-                            isExpanding, suppressAnimation);
+                    addBubbleInternally(bubble, isExpanding, suppressAnimation);
                     if (isCollapsed) {
                         // If we're collapsed, the most recently added bubble will be selected.
                         bubbleToSelect = bubble;
@@ -420,8 +417,13 @@
             // Updates mean the dot state may have changed; any other changes were updated in
             // the populateBubble step.
             BubbleBarBubble bb = mBubbles.get(update.updatedBubble.getKey());
-            mBubbleBarViewController.animateBubbleNotification(
-                    bb, /* isExpanding= */ false, /* isUpdate= */ true);
+            if (suppressAnimation) {
+                // since we're not animating this update, we should update the dot visibility here.
+                bb.getView().updateDotVisibility(/* animate= */ false);
+            } else {
+                mBubbleBarViewController.animateBubbleNotification(
+                        bb, /* isExpanding= */ false, /* isUpdate= */ true);
+            }
         }
         if (update.bubbleKeysInOrder != null && !update.bubbleKeysInOrder.isEmpty()) {
             // Create the new list
@@ -564,10 +566,8 @@
         }
     }
 
-    private void addBubbleInternally(BubbleBarBubble bubble, boolean showAppBadge,
-            boolean isExpanding, boolean suppressAnimation) {
-        //TODO(b/360652359): remove setting scale to the app badge once issue is fixed
-        bubble.getView().setBadgeScale(showAppBadge ? 1 : 0);
+    private void addBubbleInternally(BubbleBarBubble bubble, boolean isExpanding,
+            boolean suppressAnimation) {
         mBubbles.put(bubble.getKey(), bubble);
         mBubbleBarViewController.addBubble(bubble, isExpanding, suppressAnimation);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 925e93d..350f56f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -793,6 +793,7 @@
         updateLayoutParams();
         updateBubbleAccessibilityStates();
         updateContentDescription();
+        updateDotsAndBadgesIfCollapsed();
     }
 
     /** Removes the given bubble from the bubble bar. */
@@ -858,7 +859,7 @@
         updateBubbleAccessibilityStates();
         updateContentDescription();
         mDismissedByDragBubbleView = null;
-        updateNotificationDotsIfCollapsed();
+        updateDotsAndBadgesIfCollapsed();
     }
 
     /**
@@ -888,17 +889,23 @@
         return childViews;
     }
 
-    private void updateNotificationDotsIfCollapsed() {
+    private void updateDotsAndBadgesIfCollapsed() {
         if (isExpanded()) {
             return;
         }
         for (int i = 0; i < getChildCount(); i++) {
             BubbleView bubbleView = (BubbleView) getChildAt(i);
-            // when we're collapsed, the first bubble should show the dot if it has it. the rest of
-            // the bubbles should hide their dots.
-            if (i == 0 && bubbleView.hasUnseenContent()) {
-                bubbleView.showDotIfNeeded(/* animate= */ true);
+            // when we're collapsed, the first bubble should show the badge and the dot if it has
+            // it. the rest of the bubbles should hide their badges and dots.
+            if (i == 0) {
+                bubbleView.showBadge();
+                if (bubbleView.hasUnseenContent()) {
+                    bubbleView.showDotIfNeeded(/* animate= */ true);
+                } else {
+                    bubbleView.hideDot();
+                }
             } else {
+                bubbleView.hideBadge();
                 bubbleView.hideDot();
             }
         }
@@ -1102,7 +1109,7 @@
             }
             updateBubblesLayoutProperties(mBubbleBarLocation);
             updateContentDescription();
-            updateNotificationDotsIfCollapsed();
+            updateDotsAndBadgesIfCollapsed();
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index e74357c..c74fa9b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -49,6 +49,8 @@
 public class BubbleView extends ConstraintLayout {
 
     public static final int DEFAULT_PATH_SIZE = 100;
+    /** Duration for animating the scale of the dot and badge. */
+    private static final int SCALE_ANIMATION_DURATION_MS = 200;
 
     private final ImageView mBubbleIcon;
     private final ImageView mAppIcon;
@@ -318,12 +320,37 @@
     }
 
     void setBadgeScale(float fraction) {
-        if (mAppIcon.getVisibility() == VISIBLE) {
+        if (hasBadge()) {
             mAppIcon.setScaleX(fraction);
             mAppIcon.setScaleY(fraction);
         }
     }
 
+    void showBadge() {
+        animateBadgeScale(1);
+    }
+
+    void hideBadge() {
+        animateBadgeScale(0);
+    }
+
+    private boolean hasBadge() {
+        return mAppIcon.getVisibility() == VISIBLE;
+    }
+
+    private void animateBadgeScale(float scale) {
+        if (!hasBadge()) {
+            return;
+        }
+        mAppIcon.clearAnimation();
+        mAppIcon.animate()
+                .setDuration(SCALE_ANIMATION_DURATION_MS)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .scaleX(scale)
+                .scaleY(scale)
+                .start();
+    }
+
     /** Suppresses or un-suppresses drawing the dot due to an update for this bubble. */
     public void suppressDotForBubbleUpdate(boolean suppress) {
         mDotSuppressedForBubbleUpdate = suppress;
@@ -411,7 +438,7 @@
 
         clearAnimation();
         animate()
-                .setDuration(200)
+                .setDuration(SCALE_ANIMATION_DURATION_MS)
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setUpdateListener((valueAnimator) -> {
                     float fraction = valueAnimator.getAnimatedFraction();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
index 831faa1..d9589bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -176,12 +176,18 @@
                 bubbleBarTranslationYForTaskbar
             }
 
-    /** Translation Y to align the bubble bar with the hotseat. */
+    /** Translation Y to align the bubble bar with the taskbar. */
     val bubbleBarTranslationYForTaskbar: Float
 
-    /** Return translation Y to align the bubble bar with the taskbar. */
+    /** Return translation Y to align the bubble bar with the hotseat. */
     val bubbleBarTranslationYForHotseat: Float
 
+    /**
+     * Show bubble bar is if it were in-app while launcher state is still on home. Set as a progress
+     * value between 0 and 1: 0 - use home layout, 1 - use in-app layout.
+     */
+    var inAppDisplayOverrideProgress: Float
+
     /** Dumps the state of BubbleStashController. */
     fun dump(pw: PrintWriter) {
         pw.println("Bubble stash controller state:")
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
index c117ad4..9a68335 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
@@ -22,6 +22,8 @@
 import android.graphics.Rect
 import android.view.MotionEvent
 import android.view.View
+import com.android.app.animation.Interpolators
+import com.android.launcher3.Utilities
 import com.android.launcher3.anim.AnimatedFloat
 import com.android.launcher3.taskbar.TaskbarInsetsController
 import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
@@ -97,6 +99,34 @@
             return -hotseatVerticalCenter + bubbleBarHeight / 2
         }
 
+    override val bubbleBarTranslationY: Float
+        get() =
+            if (inAppDisplayOverrideProgress > 0f && launcherState == BubbleLauncherState.HOME) {
+                Utilities.mapToRange(
+                    inAppDisplayOverrideProgress,
+                    /* fromMin = */ 0f,
+                    /* fromMax = */ 1f,
+                    bubbleBarTranslationYForHotseat,
+                    bubbleBarTranslationYForTaskbar,
+                    Interpolators.LINEAR,
+                )
+            } else {
+                super.bubbleBarTranslationY
+            }
+
+    override var inAppDisplayOverrideProgress: Float = 0f
+        set(value) {
+            if (field == value) return
+            field = value
+            if (launcherState == BubbleLauncherState.HOME) {
+                bubbleBarViewController.bubbleBarTranslationY.updateValue(bubbleBarTranslationY)
+                if (value == 0f || value == 1f) {
+                    // Update insets only when we reach the end values
+                    taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+                }
+            }
+        }
+
     override fun init(
         taskbarInsetsController: TaskbarInsetsController,
         bubbleBarViewController: BubbleBarViewController,
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index fbeecaa..71303f8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -126,6 +126,9 @@
     override val bubbleBarTranslationYForTaskbar: Float =
         -taskbarHotseatDimensionsProvider.getTaskbarBottomSpace().toFloat()
 
+    /** Not supported in transient mode */
+    override var inAppDisplayOverrideProgress: Float = 0f
+
     /** Check if we have handle view controller */
     override val hasHandleView: Boolean
         get() = bubbleStashedHandleViewController != null
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index 7eb34a5..79cb748 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -35,6 +35,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarControllers;
 import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -216,6 +217,13 @@
         @Override
         protected void handleClose(boolean animate) {
             if (!mIsOpen) return;
+            if (Flags.taskbarOverflow()) {
+                // Mark the view closed before attempting to remove it, so the drag layer does not
+                // schedule another call to close. Needed for taskbar overflow in case the KQS
+                // view shown for taskbar overflow needs to be reshown - delayed close call would
+                // would result in reshown KQS view getting hidden.
+                mIsOpen = false;
+            }
             mTaskbarContext.getDragLayer().removeView(this);
             Optional.ofNullable(mOverlayContext).ifPresent(c -> {
                 if (canCloseWindow()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index fe68ebc..34d9a68 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -190,6 +190,7 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskView;
+import com.android.systemui.animation.back.FlingOnBackAnimationCallback;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.unfold.RemoteUnfoldSharedComponent;
@@ -900,12 +901,12 @@
     protected void registerBackDispatcher() {
         getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
                 OnBackInvokedDispatcher.PRIORITY_DEFAULT,
-                new OnBackAnimationCallback() {
+                new FlingOnBackAnimationCallback() {
 
                     @Nullable OnBackAnimationCallback mActiveOnBackAnimationCallback;
 
                     @Override
-                    public void onBackStarted(@NonNull BackEvent backEvent) {
+                    public void onBackStartedCompat(@NonNull BackEvent backEvent) {
                         if (mActiveOnBackAnimationCallback != null) {
                             mActiveOnBackAnimationCallback.onBackCancelled();
                         }
@@ -915,7 +916,7 @@
 
                     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
                     @Override
-                    public void onBackInvoked() {
+                    public void onBackInvokedCompat() {
                         // Recreate mActiveOnBackAnimationCallback if necessary to avoid NPE
                         // because:
                         // 1. b/260636433: In 3-button-navigation mode, onBackStarted() is not
@@ -931,7 +932,7 @@
                     }
 
                     @Override
-                    public void onBackProgressed(@NonNull BackEvent backEvent) {
+                    public void onBackProgressedCompat(@NonNull BackEvent backEvent) {
                         if (!FeatureFlags.IS_STUDIO_BUILD
                                 && mActiveOnBackAnimationCallback == null) {
                             return;
@@ -940,7 +941,7 @@
                     }
 
                     @Override
-                    public void onBackCancelled() {
+                    public void onBackCancelledCompat() {
                         if (!FeatureFlags.IS_STUDIO_BUILD
                                 && mActiveOnBackAnimationCallback == null) {
                             return;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 030a7ac..d387794 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -118,7 +118,7 @@
 
     @Override
     public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
-        if (launcher.getDeviceProfile().isTablet) {
+        if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
             return getWorkspaceScaleAndTranslation(launcher);
         } else {
             ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
@@ -133,7 +133,7 @@
     @Override
     protected <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
             float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
-        if (context.getDeviceProfile().isTablet) {
+        if (context.getDeviceProfile().shouldShowAllAppsOnSheet()) {
             return context.getDeviceProfile().bottomSheetDepth;
         } else {
             // The scrim fades in at approximately 50% of the swipe gesture.
@@ -154,7 +154,7 @@
         return new PageAlphaProvider(DECELERATE_2) {
             @Override
             public float getPageAlpha(int pageIndex) {
-                return launcher.getDeviceProfile().isTablet
+                return launcher.getDeviceProfile().shouldShowAllAppsOnSheet()
                         ? superPageAlphaProvider.getPageAlpha(pageIndex)
                         : 0;
             }
@@ -164,8 +164,8 @@
     @Override
     public int getVisibleElements(Launcher launcher) {
         int elements = ALL_APPS_CONTENT | FLOATING_SEARCH_BAR;
-        // Only add HOTSEAT_ICONS for tablets in ALL_APPS state.
-        if (launcher.getDeviceProfile().isTablet) {
+        // When All Apps is presented on a bottom sheet, HOTSEAT_ICONS are visible.
+        if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
             elements |= HOTSEAT_ICONS;
         }
         return elements;
@@ -202,7 +202,7 @@
 
     @Override
     public int getWorkspaceScrimColor(Launcher launcher) {
-        return launcher.getDeviceProfile().isTablet
+        return launcher.getDeviceProfile().shouldShowAllAppsOnSheet()
                 ? launcher.getResources().getColor(R.color.widgets_picker_scrim)
                 : Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
     }
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 054181f..6c4c74c 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -43,6 +43,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.IRemoteAnimationRunner;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -210,10 +211,10 @@
     }
 
     @Override
-    public void onBackPressed() {
+    public void onBackEvent(KeyEvent backEvent) {
         if (mSystemUiProxy != null) {
             try {
-                mSystemUiProxy.onBackPressed();
+                mSystemUiProxy.onBackEvent(backEvent);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call onBackPressed", e);
             }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 5b085d2..fcc5121 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -74,6 +74,7 @@
 import android.view.View;
 
 import androidx.annotation.BinderThread;
+import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -550,11 +551,15 @@
                 @Override
                 public void onInputDeviceAdded(int deviceId) {
                     if (isTrackpadDevice(deviceId)) {
-                        boolean wasEmpty = mTrackpadsConnected.isEmpty();
-                        mTrackpadsConnected.add(deviceId);
-                        if (wasEmpty) {
-                            update();
-                        }
+                        // This updates internal TIS state so it needs to also run on the main
+                        // thread.
+                        MAIN_EXECUTOR.execute(() -> {
+                            boolean wasEmpty = mTrackpadsConnected.isEmpty();
+                            mTrackpadsConnected.add(deviceId);
+                            if (wasEmpty) {
+                                update();
+                            }
+                        });
                     }
                 }
 
@@ -564,12 +569,17 @@
 
                 @Override
                 public void onInputDeviceRemoved(int deviceId) {
-                    mTrackpadsConnected.remove(deviceId);
-                    if (mTrackpadsConnected.isEmpty()) {
-                        update();
-                    }
+                    // This updates internal TIS state so it needs to also run on the main
+                    // thread.
+                    MAIN_EXECUTOR.execute(() -> {
+                        mTrackpadsConnected.remove(deviceId);
+                        if (mTrackpadsConnected.isEmpty()) {
+                            update();
+                        }
+                    });
                 }
 
+                @MainThread
                 private void update() {
                     if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) {
                         // Don't destroy and reinitialize input monitor due to trackpad
@@ -580,6 +590,7 @@
                 }
 
                 private boolean isTrackpadDevice(int deviceId) {
+                    // This is a blocking binder call that should run on a bg thread.
                     InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
                     if (inputDevice == null) {
                         return false;
diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
index db02f55..f719bed 100644
--- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
+++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
@@ -217,6 +217,11 @@
         animation.addListener(
             AnimatorListeners.forEndCallback(
                 Runnable {
+                    // The workspace might stay at a transparent state when the animation is
+                    // cancelled, and the alpha will not be recovered (this doesn't apply to scales
+                    // somehow). Resetting the alpha for the workspace here.
+                    workspace.alpha = 1.0F
+
                     workspace.setLayerType(View.LAYER_TYPE_NONE, null)
                     hotseat.setLayerType(View.LAYER_TYPE_NONE, null)
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 9a8041b..839c42e 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -3607,7 +3607,8 @@
      * @param dismissingForSplitSelection task dismiss animation is used for entering split
      *                                    selection state from app icon
      */
-    public void createTaskDismissAnimation(PendingAnimation anim, TaskView dismissedTaskView,
+    public void createTaskDismissAnimation(PendingAnimation anim,
+            @Nullable TaskView dismissedTaskView,
             boolean animateTaskView, boolean shouldRemoveTask, long duration,
             boolean dismissingForSplitSelection) {
         if (mPendingAnimation != null) {
@@ -3622,7 +3623,8 @@
         boolean showAsGrid = showAsGrid();
         int taskCount = getTaskViewCount();
         int dismissedIndex = indexOfChild(dismissedTaskView);
-        int dismissedTaskViewId = dismissedTaskView.getTaskViewId();
+        int dismissedTaskViewId =
+                dismissedTaskView != null ? dismissedTaskView.getTaskViewId() : INVALID_TASK_ID;
 
         // Grid specific properties.
         boolean isFocusedTaskDismissed = false;
@@ -3637,10 +3639,18 @@
         int scrollDiffPerPage = 0;
         // Non-grid specific properties.
         boolean needsCurveUpdates = false;
+        boolean areAllDesktopTasksDismissed = false;
 
         if (showAsGrid) {
-            dismissedTaskWidth = dismissedTaskView.getLayoutParams().width + mPageSpacing;
-            isFocusedTaskDismissed = dismissedTaskViewId == mFocusedTaskViewId;
+            if (dismissedTaskView != null) {
+                dismissedTaskWidth = dismissedTaskView.getLayoutParams().width + mPageSpacing;
+            }
+            isFocusedTaskDismissed = dismissedTaskViewId != INVALID_TASK_ID
+                    && dismissedTaskViewId == mFocusedTaskViewId;
+            if (dismissingForSplitSelection && getTaskViewAt(
+                    mCurrentPage) instanceof DesktopTaskView) {
+                areAllDesktopTasksDismissed = true;
+            }
             if (isFocusedTaskDismissed) {
                 if (isSplitSelectionActive()) {
                     isStagingFocusedTask = true;
@@ -3812,19 +3822,23 @@
         SplitAnimationTimings splitTimings =
                 AnimUtils.getDeviceOverviewToSplitTimings(mContainer.getDeviceProfile().isTablet);
 
-        int distanceFromDismissedTask = 0;
+        int distanceFromDismissedTask = 1;
+        int stagingTranslation = 0;
+        if (isStagingFocusedTask || areAllDesktopTasksDismissed) {
+            int nextSnappedPage = isStagingFocusedTask
+                    ? indexOfChild(mUtils.getFirstSmallTaskView(getTaskViews()))
+                    : mUtils.getDesktopTaskViewCount(getTaskViews());
+            stagingTranslation = getPagedOrientationHandler().getPrimaryScroll(this)
+                    - getScrollForPage(nextSnappedPage);
+        }
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
             if (child == dismissedTaskView) {
-                if (animateTaskView) {
-                    if (dismissingForSplitSelection) {
-                        createInitialSplitSelectAnimation(anim);
-                    } else {
-                        addDismissedTaskAnimations(dismissedTaskView, duration, anim);
-                    }
+                if (animateTaskView && !dismissingForSplitSelection) {
+                    addDismissedTaskAnimations(dismissedTaskView, duration, anim);
                 }
             } else if (!showAsGrid || (enableLargeDesktopWindowingTile()
-                    && dismissedTaskView.isLargeTile()
+                    && dismissedTaskView != null && dismissedTaskView.isLargeTile()
                     && nextFocusedTaskView == null && !dismissingForSplitSelection)) {
                 int offset = getOffsetToDismissedTask(scrollDiffPerPage, dismissedIndex, taskCount);
                 int scrollDiff = newScroll[i] - oldScroll[i] + offset;
@@ -3838,19 +3852,9 @@
                     needsCurveUpdates = true;
                 }
             } else if (child instanceof TaskView taskView) {
-                if (isFocusedTaskDismissed) {
-                    if (nextFocusedTaskView != null &&
-                            !isSameGridRow(taskView, nextFocusedTaskView)) {
-                        continue;
-                    }
-                } else if (i < dismissedIndex || !isSameGridRow(taskView, dismissedTaskView)) {
-                    continue;
-                }
-
                 // Animate task with index >= dismissed index and in the same row as the
                 // dismissed index or next focused index. Offset successive task dismissal
                 // durations for a staggered effect.
-                distanceFromDismissedTask++;
                 int staggerColumn = isStagingFocusedTask
                         ? (int) Math.ceil(distanceFromDismissedTask / 2f)
                         : distanceFromDismissedTask;
@@ -3877,16 +3881,15 @@
                         : dismissTranslationInterpolationEnd;
                 Interpolator dismissInterpolator = isStagingFocusedTask ? OVERSHOOT_0_75 : LINEAR;
 
+                float primaryTranslation = 0;
                 if (taskView == nextFocusedTaskView) {
                     // Enlarge the task to be focused next, and translate into focus position.
                     float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
                     anim.setFloat(taskView, TaskView.DISMISS_SCALE, scale,
                             clampToProgress(LINEAR, animationStartProgress,
                                     dismissTranslationInterpolationEnd));
-                    anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
-                            mIsRtl ? dismissedTaskWidth : -dismissedTaskWidth,
-                            clampToProgress(LINEAR, animationStartProgress,
-                                    dismissTranslationInterpolationEnd));
+                    primaryTranslation += mIsRtl ? dismissedTaskWidth : -dismissedTaskWidth;
+                    animationEndProgress = dismissTranslationInterpolationEnd;
                     float secondaryTranslation = -mTaskGridVerticalDiff;
                     if (!nextFocusedTaskFromTop) {
                         secondaryTranslation -= mTopBottomRowHeightDiff;
@@ -3896,25 +3899,27 @@
                                     dismissTranslationInterpolationEnd));
                     anim.add(taskView.getFocusTransitionScaleAndDimOutAnimator(),
                             clampToProgress(LINEAR, 0f, ANIMATION_DISMISS_PROGRESS_MIDPOINT));
-                } else {
-                    float primaryTranslation =
+                } else if ((isFocusedTaskDismissed && nextFocusedTaskView != null && isSameGridRow(
+                        taskView, nextFocusedTaskView))
+                        || (!isFocusedTaskDismissed && i >= dismissedIndex && isSameGridRow(
+                        taskView, dismissedTaskView))) {
+                    primaryTranslation +=
                             nextFocusedTaskView != null ? nextFocusedTaskWidth : dismissedTaskWidth;
-                    if (isStagingFocusedTask) {
-                        // Moves less if focused task is not in scroll position.
-                        int focusedTaskScroll = getScrollForPage(dismissedIndex);
-                        int primaryScroll = getPagedOrientationHandler().getPrimaryScroll(this);
-                        int focusedTaskScrollDiff = primaryScroll - focusedTaskScroll;
-                        primaryTranslation +=
-                                mIsRtl ? focusedTaskScrollDiff : -focusedTaskScrollDiff;
-                    }
+                }
+                primaryTranslation += mIsRtl ? stagingTranslation : -stagingTranslation;
 
+                if (primaryTranslation != 0) {
                     anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
                             mIsRtl ? primaryTranslation : -primaryTranslation,
                             clampToProgress(dismissInterpolator, animationStartProgress,
                                     animationEndProgress));
+                    distanceFromDismissedTask++;
                 }
             }
         }
+        if (dismissingForSplitSelection) {
+            createInitialSplitSelectAnimation(anim);
+        }
 
         if (needsCurveUpdates) {
             anim.addOnFrameCallback(this::updateCurveProperties);
@@ -3923,7 +3928,7 @@
         // Add a tiny bit of translation Z, so that it draws on top of other views. This is relevant
         // (e.g.) when we dismiss a task by sliding it upward: if there is a row of icons above, we
         // want the dragged task to stay above all other views.
-        if (animateTaskView) {
+        if (animateTaskView && dismissedTaskView != null) {
             dismissedTaskView.setTranslationZ(0.1f);
         }
 
@@ -3935,7 +3940,8 @@
         mPendingAnimation.addEndListener(new Consumer<>() {
             @Override
             public void accept(Boolean success) {
-                if (mEnableDrawingLiveTile && dismissedTaskView.isRunningTask() && success) {
+                if (mEnableDrawingLiveTile && dismissedTaskView != null
+                        && dismissedTaskView.isRunningTask() && success) {
                     finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
                             () -> onEnd(true));
                 } else {
@@ -3951,7 +3957,7 @@
 
                 if (success) {
                     mAnyTaskHasBeenDismissed = true;
-                    if (shouldRemoveTask) {
+                    if (shouldRemoveTask && dismissedTaskView != null) {
                         if (dismissedTaskView.isRunningTask()) {
                             finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
                                     () -> removeTaskInternal(dismissedTaskView));
@@ -5080,9 +5086,10 @@
         if (enableLargeDesktopWindowingTile()) {
             for (TaskView taskView : getTaskViews()) {
                 if (taskView instanceof DesktopTaskView) {
+                    // Correcting the animation for split mode since we hide DW in split.
                     builder.addFloat(taskView.getSplitAlphaProperty(),
                             MULTI_PROPERTY_VALUE, 1f, 0f,
-                            deskTopFadeInterPolator);
+                            clampToProgress(deskTopFadeInterPolator, 0f, 0.1f));
                 }
             }
         }
@@ -5140,7 +5147,15 @@
                     true /* dismissingForSplitSelection*/);
         } else {
             // Splitting from Home
-            createInitialSplitSelectAnimation(builder);
+            TaskView currentPageTaskView = getTaskViewAt(mCurrentPage);
+            // When current page is a Desktop task it needs special handling to
+            // display correct animation in split mode
+            if (currentPageTaskView instanceof DesktopTaskView) {
+                createTaskDismissAnimation(builder, null, true, false, duration,
+                        true /* dismissingForSplitSelection*/);
+            } else {
+                createInitialSplitSelectAnimation(builder);
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 28ecf96..0760618 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -636,24 +636,29 @@
     override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
         super.onInitializeAccessibilityNodeInfo(info)
         with(info) {
-            addAction(
-                AccessibilityAction(
-                    R.id.action_close,
-                    context.getText(R.string.accessibility_close),
+            // Only make actions available if the app icon menu is visible to the user.
+            // When modalness is >0, the user is in select mode and the icon menu is hidden.
+            if (modalness == 0f) {
+                addAction(
+                    AccessibilityAction(
+                        R.id.action_close,
+                        context.getText(R.string.accessibility_close),
+                    )
                 )
-            )
 
-            taskContainers.forEach {
-                TraceHelper.allowIpcs("TV.a11yInfo") {
-                    TaskOverlayFactory.getEnabledShortcuts(this@TaskView, it).forEach { shortcut ->
-                        addAction(shortcut.createAccessibilityAction(context))
+                taskContainers.forEach {
+                    TraceHelper.allowIpcs("TV.a11yInfo") {
+                        TaskOverlayFactory.getEnabledShortcuts(this@TaskView, it).forEach { shortcut
+                            ->
+                            addAction(shortcut.createAccessibilityAction(context))
+                        }
                     }
                 }
-            }
 
-            // Add DWB accessibility action at the end of the list
-            taskContainers.forEach {
-                it.digitalWellBeingToast?.getDWBAccessibilityAction()?.let(::addAction)
+                // Add DWB accessibility action at the end of the list
+                taskContainers.forEach {
+                    it.digitalWellBeingToast?.getDWBAccessibilityAction()?.let(::addAction)
+                }
             }
 
             recentsView?.let {
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index 253d921..4b04dba 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -115,7 +115,7 @@
     @Test
     public void testPressBack() {
         mNavButtonController.onButtonClick(BUTTON_BACK, mockView);
-        verify(mockSystemUiProxy, times(1)).onBackPressed();
+        verify(mockSystemUiProxy, times(1)).onBackEvent(null);
     }
 
     @Test
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
index 3912051..436dfd3 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
@@ -17,6 +17,7 @@
 package com.android.launcher3.taskbar
 
 import android.animation.AnimatorTestRule
+import android.view.KeyEvent
 import android.view.View.GONE
 import android.view.View.VISIBLE
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
@@ -47,8 +48,8 @@
         TaskbarWindowSandboxContext.create { builder ->
             builder.bindSystemUiProxy(
                 object : SystemUiProxy(this) {
-                    override fun onBackPressed() {
-                        super.onBackPressed()
+                    override fun onBackEvent(backEvent: KeyEvent?) {
+                        super.onBackEvent(backEvent)
                         backPressed = true
                     }
                 }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
index 00b42bc..e3d41e7 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
@@ -38,6 +38,7 @@
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
 import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.never
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
@@ -228,6 +229,90 @@
             .isEqualTo(TASK_BAR_TRANSLATION_Y)
     }
 
+    @Test
+    fun inAppDisplayOverrideProgress_onHome_updatesTranslationFromHomeToInApp() {
+        whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+        persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
+
+        assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+            .isEqualTo(HOTSEAT_TRANSLATION_Y)
+
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+
+        val middleBetweenHotseatAndTaskbar = (HOTSEAT_TRANSLATION_Y + TASK_BAR_TRANSLATION_Y) / 2f
+        assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+            .isWithin(0.1f)
+            .of(middleBetweenHotseatAndTaskbar)
+
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 1f
+
+        assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+            .isEqualTo(TASK_BAR_TRANSLATION_Y)
+    }
+
+    @Test
+    fun inAppDisplayOverrideProgress_onHome_updatesInsetsWhenProgressReachesOne() {
+        whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+        persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
+        // Reset invocations to track only changes from in-app display override
+        clearInvocations(taskbarInsetsController)
+
+        // Insets are not updated for values between 0 and 1
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+        verify(taskbarInsetsController, never()).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+
+        // Update insets when progress reaches 1
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 1f
+        verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+    }
+
+    @Test
+    fun inAppDisplayOverrideProgress_onHome_updatesInsetsWhenProgressReachesZero() {
+        whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+        persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 1f
+        // Reset invocations to track only changes from in-app display override
+        clearInvocations(taskbarInsetsController)
+
+        // Insets are not updated for values between 0 and 1
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+        verify(taskbarInsetsController, never()).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+
+        // Update insets when progress reaches 0
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 0f
+        verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+    }
+
+    @Test
+    fun inAppDisplayProgressUpdate_inApp_noTranslationUpdate() {
+        whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+        persistentTaskBarStashController.launcherState = BubbleLauncherState.IN_APP
+
+        assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+            .isEqualTo(TASK_BAR_TRANSLATION_Y)
+
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+
+        assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+            .isEqualTo(TASK_BAR_TRANSLATION_Y)
+    }
+
+    @Test
+    fun inAppDisplayOverrideProgress_inApp_noInsetsUpdate() {
+        whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+        persistentTaskBarStashController.launcherState = BubbleLauncherState.IN_APP
+
+        // Reset invocations to track only changes from in-app display override
+        clearInvocations(taskbarInsetsController)
+
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 0.5f
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 1f
+        persistentTaskBarStashController.inAppDisplayOverrideProgress = 0f
+
+        // Never triggers an update to insets
+        verify(taskbarInsetsController, never()).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+    }
+
     private fun advanceTimeBy(advanceMs: Long) {
         // Advance animator for on-device tests
         getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(advanceMs) }
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index 7b57c81..c53c177 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -33,6 +33,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
 
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetId;
@@ -42,6 +43,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherApps;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.text.TextUtils;
 
@@ -62,9 +65,13 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 @SmallTest
@@ -72,6 +79,9 @@
 public final class WidgetsPredicationUpdateTaskTest {
 
     @Rule
+    public final MockitoRule mocks = MockitoJUnit.rule();
+
+    @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private AppWidgetProviderInfo mApp1Provider1;
@@ -145,6 +155,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER) // Flag off
     public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() {
         // Run on model executor so that no other task runs in the middle.
         runOnExecutorSync(MODEL_EXECUTOR, () -> {
@@ -184,6 +195,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER) // Flag off
     public void widgetsRecommendationRan_shouldReturnEmptyWidgetsWhenEmpty() {
         runOnExecutorSync(MODEL_EXECUTOR, () -> {
 
@@ -213,6 +225,50 @@
         });
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER)
+    public void widgetsRecommendationRan_keepsWidgetsNotOnWorkspace_addsWidgetsFromEligibleApps() {
+        runOnExecutorSync(MODEL_EXECUTOR, () -> {
+            WidgetsFilterDataProvider spiedFilterProvider = spy(
+                    mModelHelper.getModel().getWidgetsFilterDataProvider());
+            doAnswer(i -> new Predicate<WidgetItem>() {
+                @Override
+                public boolean test(WidgetItem widgetItem) {
+                    // app5's widget is already on workspace, but, app2 is not.
+                    // And app4's second widget is also not on workspace.
+                    return Set.of("app5", "app2", "app4").contains(
+                            widgetItem.componentName.getPackageName());
+                }
+            }).when(spiedFilterProvider).getPredictedWidgetsFilter();
+            mModelHelper.getBgDataModel().widgetsModel.updateWidgetFilters(spiedFilterProvider);
+            // App5's widget that's already on workspace.
+            AppTarget widget1 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
+                    mUserHandle);
+            // App4's widget eligible and not on workspace.
+            AppTarget widget2 = new AppTarget(new AppTargetId("app4"), "app4", "provider2",
+                    mUserHandle);
+
+            mCallback.mRecommendedWidgets = null;
+            mModelHelper.getModel().enqueueModelUpdateTask(
+                    newWidgetsPredicationTask(List.of(widget1, widget2)));
+            runOnExecutorSync(MAIN_EXECUTOR, () -> {
+            });
+
+            List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+                    .stream()
+                    .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+                    .collect(Collectors.toList());
+            assertThat(recommendedWidgets).hasSize(2);
+            List<ComponentName> componentNames = recommendedWidgets.stream().map(
+                    w -> w.componentName).toList();
+            assertThat(componentNames).containsExactly(
+                    // Locally added, not on workspace, eligible app per filter
+                    mApp2Provider1.provider,
+                    // From prediction service, not on workspace, eligible app per filter
+                    mApp4Provider2.provider);
+        });
+    }
+
     private void assertWidgetInfo(
             LauncherAppWidgetProviderInfo actual, AppWidgetProviderInfo expected) {
         assertThat(actual.provider).isEqualTo(expected.provider);
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index b67bc5a..066ddc0 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -26,11 +26,13 @@
 import android.platform.test.rule.TestWatcher
 import android.testing.AndroidTestingRunner
 import com.android.internal.R
+import com.android.launcher3.BubbleTextView.RunningAppState
 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
 import com.android.launcher3.model.data.AppInfo
 import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.model.data.TaskItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.quickstep.RecentsModel
 import com.android.quickstep.RecentsModel.RecentTasksChangedListener
 import com.android.quickstep.TaskIconCache
@@ -77,7 +79,9 @@
     private var taskListChangeId: Int = 1
 
     private lateinit var recentAppsController: TaskbarRecentAppsController
-    private lateinit var userHandle: UserHandle
+    private lateinit var myUserHandle: UserHandle
+    private val USER_HANDLE_1 = UserHandle.of(1)
+    private val USER_HANDLE_2 = UserHandle.of(2)
 
     private var canShowRunningAndRecentAppsAtInit = true
     private var recentTasksChangedListener: RecentTasksChangedListener? = null
@@ -85,7 +89,7 @@
     @Before
     fun setUp() {
         super.setup()
-        userHandle = Process.myUserHandle()
+        myUserHandle = Process.myUserHandle()
 
         // Set desktop mode supported
         whenever(mockContext.getResources()).thenReturn(mockResources)
@@ -148,6 +152,115 @@
     }
 
     @Test
+    fun getDesktopItemState_nullItemInfo_returnsNotRunning() {
+        setInDesktopMode(true)
+        assertThat(recentAppsController.getDesktopItemState(/* itemInfo= */ null))
+            .isEqualTo(RunningAppState.NOT_RUNNING)
+    }
+
+    @Test
+    fun getDesktopItemState_noItemPackage_returnsNotRunning() {
+        setInDesktopMode(true)
+        assertThat(recentAppsController.getDesktopItemState(ItemInfo()))
+            .isEqualTo(RunningAppState.NOT_RUNNING)
+    }
+
+    @Test
+    fun getDesktopItemState_noMatchingTasks_returnsNotRunning() {
+        setInDesktopMode(true)
+        val itemInfo = createItemInfo("package")
+        assertThat(recentAppsController.getDesktopItemState(itemInfo))
+            .isEqualTo(RunningAppState.NOT_RUNNING)
+    }
+
+    @Test
+    fun getDesktopItemState_matchingVisibleTask_returnsVisible() {
+        setInDesktopMode(true)
+        val visibleTask = createTask(id = 1, "visiblePackage", isVisible = true)
+        updateRecentTasks(runningTasks = listOf(visibleTask), recentTaskPackages = emptyList())
+        val itemInfo = createItemInfo("visiblePackage")
+
+        assertThat(recentAppsController.getDesktopItemState(itemInfo))
+            .isEqualTo(RunningAppState.RUNNING)
+    }
+
+    @Test
+    fun getDesktopItemState_matchingMinimizedTask_returnsMinimized() {
+        setInDesktopMode(true)
+        val minimizedTask = createTask(id = 1, "minimizedPackage", isVisible = false)
+        updateRecentTasks(runningTasks = listOf(minimizedTask), recentTaskPackages = emptyList())
+        val itemInfo = createItemInfo("minimizedPackage")
+
+        assertThat(recentAppsController.getDesktopItemState(itemInfo))
+            .isEqualTo(RunningAppState.MINIMIZED)
+    }
+
+    @Test
+    fun getDesktopItemState_matchingMinimizedAndRunningTask_returnsVisible() {
+        setInDesktopMode(true)
+        updateRecentTasks(
+            runningTasks =
+                listOf(
+                    createTask(id = 1, "package", isVisible = false),
+                    createTask(id = 2, "package", isVisible = true),
+                ),
+            recentTaskPackages = emptyList(),
+        )
+        val itemInfo = createItemInfo("package")
+
+        assertThat(recentAppsController.getDesktopItemState(itemInfo))
+            .isEqualTo(RunningAppState.RUNNING)
+    }
+
+    @Test
+    fun getDesktopItemState_noMatchingUserId_returnsNotRunning() {
+        setInDesktopMode(true)
+        updateRecentTasks(
+            runningTasks =
+                listOf(
+                    createTask(id = 1, "package", isVisible = false, USER_HANDLE_1),
+                    createTask(id = 2, "package", isVisible = true, USER_HANDLE_1),
+                ),
+            recentTaskPackages = emptyList(),
+        )
+        val itemInfo = createItemInfo("package", USER_HANDLE_2)
+
+        assertThat(recentAppsController.getDesktopItemState(itemInfo))
+            .isEqualTo(RunningAppState.NOT_RUNNING)
+    }
+
+    @Test
+    fun getRunningAppState_taskNotRunningOrMinimized_returnsNotRunning() {
+        setInDesktopMode(true)
+        updateRecentTasks(runningTasks = emptyList(), recentTaskPackages = emptyList())
+
+        assertThat(recentAppsController.getRunningAppState(taskId = 1))
+            .isEqualTo(RunningAppState.NOT_RUNNING)
+    }
+
+    @Test
+    fun getRunningAppState_taskNotVisible_returnsMinimized() {
+        setInDesktopMode(true)
+        val task1 = createTask(id = 1, packageName = RUNNING_APP_PACKAGE_1, isVisible = false)
+        val task2 = createTask(id = 2, packageName = RUNNING_APP_PACKAGE_1, isVisible = true)
+        updateRecentTasks(runningTasks = listOf(task1, task2), recentTaskPackages = emptyList())
+
+        assertThat(recentAppsController.getRunningAppState(taskId = 1))
+            .isEqualTo(RunningAppState.MINIMIZED)
+    }
+
+    @Test
+    fun getRunningAppState_taskVisible_returnsRunning() {
+        setInDesktopMode(true)
+        val task1 = createTask(id = 1, packageName = RUNNING_APP_PACKAGE_1, isVisible = false)
+        val task2 = createTask(id = 2, packageName = RUNNING_APP_PACKAGE_1, isVisible = true)
+        updateRecentTasks(runningTasks = listOf(task1, task2), recentTaskPackages = emptyList())
+
+        assertThat(recentAppsController.getRunningAppState(taskId = 2))
+            .isEqualTo(RunningAppState.RUNNING)
+    }
+
+    @Test
     fun updateHotseatItemInfos_cantShowRunning_inDesktopMode_returnsAllHotseatItems() {
         recentAppsController.canShowRunningApps = false
         setInDesktopMode(true)
@@ -782,7 +895,13 @@
     private fun createTestAppInfo(
         packageName: String = "testPackageName",
         className: String = "testClassName",
-    ) = AppInfo(ComponentName(packageName, className), className /* title */, userHandle, Intent())
+    ) =
+        AppInfo(
+            ComponentName(packageName, className),
+            className /* title */,
+            myUserHandle,
+            Intent(),
+        )
 
     private fun createRecentTasksFromPackageNames(packageNames: List<String>): List<GroupTask> {
         return packageNames.map { packageName ->
@@ -801,14 +920,19 @@
         }
     }
 
-    private fun createTask(id: Int, packageName: String, isVisible: Boolean = true): Task {
+    private fun createTask(
+        id: Int,
+        packageName: String,
+        isVisible: Boolean = true,
+        localUserHandle: UserHandle? = null,
+    ): Task {
         return Task(
                 Task.TaskKey(
                     id,
                     WINDOWING_MODE_FREEFORM,
                     Intent().apply { `package` = packageName },
                     ComponentName(packageName, "TestActivity"),
-                    userHandle.identifier,
+                    localUserHandle?.identifier ?: myUserHandle.identifier,
                     0,
                 )
             )
@@ -820,6 +944,16 @@
             .thenReturn(inDesktopMode)
     }
 
+    private fun createItemInfo(
+        packageName: String,
+        userHandle: UserHandle = myUserHandle,
+    ): ItemInfo {
+        val appInfo = AppInfo()
+        appInfo.intent = Intent().setComponent(ComponentName(packageName, "className"))
+        appInfo.user = userHandle
+        return WorkspaceItemInfo(appInfo)
+    }
+
     private val GroupTask.packageNames: List<String>
         get() = tasks.map { task -> task.key.packageName }
 
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index 44c23ba..6a7b6f8 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.tapl.LaunchedAppState;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.TestUtil;
 import com.android.quickstep.views.RecentsView;
 
 import org.junit.rules.RuleChain;
@@ -56,7 +57,7 @@
     protected void assertTestActivityIsRunning(int activityNumber, String message) {
         assertTrue(message, mDevice.wait(
                 Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity" + activityNumber)),
-                DEFAULT_UI_TIMEOUT));
+                TestUtil.DEFAULT_UI_TIMEOUT));
     }
 
     protected LaunchedAppState getAndAssertLaunchedApp() {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
similarity index 73%
rename from quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
rename to quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index e981570..5b46dc8 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -15,7 +15,9 @@
  */
 package com.android.quickstep;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.util.TestUtil.resolveSystemAppInfo;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -25,10 +27,13 @@
 import android.app.usage.UsageStatsManager;
 import android.content.Intent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.BaseLauncherActivityTest;
 import com.android.quickstep.views.DigitalWellBeingToast;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskContainer;
@@ -41,30 +46,31 @@
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-public class TaplDigitalWellBeingToastTest extends AbstractQuickStepTest {
-    private static final String CALCULATOR_PACKAGE =
-            resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+public class DigitalWellBeingToastTest extends BaseLauncherActivityTest<QuickstepLauncher> {
+
+    public final String calculatorPackage =
+            resolveSystemAppInfo(Intent.CATEGORY_APP_CALCULATOR).packageName;
 
     @Test
-    public void testToast() throws Exception {
-        startAppFast(CALCULATOR_PACKAGE);
+    public void testToast() {
+        startAppFast(calculatorPackage);
 
         final UsageStatsManager usageStatsManager =
-                mTargetContext.getSystemService(UsageStatsManager.class);
+                targetContext().getSystemService(UsageStatsManager.class);
         final int observerId = 0;
 
         try {
-            final String[] packages = new String[]{CALCULATOR_PACKAGE};
+            final String[] packages = new String[]{calculatorPackage};
 
             // Set time limit for app.
             runWithShellPermission(() ->
                     usageStatsManager.registerAppUsageLimitObserver(observerId, packages,
                             Duration.ofSeconds(600), Duration.ofSeconds(300),
-                            PendingIntent.getActivity(mTargetContext, -1, new Intent()
-                                            .setPackage(mTargetContext.getPackageName()),
+                            PendingIntent.getActivity(targetContext(), -1, new Intent()
+                                            .setPackage(targetContext().getPackageName()),
                                     PendingIntent.FLAG_MUTABLE)));
 
-            mLauncher.goHome();
+            loadLauncherSync();
             final DigitalWellBeingToast toast = getToast();
 
             waitForLauncherCondition("Toast is not visible", launcher -> toast.getHasLimit());
@@ -74,7 +80,7 @@
             runWithShellPermission(
                     () -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
 
-            mLauncher.goHome();
+            goToState(LauncherState.NORMAL);
             assertFalse("Toast is visible", getToast().getHasLimit());
         } finally {
             runWithShellPermission(
@@ -83,12 +89,12 @@
     }
 
     private DigitalWellBeingToast getToast() {
-        mLauncher.getWorkspace().switchToOverview();
+        goToState(LauncherState.OVERVIEW);
         final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
 
         return getFromLauncher(launcher -> {
             TaskContainer taskContainer = task.getTaskContainers().get(0);
-            assertTrue("Latest task is not Calculator", CALCULATOR_PACKAGE.equals(
+            assertTrue("Latest task is not Calculator", calculatorPackage.equals(
                     taskContainer.getTask().getTopComponent().getPackageName()));
             return taskContainer.getDigitalWellBeingToast();
         });
@@ -105,6 +111,5 @@
         } finally {
             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
         }
-
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 1f11c14..aa105f9 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -22,9 +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.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;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.resolveSystemApp;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.startAppFast;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.startTestActivity;
@@ -56,6 +54,7 @@
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.testcomponent.TestCommandReceiver;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule;
 import com.android.launcher3.util.rule.FailureWatcher;
@@ -214,7 +213,7 @@
             }
             result[0] = f.apply(activity);
             return true;
-        }).get(), DEFAULT_UI_TIMEOUT, mLauncher);
+        }).get(), mLauncher);
         return (T) result[0];
     }
 
@@ -244,7 +243,7 @@
 
         Wait.atMost("Recents activity didn't stop",
                 () -> getFromRecents(recents -> !recents.isStarted()),
-                DEFAULT_UI_TIMEOUT, mLauncher);
+                mLauncher);
     }
 
     @Test
@@ -254,7 +253,8 @@
         startTestActivity(2);
         waitForRecentsActivityStop();
         Wait.atMost("Expected three apps in the task list",
-                () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+                () -> mLauncher.getRecentTasks().size() >= 3,
+                mLauncher);
 
         checkTestLauncher();
         BaseOverview overview = mLauncher.getLaunchedAppState().switchToOverview();
@@ -282,7 +282,7 @@
         assertNotNull("OverviewTask.open returned null", task.open());
         assertTrue("Test activity didn't open from Overview", TestHelpers.wait(Until.hasObject(
                 By.pkg(getAppPackageName()).text("TestActivity2")),
-                DEFAULT_UI_TIMEOUT));
+                TestUtil.DEFAULT_UI_TIMEOUT));
 
 
         // Test dismissing a task.
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 4459ed6..77f4c05 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -57,8 +57,6 @@
 
     static final String TAG = "QuickStepOnOffRule";
 
-    public static final int WAIT_TIME_MS = 10000;
-
     public enum Mode {
         THREE_BUTTON, ZERO_BUTTON, ALL
     }
@@ -179,12 +177,13 @@
         }
 
         Wait.atMost("Couldn't switch to " + overlayPackage,
-                () -> launcher.getNavigationModel() == expectedMode, WAIT_TIME_MS, launcher);
+                () -> launcher.getNavigationModel() == expectedMode,
+                launcher);
 
         Wait.atMost(() -> "Switching nav mode: "
                         + launcher.getNavigationModeMismatchError(false),
                 () -> launcher.getNavigationModeMismatchError(false) == null,
-                WAIT_TIME_MS, launcher);
+                launcher);
         AbstractLauncherUiTest.checkDetectedLeaks(launcher, false);
         return true;
     }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
index 120a89b..f58c84e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
@@ -26,6 +26,7 @@
 import com.android.launcher3.ui.AbstractLauncherUiTest
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape
 import com.android.launcher3.uioverrides.QuickstepLauncher
+import com.android.launcher3.util.TestUtil
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Before
 import org.junit.Test
@@ -172,7 +173,7 @@
             .that(
                 mDevice.wait(
                     Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity$index")),
-                    DEFAULT_UI_TIMEOUT,
+                    TestUtil.DEFAULT_UI_TIMEOUT,
                 )
             )
             .isTrue()
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 5ff2af7..f1fe2d2 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -47,6 +47,7 @@
 import com.android.launcher3.tapl.SelectModeButtons;
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.TestStabilityRule;
@@ -145,7 +146,7 @@
         assertNotNull("OverviewTask.open returned null", task.open());
         assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
                         By.pkg(getAppPackageName()).text("TestActivity2")),
-                DEFAULT_UI_TIMEOUT));
+                TestUtil.DEFAULT_UI_TIMEOUT));
         executeOnLauncher(launcher -> assertTrue(
                 "Launcher activity is the top activity; expecting another activity to be the top "
                         + "one",
@@ -448,7 +449,7 @@
                 mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text(
                                 mLauncher.isGridOnlyOverviewEnabled() ? "TestActivity12"
                                         : "TestActivity13")),
-                        DEFAULT_UI_TIMEOUT));
+                        TestUtil.DEFAULT_UI_TIMEOUT));
 
         // Scroll the task offscreen as it is now first
         overview = mLauncher.goHome().switchToOverview();
@@ -563,7 +564,7 @@
             mLauncher.getDevice().setOrientationLeft();
             startTestActivity(7);
             Wait.atMost("Device should not be in natural orientation",
-                    () -> !mDevice.isNaturalOrientation(), DEFAULT_UI_TIMEOUT, mLauncher);
+                    () -> !mDevice.isNaturalOrientation(), mLauncher);
             mLauncher.goHome();
         } finally {
             mLauncher.setExpectedRotationCheckEnabled(true);
diff --git a/res/drawable/bg_widgets_header_states_two_pane.xml b/res/drawable/bg_widgets_header_states_two_pane.xml
index 5f4b8c6..1ec41a9 100644
--- a/res/drawable/bg_widgets_header_states_two_pane.xml
+++ b/res/drawable/bg_widgets_header_states_two_pane.xml
@@ -14,18 +14,16 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item android:state_expanded="true">
-        <shape android:shape="rectangle">
-            <solid android:color="?attr/widgetPickerHeaderBackgroundColor" />
-            <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
-        </shape>
+    <item android:state_expanded="true" android:state_focused="false">
+        <ripple android:color="@color/accent_ripple_color">
+            <item android:drawable="@drawable/bg_widgets_header_two_pane_expanded_unfocused" />
+        </ripple>
     </item>
-
-    <item android:state_expanded="false">
-        <shape android:shape="rectangle">
-            <solid android:color="@android:color/transparent" />
-            <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
-        </shape>
+    <item android:state_expanded="false" android:state_focused="false">
+        <ripple android:color="@color/accent_ripple_color">
+            <item android:drawable="@drawable/bg_widgets_header_two_pane_unexpanded_unfocused" />
+        </ripple>
     </item>
+    <item android:drawable="@drawable/bg_widgets_header_two_pane_expanded_focused" android:state_expanded="true" android:state_focused="true" />
+    <item android:drawable="@drawable/bg_widgets_header_two_pane_unexpanded_focused" android:state_expanded="false" android:state_focused="true" />
 </selector>
diff --git a/res/drawable/bg_widgets_header_two_pane.xml b/res/drawable/bg_widgets_header_two_pane.xml
index ca3feef..e237002 100644
--- a/res/drawable/bg_widgets_header_two_pane.xml
+++ b/res/drawable/bg_widgets_header_two_pane.xml
@@ -14,13 +14,10 @@
      limitations under the License.
 -->
 <inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:insetTop="@dimen/widget_list_entry_spacing" >
-    <ripple
-        android:color="@color/accent_ripple_color"
-        android:paddingTop="@dimen/widget_list_header_view_vertical_padding"
-        android:paddingBottom="@dimen/widget_list_header_view_vertical_padding" >
-        <item android:id="@android:id/mask"
-            android:drawable="@drawable/bg_widgets_header_states_two_pane" />
+    android:insetTop="@dimen/widget_list_entry_spacing">
+    <layer-list
+        android:paddingBottom="@dimen/widget_list_header_view_vertical_padding"
+        android:paddingTop="@dimen/widget_list_header_view_vertical_padding">
         <item android:drawable="@drawable/bg_widgets_header_states_two_pane" />
-    </ripple>
-</inset>
+    </layer-list>
+</inset>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_header_two_pane_expanded_focused.xml b/res/drawable/bg_widgets_header_two_pane_expanded_focused.xml
new file mode 100644
index 0000000..0ee3d14
--- /dev/null
+++ b/res/drawable/bg_widgets_header_two_pane_expanded_focused.xml
@@ -0,0 +1,38 @@
+<?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.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Draw the focus ring -->
+    <item>
+        <shape>
+            <corners android:radius="@dimen/widget_focus_ring_corner_radius" />
+            <stroke
+                android:width="@dimen/widget_header_focus_ring_width"
+                android:color="?attr/widgetPickerTabBackgroundSelected" />
+        </shape>
+    </item>
+
+    <!-- Draw the background with padding to make it spaced within the focus ring. -->
+    <item
+        android:bottom="@dimen/widget_header_background_border"
+        android:end="@dimen/widget_header_background_border"
+        android:start="@dimen/widget_header_background_border"
+        android:top="@dimen/widget_header_background_border">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
+            <solid android:color="?attr/widgetPickerHeaderBackgroundColor" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_header_two_pane_expanded_unfocused.xml b/res/drawable/bg_widgets_header_two_pane_expanded_unfocused.xml
new file mode 100644
index 0000000..9028ebe
--- /dev/null
+++ b/res/drawable/bg_widgets_header_two_pane_expanded_unfocused.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:paddingBottom="@dimen/widget_list_header_view_vertical_padding"
+    android:paddingTop="@dimen/widget_list_header_view_vertical_padding">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
+            <solid android:color="?attr/widgetPickerHeaderBackgroundColor" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_header_two_pane_unexpanded_focused.xml b/res/drawable/bg_widgets_header_two_pane_unexpanded_focused.xml
new file mode 100644
index 0000000..12dc907
--- /dev/null
+++ b/res/drawable/bg_widgets_header_two_pane_unexpanded_focused.xml
@@ -0,0 +1,27 @@
+<?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.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Draw the focus ring and a transparent background -->
+    <item>
+        <shape>
+            <corners android:radius="@dimen/widget_focus_ring_corner_radius" />
+            <solid android:color="@android:color/transparent" />
+            <stroke
+                android:width="@dimen/widget_header_focus_ring_width"
+                android:color="?attr/widgetPickerTabBackgroundSelected" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_header_two_pane_unexpanded_unfocused.xml b/res/drawable/bg_widgets_header_two_pane_unexpanded_unfocused.xml
new file mode 100644
index 0000000..ba26f9f
--- /dev/null
+++ b/res/drawable/bg_widgets_header_two_pane_unexpanded_unfocused.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
+            <solid android:color="@android:color/transparent" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/ic_more_horiz_24.xml b/res/drawable/ic_more_horiz_24.xml
new file mode 100644
index 0000000..d46827c
--- /dev/null
+++ b/res/drawable/ic_more_horiz_24.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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:tint="#000000"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
+</vector>
diff --git a/res/drawable/widgets_list_expand_button_background.xml b/res/drawable/widgets_list_expand_button_background.xml
new file mode 100644
index 0000000..068b26d
--- /dev/null
+++ b/res/drawable/widgets_list_expand_button_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <ripple android:color="?android:attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle">
+                <corners android:radius="50dp" />
+                <solid android:color="?attr/widgetPickerExpandButtonBackgroundColor" />
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/res/layout/widgets_list_expand_button.xml b/res/layout/widgets_list_expand_button.xml
new file mode 100644
index 0000000..17c19ac
--- /dev/null
+++ b/res/layout/widgets_list_expand_button.xml
@@ -0,0 +1,33 @@
+<?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.
+  -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/Button.Rounded.Colored"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="@dimen/widgets_list_expand_button_top_margin"
+    android:background="@drawable/widgets_list_expand_button_background"
+    android:drawablePadding="@dimen/widgets_list_expand_button_drawable_padding"
+    android:drawableStart="@drawable/ic_more_horiz_24"
+    android:drawableTint="?attr/widgetPickerExpandButtonTextColor"
+    android:maxLines="1"
+    android:minHeight="48dp"
+    android:paddingEnd="@dimen/widgets_list_expand_button_end_padding"
+    android:paddingStart="@dimen/widgets_list_expand_button_start_padding"
+    android:paddingVertical="@dimen/widgets_list_expand_button_vertical_padding"
+    android:text="@string/widgets_list_expand_button_label"
+    android:contentDescription="@string/widgets_list_expand_button_content_description"
+    android:textColor="?attr/widgetPickerExpandButtonTextColor" />
\ No newline at end of file
diff --git a/res/layout/widgets_two_pane_sheet.xml b/res/layout/widgets_two_pane_sheet.xml
index 8235875..5dc1b47 100644
--- a/res/layout/widgets_two_pane_sheet.xml
+++ b/res/layout/widgets_two_pane_sheet.xml
@@ -70,7 +70,7 @@
                 android:layout_height="match_parent"
                 android:clipChildren="false"
                 android:clipToPadding="false"
-                android:paddingBottom="24dp"
+                android:paddingBottom="8dp"
                 android:layout_gravity="start"
                 android:layout_weight="0.33">
                 <TextView
@@ -84,14 +84,6 @@
                     android:layout_width="@dimen/fastscroll_width"
                     android:layout_height="match_parent"
                     android:layout_marginEnd="@dimen/fastscroll_end_margin" />
-
-                <com.android.launcher3.widget.picker.WidgetsRecyclerView
-                    android:id="@+id/search_widgets_list_view"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:clipToPadding="false"
-                    android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
-                    android:visibility="gone" />
             </FrameLayout>
 
             <FrameLayout
diff --git a/res/layout/widgets_two_pane_sheet_paged_view.xml b/res/layout/widgets_two_pane_sheet_paged_view.xml
index 33a50b0..0528d3b 100644
--- a/res/layout/widgets_two_pane_sheet_paged_view.xml
+++ b/res/layout/widgets_two_pane_sheet_paged_view.xml
@@ -143,5 +143,13 @@
                 android:clipToPadding="false" />
 
         </com.android.launcher3.widget.picker.WidgetPagedView>
+
+        <com.android.launcher3.widget.picker.WidgetsRecyclerView
+            android:id="@+id/search_widgets_list_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:clipToPadding="false"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
+            android:visibility="gone" />
     </LinearLayout>
 </merge>
diff --git a/res/layout/widgets_two_pane_sheet_recyclerview.xml b/res/layout/widgets_two_pane_sheet_recyclerview.xml
index 94f141b..45a9ac0 100644
--- a/res/layout/widgets_two_pane_sheet_recyclerview.xml
+++ b/res/layout/widgets_two_pane_sheet_recyclerview.xml
@@ -85,5 +85,13 @@
             android:layout_height="match_parent"
             android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
             android:clipToPadding="false" />
+
+        <com.android.launcher3.widget.picker.WidgetsRecyclerView
+            android:id="@+id/search_widgets_list_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:clipToPadding="false"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
+            android:visibility="gone" />
     </LinearLayout>
 </merge>
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index e5c1b61..c956621 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeer tans; <xliff:g id="PROGRESS">%2$s</xliff:g> voltooi"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> laai tans af, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wag tans om te installeer"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is geargiveer. Tik om af te laai en terug te stel."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is geargiveer."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"laai af en stel terug"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Programopdatering word vereis"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Die program vir hierdie ikoon is nie opgedateer nie. Jy kan dit handmatig opdateer om hierdie kortpad weer te aktiveer, of die ikoon verwyder."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Dateer op"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 53dc4ba..cfb5585 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> በመጫን ላይ፣ <xliff:g id="PROGRESS">%2$s</xliff:g> ተጠናቅቋል"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> በመውረድ ላይ፣ <xliff:g id="PROGRESS">%2$s</xliff:g> ተጠናቋል"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ለመጫን በመጠበቅ ላይ"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> በማህደር ተቀምጧል። ለማወረድ እና ወደነበረበት ለመመለስ መታ ያድርጉ።"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> በማህደር ተቀምጧል።"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"አውርድ እና ወደነበረበት መልስ"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"መተግበሪያ ማዘመን አስፈላጊ ነው"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"የዚህ አዶ መተግበሪያ አልተዘመነም። ይህን አቋራጭ ዳግም ለማንቃት በራስዎ ማዘመን ወይም አዶውን ማስወገድ ይችላሉ።"</string>
     <string name="dialog_update" msgid="2178028071796141234">"አዘምን"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index b15d525..6429b53 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"جارٍ تثبيت <xliff:g id="NAME">%1$s</xliff:g>، مستوى التقدم: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"جارٍ تنزيل <xliff:g id="NAME">%1$s</xliff:g>، اكتمل <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> في انتظار التثبيت"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"تمت أرشفة تطبيق \"<xliff:g id="NAME">%1$s</xliff:g>\". انقر لتنزيله واستعادته."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"تمت أرشفة \"<xliff:g id="NAME">%1$s</xliff:g>\"."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"تنزيل التطبيق واستعادته"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"مطلوب تحديث التطبيق"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"لم يتمّ تحديث التطبيق الخاص بهذا الرمز. يمكنك تحديث التطبيق يدويًا لإعادة تفعيل هذا الاختصار أو إزالة الرمز."</string>
     <string name="dialog_update" msgid="2178028071796141234">"تحديث"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index d8783a6..f1db033 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হৈছে"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনল’ড কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হ’ল"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল হোৱালৈ অপেক্ষা কৰি থকা হৈছে"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> আৰ্কাইভ কৰা হৈছে। ডাউনল’ড আৰু পুনঃস্থাপন কৰিবলৈ টিপক।"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> আৰ্কাইভ কৰা হৈছে।"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ডাউনল’ড আৰু পুনঃস্থাপন কৰক"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"এপ্‌টো আপডে’ট কৰা প্ৰয়োজন"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"এই চিহ্নটোৰ এপ্‌টো আপডে’ট কৰা হোৱা নাই। আপুনি এই শ্বৰ্টকাটটো পুনৰ সক্ষম কৰিবলৈ মেনুৱেলী আপডে’ট কৰিব পাৰে অথবা চিহ্নটো আঁতৰাব পাৰে।"</string>
     <string name="dialog_update" msgid="2178028071796141234">"আপডে’ট কৰক"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 5b86fac..f2129cc 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> quraşdırır, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlanıb"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> endirilir, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> yüklənmək üçün gözləyir"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> arxivləndi. Toxunaraq endirin və bərpa edin."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> arxivləndi."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"endirin və bərpa edin"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Tətbiqin güncəllənməsi tələb edilir"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Bu ikona üçün tətbiq güncəllənməyib. Bu qısayolu yenidən aktivləşdirmək üçün manual olaraq güncəlləyə və ya ikonanı silə bilərsiniz."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Güncəlləyin"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 002c800..bdce3b7 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se instalira, <xliff:g id="PROGRESS">%2$s</xliff:g> gotovo"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se preuzima, završeno je <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> čeka na instaliranje"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana. Dodirnite da biste je preuzeli i vratili."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"preuzmite i vratite"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Treba da ažurirate aplikaciju"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplikacija za ovu ikonu nije ažurirana. Možete da je ručno ažurirate da biste ponovo omogućili ovu prečicu ili uklonite ikonu."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Ažuriraj"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 0984f32..8f264e2 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Усталёўваецца праграма \"<xliff:g id="NAME">%1$s</xliff:g>\", завершана <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Ідзе спампоўка <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> завершана"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чакае ўсталёўкі"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Праграма \"<xliff:g id="NAME">%1$s</xliff:g>\" знаходзіцца ў архіве. Націсніце, каб спампаваць яе і аднавіць."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Праграма \"<xliff:g id="NAME">%1$s</xliff:g>\" знаходзіцца ў архіве."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"спампаваць і аднавіць"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Неабходна абнавіць праграму"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Гэта версія праграмы састарэла. Абнавіце праграму ўручную, каб зноў карыстацца гэтым ярлыком, або выдаліце значок."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Абнавіць"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index b321b42..efa941f 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> се инсталира, <xliff:g id="PROGRESS">%2$s</xliff:g> завършено"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> се изтегля. Завършено: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> изчаква инсталиране"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Приложението <xliff:g id="NAME">%1$s</xliff:g> е архивирано. Докоснете за изтегляне и възстановяване."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Приложението <xliff:g id="NAME">%1$s</xliff:g> е архивирано."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"изтегляне и възстановяване"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Изисква се актуализация на приложението"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Приложението за тази икона не е актуализирано. Можете да го актуализирате ръчно, за да активирате отново този пряк път, или да премахнете иконата."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Актуализиране"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 0722e84..0ccedd1 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ইনস্টল করা হচ্ছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূর্ণ হয়েছে"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনলোড হচ্ছে <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পন্ন হয়েছে"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনস্টলের অপেক্ষায় রয়েছে"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> আর্কাইভ করা হয়েছে। ডাউনলোড করতে এবং ফিরিয়ে আনতে ট্যাপ করুন।"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> আর্কাইভ করা হয়েছে।"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ডাউনলোড করুন ও ফিরিয়ে আনুন"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"অ্যাপটি আপডেট করা প্রয়োজন"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"এই আইকনের জন্য অ্যাপটি আপডেট করা নেই। এই শর্টকার্ট আবার চালু করতে, আপনি ম্যানুয়ালি আপডেট করতে বা সরিয়ে দিতে পারবেন।"</string>
     <string name="dialog_update" msgid="2178028071796141234">"আপডেট করুন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 2b168f6..a276431 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, završeno je <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se preuzima, završeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> čeka da se instalira"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Arhivirana je aplikacija <xliff:g id="NAME">%1$s</xliff:g>. Dodirnite da je preuzmete i vratite."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Arhivirana je aplikacija <xliff:g id="NAME">%1$s</xliff:g>."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"preuzimanje i vraćanje"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Potrebno je ažurirati aplikaciju"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplikacija za ovu ikonu nije ažurirana. Možete je ažurirati ručno da ponovo omogućite ovu prečicu ili možete ukloniti ikonu."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Ažuriraj"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 6d30ec1..8f7151e 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"S\'està instal·lant <xliff:g id="NAME">%1$s</xliff:g>; s\'ha completat un <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"S\'està baixant <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completat"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"S\'està esperant per instal·lar <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"L\'aplicació <xliff:g id="NAME">%1$s</xliff:g> està arxivada. Toca per baixar-la i restaurar-la."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"L\'aplicació <xliff:g id="NAME">%1$s</xliff:g> està arxivada."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"baixa i restaura"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Cal actualitzar l\'aplicació"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"L\'aplicació d\'aquesta icona no està actualitzada. Pots actualitzar-la manualment per tornar a activar aquesta drecera o pots suprimir la icona."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Actualitza"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 732343d..1dead61 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Instalace aplikace <xliff:g id="NAME">%1$s</xliff:g>, dokončeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Stahování aplikace <xliff:g id="NAME">%1$s</xliff:g> (dokončeno <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Instalace aplikace <xliff:g id="NAME">%1$s</xliff:g> čeká na zahájení"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Aplikace <xliff:g id="NAME">%1$s</xliff:g> je archivována. Klepnutím ji můžete stáhnout a obnovit."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Aplikace <xliff:g id="NAME">%1$s</xliff:g> je archivována."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"stáhnout a obnovit"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Je nutná aktualizace aplikace"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplikace pro tuto ikonu není nainstalována. Můžete ji ručně aktualizovat, aby zkratka znovu fungovala, případně můžete ikonu odstranit."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Aktualizovat"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 0d78d90..4851c52 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeres. <xliff:g id="PROGRESS">%2$s</xliff:g> fuldført"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloades. <xliff:g id="PROGRESS">%2$s</xliff:g> er gennemført"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> venter på at installere"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> er arkiveret Tryk for at downloade og gendanne."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> er arkiveret."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"download og gendan"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Appen skal opdateres"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Appen, der tilhører dette ikon, er ikke opdateret. Du kan opdatere appen manuelt for at genaktivere denne genvej, eller du kan fjerne ikonet."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Opdater"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index c90cf84..38f943b 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> wird installiert, <xliff:g id="PROGRESS">%2$s</xliff:g> abgeschlossen"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wird heruntergeladen, <xliff:g id="PROGRESS">%2$s</xliff:g> abgeschlossen"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Warten auf Installation von <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ist archiviert. Tippe, um die App herunterzuladen und wiederherzustellen."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ist archiviert."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"herunterladen und wiederherstellen"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"App-Update erforderlich"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Die App für dieses Symbol wurde noch nicht aktualisiert. Du kannst sie manuell aktualisieren, um die Verknüpfung wieder zu aktivieren, oder das Symbol entfernen."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Aktualisieren"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index cd1a1e0..9860f21 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Έχει ολοκληρωθεί το <xliff:g id="PROGRESS">%2$s</xliff:g> της εγκατάστασης της εφαρμογής <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Λήψη <xliff:g id="NAME">%1$s</xliff:g>, ολοκληρώθηκε <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> σε αναμονή για εγκατάσταση"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Η εφαρμογή <xliff:g id="NAME">%1$s</xliff:g> είναι αρχειοθετημένη. Πατήστε για λήψη και επαναφορά."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Η εφαρμογή <xliff:g id="NAME">%1$s</xliff:g> είναι αρχειοθετημένη."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"λήψη και επαναφορά"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Απαιτείται ενημέρωση της εφαρμογής"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Η εφαρμογή για αυτό το εικονίδιο δεν έχει ενημερωθεί. Μπορείτε να την ενημερώσετε μη αυτόματα για να ενεργοποιήσετε ξανά τη συγκεκριμένη συντόμευση ή να καταργήσετε το εικονίδιο."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Ενημέρωση"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f7b04a3..f9711f0 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is archived. Tap to download and restore."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is archived."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"download and restore"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"App update required"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut or remove the icon."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 8feccb0..f41ece6 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is archived. Tap to download and restore."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is archived."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"download and restore"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"App update required"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut, or remove the icon."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f7b04a3..f9711f0 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is archived. Tap to download and restore."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is archived."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"download and restore"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"App update required"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut or remove the icon."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f7b04a3..f9711f0 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is archived. Tap to download and restore."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is archived."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"download and restore"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"App update required"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut or remove the icon."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 0125ae5..eae2009 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Se está instalando <xliff:g id="NAME">%1$s</xliff:g>; <xliff:g id="PROGRESS">%2$s</xliff:g> completado"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Se completó el <xliff:g id="PROGRESS">%2$s</xliff:g> de la descarga de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Instalación de <xliff:g id="NAME">%1$s</xliff:g> en espera"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> está archivada. Presiona para descargar y restablecer."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> está archivada."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"descargar y restablecer"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Es necesario actualizar la app"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"No se actualizó la app de este ícono. Puedes actualizarla manualmente para rehabilitar el acceso directo, o bien quitar el ícono."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Actualizar"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index ddcee65..756912b 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Instalando <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completado"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Descargando <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="PROGRESS">%2$s</xliff:g> completado)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Esperando para instalar <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> está archivada. Toca para descargar y restaurar."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> está archivada."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"descargar y restaurar"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Debes actualizar la aplicación"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"La aplicación de este icono no está actualizada. Puedes actualizarla manualmente para volver a habilitar este acceso directo o puedes eliminar el icono."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Actualizar"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 289b9d9..f44f45b 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Üksust <xliff:g id="NAME">%1$s</xliff:g> installitakse, <xliff:g id="PROGRESS">%2$s</xliff:g> on valmis"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Rakenduse <xliff:g id="NAME">%1$s</xliff:g> allalaadimine, <xliff:g id="PROGRESS">%2$s</xliff:g> on valmis"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> on installimise ootel"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> on arhiivitud. Puudutage allalaadimiseks ja taastamiseks."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> on arhiivitud."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"laadi alla ja taasta"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Rakendust tuleb värskendada"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Selle ikooni rakendust pole värskendatud. Otsetee uuesti lubamiseks võite rakendust käsitsi värskendada või ikooni eemaldada."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Värskenda"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index db99806..5f5f76f 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> instalatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> deskargatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> instalatzeko zain"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> artxibatuta dago. Sakatu deskargatzeko eta leheneratzeko."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> artxibatuta dago."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"deskargatu eta leheneratu"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Aplikazioa eguneratu egin behar da"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Ikonoaren aplikazioa ez dago eguneratuta. Lasterbidea berriro gaitzeko, eskuz egunera dezakezu aplikazioa. Bestela, kendu ikonoa."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Eguneratu"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 17dd851..c5adfd6 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> درحال نصب است، <xliff:g id="PROGRESS">%2$s</xliff:g> تکمیل شده است"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"درحال بارگیری <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="PROGRESS">%2$s</xliff:g> کامل شد"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> درانتظار نصب"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> بایگانی شده است. برای بارگیری و بازیابی تک‌ضرب بزنید."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"‫<xliff:g id="NAME">%1$s</xliff:g> بایگانی شده است."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"بارگیری و بازیابی کردن"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"برنامه باید به‌روز شود"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"برنامه برای این نماد به‌روز نشده است. می‌توانید آن را به‌صورت دستی به‌روز کنید تا میان‌بر دوباره فعال شود، یا نماد را بردارید."</string>
     <string name="dialog_update" msgid="2178028071796141234">"به‌روزرسانی"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index c61c85a..ea5883b 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> asennetaan, <xliff:g id="PROGRESS">%2$s</xliff:g> valmis"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> latautuu, valmiina <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> odottaa asennusta"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> on arkistoitu. Lataa ja palauta napauttamalla."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> on arkistoitu."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"lataa ja palauta"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Sovelluspäivitys vaaditaan"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Kuvakkeen sovellusta ei ole päivitetty. Voit ottaa pikakuvakkeen uudelleen käyttöön päivittämällä sovelluksen tai poistaa kuvakkeen."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Päivitä"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index a941d88..f58b644 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Installation de l\'appli <xliff:g id="NAME">%1$s</xliff:g> en cours, <xliff:g id="PROGRESS">%2$s</xliff:g> terminée"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Téléchargement de <xliff:g id="NAME">%1$s</xliff:g> : <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> en attente d\'installation"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"L\'appli <xliff:g id="NAME">%1$s</xliff:g> est archivée. Touchez le bouton pour télécharger et restaurer l\'appli."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"L\'appli <xliff:g id="NAME">%1$s</xliff:g> est archivée."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"télécharger et restaurer"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Mise à jour de l\'appli requise"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"L\'appli pour cette icône n\'est pas à jour. Vous pouvez soit la mettre à jour manuellement pour réactiver ce raccourci, soit retirer l\'icône."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Mettre à jour"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 6cbc921..feac922 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Installation de <xliff:g id="NAME">%1$s</xliff:g>… (<xliff:g id="PROGRESS">%2$s</xliff:g> terminés)"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> en cours de téléchargement, <xliff:g id="PROGRESS">%2$s</xliff:g> effectué(s)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> en attente d\'installation"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"L\'application <xliff:g id="NAME">%1$s</xliff:g> est archivée. Appuyez pour la télécharger et la restaurer."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"L\'application <xliff:g id="NAME">%1$s</xliff:g> est archivée."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"télécharger et restaurer"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Mise à jour de l\'appli requise"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"L\'appli correspondant à cette icône n\'est pas mise à jour. Vous pouvez la mettre à jour manuellement pour réactiver le raccourci ou supprimer l\'icône."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Modifier"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 02389c4..c36bf18 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Instalando <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completado"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Descargando <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="PROGRESS">%2$s</xliff:g> completado)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Esperando para instalar <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> está no arquivo. Toca para descargar e restaurar."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> está arquivada."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"descargar e restaurar"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"É necesario actualizar a aplicación"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"A aplicación á que corresponde esta icona non está actualizada. Podes actualizala manualmente para activar de novo este atallo, ou ben quitar a icona."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Actualizar"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index aca3054..ee0dec7 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ઇન્સ્ટૉલ કરી રહ્યાં છીએ, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ થયું"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ડાઉનલોડ કરી રહ્યાં છે, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>, ઇન્સ્ટૉલ થવાની રાહ જોઈ રહ્યું છે"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g>ને આર્કાઇવ કર્યું છે. ડાઉનલોડ અને રિસ્ટોર કરવા માટે ટૅપ કરો."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g>ને આર્કાઇવ કર્યું છે."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ડાઉનલોડ અને રિસ્ટોર કરો"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ઍપને અપડેટ કરવી જરૂરી છે"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"આ આઇકન માટે ઍપ અપડેટ કરવામાં આવી નથી. તમે આ શૉર્ટકટ ફરી ચાલુ કરવા અથવા આઇકન કાઢી નાખવા માટે ઍપને મેન્યુઅલી અપડેટ કરી શકો છો."</string>
     <string name="dialog_update" msgid="2178028071796141234">"અપડેટ કરો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 00726ea..c57bad3 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल किया जा रहा है, <xliff:g id="PROGRESS">%2$s</xliff:g> पूरा हो गया"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड हो रहा है, <xliff:g id="PROGRESS">%2$s</xliff:g> पूरी हुई"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> के इंस्टॉल होने की प्रतीक्षा की जा रही है"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> को संग्रहित किया गया. ऐप्लिकेशन को वापस लाने और डाउनलोड करने के लिए टैप करें."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> को संग्रहित किया गया."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"डाउनलोड करें और वापस लाएं"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ऐप्लिकेशन को अपडेट करना ज़रूरी है"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"इस आइकॉन का ऐप्लिकेशन अपडेट नहीं है. इस शॉर्टकट को फिर से चालू करने या आइकॉन को हटाने के लिए, ऐप्लिकेशन को मैन्युअल रूप से अपडेट किया जा सकता है."</string>
     <string name="dialog_update" msgid="2178028071796141234">"अपडेट करें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index a9fd14e..26c9155 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> dovršeno"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Preuzimanje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, dovršeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Čekanje na instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana. Dodirnite da biste je preuzeli i vratili."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"preuzmi i vrati"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Aplikacija se treba ažurirati"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplikacija ove ikone nije ažurirana. Možete ručno ažurirati da biste ponovo omogućili ovaj prečac ili uklonite ikonu."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Ažuriraj"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index a0c089e..dd50451 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Folyamatban van a(z) <xliff:g id="NAME">%1$s</xliff:g> telepítése, <xliff:g id="PROGRESS">%2$s</xliff:g> kész"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"A(z) <xliff:g id="NAME">%1$s</xliff:g> letöltése, <xliff:g id="PROGRESS">%2$s</xliff:g> kész"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"A(z) <xliff:g id="NAME">%1$s</xliff:g> telepítésre vár"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> archiválva. Koppintson a letöltéshez és a visszaállításhoz."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> archiválva."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"letöltés és visszaállítás"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Alkalmazásfrissítés szükséges"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Az ikonhoz tartozó alkalmazás nincs frissítve. A parancsikon újbóli engedélyezéséhez frissítse az alkalmazást, vagy távolítsa ez az ikont."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Frissítés"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index ba2edb1..74ea382 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> հավելվածը տեղադրվում է, կատարված է <xliff:g id="PROGRESS">%2$s</xliff:g>-ը"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>–ի ներբեռնում (<xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>-ի տեղադրման սպասում"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> հավելվածն արխիվացված է։ Հպեք՝ ներբեռնելու և վերականգնելու համար։"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> հավելվածն արխիվացված է։"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ներբեռնել և վերականգնել"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Պահանջվում է թարմացնել հավելվածը"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Հավելվածը հնացել է։ Թարմացրեք այն ձեռքով, որպեսզի շարունակեք օգտագործել դյուրանցումը, կամ հեռացրեք հավելվածի պատկերակը։"</string>
     <string name="dialog_update" msgid="2178028071796141234">"Թարմացնել"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 40f2e57..296b12e 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> sedang diinstal, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> sedang didownload, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> menunggu dipasang"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> diarsipkan. Ketuk untuk mendownload dan memulihkan."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> diarsipkan."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"download dan pulihkan"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Aplikasi perlu diupdate"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplikasi untuk ikon ini belum diupdate. Anda dapat mengupdate secara manual untuk mengaktifkan kembali pintasan ini, atau hapus ikon."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 0014317..ad1742d 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Setur upp <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> lokið"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> í niðurhali, <xliff:g id="PROGRESS">%2$s</xliff:g> lokið"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> bíður uppsetningar"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> er í geymslu. Ýttu til að sækja og endurheimta."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> er í geymslu."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"sækja og endurheimta"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Uppfæra þarf forritið"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Forritið fyrir þetta tákn er ekki uppfært. Þú getur uppfært það handvirkt til að kveikja aftur á þessari flýtileið eða fjarlægt táknið."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Uppfæra"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 9cdb5aa..3177dba 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Installazione di <xliff:g id="NAME">%1$s</xliff:g>, completamento: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Download di <xliff:g id="NAME">%1$s</xliff:g> in corso, <xliff:g id="PROGRESS">%2$s</xliff:g> completato"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> in attesa di installazione"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"App <xliff:g id="NAME">%1$s</xliff:g> archiviata. Tocca per scaricare e ripristinare."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"App <xliff:g id="NAME">%1$s</xliff:g> archiviata."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"download e ripristino"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"È necessario aggiornare l\'app"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"L\'app relativa a questa icona non è aggiornata. Puoi eseguire manualmente l\'aggiornamento per riattivare questa scorciatoia oppure rimuovere l\'icona."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Aggiorna"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 82eb5f8..823e915 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> בתהליך התקנה, <xliff:g id="PROGRESS">%2$s</xliff:g> הושלמו"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"הורדת <xliff:g id="NAME">%1$s</xliff:g> מתבצעת, <xliff:g id="PROGRESS">%2$s</xliff:g> הושלמו"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"מחכה להתקנה של <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"אפליקציית <xliff:g id="NAME">%1$s</xliff:g> הועברה לארכיון. אפשר להקיש כדי להוריד ולשחזר אותה."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"אפליקציית <xliff:g id="NAME">%1$s</xliff:g> הועברה לארכיון."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"הורדה ושחזור"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"נדרש עדכון לאפליקציה"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"האפליקציה של הסמל הזה לא מעודכנת. אפשר לעדכן אותה ידנית כדי להפעיל מחדש את קיצור הדרך הזה, או להסיר את הסמל."</string>
     <string name="dialog_update" msgid="2178028071796141234">"עדכון"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index fc89041..0ffd066 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> をインストールしています: <xliff:g id="PROGRESS">%2$s</xliff:g> 完了"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>をダウンロード中、<xliff:g id="PROGRESS">%2$s</xliff:g>完了"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>のインストール待ち"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g>はアーカイブ済みです。ダウンロードして復元するには、タップしてください。"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> はアーカイブ済みです。"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ダウンロードして復元"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"アプリの更新が必要"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"このアイコンのアプリは更新されていません。手動で更新して、このショートカットを再度有効にできます。また、アイコンを削除することもできます。"</string>
     <string name="dialog_update" msgid="2178028071796141234">"更新"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index f099bcd..a2b83df 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"ინსტალირდება <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> დასრულებულია"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"მიმდინარეობს <xliff:g id="NAME">%1$s</xliff:g>-ის ჩამოტვირთვა, <xliff:g id="PROGRESS">%2$s</xliff:g> დასრულდა"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ელოდება ინსტალაციას"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> დაარქივებულია. შეეხეთ გადმოსაწერად და აღსადგენად."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> დაარქივებულია."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ჩამოტვირთვა და აღდგენა"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"საჭიროა აპის განახლება"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"ამ ხატულის აპი განახლებული არ არის. შეგიძლიათ, ხელით განაახლოთ ამ მალსახმობის ხელახლა გასააქტიურებლად, ან ამოშალოთ ხატულა."</string>
     <string name="dialog_update" msgid="2178028071796141234">"განახლება"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index ebaacc9..0e5472c 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> орнатылуда, <xliff:g id="PROGRESS">%2$s</xliff:g> аяқталды"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> жүктелуде, <xliff:g id="PROGRESS">%2$s</xliff:g> аяқталды"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> орнату күтілуде"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> мұрағатталды. Жүктеп алу және қалпына келтіру үшін түртіңіз."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> мұрағатталды."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"жүктеп алу және қалпына келтіру"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Қолданбаны жаңарту қажет"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Осы белгіше үшін қолданба жаңартылмаған. Оны қолмен жаңартып, осы таңбашаны қайта іске қоса аласыз немесе белгішені өшіріп тастаңыз."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Жаңарту"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index b05eeb0..1f06e35 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"កំពុង​ដំឡើង <xliff:g id="NAME">%1$s</xliff:g>, បាន​បញ្ចប់ <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"កំពុងដោនឡូត <xliff:g id="NAME">%1$s</xliff:g> បានបញ្ចប់ <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> កំពុងរង់ចាំការដំឡើង"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ត្រូវបានទុក​ក្នុង​បណ្ណសារ។ សូមចុចដើម្បីទាញយក និងស្ដារ។"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ត្រូវបានទុក​ក្នុង​បណ្ណសារ។"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ទាញយក និងស្ដារ"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"តម្រូវឱ្យមាន​កំណែកម្មវិធីថ្មី"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"កម្មវិធីសម្រាប់​រូបតំណាងនេះ​មិនត្រូវបានដំឡើងកំណែ​ទេ។ អ្នកអាច​ដំឡើងកំណែ​ដោយផ្ទាល់ ដើម្បីបើក​ផ្លូវកាត់នេះឡើងវិញ ឬលុបរូបតំណាងនេះ។"</string>
     <string name="dialog_update" msgid="2178028071796141234">"ដំឡើងកំណែ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 786cea1..08b4e47 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ಡೌನ್‌ಲೋಡ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ಸ್ಥಾಪಿಸಲು ಕಾಯಲಾಗುತ್ತಿದೆ"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ಅನ್ನು ಆರ್ಕೈವ್ ಮಾಡಲಾಗಿದೆ. ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು ಮತ್ತು ಮರುಸ್ಥಾಪಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ಅನ್ನು ಆರ್ಕೈವ್ ಮಾಡಲಾಗಿದೆ."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ ಮತ್ತು ಮರುಸ್ಥಾಪಿಸಿ"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ಆ್ಯಪ್ ಅಪ್‌ಡೇಟ್ ಅಗತ್ಯವಿದೆ"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"ಈ ಐಕಾನ್‌ಗಾಗಿ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾಗಿಲ್ಲ. ಈ ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ಮರು-ಸಕ್ರಿಯಗೊಳಿಸಲು ನೀವು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅಪ್‌ಡೇಟ್ ಮಾಡಬಹುದು ಅಥವಾ ಐಕಾನ್ ಅನ್ನು ತೆಗೆದುಹಾಕಬಹುದು."</string>
     <string name="dialog_update" msgid="2178028071796141234">"ಅಪ್‌ಡೇಟ್ ಮಾಡಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 0e7ef7b..2130382 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> 설치 중, <xliff:g id="PROGRESS">%2$s</xliff:g> 완료"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> 다운로드 중, <xliff:g id="PROGRESS">%2$s</xliff:g> 완료"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> 설치 대기 중"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> 앱이 보관처리되었습니다. 탭하여 다운로드하고 복원하세요"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> 앱이 보관처리되었습니다"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"다운로드 및 복원"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"앱 업데이트 필요"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"바로가기 아이콘의 앱이 업데이트되지 않았습니다. 직접 업데이트하여 앱 바로가기를 다시 사용할 수 있도록 하거나 아이콘을 삭제하세요."</string>
     <string name="dialog_update" msgid="2178028071796141234">"업데이트"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 753d2dd..b803fa5 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> орнотулууда, <xliff:g id="PROGRESS">%2$s</xliff:g> аткарылды"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> жүктөлүп алынууда, <xliff:g id="PROGRESS">%2$s</xliff:g> аяктады"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> орнотулушу күтүлүүдө"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> архивделди. Жүктөп алуу жана калыбына келтирүү үчүн таптаңыз."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> архивделди."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"жүктөп алуу жана калыбына келтирүү"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Колдонмону жаңыртыңыз"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Бул сүрөтчөнүн колдонмосу жаңыртылган эмес. Ыкчам баскычты кайра иштетүү үчүн аны кол менен жаңыртып же сүрөтчөнү өчүрүп койсоңуз болот."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Жаңыртуу"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 9ddf0b3..d90f563 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"ກຳລັງຕິດຕັ້ງ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> ສຳເລັດແລ້ວ"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ກຳ​ລັງ​ດາວ​ໂຫຼດ, <xliff:g id="PROGRESS">%2$s</xliff:g> ສຳ​ເລັດ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ກຳ​ລັງ​ລໍ​ຖ້າ​ຕິດ​ຕັ້ງ"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ຖືກເກັບໄວ້ໃນແຟ້ມ. ແຕະເພື່ອດາວໂຫຼດ ແລະ ກູ້ຄືນ."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ຖືກເກັບໄວ້ໃນແຟ້ມ."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ດາວໂຫຼດ ແລະ ກູ້ຄືນ"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ຈຳເປັນຕ້ອງອັບເດດແອັບ"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"ບໍ່ໄດ້ອັບເດດແອັບສຳລັບໄອຄອນນີ້. ທ່ານສາມາດອັບເດດເອງໄດ້ເພື່ອເປີດການນຳໃຊ້ທາງລັດນີ້ຄືນໃໝ່ ຫຼື ລຶບໄອຄອນດັ່ງກ່າວອອກ."</string>
     <string name="dialog_update" msgid="2178028071796141234">"ອັບເດດ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 6879862..b9495db 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Įdiegiama: „<xliff:g id="NAME">%1$s</xliff:g>“; baigta: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Atsisiunčiama programa „<xliff:g id="NAME">%1$s</xliff:g>“, <xliff:g id="PROGRESS">%2$s</xliff:g> baigta"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Laukiama, kol bus įdiegta programa „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Programa „<xliff:g id="NAME">%1$s</xliff:g>“ suarchyvuota. Palieskite, jei norite atsisiųsti ir atkurti."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Programa „<xliff:g id="NAME">%1$s</xliff:g>“ suarchyvuota."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"atsisiųsti ir atkurti"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Būtina atnaujinti programą"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Šios piktogramos programa neatnaujinta. Galite patys atnaujinti, kad iš naujo įgalintumėte šį spartųjį klavišą, arba pašalinkite piktogramą."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Atnaujinti"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 8166bce..1c3faf4 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Notiek lietotnes “<xliff:g id="NAME">%1$s</xliff:g>” instalēšana. Norise: <xliff:g id="PROGRESS">%2$s</xliff:g>."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Lietotnes <xliff:g id="NAME">%1$s</xliff:g> lejupielāde (<xliff:g id="PROGRESS">%2$s</xliff:g> pabeigti)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Notiek <xliff:g id="NAME">%1$s</xliff:g> instalēšana"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Lietotne <xliff:g id="NAME">%1$s</xliff:g> ir arhivēta; lai lejupielādētu un atjaunotu, pieskarieties"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Lietotne <xliff:g id="NAME">%1$s</xliff:g> ir arhivēta."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"lejupielādēt un atjaunot"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Lietotne ir jāatjaunina"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Šai ikonai paredzētā lietotne nav atjaunināta. Varat to atjaunināt manuāli, lai atkārtoti iespējotu šo saīsni, vai noņemt ikonu."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Atjaunināt"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 0511c3f..cf3363e 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> се инсталира, <xliff:g id="PROGRESS">%2$s</xliff:g> завршено"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Се презема <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> завршено"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чека да се инсталира"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Апликацијата <xliff:g id="NAME">%1$s</xliff:g> е архивирана. Допрете за да преземете и вратите."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Апликацијата <xliff:g id="NAME">%1$s</xliff:g> е архивирана."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"преземете и вратете"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Потребно е ажурирање на апликацијата"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Апликацијата за оваа икона не е ажурирана. Може да ажурирате рачно за да повторно се овозможи кратенкава или отстранете ја иконата."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Ажурирај"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index b9e68e0..dd6aa71 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ഇൻസ്‌റ്റാൾ ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ഡൗൺലോഡ് ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"ഇൻസ്റ്റാൾ ചെയ്യാൻ <xliff:g id="NAME">%1$s</xliff:g> കാക്കുന്നു"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ആർക്കൈവ് ചെയ്തു. ഡൗൺലോഡ് ചെയ്യാനും പുനഃസ്ഥാപിക്കാനും ടാപ്പ് ചെയ്യുക."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ആർക്കൈവ് ചെയ്തു."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ഡൗൺലോഡ് ചെയ്ത് പുനഃസ്ഥാപിക്കുക"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ആപ്പ് അപ്‌ഡേറ്റ് ചെയ്യേണ്ടതുണ്ട്"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"ഈ ഐക്കണിനുള്ള ആപ്പ് അപ്‌ഡേറ്റ് ചെയ്തിട്ടില്ല. ഈ കുറുക്കുവഴി വീണ്ടും പ്രവർത്തനക്ഷമമാക്കാൻ നിങ്ങൾക്ക് നേരിട്ട് അപ്‌ഡേറ്റ് ചെയ്യാം അല്ലെങ്കിൽ ഐക്കൺ നീക്കം ചെയ്യാം."</string>
     <string name="dialog_update" msgid="2178028071796141234">"അപ്ഡേറ്റ് ചെയ്യുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 28a1438..659f82c 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g>-г суулгаж байна. <xliff:g id="PROGRESS">%2$s</xliff:g> дууссан"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>-г татаж байна, <xliff:g id="PROGRESS">%2$s</xliff:g> татсан"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> нь суулгахыг хүлээж байна"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g>-г архивласан. Татаж, сэргээхийн тулд товшино уу."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g>-г архивласан."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"татах, сэргээх"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Аппын шинэчлэлт шаардлагатай"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Энэ дүрс тэмдгийн аппыг шинэчлээгүй. Та энэ товчлолыг дахин идэвхжүүлэх эсвэл дүрсийг хасахын тулд гараар шинэчлэх боломжтой."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Шинэчлэх"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 0190f1f..aa4b433 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करत आहे, <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड होत आहे , <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करण्याची प्रतिक्षा करत आहे"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> संग्रहित केले आहे. डाउनलोड करून रिस्टोअर करण्यासाठी टॅप करा."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> संग्रहित केले आहे."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"डाउनलोड करून रिस्टोअर करा"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"अ‍ॅप अपडेट करणे आवश्‍यक आहे"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"या आयकनसाठी अ‍ॅप अपडेट केलेले नाही. हा शॉटकर्ट पुन्हा सुरू करण्यासाठी तुम्ही मॅन्युअली अपडेट करू शकता किंवा आयकन काढून टाका."</string>
     <string name="dialog_update" msgid="2178028071796141234">"अपडेट करा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 6941139..89345ce 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> dipasang, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> memuat turun, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> menunggu untuk dipasang"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> diarkibkan. Ketik untuk memuat turun dan memulihkan apl tersebut."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> diarkibkan."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"muat turun dan pulihkan"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Kemas kini apl diperlukan"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Apl untuk ikon ini tidak dikemas kini. Anda boleh mengemas kini secara manual untuk mendayakan semula pintasan atau mengalih keluar ikon."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Kemas kini"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 948f2eb..7d5d275 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ကို ထည့်သွင်းနေသည်၊ <xliff:g id="PROGRESS">%2$s</xliff:g> ပြီးပါပြီ"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ဒေါင်းလုဒ်လုပ်နေသည်၊ <xliff:g id="PROGRESS">%2$s</xliff:g> ပြီးပါပြီ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ကိုထည့်သွင်းရန်စောင့်နေသည်"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ကို သိမ်းထားသည်။ ဒေါင်းလုဒ်လုပ်ပြီး ပြန်ယူရန် တို့ပါ။"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ကို သိမ်းထားသည်။"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ဒေါင်းလုဒ်လုပ်ပြီး ပြန်ယူရန်"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"အက်ပ်ကို အပ်ဒိတ်လုပ်ရန် လိုအပ်သည်"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"ဤသင်္ကေတအတွက် အက်ပ်ကို အပ်ဒိတ်လုပ်မထားပါ။ ဤဖြတ်လမ်းလင့်ခ်ကို ပြန်ဖွင့်ရန် ကိုယ်တိုင်အပ်ဒိတ်လုပ်နိုင်သည် (သို့) သင်္ကေတကို ဖယ်ရှားနိုင်သည်။"</string>
     <string name="dialog_update" msgid="2178028071796141234">"အပ်ဒိတ်လုပ်ရန်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 0dc6160..bb92d90 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installerer, <xliff:g id="PROGRESS">%2$s</xliff:g> er fullført"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Laster ned <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> er fullført"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Venter på å installere <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> er arkivert. Trykk for å laste ned og gjenopprette."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> er arkivert."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"last ned og gjenopprett"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Appen må oppdateres"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Appen for dette ikonet er ikke oppdatert. Du kan oppdatere manuelt for å aktivere denne snarveien igjen, eller du kan fjerne ikonet."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Oppdater"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 4fae68b..49efec9 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> इन्स्टल गरिँदै छ, <xliff:g id="PROGRESS">%2$s</xliff:g> पूरा भयो"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड गर्दै, <xliff:g id="PROGRESS">%2$s</xliff:g> सम्पन्‍न"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> स्थापना गर्न प्रतीक्षा गर्दै"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> अभिलेखमा राखिएको छ। डाउनलोड गरी रिस्टोर गर्न ट्याप गर्नुहोस्।"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> अभिलेखमा राखिएको छ।"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"डाउनलोड र रिस्टोर गर्नुहोस्"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"एप अपडेट गरिनु पर्छ"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"यो आइकनले जनाउने एप अपडेट गरिएको छैन। तपाईं यो सर्टकट फेरि अन गर्न म्यानुअल रूपमा अपडेट गर्न सक्नुहुन्छ वा आइकन नै हटाउनुहोस्।"</string>
     <string name="dialog_update" msgid="2178028071796141234">"अपडेट गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 35f8d65..d898270 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeren, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wordt gedownload, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wacht op installatie"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is gearchiveerd. Tik om te downloaden en te herstellen."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is gearchiveerd."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"downloaden en herstellen"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"App-update vereist"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"De app voor dit icoon is niet geüpdatet. Je kunt handmatig updaten om deze snelkoppeling weer aan te zetten of het icoon verwijderen."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Updaten"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index abc0787..788b168 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ଇନଷ୍ଟଲ୍ କରାଯାଉଛି, <xliff:g id="PROGRESS">%2$s</xliff:g> ସମ୍ପୂର୍ଣ୍ଣ ହୋଇଛି"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ଡାଉନଲୋଡ୍‌ ହେଉଛି, <xliff:g id="PROGRESS">%2$s</xliff:g> ସମ୍ପୂର୍ଣ୍ଣ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ଇନଷ୍ଟଲ୍‌ ହେବାକୁ ଅପେକ୍ଷା କରିଛି"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g>କୁ ଆର୍କାଇଭ କରାଯାଇଛି। ଡାଉନଲୋଡ ଏବଂ ରିଷ୍ଟୋର କରିବା ପାଇଁ ଟାପ କରନ୍ତୁ।"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g>କୁ ଆର୍କାଇଭ କରାଯାଇଛି।"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ଡାଉନଲୋଡ ଏବଂ ରିଷ୍ଟୋର କରନ୍ତୁ"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ଆପକୁ ଅପଡେଟ କରିବା ଆବଶ୍ୟକ"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"ଏହି ଆଇକନ ପାଇଁ ଆପକୁ ଅପଡେଟ କରାଯାଇନାହିଁ। ଏହି ସର୍ଟକଟକୁ ପୁଣି-ସକ୍ଷମ କରିବା ପାଇଁ ଆପଣ ମାନୁଆଲୀ ଅପଡେଟ କରିପାରିବେ କିମ୍ବା ଆଇକନଟିକୁ କାଢ଼ି ଦେଇପାରିବେ।"</string>
     <string name="dialog_update" msgid="2178028071796141234">"ଅପଡେଟ କରନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 715f61b..4dc26aa 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ਨੂੰ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਪੂਰਾ ਹੋਇਆ"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਸੰਪੂਰਣ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ਸਥਾਪਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ਪੁਰਾਲੇਖਬੱਧ ਹੈ। ਡਾਊਨਲੋਡ ਅਤੇ ਮੁੜ-ਬਹਾਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ਪੁਰਾਲੇਖਬੱਧ ਹੈ।"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ਡਾਊਨਲੋਡ ਕਰ ਕੇ ਮੁੜ-ਬਹਾਲ ਕਰੋ"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ਐਪ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਲੋੜ ਹੈ"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"ਇਸ ਪ੍ਰਤੀਕ ਲਈ ਐਪ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਇਸ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕਰਨ ਜਾਂ ਪ੍ਰਤੀਕ ਨੂੰ ਹਟਾਉਣ ਲਈ ਤੁਸੀਂ ਹੱਥੀਂ ਅੱਪਡੇਟ ਕਰ ਸਕਦੇ ਹੋ।"</string>
     <string name="dialog_update" msgid="2178028071796141234">"ਅੱਪਡੇਟ ਕਰੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 47f8a51..1b0f841 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Instaluję aplikację <xliff:g id="NAME">%1$s</xliff:g>, postęp: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Pobieranie elementu <xliff:g id="NAME">%1$s</xliff:g>, ukończono: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> oczekuje na instalację"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Aplikacja <xliff:g id="NAME">%1$s</xliff:g> jest zarchiwizowana. Kliknij, aby ją pobrać i przywrócić."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Aplikacja <xliff:g id="NAME">%1$s</xliff:g> jest zarchiwizowana."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"pobierz i przywróć"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Wymagana aktualizacja aplikacji"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplikacja z tą ikoną nie jest aktualizowana. Możesz zaktualizować ją ręcznie, aby ponownie uruchomić ten skrót, lub usunąć ikonę."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Aktualizuj"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index d2b214e..2cb3c8f 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"A instalar <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"A transferir o <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"A aguardar a instalação do <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"A app <xliff:g id="NAME">%1$s</xliff:g> está arquivada. Toque para transferir e restaurar."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"A app <xliff:g id="NAME">%1$s</xliff:g> está arquivada."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"transferir e restaurar"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Atualização da app necessária"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"A app deste ícone não está atualizada. Pode atualizar manualmente para reativar este atalho ou remover o ícone."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Atualizar"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index f864b3e..414f55d 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Instalando <xliff:g id="NAME">%1$s</xliff:g>. <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Fazendo download de <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Aguardando instalação de <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"O app <xliff:g id="NAME">%1$s</xliff:g> está arquivado. Toque para baixar e restaurar."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"O app <xliff:g id="NAME">%1$s</xliff:g> está arquivado."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"baixar e restaurar"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Atualização obrigatória do app"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"O app desse ícone não está atualizado. Você pode remover o ícone ou atualizar o app manualmente para reativar esse atalho."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Atualizar"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 30e33dc..24e6cde 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se instalează, <xliff:g id="PROGRESS">%2$s</xliff:g> finalizat"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se descarcă (finalizat <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> așteaptă instalarea"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> s-a arhivat. Atinge pentru a descărca și restabili."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> s-a arhivat."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"descarcă și restabilește"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Este necesară actualizarea aplicației"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplicația pentru această pictogramă nu este actualizată. Poți să actualizezi manual ca să reactivezi comanda rapidă sau să elimini pictograma."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Actualizează"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 126e3ad..f94fd59 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Установка приложения \"<xliff:g id="NAME">%1$s</xliff:g>\" (выполнено <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Скачивается \"<xliff:g id="NAME">%1$s</xliff:g>\" (<xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Ожидание установки \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Приложение \"<xliff:g id="NAME">%1$s</xliff:g>\" находится в архиве. Нажмите, чтобы скачать его и восстановить"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Приложение \"<xliff:g id="NAME">%1$s</xliff:g>\" находится в архиве."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"скачать и восстановить"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Обновите приложение"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Эта версия приложения устарела. Обновите его вручную, чтобы снова пользоваться ярлыком, или удалите значок."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Обновить"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 328f2f5..1938c54 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ස්ථාපනය කරමින්, <xliff:g id="PROGRESS">%2$s</xliff:g> සම්පූර්ණයි"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> බාගත කරමින්, <xliff:g id="PROGRESS">%2$s</xliff:g> සම්පූර්ණයි"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ස්ථාපනය කිරීමට බලා සිටිමින්"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> සංරක්‍ෂිතයි. බා ගෙන ප්‍රතිසාධන කිරීමට තට්ටු කරන්න."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ලේඛනාරක්ෂණය කර ඇත."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"බාගත කර ප්‍රතිසාධනය කරන්න"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"යෙදුම් යාවත්කාලීනයක් අවශ්‍යයි"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"මෙම නිරූපකය සඳහා යෙදුම යාවත්කාලීන කර නැත. ඔබට මෙම කෙටි මඟ යළි සබල කිරීමට හෝ නිරූපකය ඉවත් කිරීමට හස්තීයව යාවත්කාලීන කළ හැකිය."</string>
     <string name="dialog_update" msgid="2178028071796141234">"යාවත්කාලීන කරන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 88cb1b1..528ba28 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Inštaluje sa <xliff:g id="NAME">%1$s</xliff:g>. Dokončené: <xliff:g id="PROGRESS">%2$s</xliff:g>."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Sťahuje sa aplikácia <xliff:g id="NAME">%1$s</xliff:g>. Stiahnuté: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Aplikácia <xliff:g id="NAME">%1$s</xliff:g> čaká na inštaláciu"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Aplikácia <xliff:g id="NAME">%1$s</xliff:g> je archivovaná. Klepnutím ju stiahnite a obnovte."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Aplikácia <xliff:g id="NAME">%1$s</xliff:g> je archivovaná."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"stiahnuť a obnoviť"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Vyžaduje sa aktualizácia aplikácie"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplikácia, ktorú zastupuje táto ikona, nie je aktualizovaná. Môžete ju ručne aktualizovať, aby odkaz znova fungoval, prípadne môžete ikonu odstrániť."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Aktualizovať"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 37a02e4..e0288c4 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se namešča, dokončano: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Prenašanje aplikacije <xliff:g id="NAME">%1$s</xliff:g>; preneseno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> čaka na namestitev"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana. Dotaknite se za prenos in obnovitev."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"prenos in obnovitev"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Zahtevana je posodobitev aplikacije"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplikacija za to ikono ni posodobljena. Lahko jo ročno posodobite, da znova omogočite to bližnjico, ali pa odstranite ikono."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Posodobi"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 5be991a..96b1bff 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> po instalohet, <xliff:g id="PROGRESS">%2$s</xliff:g> i përfunduar"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> po shkarkohet, <xliff:g id="PROGRESS">%2$s</xliff:g> të përfunduara"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> po pret të instalohet"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> është arkivuar. Trokit për ta shkarkuar dhe restauruar."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> është arkivuar."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"shkarko dhe restauro"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Kërkohet përditësimi i aplikacionit"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Aplikacioni për këtë ikonë nuk është përditësuar. Mund ta përditësosh manualisht për të riaktivizuar këtë shkurtore ose hiq ikonën."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Përditëso"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index afaeb61..054ddff 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> се инсталира, <xliff:g id="PROGRESS">%2$s</xliff:g> готово"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> се преузима, завршено је <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чека на инсталирање"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Апликација <xliff:g id="NAME">%1$s</xliff:g> је архивирана. Додирните да бисте је преузели и вратили."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Апликација <xliff:g id="NAME">%1$s</xliff:g> је архивирана."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"преузмите и вратите"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Треба да ажурирате апликацију"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Апликација за ову икону није ажурирана. Можете да је ручно ажурирате да бисте поново омогућили ову пречицу или уклоните икону."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Ажурирај"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 547f60e..482cd88 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeras. <xliff:g id="PROGRESS">%2$s</xliff:g> har slutförts"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> laddas ned, <xliff:g id="PROGRESS">%2$s</xliff:g> klart"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> väntar på installation"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> har arkiverats. Tryck för att ladda ner och återställa."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> har arkiverats."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ladda ned och återställ"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Du måste uppdatera appen"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Appen för den här ikonen har inte uppdaterats. Du kan uppdatera den manuellt för att återaktivera genvägen eller ta bort ikonen."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Uppdatera"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 71f8d8b..015ff8d 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Inasakinisha <xliff:g id="NAME">%1$s</xliff:g>, imekamilika <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> inapakuliwa, <xliff:g id="PROGRESS">%2$s</xliff:g> imekamilika"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> inasubiri kusakinisha"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> imewekwa kwenye kumbukumbu. Gusa ili upakue na urejeshe."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> imewekwa kwenye kumbukumbu."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"pakua na urejeshe"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Unahitaji kusasisha programu"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Programu ya aikoni hii haijasasishwa. Unaweza kusasisha mwenyewe ili uruhusu upya njia hii ya mkato au uondoe aikoni."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Sasisha"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index e87e2eb..704361a 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> நிறுவப்படுகிறது, <xliff:g id="PROGRESS">%2$s</xliff:g> முடிந்தது"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>ஐப் பதிவிறக்குகிறது, <xliff:g id="PROGRESS">%2$s</xliff:g> முடிந்தது"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>ஐ நிறுவுவதற்காகக் காத்திருக்கிறது"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> காப்பிடப்பட்டுள்ளது. அதைப் பதிவிறக்கி மீட்டெடுக்க தட்டுங்கள்."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> காப்பிடப்பட்டுள்ளது."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"பதிவிறக்கி மீட்டெடுக்கும்"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ஆப்ஸைப் புதுப்பியுங்கள்"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"இந்த ஐகானுக்கான ஆப்ஸ் புதுப்பிக்கப்படவில்லை. இந்த ஷார்ட்கட்டை மீண்டும் இயக்கவோ ஐகானை அகற்றவோ நீங்களாகவே புதுப்பிக்கலாம்."</string>
     <string name="dialog_update" msgid="2178028071796141234">"புதுப்பி"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 75dc7f0..4550961 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g>‌ను ఇన్‌స్టాల్ చేయడం, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> డౌన్‌లోడ్ అవుతోంది, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ఇన్‌స్టాల్ కావడానికి వేచి ఉంది"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ఆర్కైవ్ చేయబడింది. డౌన్‌లోడ్ చేయడానికి, రీస్టోర్ చేయడానికి ట్యాప్ చేయండి."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ఆర్కైవ్ చేయబడింది."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"డౌన్‌లోడ్ చేసి, రీస్టోర్ చేయండి"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"యాప్‌ను అప్‌డేట్ చేయడం అవసరం"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"ఈ చిహ్నం కోసం యాప్ అప్‌డేట్ చేయబడలేదు. మీరు ఈ షార్ట్‌కట్‌ను మళ్లీ ఎనేబుల్ చేయడానికి మాన్యువల్‌గా అప్‌డేట్ చేయవచ్చు లేదా చిహ్నాన్ని తీసివేయవచ్చు."</string>
     <string name="dialog_update" msgid="2178028071796141234">"అప్‌డేట్ చేయండి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 50a0e52..27cba7f 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"กำลังติดตั้ง <xliff:g id="NAME">%1$s</xliff:g> เสร็จแล้ว <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"กำลังดาวน์โหลด <xliff:g id="NAME">%1$s</xliff:g> เสร็จแล้ว <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> กำลังรอติดตั้ง"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"เก็บถาวร <xliff:g id="NAME">%1$s</xliff:g> แล้ว แตะเพื่อดาวน์โหลดและกู้คืน"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"เก็บถาวร <xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ดาวน์โหลดและกู้คืน"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ต้องอัปเดตแอป"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"แอปสำหรับไอคอนนี้ยังไม่ได้อัปเดต คุณอัปเดตด้วยตนเองได้โดยเปิดใช้ทางลัดนี้อีกครั้งหรือนำไอคอนออก"</string>
     <string name="dialog_update" msgid="2178028071796141234">"อัปเดต"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 86ad330..5dea339 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Ini-install ang <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> kumpleto"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Dina-download na ang <xliff:g id="NAME">%1$s</xliff:g>, tapos na ang <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Hinihintay nang mag-install ang <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Naka-archive ang <xliff:g id="NAME">%1$s</xliff:g>. I-tap para i-download at i-restore."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Naka-archive ang <xliff:g id="NAME">%1$s</xliff:g>."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"i-download at i-restore"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Kinakailangang i-update ang app"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Hindi updated ang app para sa icon na ito. Puwede kang manual na mag-update para ma-enable ulit ang shortcut na ito, o alisin ang icon."</string>
     <string name="dialog_update" msgid="2178028071796141234">"I-update"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 9955bbf..6b2cb60 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> yükleniyor, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> indiriliyor, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> uygulaması yüklenmek için bekliyor"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> arşivlendi. İndirip geri yüklemek için dokunun."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> arşivlendi."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"indir ve geri yükle"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Uygulama güncellemesi gerekli"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Bu simgenin uygulaması güncellenmemiş. Simgeyi kaldırabilir ya da uygulamayı manuel olarak güncelleyerek bu kısayolu yeniden etkinleştirebilirsiniz."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Güncelle"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index e777262..3b35bc8 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> встановлюється, виконано <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> завантажується, <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> очікує на завантаження"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Додаток <xliff:g id="NAME">%1$s</xliff:g> заархівовано. Натисніть, щоб завантажити й відновити."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Додаток <xliff:g id="NAME">%1$s</xliff:g> заархівовано."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"завантажити й відновити"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Потрібно оновити додаток"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Додаток для цього значка не оновлено. Ви можете оновити його вручну, щоб знову ввімкнути цю швидку команду, або можете вилучити значок."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Оновити"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 9fb85b2..8d158ce 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> انسٹال کی جا رہی ہے، <xliff:g id="PROGRESS">%2$s</xliff:g> مکمل ہو گئی"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ڈاؤن لوڈ ہو رہا ہے، <xliff:g id="PROGRESS">%2$s</xliff:g> مکمل ہو گیا"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> انسٹال ہونے کا انتظار کر رہی ہے"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> کو آرکائیو کر لیا گیا ہے۔ ڈاؤن لوڈ اور بحال کرنے کیلئے تھپتھپائیں۔"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"‫<xliff:g id="NAME">%1$s</xliff:g> کو آرکائیو کر لیا گیا ہے۔"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"ڈاؤن لوڈ کریں اور بحال کریں"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"ایپ کی اپ ڈیٹ درکار ہے"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"اس آئیکن کیلئے ایپ کو اپ ڈیٹ نہیں کیا گیا ہے۔ آپ اس شارٹ کٹ کو دوبارہ فعال کرنے کے لیے دستی طور پر اپ ڈیٹ کر سکتے ہیں، یا آئیکن کو ہٹا سکتے ہیں۔"</string>
     <string name="dialog_update" msgid="2178028071796141234">"اپ ڈیٹ کریں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 83cabc9..511d44b 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> oʻrnatlmoqda, <xliff:g id="PROGRESS">%2$s</xliff:g> yakunlandi"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> yuklab olinmoqda, <xliff:g id="PROGRESS">%2$s</xliff:g> bajarildi"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi o‘rnatilishi kutilmoqda"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> arxivlangan. Yuklab olish va tiklash uchun bosing."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> arxivlangan."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"yuklab olish va tiklash"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Ilovani yangilash zarur"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Bu belgi uchun ilova yangilanmagan. Ushbu yorliqni qayta yoqish uchun oddiy usulda yangilashingiz yoki belgini olib tashlashingiz mumkin."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Yangilash"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index d67f661..d1f2adc 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"Đang cài đặt <xliff:g id="NAME">%1$s</xliff:g>, hoàn tất <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Đang tải xuống <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> hoàn tất"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Đang chờ cài đặt <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> đã được lưu trữ. Hãy nhấn để tải xuống và khôi phục."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> đã được lưu trữ."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"tải xuống và khôi phục"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Cần cập nhật ứng dụng"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Ứng dụng cho biểu tượng này chưa được cập nhật. Bạn có thể cập nhật theo cách thủ công để bật lại phím tắt này hoặc xóa biểu tượng."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Cập nhật"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 1097e57..ed30c33 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"正在安装<xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"正在下载<xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>正在等待安装"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"已归档“<xliff:g id="NAME">%1$s</xliff:g>”。点按即可进行下载并恢复。"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"已归档“<xliff:g id="NAME">%1$s</xliff:g>”。"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"下载并恢复"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"需要更新应用"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"此图标对应的应用未更新。您可以手动更新以重新启用该快捷方式,或者移除此图标。"</string>
     <string name="dialog_update" msgid="2178028071796141234">"更新"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 6471a9a..1ef3070 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"正在安裝「<xliff:g id="NAME">%1$s</xliff:g>」(已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"正在下載 <xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"正在等待安裝 <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"「<xliff:g id="NAME">%1$s</xliff:g>」已封存。輕按即可下載並還原。"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"「<xliff:g id="NAME">%1$s</xliff:g>」已封存。"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"下載及還原"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"必須更新應用程式"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"你尚未更新這個圖示代表的應用程式。你可以手動更新以重新啟用此快速鍵,或者移除圖示。"</string>
     <string name="dialog_update" msgid="2178028071796141234">"更新"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 0a9ffa2..fa1b347 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"正在安裝「<xliff:g id="NAME">%1$s</xliff:g>」(已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"正在下載「<xliff:g id="NAME">%1$s</xliff:g>」,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"正在等待安裝「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"「<xliff:g id="NAME">%1$s</xliff:g>」已封存。輕觸即可下載並還原。"</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"「<xliff:g id="NAME">%1$s</xliff:g>」已封存。"</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"下載及還原"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"必須更新應用程式"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"這個圖示代表的應用程式未更新。手動更新即可重新啟用這個捷徑,你也可以移除圖示。"</string>
     <string name="dialog_update" msgid="2178028071796141234">"更新"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 59c99c4..c6753fc 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -142,7 +142,8 @@
     <string name="app_installing_title" msgid="5864044122733792085">"I-<xliff:g id="NAME">%1$s</xliff:g> iyafakwa, seyiqede <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"I-<xliff:g id="NAME">%1$s</xliff:g> iyalandwa, <xliff:g id="PROGRESS">%2$s</xliff:g> kuqediwe"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ilinde ukufakwa"</string>
-    <string name="app_archived_title" msgid="7717956158562544081">"Okuthi <xliff:g id="NAME">%1$s</xliff:g> kufakwe kungobo yomlando. Thepha ukuze udawunilode futhi ubuyisele."</string>
+    <string name="app_archived_title" msgid="4548283110222420708">"Okuthi <xliff:g id="NAME">%1$s</xliff:g> kufakwe kungobo yomlando."</string>
+    <string name="app_unarchiving_action" msgid="5736107006413929484">"dawuniloda uphinde ubuyisele"</string>
     <string name="dialog_update_title" msgid="114234265740994042">"Kudingeka isibuyekezo se-app"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"I-app yalesi sithonjana ibuyekeziwe. Ungabuyekeza mathupha ukuze uphinde unike amandla lesi sinqamuleli, noma ususe isithonjana."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Vuselela"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 4dddb9a..f24707f 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -126,6 +126,8 @@
     <attr name="widgetPickerCollapseHandleColor" format="color"/>
     <attr name="widgetPickerAddButtonBackgroundColor" format="color"/>
     <attr name="widgetPickerAddButtonTextColor" format="color"/>
+    <attr name="widgetPickerExpandButtonBackgroundColor" format="color"/>
+    <attr name="widgetPickerExpandButtonTextColor" format="color"/>
     <attr name="widgetCellTitleColor" format="color" />
     <attr name="widgetCellSubtitleColor" format="color" />
 
@@ -309,6 +311,9 @@
         <!-- defaults to allAppsCellSpecsId, if not specified -->
         <attr name="allAppsCellSpecsTwoPanelId" format="reference" />
 
+        <!-- defaults to false, if not specified -->
+        <attr name="isFixedLandscape" format="boolean" />
+
         <!-- By default all categories are enabled -->
         <attr name="deviceCategory" format="integer">
             <!-- Enable on phone only -->
diff --git a/res/values/colors.xml b/res/values/colors.xml
index fa1626e..4549b86 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -117,6 +117,12 @@
     <color name="widget_picker_collapse_handle_color_light">#C4C7C5</color>
     <color name="widget_picker_add_button_background_color_light">#0B57D0</color>
     <color name="widget_picker_add_button_text_color_light">#0B57D0</color>
+    <color name="widget_picker_expand_button_background_color_light">
+        @color/widget_picker_secondary_surface_color_light
+    </color>
+    <color name="widget_picker_expand_button_text_color_light">
+        @color/widget_picker_header_app_title_color_light
+    </color>
     <color name="widget_cell_title_color_light">@color/system_on_surface_light</color>
     <color name="widget_cell_subtitle_color_light">@color/system_on_surface_variant_light</color>
 
@@ -138,6 +144,12 @@
     <color name="widget_picker_collapse_handle_color_dark">#444746</color>
     <color name="widget_picker_add_button_background_color_dark">#062E6F</color>
     <color name="widget_picker_add_button_text_color_dark">#FFFFFF</color>
+    <color name="widget_picker_expand_button_background_color_dark">
+        @color/widget_picker_secondary_surface_color_dark
+    </color>
+    <color name="widget_picker_expand_button_text_color_dark">
+        @color/widget_picker_header_app_title_color_dark
+    </color>
     <color name="widget_cell_title_color_dark">@color/system_on_surface_dark</color>
     <color name="widget_cell_subtitle_color_dark">@color/system_on_surface_variant_dark</color>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d4773c3..c69778a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -228,8 +228,18 @@
     <!-- Bottom margin for the search and recommended widgets container with work profile -->
     <dimen name="search_and_recommended_widgets_container_small_bottom_margin">10dp</dimen>
 
+    <dimen name="widget_header_focus_ring_width">3dp</dimen>
+    <dimen name="widget_focus_ring_corner_radius">28dp</dimen>
+    <dimen name="widget_header_background_border">5dp</dimen>
+
     <dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
     <dimen name="widget_list_content_corner_radius">4dp</dimen>
+    <!-- Button that expands the widget apps list in the widget picker. -->
+    <dimen name="widgets_list_expand_button_drawable_padding">8dp</dimen>
+    <dimen name="widgets_list_expand_button_start_padding">16dp</dimen>
+    <dimen name="widgets_list_expand_button_end_padding">24dp</dimen>
+    <dimen name="widgets_list_expand_button_vertical_padding">16dp</dimen>
+    <dimen name="widgets_list_expand_button_top_margin">14dp</dimen>
 
     <dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
     <dimen name="widget_list_entry_spacing">2dp</dimen>
diff --git a/res/values/id.xml b/res/values/id.xml
index 28496b5..67692d8 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -19,6 +19,7 @@
     <item type="id" name="view_type_widgets_space" />
     <item type="id" name="view_type_widgets_list" />
     <item type="id" name="view_type_widgets_header" />
+    <item type="id" name="view_type_widgets_list_expand" />
 
     <!-- Accessibility actions -->
     <item type="id" name="action_remove" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 746bd7e..123e2b8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -157,6 +157,12 @@
     <!-- Accessibility content description for the button that adds a widget to the home screen. The
          placeholder text is the widget name. [CHAR_LIMIT=none] -->
     <string name="widget_add_button_content_description">Add <xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget</string>
+    <!-- Text on the button that enables users to expand the widgets list to see all widget apps besides the default ones displayed. [CHAR_LIMIT=15] -->
+    <string name="widgets_list_expand_button_label">Show all</string>
+    <!-- Accessibility content description for the button that enables users to expand the widgets list to see all widget apps besides the default ones displayed. [CHAR_LIMIT=none]  -->
+    <string name="widgets_list_expand_button_content_description">Show all widgets</string>
+    <!-- Accessibility announcement to indicate to the users that widgets list is now expanded -->
+    <string name="widgets_list_expanded">Showing all widgets</string>
 
     <!-- Text on an educational tip on widget informing users that they can change widget settings.
          [CHAR_LIMIT=NONE] -->
@@ -311,6 +317,12 @@
     <string name="allow_rotation_title">Allow home screen rotation</string>
     <!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
     <string name="allow_rotation_desc">When phone is rotated</string>
+
+    <!-- Title for Landscape Mode setting. [CHAR LIMIT=50] -->
+    <string name="landscape_mode_title">Landscape mode</string>
+    <!--  [CHAR LIMIT=100] -->
+    <string name="landscape_mode_desc">Set phone into landscape mode</string>
+
     <!-- Title for Notification dots setting. Tapping this will link to the system Notifications settings screen where the user can turn off notification dots globally. [CHAR LIMIT=50] -->
     <string name="notification_dots_title">Notification dots</string>
     <!-- Text to indicate that the system notification dots setting is on [CHAR LIMIT=100] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 728c523..6d3579b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -275,6 +275,12 @@
             @color/widget_picker_add_button_background_color_light</item>
         <item name="widgetPickerAddButtonTextColor">
             @color/widget_picker_add_button_text_color_light</item>
+        <item name="widgetPickerExpandButtonBackgroundColor">
+            @color/widget_picker_expand_button_background_color_light
+        </item>
+        <item name="widgetPickerExpandButtonTextColor">
+            @color/widget_picker_expand_button_text_color_light
+        </item>
         <item name="widgetCellTitleColor">
             @color/widget_cell_title_color_light</item>
         <item name="widgetCellSubtitleColor">
@@ -316,6 +322,12 @@
             @color/widget_picker_add_button_background_color_dark</item>
         <item name="widgetPickerAddButtonTextColor">
             @color/widget_picker_add_button_text_color_dark</item>
+        <item name="widgetPickerExpandButtonBackgroundColor">
+            @color/widget_picker_expand_button_background_color_dark
+        </item>
+        <item name="widgetPickerExpandButtonTextColor">
+            @color/widget_picker_expand_button_text_color_dark
+        </item>
         <item name="widgetCellTitleColor">
             @color/widget_cell_title_color_dark</item>
         <item name="widgetCellSubtitleColor">
diff --git a/res/xml/backupscheme.xml b/res/xml/backupscheme.xml
index 58916a8..34b80b1 100644
--- a/res/xml/backupscheme.xml
+++ b/res/xml/backupscheme.xml
@@ -10,6 +10,7 @@
     <include domain="database" path="launcher_4_by_4.db" />
     <include domain="database" path="launcher_3_by_3.db" />
     <include domain="database" path="launcher_2_by_2.db" />
+    <include domain="database" path="launcher_7_by_3.db" />
     <include domain="sharedpref" path="com.android.launcher3.prefs.xml" />
     <include domain="file" path="downgrade_schema.json" />
 
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 4305703..78535a1 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -617,7 +617,7 @@
                 || inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE]
                 : inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE])
                 && hotseatQsbHeight > 0;
-        isQsbInline = mIsScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline;
+        isQsbInline = isQsbInline(inv);
 
         areNavButtonsInline = isTaskbarPresent && !isGestureMode;
         numShownHotseatIcons =
@@ -827,7 +827,7 @@
             hotseatBorderSpace = cellLayoutBorderSpacePx.y;
         }
 
-        if (isTablet) {
+        if (shouldShowAllAppsOnSheet()) {
             allAppsPadding.top = mInsets.top;
             allAppsShiftRange = heightPx;
         } else {
@@ -850,6 +850,24 @@
         mDotRendererAllApps = createDotRenderer(context, allAppsIconSizePx, dotRendererCache);
     }
 
+    /**
+     * Takes care of the logic that determines if we show a the QSB inline or not.
+     */
+    private boolean isQsbInline(InvariantDeviceProfile inv) {
+        // For foldable (two panel), we inline the qsb if we have the screen open and we are in
+        // either Landscape or Portrait. This cal also be disabled in the device_profile.xml
+        boolean twoPanelCanInline = inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT]
+                || inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE];
+
+        // In tablets we inline in both orientations but only if we have enough space in the QSB
+        boolean tabletInlineQsb = inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE];
+        boolean canQsbInline = isTwoPanels ? twoPanelCanInline : tabletInlineQsb;
+        canQsbInline = canQsbInline && hotseatQsbHeight > 0;
+
+        return (mIsScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline)
+                || inv.isFixedLandscapeMode;
+    }
+
     private static DotRenderer createDotRenderer(
             @NonNull Context context, int size, @NonNull SparseArray<DotRenderer> cache) {
         DotRenderer renderer = cache.get(size);
@@ -1516,6 +1534,11 @@
         }
     }
 
+    /** Whether All Apps should be presented on a bottom sheet. */
+    public boolean shouldShowAllAppsOnSheet() {
+        return isTablet || Flags.allAppsSheetForHandheld();
+    }
+
     private void setupAllAppsStyle(Context context) {
         TypedArray allAppsStyle = context.obtainStyledAttributes(
                 inv.allAppsStyle != INVALID_RESOURCE_HANDLE ? inv.allAppsStyle
@@ -1898,7 +1921,7 @@
                 hotseatBarPadding.set(mHotseatBarWorkspaceSpacePx, paddingTop,
                         mInsets.right + mHotseatBarEdgePaddingPx, paddingBottom);
             }
-        } else if (isTaskbarPresent) {
+        } else if (isTaskbarPresent || inv.isFixedLandscapeMode) {
             // Center the QSB vertically with hotseat
             int hotseatBarBottomPadding = getHotseatBarBottomPadding();
             int hotseatBarTopPadding =
@@ -1917,6 +1940,13 @@
             }
             startSpacing += getAdditionalQsbSpace();
 
+            if (inv.isFixedLandscapeMode) {
+                endSpacing += workspacePadding.right + cellLayoutPaddingPx.right
+                        + mInsets.right;
+                startSpacing += workspacePadding.left + cellLayoutPaddingPx.left
+                        + mInsets.left;
+            }
+
             hotseatBarPadding.top = hotseatBarTopPadding;
             hotseatBarPadding.bottom = hotseatBarBottomPadding;
             boolean isRtl = Utilities.isRtl(context.getResources());
@@ -2512,7 +2542,8 @@
                 throw new IllegalArgumentException("Window bounds not set");
             }
             if (mTransposeLayoutWithOrientation == null) {
-                mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
+                mTransposeLayoutWithOrientation =
+                        !(mInfo.isTablet(mWindowBounds) || mInv.isFixedLandscapeMode);
             }
             if (mIsGestureMode == null) {
                 mIsGestureMode = mInfo.getNavigationMode().hasGestures;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 27602af..6468f74 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -186,18 +186,20 @@
      */
     public void adjustForBubbleBar(boolean isBubbleBarVisible) {
         DeviceProfile dp = mActivity.getDeviceProfile();
+        float adjustedBorderSpace = dp.getHotseatAdjustedBorderSpaceForBubbleBar(getContext());
+        boolean adjustmentRequired = Float.compare(adjustedBorderSpace, 0f) != 0;
 
         ShortcutAndWidgetContainer icons = getShortcutsAndWidgets();
-        AnimatorSet animatorSet = new AnimatorSet();
-
         // update the translation provider for future layout passes of hotseat icons.
-        if (isBubbleBarVisible) {
+        if (adjustmentRequired && isBubbleBarVisible) {
             icons.setTranslationProvider(
                     cellX -> dp.getHotseatAdjustedTranslation(getContext(), cellX));
         } else {
             icons.setTranslationProvider(null);
         }
+        if (!adjustmentRequired) return;
 
+        AnimatorSet animatorSet = new AnimatorSet();
         for (int i = 0; i < icons.getChildCount(); i++) {
             View child = icons.getChildAt(i);
             float tx = isBubbleBarVisible ? dp.getHotseatAdjustedTranslation(getContext(), i) : 0;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 7112a1b..090fe51 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
 import static com.android.launcher3.LauncherPrefs.GRID_NAME;
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
@@ -86,7 +87,8 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({TYPE_PHONE, TYPE_MULTI_DISPLAY, TYPE_TABLET})
-    public @interface DeviceType {}
+    public @interface DeviceType {
+    }
 
     public static final int TYPE_PHONE = 0;
     public static final int TYPE_MULTI_DISPLAY = 1;
@@ -132,6 +134,7 @@
     public int iconBitmapSize;
     public int fillResIconDpi;
     public @DeviceType int deviceType;
+    public Info displayInfo;
 
     public PointF[] minCellSize;
 
@@ -209,6 +212,13 @@
     @XmlRes
     public int allAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
 
+
+    /**
+     * Fixed landscape mode is the landscape on the phones.
+     */
+    public boolean isFixedLandscapeMode = false;
+    private LauncherPrefChangeListener mLandscapeModePreferenceListener;
+
     public String dbFile;
     public int defaultLayoutId;
     public int demoModeLayoutId;
@@ -224,7 +234,8 @@
     private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
 
     @VisibleForTesting
-    public InvariantDeviceProfile() { }
+    public InvariantDeviceProfile() {
+    }
 
     @TargetApi(23)
     private InvariantDeviceProfile(Context context) {
@@ -242,6 +253,18 @@
                         onConfigChanged(displayContext);
                     }
                 });
+        if (Flags.oneGridSpecs()) {
+            mLandscapeModePreferenceListener = (String s) -> {
+                boolean newFixedLandscapeValue = FIXED_LANDSCAPE_MODE.get(context);
+                if (isFixedLandscapeMode != newFixedLandscapeValue) {
+                    setFixedLandscape(context, newFixedLandscapeValue);
+                }
+            };
+            LauncherPrefs.INSTANCE.get(context).addListener(
+                    mLandscapeModePreferenceListener,
+                    FIXED_LANDSCAPE_MODE
+            );
+        }
     }
 
     /**
@@ -267,8 +290,13 @@
         @DeviceType int defaultDeviceType = defaultInfo.getDeviceType();
         DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
                 defaultInfo,
-                getPredefinedDeviceProfiles(context, gridName, defaultDeviceType,
-                        /*allowDisabledGrid=*/false),
+                getPredefinedDeviceProfiles(
+                        context,
+                        gridName,
+                        defaultInfo,
+                        /*allowDisabledGrid=*/false,
+                        isFixedLandscapeMode
+                ),
                 defaultDeviceType);
 
         Context displayContext = context.createDisplayContext(display);
@@ -276,8 +304,13 @@
         @DeviceType int deviceType = myInfo.getDeviceType();
         DisplayOption myDisplayOption = invDistWeightedInterpolate(
                 myInfo,
-                getPredefinedDeviceProfiles(context, gridName, deviceType,
-                        /*allowDisabledGrid=*/false),
+                getPredefinedDeviceProfiles(
+                        context,
+                        gridName,
+                        myInfo,
+                        /*allowDisabledGrid=*/false,
+                        isFixedLandscapeMode
+                ),
                 deviceType);
 
         DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
@@ -294,12 +327,17 @@
         System.arraycopy(defaultDisplayOption.borderSpaces, 0, result.borderSpaces, 0,
                 COUNT_SIZES);
 
-        initGrid(context, myInfo, result, deviceType);
+        initGrid(context, myInfo, result);
     }
 
     @Override
     public void close() {
         DisplayController.INSTANCE.executeIfCreated(dc -> dc.setPriorityListener(null));
+        if (mLandscapeModePreferenceListener != null) {
+            LauncherPrefs.INSTANCE.executeIfCreated(
+                    lp -> lp.removeListener(mLandscapeModePreferenceListener, FIXED_LANDSCAPE_MODE)
+            );
+        }
     }
 
     public static String getCurrentGridName(Context context) {
@@ -308,11 +346,14 @@
 
     private String initGrid(Context context, String gridName) {
         Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
-        @DeviceType int deviceType = displayInfo.getDeviceType();
 
-        List<DisplayOption> allOptions =
-                getPredefinedDeviceProfiles(context, gridName, deviceType,
-                        RestoreDbTask.isPending(context));
+        List<DisplayOption> allOptions = getPredefinedDeviceProfiles(
+                context,
+                gridName,
+                displayInfo,
+                RestoreDbTask.isPending(context),
+                FIXED_LANDSCAPE_MODE.get(context)
+        );
 
         // Filter out options that don't have the same number of columns as the grid
         DeviceGridState deviceGridState = new DeviceGridState(context);
@@ -321,9 +362,10 @@
 
         DisplayOption displayOption =
                 invDistWeightedInterpolate(displayInfo, allOptionsFilteredByColCount.isEmpty()
-                        ? new ArrayList<>(allOptions)
-                        : new ArrayList<>(allOptionsFilteredByColCount), deviceType);
-        initGrid(context, displayInfo, displayOption, deviceType);
+                                ? new ArrayList<>(allOptions)
+                                : new ArrayList<>(allOptionsFilteredByColCount),
+                        displayInfo.getDeviceType());
+        initGrid(context, displayInfo, displayOption);
         return displayOption.grid.name;
     }
 
@@ -347,8 +389,7 @@
         return new InvariantDeviceProfile().initGrid(context, null);
     }
 
-    private void initGrid(Context context, Info displayInfo, DisplayOption displayOption,
-            @DeviceType int deviceType) {
+    private void initGrid(Context context, Info displayInfo, DisplayOption displayOption) {
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         GridOption closestProfile = displayOption.grid;
         numRows = closestProfile.numRows;
@@ -381,7 +422,8 @@
         allAppsCellSpecsTwoPanelId = closestProfile.mAllAppsCellSpecsTwoPanelId;
         numAllAppsRowsForCellHeightCalculation =
                 closestProfile.mNumAllAppsRowsForCellHeightCalculation;
-        this.deviceType = deviceType;
+        this.deviceType = displayInfo.getDeviceType();
+        this.displayInfo = displayInfo;
 
         inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing;
 
@@ -425,6 +467,9 @@
 
         startAlignTaskbar = displayOption.startAlignTaskbar;
 
+        // Fixed Landscape mode
+        isFixedLandscapeMode = FIXED_LANDSCAPE_MODE.get(context) && Flags.oneGridSpecs();
+
         // If the partner customization apk contains any grid overrides, apply them
         // Supported overrides: numRows, numColumns, iconSize
         applyPartnerDeviceProfileOverrides(context, metrics);
@@ -480,8 +525,12 @@
         mChangeListeners.remove(listener);
     }
 
-    public void setCurrentGrid(Context context, String gridName) {
-        LauncherPrefs.get(context).put(GRID_NAME, gridName);
+    /**
+     * Updates the current grid, this triggers a new IDP, reloads the database and triggers a grid
+     * migration.
+     */
+    public void setCurrentGrid(Context context, String newGridName) {
+        LauncherPrefs.get(context).put(GRID_NAME, newGridName);
         MAIN_EXECUTOR.execute(() -> {
             Trace.beginSection("InvariantDeviceProfile#setCurrentGrid");
             onConfigChanged(context.getApplicationContext());
@@ -489,6 +538,24 @@
         });
     }
 
+    /**
+     * Updates the mounted mode, this triggers a new IDP, reloads the database and triggers a grid
+     * migration.
+     */
+    public void setFixedLandscape(Context context, boolean isFixedLandscape) {
+        this.isFixedLandscapeMode = isFixedLandscape;
+        if (isFixedLandscape) {
+            // When in isFixedLandscape there should only be one default grid to choose from
+            MAIN_EXECUTOR.execute(() -> {
+                Trace.beginSection("InvariantDeviceProfile#setFixedLandscape");
+                onConfigChanged(context.getApplicationContext());
+                Trace.endSection();
+            });
+        } else {
+            setCurrentGrid(context, LauncherPrefs.get(context).get(GRID_NAME));
+        }
+    }
+
     private Object[] toModelState() {
         return new Object[]{
                 numColumns, numRows, numSearchContainerColumns, numDatabaseHotseatIcons,
@@ -510,8 +577,20 @@
         }
     }
 
-    private static List<DisplayOption> getPredefinedDeviceProfiles(Context context,
-            String gridName, @DeviceType int deviceType, boolean allowDisabledGrid) {
+    private static boolean firstGridFilter(GridOption gridOption, int deviceType,
+            boolean allowDisabledGrid, boolean isFixedLandscapeMode) {
+        return (gridOption.isEnabled(deviceType) || allowDisabledGrid)
+                && ((gridOption.mIsFixedLandscape == isFixedLandscapeMode)
+                && gridOption.filterByFlag(deviceType));
+    }
+
+    private static List<DisplayOption> getPredefinedDeviceProfiles(
+            Context context,
+            String gridName,
+            Info displayInfo,
+            boolean allowDisabledGrid,
+            boolean isFixedLandscapeMode
+    ) {
         ArrayList<DisplayOption> profiles = new ArrayList<>();
 
         try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
@@ -521,10 +600,10 @@
                     parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
                 if ((type == XmlPullParser.START_TAG)
                         && GridOption.TAG_NAME.equals(parser.getName())) {
-
-                    GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser));
-                    if ((gridOption.isEnabled(deviceType) || allowDisabledGrid)
-                            && gridOption.filterByFlag(deviceType)) {
+                    GridOption gridOption = new GridOption(
+                            context, Xml.asAttributeSet(parser), displayInfo);
+                    if (firstGridFilter(gridOption, displayInfo.getDeviceType(), allowDisabledGrid,
+                            isFixedLandscapeMode)) {
                         final int displayDepth = parser.getDepth();
                         while (((type = parser.next()) != XmlPullParser.END_TAG
                                 || parser.getDepth() > displayDepth)
@@ -541,12 +620,11 @@
         } catch (IOException | XmlPullParserException e) {
             throw new RuntimeException(e);
         }
-
         ArrayList<DisplayOption> filteredProfiles = new ArrayList<>();
         if (!TextUtils.isEmpty(gridName)) {
             for (DisplayOption option : profiles) {
-                if (gridName.equals(option.grid.name)
-                        && (option.grid.isEnabled(deviceType) || allowDisabledGrid)) {
+                if (gridName.equals(option.grid.name) && (option.grid.isEnabled(
+                        displayInfo.getDeviceType()) || allowDisabledGrid)) {
                     filteredProfiles.add(option);
                 }
             }
@@ -572,10 +650,10 @@
      * Parses through the xml to find NumRows specs. Then calls findBestRowCount to get the correct
      * row count for this GridOption.
      *
-     * @return the result of {@link #findBestRowCount(List, Context, int)}.
+     * @return the result of {@link #findBestRowCount(List, Info)}.
      */
     public static NumRows getRowCount(ResourceHelper resourceHelper, Context context,
-            int deviceType) {
+            Info displayInfo) {
         ArrayList<NumRows> rowCounts = new ArrayList<>();
 
         try (XmlResourceParser parser = resourceHelper.getXml()) {
@@ -592,21 +670,19 @@
             throw new RuntimeException(e);
         }
 
-        return findBestRowCount(rowCounts, context, deviceType);
+        return findBestRowCount(rowCounts, displayInfo);
     }
 
     /**
      * @return the biggest row count that fits the display dimensions spec using NumRows to
      * determine that. If no best row count is found, return -1.
      */
-    public static NumRows findBestRowCount(List<NumRows> list, Context context,
-            @DeviceType int deviceType) {
-        Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
+    public static NumRows findBestRowCount(List<NumRows> list, Info displayInfo) {
         int minWidthPx = Integer.MAX_VALUE;
         int minHeightPx = Integer.MAX_VALUE;
         for (WindowBounds bounds : displayInfo.supportedBounds) {
             boolean isTablet = displayInfo.isTablet(bounds);
-            if (isTablet && deviceType == TYPE_MULTI_DISPLAY) {
+            if (isTablet && displayInfo.getDeviceType() == TYPE_MULTI_DISPLAY) {
                 // For split displays, take half width per page
                 minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2);
                 minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
@@ -651,7 +727,6 @@
      * supported. Ej. 4x4 -> normal, 5x4 -> practical, etc.
      * (Note: the name of the grid can be different for the same grid size depending of
      * the values of the InvariantDeviceProfile)
-     *
      */
     public String getGridNameFromSize(Context context, Point size) {
         return parseAllGridOptions(context).stream()
@@ -677,9 +752,11 @@
      * @return all the grid options that can be shown on the device
      */
     public List<GridOption> parseAllGridOptions(Context context) {
-        return parseAllDefinedGridOptions(context)
+        return parseAllDefinedGridOptions(context, displayInfo)
                 .stream()
                 .filter(go -> go.isEnabled(deviceType))
+                // if in fixedLandscape, then only show fixed landscape grids
+                .filter(go -> go.mIsFixedLandscape == isFixedLandscapeMode)
                 .filter(go -> go.filterByFlag(deviceType))
                 .collect(Collectors.toList());
     }
@@ -687,9 +764,8 @@
     /**
      * @return all the grid options that can be shown on the device
      */
-    public static List<GridOption> parseAllDefinedGridOptions(Context context) {
+    public static List<GridOption> parseAllDefinedGridOptions(Context context, Info displayInfo) {
         List<GridOption> result = new ArrayList<>();
-
         try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
             final int depth = parser.getDepth();
             int type;
@@ -697,7 +773,7 @@
                     || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
                 if ((type == XmlPullParser.START_TAG)
                         && GridOption.TAG_NAME.equals(parser.getName())) {
-                    result.add(new GridOption(context, Xml.asAttributeSet(parser)));
+                    result.add(new GridOption(context, Xml.asAttributeSet(parser), displayInfo));
                 }
             }
         } catch (IOException | XmlPullParserException e) {
@@ -952,8 +1028,9 @@
         private final int mAllAppsCellSpecsId;
         private final int mAllAppsCellSpecsTwoPanelId;
         private final int mRowCountSpecsId;
+        private final boolean mIsFixedLandscape;
 
-        public GridOption(Context context, AttributeSet attrs) {
+        public GridOption(Context context, AttributeSet attrs, Info displayInfo) {
             TypedArray a = context.obtainStyledAttributes(
                     attrs, R.styleable.GridDisplayOption);
             name = a.getString(R.styleable.GridDisplayOption_name);
@@ -965,7 +1042,7 @@
             mIsDualGrid = a.getBoolean(R.styleable.GridDisplayOption_isDualGrid, false);
             if (mRowCountSpecsId != INVALID_RESOURCE_HANDLE) {
                 ResourceHelper resourceHelper = new ResourceHelper(context, mRowCountSpecsId);
-                NumRows numR = getRowCount(resourceHelper, context, deviceCategory);
+                NumRows numR = getRowCount(resourceHelper, context, displayInfo);
                 numRows = numR.mNumRowsNew;
                 dbFile = numR.mDbFile;
             } else {
@@ -1093,6 +1170,8 @@
                 mNumAllAppsRowsForCellHeightCalculation = numRows;
             }
 
+            mIsFixedLandscape = a.getBoolean(R.styleable.GridDisplayOption_isFixedLandscape, false);
+
             int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
                     DONT_INLINE_QSB);
             inlineQsb[INDEX_DEFAULT] =
@@ -1124,7 +1203,8 @@
         }
 
         public boolean isNewGridOption() {
-            return mRowCountSpecsId != INVALID_RESOURCE_HANDLE;
+            Log.d("HHHH", "GRID = " + mIsFixedLandscape);
+            return mRowCountSpecsId != INVALID_RESOURCE_HANDLE || mIsFixedLandscape;
         }
 
         public boolean filterByFlag(int deviceType) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 6446f7b..6145077 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -279,6 +279,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
@@ -761,7 +762,6 @@
             if (!initDeviceProfile(mDeviceProfile.inv) && !mForceConfigUpdate) {
                 return;
             }
-
             dispatchDeviceProfileChanged();
             reapplyUi();
             mDragLayer.recreateControllers();
@@ -777,6 +777,15 @@
         }
     }
 
+    private void updateFixedLandscape() {
+        if (!com.android.launcher3.Flags.oneGridSpecs()) {
+            return;
+        }
+        getRotationHelper().setFixedLandscape(
+                Objects.requireNonNull(mDeviceProfile.inv).isFixedLandscapeMode
+        );
+    }
+
     public void onAssistantVisibilityChanged(float visibility) {
         mHotseat.getQsb().setAlpha(1f - visibility);
     }
@@ -805,6 +814,7 @@
                     mDeviceProfile.numShownHotseatIcons);
         }
         mModelWriter = mModel.getWriter(true, mCellPosMapper, this);
+        updateFixedLandscape();
         return true;
     }
 
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 95c0ee8..df75470 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -24,6 +24,7 @@
     public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
     public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
     public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db";
+    public static final String LAUNCHER_7_BY_3_DB = "launcher_7_by_3.db";
     public static final String BACKUP_DB = "backup.db";
     public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
     public static final String MANAGED_USER_PREFERENCES_KEY =
@@ -43,7 +44,8 @@
             LAUNCHER_5_BY_6_DB,
             LAUNCHER_4_BY_4_DB,
             LAUNCHER_3_BY_3_DB,
-            LAUNCHER_2_BY_2_DB));
+            LAUNCHER_2_BY_2_DB,
+            LAUNCHER_7_BY_3_DB));
 
     public static final List<String> OTHER_FILES = Collections.unmodifiableList(Arrays.asList(
             BACKUP_DB,
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 5c03644..712c56c 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -26,6 +26,7 @@
 import com.android.launcher3.pm.InstallSessionHelper
 import com.android.launcher3.provider.RestoreDbTask
 import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
+import com.android.launcher3.settings.SettingsActivity
 import com.android.launcher3.states.RotationHelper
 import com.android.launcher3.util.DisplayController
 import com.android.launcher3.util.MainThreadInitializedObject
@@ -133,6 +134,7 @@
             nonRestorableItem(FIRST_LOAD_AFTER_RESTORE_KEY, false, EncryptionType.ENCRYPTED)
         @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
         @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
+
         @JvmField
         val GRID_NAME =
             ConstantItem(
@@ -148,6 +150,9 @@
                 RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
             }
 
+        @JvmField
+        val FIXED_LANDSCAPE_MODE = backedUpItem(SettingsActivity.FIXED_LANDSCAPE_MODE, false)
+
         // Preferences for widget configurations
         @JvmField
         val RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 102189b..7d5e481 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -375,8 +375,14 @@
     }
 
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
-        if ((this != NORMAL && this != HINT_STATE)
-                || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
+        DeviceProfile dp = launcher.getDeviceProfile();
+        boolean shouldFadeAdjacentScreens = (this == NORMAL || this == HINT_STATE)
+                && dp.shouldFadeAdjacentWorkspaceScreens();
+        // Avoid showing adjacent screens behind handheld All Apps sheet.
+        if (Flags.allAppsSheetForHandheld() && dp.isPhone && this == ALL_APPS) {
+            shouldFadeAdjacentScreens = true;
+        }
+        if (!shouldFadeAdjacentScreens) {
             return DEFAULT_ALPHA_PROVIDER;
         }
         final int centerPage = launcher.getWorkspace().getNextPage();
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 2456980..1b58987 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -47,7 +47,6 @@
 import android.os.Process;
 import android.os.UserManager;
 import android.util.AttributeSet;
-import android.util.FloatProperty;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.KeyEvent;
@@ -96,6 +95,7 @@
 import com.android.launcher3.views.ScrimView;
 import com.android.launcher3.views.SpringRelativeLayout;
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
+import com.android.systemui.plugins.AllAppsRow;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -115,19 +115,6 @@
         ScrimView.ScrimDrawingController {
 
 
-    public static final FloatProperty<ActivityAllAppsContainerView<?>> BOTTOM_SHEET_ALPHA =
-            new FloatProperty<>("bottomSheetAlpha") {
-                @Override
-                public Float get(ActivityAllAppsContainerView<?> containerView) {
-                    return containerView.mBottomSheetAlpha;
-                }
-
-                @Override
-                public void setValue(ActivityAllAppsContainerView<?> containerView, float v) {
-                    containerView.setBottomSheetAlpha(v);
-                }
-            };
-
     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";
@@ -166,6 +153,7 @@
     private final RectF mTmpRectF = new RectF();
     protected AllAppsPagedView mViewPager;
     protected FloatingHeaderView mHeader;
+    protected final List<AllAppsRow> mAdditionalHeaderRows = new ArrayList<>();
     protected View mBottomSheetBackground;
     protected RecyclerViewFastScroller mFastScroller;
     private ConstraintLayout mFastScrollLetterLayout;
@@ -191,8 +179,6 @@
     private ScrimView mScrimView;
     private int mHeaderColor;
     private int mBottomSheetBackgroundColor;
-    private float mBottomSheetAlpha = 1f;
-    private boolean mForceBottomSheetVisible;
     private int mTabsProtectionAlpha;
     @Nullable private AllAppsTransitionController mAllAppsTransitionController;
 
@@ -278,6 +264,8 @@
 
         getLayoutInflater().inflate(R.layout.all_apps_content, this);
         mHeader = findViewById(R.id.all_apps_header);
+        mAdditionalHeaderRows.clear();
+        mAdditionalHeaderRows.addAll(getAdditionalHeaderRows());
         mBottomSheetBackground = findViewById(R.id.bottom_sheet_background);
         mBottomSheetHandleArea = findViewById(R.id.bottom_sheet_handle_area);
         mSearchRecyclerView = findViewById(R.id.search_results_list_view);
@@ -304,6 +292,10 @@
         mSearchUiManager = (SearchUiManager) mSearchContainer;
     }
 
+    public List<AllAppsRow> getAdditionalHeaderRows() {
+        return List.of();
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -351,20 +343,6 @@
         return mSearchUiManager;
     }
 
-    public View getBottomSheetBackground() {
-        return mBottomSheetBackground;
-    }
-
-    /**
-     * Temporarily force the bottom sheet to be visible on non-tablets.
-     *
-     * @param force {@code true} means bottom sheet will be visible on phones until {@code reset()}.
-     */
-    public void forceBottomSheetVisible(boolean force) {
-        mForceBottomSheetVisible = force;
-        updateBackgroundVisibility(mActivityContext.getDeviceProfile());
-    }
-
     public View getSearchView() {
         return mSearchContainer;
     }
@@ -496,7 +474,7 @@
         if (mHeader != null && mHeader.getVisibility() == VISIBLE) {
             mHeader.reset(animate);
         }
-        forceBottomSheetVisible(false);
+        updateBackgroundVisibility(mActivityContext.getDeviceProfile());
         // Reset the base recycler view after transitioning home.
         updateHeaderScroll(0);
         if (exitSearch) {
@@ -744,6 +722,8 @@
     }
 
     void setupHeader() {
+        mAdditionalHeaderRows.forEach(row -> mHeader.onPluginDisconnected(row));
+
         mHeader.setVisibility(View.VISIBLE);
         boolean tabsHidden = !mUsingTabs;
         mHeader.setup(
@@ -761,6 +741,7 @@
                 adapterHolder.mRecyclerView.scrollToTop();
             }
         });
+        mAdditionalHeaderRows.forEach(row -> mHeader.onPluginConnected(row, mActivityContext));
 
         removeCustomRules(mHeader);
         if (isSearchBarFloating()) {
@@ -1002,18 +983,13 @@
     }
 
     protected void updateBackgroundVisibility(DeviceProfile deviceProfile) {
-        boolean visible = deviceProfile.isTablet || mForceBottomSheetVisible;
-        mBottomSheetBackground.setVisibility(visible ? View.VISIBLE : View.GONE);
-        // Note: For tablets, the opaque background and header protection are added in drawOnScrim.
+        mBottomSheetBackground.setVisibility(
+                deviceProfile.shouldShowAllAppsOnSheet() ? View.VISIBLE : View.GONE);
+        // Note: The opaque sheet background and header protection are added in drawOnScrim.
         // For the taskbar entrypoint, the scrim is drawn by its abstract slide in view container,
         // so its header protection is derived from this scrim instead.
     }
 
-    private void setBottomSheetAlpha(float alpha) {
-        // Bottom sheet alpha is always 1 for tablets.
-        mBottomSheetAlpha = mActivityContext.getDeviceProfile().isTablet ? 1f : alpha;
-    }
-
     @VisibleForTesting
     public void onAppsUpdated() {
         mHasWorkApps = Stream.of(mAllAppsStore.getApps())
@@ -1151,8 +1127,8 @@
         applyAdapterSideAndBottomPaddings(grid);
 
         MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
-        // Ignore left/right insets on tablet because we are already centered in-screen.
-        if (grid.isTablet) {
+        // Ignore left/right insets on bottom sheet because we are already centered in-screen.
+        if (grid.shouldShowAllAppsOnSheet()) {
             mlp.leftMargin = mlp.rightMargin = 0;
         } else {
             mlp.leftMargin = insets.left;
@@ -1398,7 +1374,7 @@
         // Draw full background panel for tablets.
         if (hasBottomSheet) {
             mHeaderPaint.setColor(mBottomSheetBackgroundColor);
-            mHeaderPaint.setAlpha((int) (255 * mBottomSheetAlpha));
+            mHeaderPaint.setAlpha(255);
 
             mTmpRectF.set(
                     leftWithScale,
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index c6852e0..cfb238b 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.allapps;
 
 import static com.android.app.animation.Interpolators.DECELERATE_1_7;
-import static com.android.app.animation.Interpolators.INSTANT;
 import static com.android.app.animation.Interpolators.LINEAR;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -28,7 +27,6 @@
 import static com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree;
 import static com.android.launcher3.UtilitiesKt.restoreAttributesOnViewTree;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV;
@@ -45,7 +43,6 @@
 import androidx.annotation.FloatRange;
 import androidx.annotation.Nullable;
 
-import com.android.app.animation.Interpolators;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.Launcher;
@@ -106,7 +103,7 @@
 
                 @Override
                 public Float get(AllAppsTransitionController controller) {
-                    if (controller.mIsTablet) {
+                    if (controller.mShouldShowAllAppsOnSheet) {
                         return controller.mAppsView.getActiveRecyclerView().getTranslationY();
                     } else {
                         return controller.getAppsViewPullbackTranslationY().getValue();
@@ -115,7 +112,7 @@
 
                 @Override
                 public void setValue(AllAppsTransitionController controller, float translation) {
-                    if (controller.mIsTablet) {
+                    if (controller.mShouldShowAllAppsOnSheet) {
                         controller.mAppsView.getActiveRecyclerView().setTranslationY(translation);
                         controller.getAppsViewPullbackTranslationY().setValue(
                                 ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
@@ -134,7 +131,7 @@
 
                 @Override
                 public Float get(AllAppsTransitionController controller) {
-                    if (controller.mIsTablet) {
+                    if (controller.mShouldShowAllAppsOnSheet) {
                         return controller.mAppsView.getActiveRecyclerView().getAlpha();
                     } else {
                         return controller.getAppsViewPullbackAlpha().getValue();
@@ -143,7 +140,7 @@
 
                 @Override
                 public void setValue(AllAppsTransitionController controller, float alpha) {
-                    if (controller.mIsTablet) {
+                    if (controller.mShouldShowAllAppsOnSheet) {
                         controller.mAppsView.getActiveRecyclerView().setAlpha(alpha);
                         controller.getAppsViewPullbackAlpha().setValue(
                                 ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
@@ -168,6 +165,7 @@
     @Nullable private Animator.AnimatorListener mAllAppsSearchBackAnimationListener;
 
     private boolean mIsVerticalLayout;
+    private boolean mShouldShowAllAppsOnSheet;
 
     // Animation in this class is controlled by a single variable {@link mProgress}.
     // Visually, it represents top y coordinate of the all apps container if multiplied with
@@ -183,8 +181,6 @@
     private MultiValueAlpha mAppsViewAlpha;
     private MultiPropertyFactory<View> mAppsViewTranslationY;
 
-    private boolean mIsTablet;
-
     private boolean mHasScaleEffect;
     private final VibratorWrapper mVibratorWrapper;
 
@@ -193,7 +189,7 @@
         DeviceProfile dp = mLauncher.getDeviceProfile();
         mProgress = 1f;
         mIsVerticalLayout = dp.isVerticalBarLayout();
-        mIsTablet = dp.isTablet;
+        mShouldShowAllAppsOnSheet = dp.shouldShowAllAppsOnSheet();
         mNavScrimFlag = Themes.getAttrBoolean(l, R.attr.isMainColorDark)
                 ? FLAG_DARK_NAV : FLAG_LIGHT_NAV;
 
@@ -217,7 +213,7 @@
             mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
         }
 
-        mIsTablet = dp.isTablet;
+        mShouldShowAllAppsOnSheet = dp.shouldShowAllAppsOnSheet();
     }
 
     /**
@@ -280,10 +276,9 @@
             return;
         }
 
-        float deceleratedProgress = Interpolators.BACK_GESTURE.getInterpolation(backProgress);
         float scaleProgress = ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE
                 + (1 - ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE)
-                * (1 - deceleratedProgress);
+                * (1 - backProgress);
 
         mAllAppScale.updateValue(scaleProgress);
     }
@@ -395,10 +390,6 @@
         setter.setFloat(getAppsViewPullbackAlpha(), MultiPropertyFactory.MULTI_PROPERTY_VALUE,
                 hasAllAppsContent ? 1 : 0, allAppsFade);
 
-        setter.setFloat(mLauncher.getAppsView(),
-                ActivityAllAppsContainerView.BOTTOM_SHEET_ALPHA, hasAllAppsContent ? 1 : 0,
-                config.getInterpolator(ANIM_ALL_APPS_BOTTOM_SHEET_FADE, INSTANT));
-
         boolean shouldProtectHeader = !config.hasAnimationFlag(StateAnimationConfig.SKIP_SCRIM)
                 && (ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS);
         mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null);
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index ac06ab4..8193511 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.allapps.FloatingHeaderRow.NO_ROWS;
+
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Point;
@@ -109,11 +111,11 @@
 
     // This is initialized once during inflation and stays constant after that. Fixed views
     // cannot be added or removed dynamically.
-    private FloatingHeaderRow[] mFixedRows = FloatingHeaderRow.NO_ROWS;
+    private FloatingHeaderRow[] mFixedRows = NO_ROWS;
 
     // Array of all fixed rows and plugin rows. This is initialized every time a plugin is
     // enabled or disabled, and represent the current set of all rows.
-    private FloatingHeaderRow[] mAllRows = FloatingHeaderRow.NO_ROWS;
+    private FloatingHeaderRow[] mAllRows = NO_ROWS;
 
     public FloatingHeaderView(@NonNull Context context) {
         this(context, null);
@@ -180,6 +182,10 @@
 
     @Override
     public void onPluginConnected(AllAppsRow allAppsRowPlugin, Context context) {
+        if (mPluginRows.containsKey(allAppsRowPlugin)) {
+            // Plugin has already been connected
+            return;
+        }
         PluginHeaderRow headerRow = new PluginHeaderRow(allAppsRowPlugin, this);
         addView(headerRow.mView, indexOfChild(mTabLayout));
         mPluginRows.put(allAppsRowPlugin, headerRow);
@@ -211,6 +217,9 @@
     @Override
     public void onPluginDisconnected(AllAppsRow plugin) {
         PluginHeaderRow row = mPluginRows.get(plugin);
+        if (row == null) {
+            return;
+        }
         removeView(row.mView);
         mPluginRows.remove(plugin);
         recreateAllRowsArray();
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 94c36c0..3000b25 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.graphics;
 
+import static android.content.res.Configuration.UI_MODE_NIGHT_NO;
+import static android.content.res.Configuration.UI_MODE_NIGHT_YES;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
@@ -25,6 +27,7 @@
 import android.app.WallpaperColors;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.database.Cursor;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
@@ -84,6 +87,7 @@
     private static final String KEY_COLORS = "wallpaper_colors";
     private static final String KEY_COLOR_RESOURCE_IDS = "color_resource_ids";
     private static final String KEY_COLOR_VALUES = "color_values";
+    private static final String KEY_DARK_MODE = "use_dark_mode";
 
     private Context mContext;
     private final IBinder mHostToken;
@@ -95,6 +99,7 @@
     private final Display mDisplay;
     private final WallpaperColors mWallpaperColors;
     private SparseIntArray mPreviewColorOverride;
+    @Nullable private Boolean mDarkMode;
     private final RunnableList mLifeCycleTracker;
 
     private final SurfaceControlViewHost mSurfaceControlViewHost;
@@ -235,6 +240,8 @@
     }
 
     private void updateColorOverrides(Bundle bundle) {
+        mDarkMode =
+                bundle.containsKey(KEY_DARK_MODE) ? bundle.getBoolean(KEY_DARK_MODE) : null;
         int[] ids = bundle.getIntArray(KEY_COLOR_RESOURCE_IDS);
         int[] colors = bundle.getIntArray(KEY_COLOR_VALUES);
         if (ids != null && colors != null) {
@@ -253,6 +260,18 @@
      */
     private Context getPreviewContext() {
         Context context = mContext.createDisplayContext(mDisplay);
+        if (mDarkMode != null) {
+            Configuration configuration = new Configuration(
+                    context.getResources().getConfiguration());
+            if (mDarkMode) {
+                configuration.uiMode &= ~UI_MODE_NIGHT_NO;
+                configuration.uiMode |= UI_MODE_NIGHT_YES;
+            } else {
+                configuration.uiMode &= ~UI_MODE_NIGHT_YES;
+                configuration.uiMode |= UI_MODE_NIGHT_NO;
+            }
+            context = context.createConfigurationContext(configuration);
+        }
         if (Flags.newCustomizationPickerUi()) {
             if (mPreviewColorOverride != null) {
                 LocalColorExtractor.newInstance(context)
diff --git a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
index f8a5552..a78da23 100644
--- a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
+++ b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
@@ -121,7 +121,10 @@
         item: CacheableShortcutInfo,
         provider: IconProvider,
     ): String? =
-        item.shortcutInfo.lastChangedTimestamp.toString() +
+        // Manifest shortcuts get updated on every reboot. Don't include their change timestamp as
+        // it gets covered by the app's version
+        (if (item.shortcutInfo.isDeclaredInManifest) ""
+        else item.shortcutInfo.lastChangedTimestamp.toString()) +
             "-" +
             provider.getStateForApp(getApplicationInfo(item))
 }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index e5cd76a..2550ebb 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -175,6 +175,10 @@
         @UiEvent(doc = "User searched for a widget in the widget picker.")
         LAUNCHER_WIDGETSTRAY_SEARCHED(819),
 
+        @UiEvent(doc = "User clicked on view all button to expand the displayed list in the "
+                + "widget picker.")
+        LAUNCHER_WIDGETSTRAY_EXPAND_PRESS(1978),
+
         @UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
 
diff --git a/src/com/android/launcher3/model/GridSizeMigrationDBController.java b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
index 2d6be7e..617cac7 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationDBController.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
-import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
 import static com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE;
 import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
@@ -118,13 +117,14 @@
             @NonNull DeviceGridState srcDeviceState,
             @NonNull DeviceGridState destDeviceState,
             @NonNull DatabaseHelper target,
-            @NonNull SQLiteDatabase source) {
+            @NonNull SQLiteDatabase source,
+            boolean isDestNewDb) {
 
         if (!needsToMigrate(srcDeviceState, destDeviceState)) {
             return true;
         }
 
-        if (LauncherPrefs.get(context).get(IS_FIRST_LOAD_AFTER_RESTORE)
+        if (isDestNewDb
                 && srcDeviceState.getColumns().equals(destDeviceState.getColumns())
                 && srcDeviceState.getRows() < destDeviceState.getRows()) {
             // Only use this strategy when comparing the previous grid to the new grid and the
diff --git a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
index 07316ef..c856d4b 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
+++ b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
@@ -46,6 +46,7 @@
         destDeviceState: DeviceGridState,
         target: DatabaseHelper,
         source: SQLiteDatabase,
+        isDestNewDb: Boolean,
     ) {
         if (!GridSizeMigrationDBController.needsToMigrate(srcDeviceState, destDeviceState)) {
             return
@@ -57,7 +58,7 @@
         // This is a special case where if the grid is the same amount of columns but a larger
         // amount of rows we simply copy over the source grid to the destination grid, rather
         // than undergoing the general grid migration.
-        if (shouldMigrateToStrictlyTallerGrid(isFirstLoad, srcDeviceState, destDeviceState)) {
+        if (shouldMigrateToStrictlyTallerGrid(isDestNewDb, srcDeviceState, destDeviceState)) {
             GridSizeMigrationDBController.copyCurrentGridToNewGrid(
                 context,
                 destDeviceState,
@@ -335,11 +336,11 @@
 
     /** Only migrate the grid in this manner if the target grid is taller and not wider. */
     private fun shouldMigrateToStrictlyTallerGrid(
-        isFirstLoad: Boolean,
+        isDestNewDb: Boolean,
         srcDeviceState: DeviceGridState,
         destDeviceState: DeviceGridState,
     ): Boolean {
-        return isFirstLoad &&
+        return isDestNewDb &&
             srcDeviceState.columns == destDeviceState.columns &&
             srcDeviceState.rows < destDeviceState.rows
     }
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 094798b..6ff8547 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -89,6 +89,7 @@
 import java.io.File;
 import java.io.InputStream;
 import java.io.StringReader;
+import java.util.List;
 
 /**
  * Utility class which maintains an instance of Launcher database and provides utility methods
@@ -368,6 +369,13 @@
 
         InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
         DatabaseHelper oldHelper = mOpenHelper;
+
+        // We save the existing db's before creating the destination db helper so we know what logic
+        // to run in grid migration based on if that grid already existed before migration or not.
+        List<String> existingDBs = LauncherFiles.GRID_DB_FILES.stream()
+                .filter(dbName -> mContext.getDatabasePath(dbName).exists())
+                .toList();
+
         mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
                 : createDatabaseHelper(true, new DeviceGridState(idp).getDbFile());
         try {
@@ -376,9 +384,11 @@
             // This is the state we want to migrate to that is given by the idp
             DeviceGridState destDeviceState = new DeviceGridState(idp);
 
+            boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
+
             GridSizeMigrationLogic gridSizeMigrationLogic = new GridSizeMigrationLogic();
             gridSizeMigrationLogic.migrateGrid(mContext, srcDeviceState, destDeviceState,
-                    mOpenHelper, oldHelper.getWritableDatabase());
+                    mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
         } catch (Exception e) {
             resetLauncherDb(restoreEventLogger);
             throw new Exception("Failed to migrate grid", e);
@@ -441,6 +451,13 @@
             return false;
         }
         DatabaseHelper oldHelper = mOpenHelper;
+
+        // We save the existing db's before creating the destination db helper so we know what logic
+        // to run in grid migration based on if that grid already existed before migration or not.
+        List<String> existingDBs = LauncherFiles.GRID_DB_FILES.stream()
+                .filter(dbName -> mContext.getDatabasePath(dbName).exists())
+                .toList();
+
         mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
                 : createDatabaseHelper(true /* forMigration */, targetDbName);
         try {
@@ -448,8 +465,11 @@
             DeviceGridState srcDeviceState = new DeviceGridState(mContext);
             // This is the state we want to migrate to that is given by the idp
             DeviceGridState destDeviceState = new DeviceGridState(idp);
+
+            boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
+
             return GridSizeMigrationDBController.migrateGridIfNeeded(mContext, srcDeviceState,
-                    destDeviceState, mOpenHelper, oldHelper.getWritableDatabase());
+                    destDeviceState, mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
         } catch (Exception e) {
             FileLog.e(TAG, "Failed to migrate grid", e);
             return false;
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index bd9298b..5851f62 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -26,6 +26,7 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -66,6 +67,8 @@
     @VisibleForTesting
     static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
 
+    public static final String FIXED_LANDSCAPE_MODE = "pref_fixed_landscape_mode";
+
     private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging";
 
     public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
@@ -236,7 +239,7 @@
         /**
          * Finds the parent preference screen for the given target key.
          *
-         * @param parent the parent preference screen
+         * @param parent    the parent preference screen
          * @param targetKey the key of the preference to find
          * @return the parent preference screen that contains the target preference
          */
@@ -286,13 +289,14 @@
          * will remove that preference from the list.
          */
         protected boolean initPreference(Preference preference) {
+            DisplayController.Info info = DisplayController.INSTANCE.get(getContext()).getInfo();
             switch (preference.getKey()) {
                 case NOTIFICATION_DOTS_PREFERENCE_KEY:
                     return BuildConfig.NOTIFICATION_DOTS_ENABLED;
-
                 case ALLOW_ROTATION_PREFERENCE_KEY:
-                    DisplayController.Info info =
-                            DisplayController.INSTANCE.get(getContext()).getInfo();
+                    if (Flags.oneGridSpecs()) {
+                        return false;
+                    }
                     if (info.isTablet(info.realBounds)) {
                         // Launcher supports rotation by default. No need to show this setting.
                         return false;
@@ -300,14 +304,29 @@
                     // Initialize the UI once
                     preference.setDefaultValue(RotationHelper.getAllowRotationDefaultValue(info));
                     return true;
-
                 case DEVELOPER_OPTIONS_KEY:
                     if (IS_STUDIO_BUILD) {
                         preference.setOrder(0);
                     }
                     return mDeveloperOptionsEnabled;
+                case FIXED_LANDSCAPE_MODE:
+                    if (!Flags.oneGridSpecs()) {
+                        return false;
+                    }
+                    // When the setting changes rotate the screen accordingly to showcase the result
+                    // of the setting
+                    preference.setOnPreferenceChangeListener(
+                            (pref, newValue) -> {
+                                getActivity().setRequestedOrientation(
+                                        (boolean) newValue
+                                                ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+                                                : ActivityInfo.SCREEN_ORIENTATION_USER
+                                );
+                                return true;
+                            }
+                    );
+                    return !info.isTablet(info.realBounds);
             }
-
             return true;
         }
 
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 4c9da5d..7d7ccd3 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -18,6 +18,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 
 import static com.android.launcher3.LauncherPrefs.ALLOW_ROTATION;
@@ -63,6 +64,8 @@
     public static final int REQUEST_ROTATE = 1;
     public static final int REQUEST_LOCK = 2;
 
+    private boolean mIsFixedLandscape = false;
+
     @NonNull
     private final BaseActivity mActivity;
     private final Handler mRequestOrientationHandler;
@@ -163,6 +166,18 @@
         notifyChange();
     }
 
+    public boolean isFixedLandscape() {
+        return mIsFixedLandscape;
+    }
+
+    /**
+     * If fixedLandscape is true then the Launcher become landscape until set false..
+     */
+    public void setFixedLandscape(boolean fixedLandscape) {
+        mIsFixedLandscape = fixedLandscape;
+        notifyChange();
+    }
+
     // Used by tests only.
     public void forceAllowRotationForTesting(boolean allowRotation) {
         if (mDestroyed) return;
@@ -203,6 +218,8 @@
                     SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
         } else if (mCurrentStateRequest == REQUEST_LOCK) {
             activityFlags = SCREEN_ORIENTATION_LOCKED;
+        } else if (mIsFixedLandscape) {
+            activityFlags = SCREEN_ORIENTATION_USER_LANDSCAPE;
         } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE
                 || mHomeRotationEnabled || mForceAllowRotationForTesting) {
             activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 0ca5afd..2ffbbf4 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -77,7 +77,6 @@
             ANIM_WORKSPACE_PAGE_TRANSLATE_X,
             ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
             ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
-            ANIM_ALL_APPS_BOTTOM_SHEET_FADE,
             ANIM_ALL_APPS_KEYBOARD_FADE
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -101,8 +100,7 @@
     public static final int ANIM_WORKSPACE_PAGE_TRANSLATE_X = 15;
     public static final int ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN = 17;
     public static final int ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE = 18;
-    public static final int ANIM_ALL_APPS_BOTTOM_SHEET_FADE = 19;
-    public static final int ANIM_ALL_APPS_KEYBOARD_FADE = 20;
+    public static final int ANIM_ALL_APPS_KEYBOARD_FADE = 19;
 
     private static final int ANIM_TYPES_COUNT = 21;
 
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 9dcdf22..107bcc1 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -198,7 +198,7 @@
      * Applies Animation config values for transition from all apps to home.
      */
     public static void applyAllAppsToNormalConfig(Launcher launcher, StateAnimationConfig config) {
-        if (launcher.getDeviceProfile().isTablet) {
+        if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
             config.setInterpolator(ANIM_SCRIM_FADE,
                     Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER));
             config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME);
@@ -240,7 +240,7 @@
      */
     public static void applyNormalToAllAppsAnimConfig(
             Launcher launcher, StateAnimationConfig config) {
-        if (launcher.getDeviceProfile().isTablet) {
+        if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
             config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
             config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER);
             if (!config.isUserControlled()) {
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 85aad89..65d02d0 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -298,8 +298,7 @@
     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     public void onBackProgressed(BackEvent backEvent) {
         final float progress = backEvent.getProgress();
-        float deceleratedProgress = Interpolators.BACK_GESTURE.getInterpolation(progress);
-        mSwipeToDismissProgress.updateValue(deceleratedProgress);
+        mSwipeToDismissProgress.updateValue(progress);
     }
 
     /**
diff --git a/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java b/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
new file mode 100644
index 0000000..8c84030
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
@@ -0,0 +1,38 @@
+/*
+ * 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.widget.model;
+
+import android.os.Process;
+
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.util.Collections;
+
+/**
+ * Binds the section to be displayed at the bottom of the widgets list that enables user to expand
+ * and view all the widget apps including non-default. Bound when
+ * {@link WidgetsListExpandActionEntry} exists in the list on adapter.
+ */
+public class WidgetsListExpandActionEntry extends WidgetsListBaseEntry {
+
+    public WidgetsListExpandActionEntry() {
+        super(/*pkgItem=*/ new PackageItemInfo(/* packageName= */ "", Process.myUserHandle()),
+                /*titleSectionName=*/ "",
+                /*items=*/ Collections.EMPTY_LIST);
+        mPkgItem.title = "";
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 1860977..2f64ab1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -16,12 +16,16 @@
 package com.android.launcher3.widget.picker;
 
 import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
+import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
 import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_EXPAND_PRESS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 import static com.android.launcher3.views.RecyclerViewFastScroller.FastScrollerLocation.WIDGET_SCROLLER;
 
+import static java.util.Collections.emptyList;
+
 import android.animation.Animator;
 import android.content.Context;
 import android.content.res.Resources;
@@ -68,6 +72,7 @@
 import com.android.launcher3.widget.BaseWidgetSheet;
 import com.android.launcher3.widget.WidgetCell;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
 import com.android.launcher3.widget.picker.search.SearchModeListener;
 import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
 import com.android.launcher3.widget.picker.search.WidgetsSearchBar.WidgetsSearchDataProvider;
@@ -87,7 +92,8 @@
  */
 public class WidgetsFullSheet extends BaseWidgetSheet
         implements OnActivePageChangedListener,
-        WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
+        WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener,
+        WidgetsListAdapter.ExpandButtonClickListener {
 
     private static final long FADE_IN_DURATION = 150;
 
@@ -257,7 +263,13 @@
         mSearchBar.initialize(new WidgetsSearchDataProvider() {
             @Override
             public List<WidgetsListBaseEntry> getWidgets() {
-                return getWidgetsToDisplay();
+                if (enableTieredWidgetsByDefaultInPicker()) {
+                    // search all
+                    return mActivityContext.getWidgetPickerDataProvider().get().getAllWidgets();
+                } else {
+                    // Can be removed when inlining enableTieredWidgetsByDefaultInPicker flag
+                    return getWidgetsToDisplay();
+                }
             }
         }, /* searchModeListener= */ this);
     }
@@ -482,6 +494,9 @@
     /**
      * Returns all displayable widgets.
      */
+    // Used by the two pane sheet to show 3-dot menu to toggle between default lists and all lists
+    // when enableTieredWidgetsByDefaultInPicker is OFF. This code path and the 3-dot menu can be
+    // safely deleted when it's alternative "enableTieredWidgetsByDefaultInPicker" flag is inlined.
     protected List<WidgetsListBaseEntry> getWidgetsToDisplay() {
         return mActivityContext.getWidgetPickerDataProvider().get().getAllWidgets();
     }
@@ -491,16 +506,27 @@
         if (mIsInSearchMode) {
             return;
         }
-        List<WidgetsListBaseEntry> widgets = getWidgetsToDisplay();
+        List<WidgetsListBaseEntry> widgets;
+        List<WidgetsListBaseEntry> defaultWidgets = emptyList();
+
+        if (enableTieredWidgetsByDefaultInPicker()) {
+            WidgetPickerData dataProvider =
+                    mActivityContext.getWidgetPickerDataProvider().get();
+            widgets = dataProvider.getAllWidgets();
+            defaultWidgets = dataProvider.getDefaultWidgets();
+        } else {
+            // This code path can be deleted once enableTieredWidgetsByDefaultInPicker is inlined.
+            widgets = getWidgetsToDisplay();
+        }
 
         AdapterHolder primaryUserAdapterHolder = mAdapters.get(AdapterHolder.PRIMARY);
-        primaryUserAdapterHolder.mWidgetsListAdapter.setWidgets(widgets);
+        primaryUserAdapterHolder.mWidgetsListAdapter.setWidgets(widgets, defaultWidgets);
 
         if (mHasWorkProfile) {
             mViewPager.setVisibility(VISIBLE);
             mTabBar.setVisibility(VISIBLE);
             AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
-            workUserAdapterHolder.mWidgetsListAdapter.setWidgets(widgets);
+            workUserAdapterHolder.mWidgetsListAdapter.setWidgets(widgets, defaultWidgets);
             onActivePageChanged(mViewPager.getCurrentPage());
         } else {
             onActivePageChanged(0);
@@ -518,6 +544,16 @@
     }
 
     @Override
+    public void onWidgetsListExpandButtonClick(View v) {
+        AdapterHolder currentAdapterHolder = mAdapters.get(getCurrentAdapterHolderType());
+        currentAdapterHolder.mWidgetsListAdapter.useExpandedList();
+        onWidgetsBound();
+        currentAdapterHolder.mWidgetsRecyclerView.announceForAccessibility(
+                mActivityContext.getString(R.string.widgets_list_expanded));
+        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_EXPAND_PRESS);
+    }
+
+    @Override
     public void enterSearchMode(boolean shouldLog) {
         if (mIsInSearchMode) return;
         setViewVisibilityBasedOnSearch(/*isInSearchMode= */ true);
@@ -832,7 +868,7 @@
                 + marginLayoutParams.topMargin;
     }
 
-    private int getCurrentAdapterHolderType() {
+    protected int getCurrentAdapterHolderType() {
         if (mIsInSearchMode) {
             return SEARCH;
         } else if (mViewPager != null) {
@@ -861,6 +897,7 @@
             WidgetsFullSheet sheet = show(BaseActivity.fromContext(getContext()), false);
             sheet.restoreRecommendations(mRecommendedWidgets, mRecommendedWidgetsMap);
             sheet.restoreHierarchyState(widgetsState);
+            sheet.restoreAdapterStates(mAdapters);
             sheet.restorePreviousAdapterHolderType(getCurrentAdapterHolderType());
         } else if (!isTwoPane()) {
             reset();
@@ -876,6 +913,17 @@
         mRecommendedWidgetsMap = recommendedWidgetsMap;
     }
 
+    private void restoreAdapterStates(SparseArray<AdapterHolder> adapters) {
+        if (adapters.contains(AdapterHolder.WORK)) {
+            mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.restoreState(
+                    adapters.get(AdapterHolder.WORK).mWidgetsListAdapter);
+        }
+        mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.restoreState(
+                adapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter);
+        mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.restoreState(
+                adapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter);
+    }
+
     /**
      * Indicates if layout should be re-created on device profile change - so that a different
      * layout can be displayed.
@@ -1045,6 +1093,7 @@
                     this::getEmptySpaceHeight,
                     /* iconClickListener= */ WidgetsFullSheet.this,
                     /* iconLongClickListener= */ WidgetsFullSheet.this,
+                    /* expandButtonClickListener= */ WidgetsFullSheet.this,
                     isTwoPane());
             mWidgetsListAdapter.setHasStableIds(true);
             switch (mAdapterType) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 3d3a669..3c67538 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -49,6 +49,7 @@
 import com.android.launcher3.widget.model.WidgetListSpaceEntry;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.model.WidgetsListExpandActionEntry;
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
 import com.android.launcher3.widget.util.WidgetSizes;
 
@@ -82,6 +83,7 @@
     public static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space;
     public static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
     public static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
+    public static final int VIEW_TYPE_WIDGETS_EXPAND = R.id.view_type_widgets_list_expand;
 
     private final Context mContext;
     private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
@@ -90,7 +92,9 @@
     @Nullable private WidgetsTwoPaneSheet.HeaderChangeListener mHeaderChangeListener;
 
     private final List<WidgetsListBaseEntry> mAllEntries = new ArrayList<>();
+    private final List<WidgetsListBaseEntry> mAllDefaultEntries = new ArrayList<>();
     private ArrayList<WidgetsListBaseEntry> mVisibleEntries = new ArrayList<>();
+
     @Nullable private PackageUserKey mWidgetsContentVisiblePackageUserKey = null;
 
     private Predicate<WidgetsListBaseEntry> mHeaderAndSelectedContentFilter = entry ->
@@ -102,9 +106,12 @@
     @Nullable private PackageUserKey mPendingClickHeader;
     @Px private int mMaxHorizontalSpan;
 
+    private boolean mShowOnlyDefaultList = true;
+
     public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
             IntSupplier emptySpaceHeightProvider, OnClickListener iconClickListener,
             OnLongClickListener iconLongClickListener,
+            ExpandButtonClickListener expandButtonClickListener,
             boolean isTwoPane) {
         mContext = context;
         mMaxHorizontalSpan = WidgetSizes.getWidgetSizePx(
@@ -123,6 +130,16 @@
         mViewHolderBinders.put(
                 VIEW_TYPE_WIDGETS_SPACE,
                 new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
+        mViewHolderBinders.put(VIEW_TYPE_WIDGETS_EXPAND,
+                new WidgetsListExpandActionViewHolderBinder(layoutInflater,
+                        expandButtonClickListener::onWidgetsListExpandButtonClick));
+    }
+
+    /**
+     * Copies state info from another adapter.
+     */
+    public void restoreState(WidgetsListAdapter adapter) {
+        mShowOnlyDefaultList = adapter.mShowOnlyDefaultList;
     }
 
     public void setHeaderChangeListener(WidgetsTwoPaneSheet.HeaderChangeListener
@@ -168,10 +185,21 @@
     }
 
     /** Updates the widget list based on {@code tempEntries}. */
-    public void setWidgets(List<WidgetsListBaseEntry> tempEntries) {
+    public void setWidgets(List<WidgetsListBaseEntry> tempEntries,
+            List<WidgetsListBaseEntry> tempDefaultEntries) {
         mAllEntries.clear();
         mAllEntries.add(new WidgetListSpaceEntry());
         tempEntries.stream().sorted(mRowComparator).forEach(mAllEntries::add);
+
+        mAllDefaultEntries.clear();
+
+        if (mShowOnlyDefaultList && !tempDefaultEntries.isEmpty()) {
+            mAllDefaultEntries.add(new WidgetListSpaceEntry());
+            tempDefaultEntries.stream().sorted(mRowComparator).forEach(mAllDefaultEntries::add);
+            // Include view all action when default entries exist.
+            mAllDefaultEntries.add(new WidgetsListExpandActionEntry());
+        }
+
         updateVisibleEntries();
     }
 
@@ -179,7 +207,8 @@
     public void setWidgetsOnSearch(List<WidgetsListBaseEntry> searchResults) {
         // Forget the expanded package every time widget list is refreshed in search mode.
         mWidgetsContentVisiblePackageUserKey = null;
-        setWidgets(searchResults);
+        mShowOnlyDefaultList = false;
+        setWidgets(searchResults, /*tempDefaultEntries=*/ List.of());
     }
 
     private void updateVisibleEntries() {
@@ -190,10 +219,11 @@
         OptionalInt topForPackageUserKey =
                 getOffsetForPosition(previousPositionForPackageUserKey);
 
-        List<WidgetsListBaseEntry> newVisibleEntries = mAllEntries.stream()
+        List<WidgetsListBaseEntry> newVisibleEntries = getAllEntries().stream()
                 .filter(entry -> (((mFilter == null || mFilter.test(entry))
                         && mHeaderAndSelectedContentFilter.test(entry))
-                        || entry instanceof WidgetListSpaceEntry)
+                        || entry instanceof WidgetListSpaceEntry
+                        || entry instanceof WidgetsListExpandActionEntry)
                         && (mHeaderChangeListener == null
                         || !(entry instanceof WidgetsListContentEntry)))
                 .map(entry -> {
@@ -227,6 +257,11 @@
         }
     }
 
+    private List<WidgetsListBaseEntry> getAllEntries() {
+        return (mShowOnlyDefaultList && !mAllDefaultEntries.isEmpty()) ? mAllDefaultEntries
+                : mAllEntries;
+    }
+
     /** Returns whether {@code entry} matches {@code key}. */
     private static boolean isHeaderForPackageUserKey(
             @NonNull WidgetsListBaseEntry entry, @Nullable PackageUserKey key) {
@@ -262,7 +297,13 @@
 
         // The first entry has an empty space, count from second entries.
         int listPos = (pos > 1) ? POSITION_DEFAULT : POSITION_FIRST;
-        if (pos == (getItemCount() - 1)) {
+        int lastIndex = getItemCount() - 1;
+        // Last index may be the view all entry
+        int actualLastItemIndex = (mVisibleEntries.get(
+                lastIndex) instanceof WidgetsListExpandActionEntry) ? getItemCount() - 2
+                : getItemCount() - 1;
+
+        if (pos == (actualLastItemIndex)) {
             listPos |= POSITION_LAST;
         }
         viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos, payloads);
@@ -319,6 +360,8 @@
             return VIEW_TYPE_WIDGETS_HEADER;
         } else if (entry instanceof WidgetListSpaceEntry) {
             return VIEW_TYPE_WIDGETS_SPACE;
+        } else if (entry instanceof WidgetsListExpandActionEntry) {
+            return VIEW_TYPE_WIDGETS_EXPAND;
         }
         throw new UnsupportedOperationException("ViewHolderBinder not found for " + entry);
     }
@@ -412,6 +455,23 @@
         updateVisibleEntries();
     }
 
+    /**
+     * Returns the widget content {@link WidgetsListContentEntry} for a selected header.
+     */
+    public WidgetsListContentEntry getContentEntry(PackageUserKey selectedHeader) {
+        return getAllEntries().stream().filter(entry -> entry instanceof WidgetsListContentEntry)
+                .map(entry -> (WidgetsListContentEntry) entry)
+                .filter(entry -> PackageUserKey.fromPackageItemInfo(entry.mPkgItem).equals(
+                        selectedHeader)).findFirst().orElse(null);
+    }
+
+    /**
+     * Sets adapter to use expanded list when updating widgets.
+     */
+    public void useExpandedList() {
+        mShowOnlyDefaultList = false;
+    }
+
     /** Comparator for sorting WidgetListRowEntry based on package title. */
     public static class WidgetListBaseRowEntryComparator implements
             Comparator<WidgetsListBaseEntry> {
@@ -430,4 +490,10 @@
             return 1;
         }
     }
+
+    /** Callback interface for the interaction with the expand button */
+    public interface ExpandButtonClickListener {
+        /** Called when user clicks the button at end of widget apps list to expand it. */
+        void onWidgetsListExpandButtonClick(View view);
+    }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListExpandActionViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListExpandActionViewHolderBinder.java
new file mode 100644
index 0000000..288c456
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListExpandActionViewHolderBinder.java
@@ -0,0 +1,60 @@
+/*
+ * 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.widget.picker;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.widget.model.WidgetsListExpandActionEntry;
+
+import java.util.List;
+
+/**
+ * Creates and populates views for the {@link WidgetsListExpandActionEntry}.
+ */
+public class WidgetsListExpandActionViewHolderBinder implements
+        ViewHolderBinder<WidgetsListExpandActionEntry, RecyclerView.ViewHolder> {
+    @NonNull
+    View.OnClickListener mExpandListClickListener;
+    private final LayoutInflater mLayoutInflater;
+
+    public WidgetsListExpandActionViewHolderBinder(
+            @NonNull LayoutInflater layoutInflater,
+            @NonNull View.OnClickListener expandListClickListener) {
+        mLayoutInflater = layoutInflater;
+        mExpandListClickListener = expandListClickListener;
+    }
+
+    @Override
+    public RecyclerView.ViewHolder newViewHolder(ViewGroup parent) {
+        return new RecyclerView.ViewHolder(mLayoutInflater.inflate(
+                R.layout.widgets_list_expand_button, parent, false)) {
+        };
+    }
+
+    @Override
+    public void bindViewHolder(RecyclerView.ViewHolder viewHolder,
+            WidgetsListExpandActionEntry data, int position, List<Object> payloads) {
+        viewHolder.itemView.setOnClickListener(mExpandListClickListener);
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index f4b99a0..f9bd5f1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.widget.picker;
 
 import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
+import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
 import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
 import static com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER;
 import static com.android.launcher3.UtilitiesKt.CLIP_TO_PADDING_FALSE_MODIFIER;
@@ -147,7 +148,9 @@
         mHeaderDescription = mContent.findViewById(R.id.widget_picker_description);
 
         mWidgetOptionsMenu = mContent.findViewById(R.id.widget_picker_widget_options_menu);
-        setupWidgetOptionsMenu();
+        if (!enableTieredWidgetsByDefaultInPicker()) {
+            setupWidgetOptionsMenu();
+        }
 
         mRightPane = mContent.findViewById(R.id.right_pane);
         mRightPaneScrollView = mContent.findViewById(R.id.right_pane_scroll_view);
@@ -286,6 +289,9 @@
         }
     }
 
+    // Used by the two pane sheet to show 3-dot menu to toggle between default lists and all lists
+    // when enableTieredWidgetsByDefaultInPicker is OFF. This code path and the 3-dot menu can be
+    // safely deleted when it's alternative "enableTieredWidgetsByDefaultInPicker" flag is inlined.
     @Override
     protected List<WidgetsListBaseEntry> getWidgetsToDisplay() {
         List<WidgetsListBaseEntry> allWidgets =
@@ -319,6 +325,15 @@
     }
 
     @Override
+    public void onWidgetsListExpandButtonClick(View v) {
+        super.onWidgetsListExpandButtonClick(v);
+        // Refresh right pane with updated data for the selected header.
+        if (mSelectedHeader != null && mSelectedHeader != mSuggestedWidgetsPackageUserKey) {
+            getHeaderChangeListener().onHeaderChanged(mSelectedHeader);
+        }
+    }
+
+    @Override
     public void onRecommendedWidgetsBound() {
         super.onRecommendedWidgetsBound();
 
@@ -511,11 +526,20 @@
                         && !mOpenCloseAnimation.getAnimationPlayer().isRunning()
                         && !getAccessibilityInitialFocusView().isAccessibilityFocused();
                 mSelectedHeader = selectedHeader;
-                final boolean showDefaultWidgets = mWidgetOptionsMenuState != null
-                        && !mWidgetOptionsMenuState.showAllWidgets;
-                WidgetsListContentEntry contentEntry = findContentEntryForPackageUser(
-                        mActivityContext.getWidgetPickerDataProvider().get(),
-                        selectedHeader, showDefaultWidgets);
+
+                WidgetsListContentEntry contentEntry;
+                if (enableTieredWidgetsByDefaultInPicker()) {
+                    contentEntry = mAdapters.get(
+                            getCurrentAdapterHolderType()).mWidgetsListAdapter.getContentEntry(
+                            selectedHeader);
+                } else { // Can be deleted when inlining the "enableTieredWidgetsByDefaultInPicker"
+                    // flag
+                    final boolean showDefaultWidgets = mWidgetOptionsMenuState != null
+                            && !mWidgetOptionsMenuState.showAllWidgets;
+                    contentEntry = findContentEntryForPackageUser(
+                            mActivityContext.getWidgetPickerDataProvider().get(),
+                            selectedHeader, showDefaultWidgets);
+                }
 
                 if (contentEntry == null || mRightPane == null) {
                     return;
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 55a028b..62c8426 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -307,6 +307,7 @@
 
         whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING)).thenReturn(false)
         whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(true)
+        whenever(launcherPrefs.get(LauncherPrefs.FIXED_LANDSCAPE_MODE)).thenReturn(false)
         whenever(launcherPrefs.get(LauncherPrefs.HOTSEAT_COUNT)).thenReturn(-1)
         whenever(launcherPrefs.get(LauncherPrefs.DEVICE_TYPE)).thenReturn(-1)
         whenever(launcherPrefs.get(LauncherPrefs.WORKSPACE_SIZE)).thenReturn("")
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
index ed9a080..bae74c8 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
@@ -19,6 +19,7 @@
 import android.content.ComponentName
 import android.content.pm.ApplicationInfo
 import android.database.MatrixCursor
+import android.os.Handler
 import android.os.Process.myUserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -34,9 +35,18 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.doAnswer
 import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
 @SmallTest
@@ -45,14 +55,25 @@
 
     @Mock private lateinit var iconProvider: IconProvider
     @Mock private lateinit var baseIconCache: BaseIconCache
+    @Mock private lateinit var cacheDb: IconDB
+    @Mock private lateinit var workerHandler: Handler
 
-    private var cursor: MatrixCursor? = null
-    private var cachingLogic = CachedObjectCachingLogic
+    @Captor private lateinit var deleteCaptor: ArgumentCaptor<String>
+
+    private var cursor =
+        MatrixCursor(
+            arrayOf(IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, IconDB.COLUMN_FRESHNESS_ID)
+        )
+
+    private lateinit var updateHandlerUnderTest: IconCacheUpdateHandler
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
         doReturn(iconProvider).whenever(baseIconCache).iconProvider
+        doReturn(cursor).whenever(cacheDb).query(any(), any(), any())
+
+        updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache, cacheDb, workerHandler)
     }
 
     @After
@@ -61,63 +82,136 @@
     }
 
     @Test
-    fun `IconCacheUpdateHandler returns null if the component name is malformed`() {
-        val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
-        val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
+    fun `keeps correct icons irrespective of call order`() {
+        val obj1 = TestCachedObject(1).apply { addToCursor(cursor) }
+        val obj2 = TestCachedObject(2).apply { addToCursor(cursor) }
 
-        val result =
-            updateHandlerUnderTest.updateOrDeleteIcon(
-                createCursor(1, cn.flattenToString() + "#", "freshId-old"),
-                hashMapOf(cn to TestCachedObject(cn, "freshId")),
-                setOf(),
-                myUserHandle(),
-                cachingLogic,
-            )
-        assertThat(result).isNull()
+        updateHandlerUnderTest.updateIcons(obj1)
+        updateHandlerUnderTest.updateIcons(obj2)
+        updateHandlerUnderTest.finish()
+
+        verify(cacheDb, never()).delete(any(), anyOrNull())
     }
 
     @Test
-    fun `IconCacheUpdateHandler returns null if the freshId match`() {
-        val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
-        val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
+    fun `removes missing entries in single call`() {
+        TestCachedObject(1).addToCursor(cursor)
+        TestCachedObject(2).addToCursor(cursor)
+        TestCachedObject(3).addToCursor(cursor)
+        TestCachedObject(4).addToCursor(cursor)
+        TestCachedObject(5).addToCursor(cursor)
 
-        val result =
-            updateHandlerUnderTest.updateOrDeleteIcon(
-                createCursor(1, cn.flattenToString(), "freshId"),
-                hashMapOf(cn to TestCachedObject(cn, "freshId")),
-                setOf(),
-                myUserHandle(),
-                cachingLogic,
-            )
-        assertThat(result).isNull()
+        updateHandlerUnderTest.updateIcons(TestCachedObject(1), TestCachedObject(4))
+        updateHandlerUnderTest.finish()
+
+        verifyItemsDeleted(2, 3, 5)
     }
 
     @Test
-    fun `IconCacheUpdateHandler returns non-null if the freshId do not match`() {
-        val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
-        val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
-        val testObj = TestCachedObject(cn, "freshId")
+    fun `removes missing entries in multiple calls`() {
+        TestCachedObject(1).addToCursor(cursor)
+        TestCachedObject(2).addToCursor(cursor)
+        TestCachedObject(3).addToCursor(cursor)
+        TestCachedObject(4).addToCursor(cursor)
+        TestCachedObject(5).addToCursor(cursor)
+        TestCachedObject(6).addToCursor(cursor)
 
-        val result =
-            updateHandlerUnderTest.updateOrDeleteIcon(
-                createCursor(1, cn.flattenToString(), "freshId-old"),
-                hashMapOf(cn to testObj),
-                setOf(),
-                myUserHandle(),
-                cachingLogic,
-            )
-        assertThat(result).isEqualTo(testObj)
+        updateHandlerUnderTest.updateIcons(TestCachedObject(1), TestCachedObject(2))
+        updateHandlerUnderTest.updateIcons(TestCachedObject(4), TestCachedObject(5))
+        updateHandlerUnderTest.finish()
+
+        verifyItemsDeleted(3, 6)
     }
 
-    private fun createCursor(row: Long, component: String, appState: String) =
-        MatrixCursor(
-                arrayOf(IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, IconDB.COLUMN_FRESHNESS_ID)
-            )
-            .apply { addRow(arrayOf(row, component, appState)) }
-            .apply {
-                cursor = this
-                moveToNext()
+    @Test
+    fun `keeps valid app infos`() {
+        val appInfo = ApplicationInfo()
+        doReturn("app-fresh").whenever(iconProvider).getStateForApp(eq(appInfo))
+
+        TestCachedObject(1).addToCursor(cursor)
+        TestCachedObject(2).addToCursor(cursor)
+        cursor.addRow(arrayOf(33, TestCachedObject(1).getPackageKey(), "app-fresh"))
+
+        updateHandlerUnderTest.updateIcons(
+            TestCachedObject(1, appInfo = appInfo),
+            TestCachedObject(2),
+        )
+        updateHandlerUnderTest.finish()
+
+        verify(cacheDb, never()).delete(any(), anyOrNull())
+    }
+
+    @Test
+    fun `deletes stale app infos`() {
+        val appInfo1 = ApplicationInfo()
+        doReturn("app1-fresh").whenever(iconProvider).getStateForApp(eq(appInfo1))
+
+        val appInfo2 = ApplicationInfo()
+        doReturn("app2-fresh").whenever(iconProvider).getStateForApp(eq(appInfo2))
+
+        TestCachedObject(1).addToCursor(cursor)
+        TestCachedObject(2).addToCursor(cursor)
+        cursor.addRow(arrayOf(33, TestCachedObject(1).getPackageKey(), "app1-not-fresh"))
+        cursor.addRow(arrayOf(34, TestCachedObject(2).getPackageKey(), "app2-fresh"))
+
+        updateHandlerUnderTest.updateIcons(
+            TestCachedObject(1, appInfo = appInfo1),
+            TestCachedObject(2, appInfo = appInfo2),
+        )
+        updateHandlerUnderTest.finish()
+
+        verifyItemsDeleted(33)
+    }
+
+    @Test
+    fun `updates stale entries`() {
+        doAnswer { i ->
+                (i.arguments[0] as Runnable).run()
+                true
             }
+            .whenever(workerHandler)
+            .postAtTime(any(), anyOrNull(), any())
+
+        TestCachedObject(1).addToCursor(cursor)
+        TestCachedObject(2).addToCursor(cursor)
+        TestCachedObject(3).addToCursor(cursor)
+
+        var updatedPackages = mutableSetOf<String>()
+        updateHandlerUnderTest.updateIcons(
+            listOf(
+                TestCachedObject(1, freshnessId = "not-fresh"),
+                TestCachedObject(2, freshnessId = "not-fresh"),
+                TestCachedObject(3),
+            ),
+            CachedObjectCachingLogic,
+        ) { apps, _ ->
+            updatedPackages.addAll(apps)
+        }
+        updateHandlerUnderTest.finish()
+
+        assertThat(updatedPackages)
+            .isEqualTo(
+                mutableSetOf(TestCachedObject(1).cn.packageName, TestCachedObject(2).cn.packageName)
+            )
+    }
+
+    private fun IconCacheUpdateHandler.updateIcons(vararg items: TestCachedObject) {
+        updateIcons(items.toList(), CachedObjectCachingLogic) { _, _ -> }
+    }
+
+    private fun verifyItemsDeleted(vararg rowIds: Long) {
+        verify(cacheDb, times(1)).delete(deleteCaptor.capture(), anyOrNull())
+        val actual =
+            deleteCaptor.value
+                .split('(')
+                ?.get(1)
+                ?.split(')')
+                ?.get(0)
+                ?.split(",")
+                ?.map { it.trim().toLong() }!!
+                .sorted()
+        assertThat(actual).isEqualTo(rowIds.toList().sorted())
+    }
 }
 
 /** Utility method to wait for the icon update handler to finish */
@@ -135,7 +229,13 @@
     }
 }
 
-class TestCachedObject(val cn: ComponentName, val freshnessId: String) : CachedObject {
+class TestCachedObject(
+    val rowId: Long,
+    val cn: ComponentName =
+        ComponentName.unflattenFromString("com.android.fake$rowId/.FakeActivity")!!,
+    val freshnessId: String = "fresh-$rowId",
+    val appInfo: ApplicationInfo? = null,
+) : CachedObject {
 
     override fun getComponent() = cn
 
@@ -143,7 +243,13 @@
 
     override fun getLabel(): CharSequence? = null
 
-    override fun getApplicationInfo(): ApplicationInfo? = null
+    override fun getApplicationInfo(): ApplicationInfo? = appInfo
 
     override fun getFreshnessIdentifier(iconProvider: IconProvider): String? = freshnessId
+
+    fun addToCursor(cursor: MatrixCursor) =
+        cursor.addRow(arrayOf(rowId, cn.flattenToString(), freshnessId))
+
+    fun getPackageKey() =
+        BaseIconCache.getPackageKey(cn.packageName, user).componentName.flattenToString()
 }
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
index ce682f1..eb25acf 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
@@ -29,7 +29,10 @@
 import android.app.blob.BlobHandle;
 import android.app.blob.BlobStoreManager;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.os.AsyncTask;
@@ -231,6 +234,15 @@
                 .adoptShellPermissionIdentity(Manifest.permission.WRITE_SECURE_SETTINGS);
     }
 
+    /**
+     * Returns the activity info corresponding to the system app for the provided category
+     */
+    public static ActivityInfo resolveSystemAppInfo(String category) {
+        return getInstrumentation().getTargetContext().getPackageManager().resolveActivity(
+                new Intent(Intent.ACTION_MAIN).addCategory(category),
+                PackageManager.MATCH_SYSTEM_ONLY).activityInfo;
+    }
+
     /** Interface to indicate a runnable which can throw any exception. */
     public interface UncheckedRunnable {
         /** Method to run the task */
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/VibratorWrapperTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/VibratorWrapperTest.kt
index dee98e7..0f212eb 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/VibratorWrapperTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/VibratorWrapperTest.kt
@@ -66,6 +66,7 @@
 
     @Test
     fun init_register_onChangeListener() {
+        TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) {}
         verify(settingsCache).register(HAPTIC_FEEDBACK_URI, underTest.mHapticChangeListener)
     }
 
diff --git a/tests/src/com/android/launcher3/LauncherIntentTest.java b/tests/src/com/android/launcher3/LauncherIntentTest.java
index aeeb42a..a3d9614 100644
--- a/tests/src/com/android/launcher3/LauncherIntentTest.java
+++ b/tests/src/com/android/launcher3/LauncherIntentTest.java
@@ -23,21 +23,27 @@
 import android.platform.test.annotations.LargeTest;
 import android.view.KeyEvent;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
 import com.android.launcher3.allapps.SearchRecyclerView;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.BaseLauncherActivityTest;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-public class LauncherIntentTest extends AbstractLauncherUiTest<Launcher> {
+public class LauncherIntentTest extends BaseLauncherActivityTest<Launcher> {
 
     public final Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
 
+    @Before
+    public void setUp() {
+        loadLauncherSync();
+    }
+
     @Test
     public void testAllAppsIntent() {
         // Try executing ALL_APPS intent
@@ -45,7 +51,6 @@
         // A-Z view with Main adapter should be loaded
         assertOnMainAdapterAToZView();
 
-
         // Try Moving to search view now
         moveToSearchView();
         // Try executing ALL_APPS intent
@@ -63,12 +68,14 @@
         // Search view should be in focus
         waitForLauncherCondition("Search view is not in focus.",
                 launcher -> launcher.getAppsView().getSearchView().hasFocus());
-        mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_C, 0);
+
+        injectKeyEvent(KeyEvent.KEYCODE_C, true);
         // Upon key press, search recycler view should be loaded
         waitForLauncherCondition("Search view not active.",
                 launcher -> launcher.getAppsView().getActiveRecyclerView()
                         instanceof SearchRecyclerView);
-        mLauncher.unpressKeyCode(KeyEvent.KEYCODE_C, 0);
+
+        injectKeyEvent(KeyEvent.KEYCODE_C, false);
     }
 
     // Checks if main adapter view is selected, search bar is out of focus and scroller is at start.
diff --git a/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java b/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java
new file mode 100644
index 0000000..44df5b8
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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 android.view.KeyEvent;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.util.BaseLauncherActivityTest;
+import com.android.launcher3.views.ActivityContext;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyboardFocusTest extends BaseLauncherActivityTest<Launcher> {
+
+    @Test
+    public void testAllAppsFocusApp() {
+        loadLauncherSync();
+        goToState(LauncherState.ALL_APPS);
+        freezeAllApps();
+
+        injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, true);
+        injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, false);
+        waitForLauncherCondition("No focused child", launcher ->
+                launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()
+                        != null);
+    }
+
+    @Test
+    public void testAllAppsExitSearchAndFocusApp() {
+        loadLauncherSync();
+        goToState(LauncherState.ALL_APPS);
+        freezeAllApps();
+
+        executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus());
+        waitForLauncherCondition("Search view does not have focus.",
+                launcher -> launcher.getAppsView().getSearchView().hasFocus());
+
+        injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, true);
+        injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, false);
+        waitForLauncherCondition("No focused child", launcher ->
+                launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()
+                        != null);
+    }
+
+    @Test
+    public void testAllAppsExitSearchAndFocusSearchResults() {
+        loadLauncherSync();
+        goToState(LauncherState.ALL_APPS);
+        freezeAllApps();
+
+        executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus());
+        waitForLauncherCondition("Search view does not have focus.",
+                launcher -> launcher.getAppsView().getSearchView().hasFocus());
+
+        injectKeyEvent(KeyEvent.KEYCODE_C, true);
+        waitForLauncherCondition("Search view not active.",
+                launcher -> launcher.getAppsView().getActiveRecyclerView()
+                        instanceof SearchRecyclerView);
+        injectKeyEvent(KeyEvent.KEYCODE_C, false);
+
+        executeOnLauncher(launcher -> launcher.getAppsView().getSearchUiManager().getEditText()
+                .hideKeyboard(/* clearFocus= */ false));
+        waitForLauncherCondition("Keyboard still visible.",
+                ActivityContext::isSoftwareKeyboardHidden);
+
+        injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, true);
+        injectKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, false);
+        waitForLauncherCondition("No focused child", launcher ->
+                launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()
+                        != null);
+    }
+}
diff --git a/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java b/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
deleted file mode 100644
index 4e627a9..0000000
--- a/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
+++ /dev/null
@@ -1,108 +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 com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.view.KeyEvent;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.tapl.HomeAllApps;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
-import com.android.launcher3.util.rule.TestStabilityRule;
-import com.android.launcher3.views.ActivityContext;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TaplKeyboardFocusTest extends AbstractLauncherUiTest<Launcher> {
-
-    @Test
-    public void testAllAppsFocusApp() {
-        final HomeAllApps allApps = mLauncher.goHome().switchToAllApps();
-        assertTrue("Launcher internal state is not All Apps",
-                isInState(() -> LauncherState.ALL_APPS));
-        allApps.freeze();
-        try {
-            mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);
-            executeOnLauncher(launcher -> assertNotNull("No focused child.",
-                    launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()));
-        } finally {
-            allApps.unfreeze();
-        }
-    }
-
-    @Test
-    public void testAllAppsExitSearchAndFocusApp() {
-        final HomeAllApps allApps = mLauncher.goHome().switchToAllApps();
-        assertTrue("Launcher internal state is not All Apps",
-                isInState(() -> LauncherState.ALL_APPS));
-        allApps.freeze();
-        try {
-            executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus());
-            waitForLauncherCondition("Search view does not have focus.",
-                    launcher -> launcher.getAppsView().getSearchView().hasFocus());
-
-            mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);
-            executeOnLauncher(launcher -> assertNotNull("No focused child.",
-                    launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()));
-        } finally {
-            allApps.unfreeze();
-        }
-    }
-
-    @Test
-    public void testAllAppsExitSearchAndFocusSearchResults() {
-        final HomeAllApps allApps = mLauncher.goHome().switchToAllApps();
-        assertTrue("Launcher internal state is not All Apps",
-                isInState(() -> LauncherState.ALL_APPS));
-        allApps.freeze();
-        try {
-            executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus());
-            waitForLauncherCondition("Search view does not have focus.",
-                    launcher -> launcher.getAppsView().getSearchView().hasFocus());
-
-            mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_C, 0);
-            waitForLauncherCondition("Search view not active.",
-                    launcher -> launcher.getAppsView().getActiveRecyclerView()
-                            instanceof SearchRecyclerView);
-            mLauncher.unpressKeyCode(KeyEvent.KEYCODE_C, 0);
-
-            executeOnLauncher(launcher -> launcher.getAppsView().getSearchUiManager().getEditText()
-                    .hideKeyboard(/* clearFocus= */ false));
-            waitForLauncherCondition("Keyboard still visible.",
-                    ActivityContext::isSoftwareKeyboardHidden);
-
-            mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);
-            mLauncher.unpressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);
-            waitForLauncherCondition("No focused child", launcher ->
-                    launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild()
-                            != null);
-        } finally {
-            allApps.unfreeze();
-        }
-    }
-}
diff --git a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
similarity index 73%
rename from tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
rename to tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
index 1500538..34b292c 100644
--- a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -1,18 +1,19 @@
 /*
  * Copyright (C) 2019 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
+ * 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
+ *      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.
+ * 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.compat;
 
 import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING;
@@ -27,32 +28,31 @@
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.text.TextUtils;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.tapl.AllApps;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.BaseLauncherActivityTest;
 import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 import com.android.launcher3.util.TestUtil;
-import com.android.launcher3.util.rule.ViewCaptureRule;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.UUID;
 
-
 /**
  * Test to verify promise icon flow.
  */
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-public class TaplPromiseIconUiTest extends AbstractLauncherUiTest<Launcher> {
+public class PromiseIconUiTest extends BaseLauncherActivityTest<Launcher> {
 
     @Rule
     public final CheckFlagsRule mCheckFlagsRule =
@@ -64,19 +64,17 @@
 
     private int mSessionId = -1;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-        mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
-        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+        loadLauncherSync();
+        goToState(LauncherState.NORMAL);
         mSessionId = -1;
     }
 
     @After
     public void tearDown() throws IOException {
         if (mSessionId > -1) {
-            mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+            targetContext().getPackageManager().getPackageInstaller().abandonSession(mSessionId);
         }
         TestUtil.uninstallDummyApp();
     }
@@ -90,7 +88,7 @@
         params.setAppLabel(label);
         params.setAppIcon(icon);
         params.setInstallReason(PackageManager.INSTALL_REASON_USER);
-        return mTargetContext.getPackageManager().getPackageInstaller().createSession(params);
+        return targetContext().getPackageManager().getPackageInstaller().createSession(params);
     }
 
     @Test
@@ -108,7 +106,7 @@
                 launcher.getWorkspace().getFirstMatch(findPromiseApp) != null);
 
         // Remove session
-        mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+        targetContext().getPackageManager().getPackageInstaller().abandonSession(mSessionId);
         mSessionId = -1;
 
         // Verify promise icon is removed
@@ -117,7 +115,6 @@
     }
 
     @Test
-    @ViewCaptureRule.MayProduceNoFrames
     public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
         final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
         final ItemOperator findPromiseApp = (info, view) ->
@@ -138,7 +135,8 @@
     @RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
     public void testPromiseIcon_addedArchivedApp() throws Throwable {
         installDummyAppAndWaitForUIUpdate();
-        assertThat(mDevice.executeShellCommand(String.format("pm archive %s", DUMMY_PACKAGE)))
+        assertThat(executeShellCommand(
+                String.format("pm archive %s", DUMMY_PACKAGE)))
                 .isEqualTo("Success\n");
 
         // Create and add test session
@@ -148,28 +146,19 @@
         // Verify promise icon is added to all apps view. The icon may not be added to the
         // workspace even if there might be no icon present for archived app. But icon will
         // always be in all apps view. In case an icon is not added, an exception would be thrown.
-        final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        goToState(LauncherState.ALL_APPS);
 
         // Wait for the promise icon to be added.
         waitForLauncherCondition(
                 DUMMY_PACKAGE + " app was not found on all apps after being archived",
-                launcher -> {
-                    try {
-                        allApps.getAppIcon(DUMMY_LABEL);
-                    } catch (Throwable t) {
-                        return false;
-                    }
-                    return true;
-                });
-
-        // Remove session
-        mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
-        mSessionId = -1;
+                launcher -> Arrays.stream(launcher.getAppsView().getAppsStore().getApps())
+                        .filter(info -> DUMMY_LABEL.equals(info.title.toString()))
+                        .findAny()
+                        .isPresent());
     }
 
     private void installDummyAppAndWaitForUIUpdate() throws IOException {
         TestUtil.installDummyApp();
-        mLauncher.waitForModelQueueCleared();
-        mLauncher.waitForLauncherInitialized();
+        loadLauncherSync();
     }
 }
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 44b8ff8..1816030 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -69,8 +69,7 @@
     private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) {
         final HomeAllApps allApps = workspace.switchToAllApps();
         Wait.atMost(appName + " app was found on all apps after being uninstalled",
-                () -> allApps.tryGetAppIcon(appName) == null,
-                DEFAULT_UI_TIMEOUT, mLauncher);
+                () -> allApps.tryGetAppIcon(appName) == null, mLauncher);
     }
 
     private void installDummyAppAndWaitForUIUpdate() throws IOException {
diff --git a/tests/src/com/android/launcher3/model/GridMigrationTest.kt b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
index 666ec16..379e98d 100644
--- a/tests/src/com/android/launcher3/model/GridMigrationTest.kt
+++ b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
@@ -101,6 +101,7 @@
                 dst.gridState,
                 dst.dbHelper,
                 src.dbHelper.readableDatabase,
+                false,
             )
         } else {
             GridSizeMigrationDBController.migrateGridIfNeeded(
@@ -109,6 +110,7 @@
                 dst.gridState,
                 dst.dbHelper,
                 src.dbHelper.readableDatabase,
+                false,
             )
         }
     }
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 206647a..a273648 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -21,6 +21,7 @@
 
 import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.TestUtil.resolveSystemAppInfo;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -31,9 +32,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Point;
@@ -97,10 +95,8 @@
  */
 public abstract class AbstractLauncherUiTest<LAUNCHER_TYPE extends Launcher> {
 
-    public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
     public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 10;
 
-    public static final long DEFAULT_UI_TIMEOUT = TestUtil.DEFAULT_UI_TIMEOUT;
     private static final String TAG = "AbstractLauncherUiTest";
 
     private static final long BYTES_PER_MEGABYTE = 1 << 20;
@@ -151,7 +147,7 @@
                     launcher.forceGc();
                     return MAIN_EXECUTOR.submit(
                             () -> launcher.noLeakedActivities(requireOneActiveActivity)).get();
-                }, DEFAULT_UI_TIMEOUT, launcher);
+                }, launcher);
     }
 
     public static String getAppPackageName() {
@@ -443,7 +439,7 @@
      */
     protected <T> T getOnUiThread(final Callable<T> callback) {
         try {
-            return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT,
+            return mMainThreadExecutor.submit(callback).get(TestUtil.DEFAULT_UI_TIMEOUT,
                     TimeUnit.MILLISECONDS);
         } catch (TimeoutException e) {
             Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
@@ -498,13 +494,7 @@
     // flakiness.
     protected void waitForLauncherCondition(String
             message, Function<LAUNCHER_TYPE, Boolean> condition) {
-        waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
-    }
-
-    // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
-    // flakiness.
-    protected <O> O getOnceNotNull(String message, Function<LAUNCHER_TYPE, O> f) {
-        return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT);
+        waitForLauncherCondition(message, condition, TestUtil.DEFAULT_UI_TIMEOUT);
     }
 
     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
@@ -513,12 +503,12 @@
             String message, Function<LAUNCHER_TYPE, Boolean> condition, long timeout) {
         verifyKeyguardInvisible();
         if (!TestHelpers.isInLauncherProcess()) return;
-        Wait.atMost(message, () -> getFromLauncher(condition), timeout, mLauncher);
+        Wait.atMost(message, () -> getFromLauncher(condition), mLauncher, timeout);
     }
 
     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
     // flakiness.
-    protected <T> T getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f, long timeout) {
+    protected <T> T getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f) {
         if (!TestHelpers.isInLauncherProcess()) return null;
 
         final Object[] output = new Object[1];
@@ -526,7 +516,7 @@
             final Object fromLauncher = getFromLauncher(f);
             output[0] = fromLauncher;
             return fromLauncher != null;
-        }, timeout, mLauncher);
+        }, mLauncher);
         return (T) output[0];
     }
 
@@ -540,12 +530,7 @@
         Wait.atMost(message, () -> {
             testThreadAction.run();
             return getFromLauncher(condition);
-        }, timeout, mLauncher);
-    }
-
-    protected LauncherActivityInfo getSettingsApp() {
-        return mTargetContext.getSystemService(LauncherApps.class)
-                .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
+        }, mLauncher, timeout);
     }
 
     /**
@@ -633,20 +618,13 @@
         }
         getInstrumentation().getTargetContext().startActivity(intent);
         assertTrue("App didn't start: " + selector,
-                TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
+                TestHelpers.wait(Until.hasObject(selector), TestUtil.DEFAULT_UI_TIMEOUT));
 
         // Wait for the Launcher to stop.
         final LauncherInstrumentation launcherInstrumentation = new LauncherInstrumentation();
         Wait.atMost("Launcher activity didn't stop",
                 () -> !launcherInstrumentation.isLauncherActivityStarted(),
-                DEFAULT_ACTIVITY_TIMEOUT, launcherInstrumentation);
-    }
-
-    public static ActivityInfo resolveSystemAppInfo(String category) {
-        return getInstrumentation().getContext().getPackageManager().resolveActivity(
-                new Intent(Intent.ACTION_MAIN).addCategory(category),
-                PackageManager.MATCH_SYSTEM_ONLY).
-                activityInfo;
+                launcherInstrumentation);
     }
 
 
@@ -662,8 +640,7 @@
                 launcher.finish();
             }
         });
-        waitForLauncherCondition(
-                "Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT);
+        waitForLauncherCondition("Launcher still active", launcher -> launcher == null);
     }
 
     protected boolean isInLaunchedApp(LAUNCHER_TYPE launcher) {
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
index 7ff4f22..7845222 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
@@ -103,12 +103,12 @@
 
         setResultAndWaitForAnimation(acceptConfig);
         if (acceptConfig) {
-            Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+            Wait.atMost("", new WidgetSearchCondition(), mLauncher);
             assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
         } else {
             // Verify that the widget id is deleted.
             Wait.atMost("", () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
-                    DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+                    mLauncher);
         }
     }
 
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 9a2147a..460ffc4 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
@@ -68,7 +69,7 @@
         resizeFrame.dismiss();
 
         final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label,
-                DEFAULT_UI_TIMEOUT);
+                TestUtil.DEFAULT_UI_TIMEOUT);
         assertNotNull("Widget not found on the workspace", widget);
         widget.launch(getAppPackageName());
         mLauncher.disableDebugTracing(); // b/289161193
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
index d40d3bc..4cdbd96 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
@@ -53,6 +53,7 @@
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
@@ -233,13 +234,15 @@
     }
 
     private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
-        final Widget widget = mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT);
+        final Widget widget = mLauncher.getWorkspace().tryGetWidget(info.label,
+                TestUtil.DEFAULT_UI_TIMEOUT);
         assertTrue("Widget is not present",
                 widget != null);
     }
 
     private void verifyPendingWidgetPresent() {
-        final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT);
+        final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget(
+                TestUtil.DEFAULT_UI_TIMEOUT);
         assertTrue("Pending widget is not present",
                 widget != null);
     }
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
index 35c7cab..fe3b2ee 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
@@ -169,8 +169,7 @@
 
         // Go back to home
         mLauncher.goHome();
-        Wait.atMost("", new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT,
-                mLauncher);
+        Wait.atMost("", new ItemSearchCondition(itemMatcher), mLauncher);
     }
 
     /**
diff --git a/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt b/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt
new file mode 100644
index 0000000..bacce40
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.SystemClock
+import android.view.InputDevice
+import android.view.KeyCharacterMap
+import android.view.KeyEvent
+import android.view.MotionEvent
+import androidx.lifecycle.Lifecycle.State.RESUMED
+import androidx.test.core.app.ActivityScenario
+import androidx.test.core.app.ActivityScenario.ActivityAction
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.Launcher
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherState
+import com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST
+import com.android.launcher3.tapl.TestHelpers
+import com.android.launcher3.util.ModelTestExtensions.loadModelSync
+import com.android.launcher3.util.Wait.atMost
+import java.util.function.Function
+import java.util.function.Supplier
+import org.junit.After
+
+/**
+ * Base class for tests which use Launcher activity with some utility methods.
+ *
+ * This should instead be a rule, but is kept as a base class for easier migration from TAPL
+ */
+open class BaseLauncherActivityTest<LAUNCHER_TYPE : Launcher> {
+
+    private var currentScenario: ActivityScenario<LAUNCHER_TYPE>? = null
+
+    val scenario: ActivityScenario<LAUNCHER_TYPE>
+        get() =
+            currentScenario
+                ?: ActivityScenario.launch<LAUNCHER_TYPE>(
+                        TestHelpers.getHomeIntentInPackage(targetContext()),
+                        null,
+                    )
+                    .also { currentScenario = it }
+
+    @After
+    fun closeCurrentActivity() {
+        currentScenario?.close()
+        currentScenario = null
+    }
+
+    protected fun loadLauncherSync() {
+        LauncherAppState.getInstance(targetContext()).model.loadModelSync()
+        scenario.moveToState(RESUMED)
+    }
+
+    protected fun targetContext(): Context = getInstrumentation().targetContext
+
+    protected fun goToState(state: LauncherState) = executeOnLauncher {
+        it.stateManager.goToState(state, 0)
+    }
+
+    protected fun executeOnLauncher(f: ActivityAction<LAUNCHER_TYPE>) = scenario.onActivity(f)
+
+    protected fun <T> getFromLauncher(f: Function<in LAUNCHER_TYPE, out T?>): T? {
+        var result: T? = null
+        executeOnLauncher { result = f.apply(it) }
+        return result
+    }
+
+    protected fun isInState(state: Supplier<LauncherState>): Boolean =
+        getFromLauncher { it.stateManager.state == state.get() }!!
+
+    protected fun waitForState(message: String, state: Supplier<LauncherState>) =
+        waitForLauncherCondition(message) { it.stateManager.currentStableState === state.get() }
+
+    protected fun waitForLauncherCondition(
+        message: String,
+        condition: Function<LAUNCHER_TYPE, Boolean>,
+    ) = atMost(message, { getFromLauncher(condition)!! })
+
+    protected fun <T> getOnceNotNull(message: String, f: Function<LAUNCHER_TYPE, T?>): T? {
+        var output: T? = null
+        atMost(
+            message,
+            {
+                val fromLauncher = getFromLauncher<T>(f)
+                output = fromLauncher
+                fromLauncher != null
+            },
+        )
+        return output
+    }
+
+    protected fun getAllAppsScroll(launcher: LAUNCHER_TYPE) =
+        launcher.appsView.activeRecyclerView.computeVerticalScrollOffset()
+
+    @JvmOverloads
+    protected fun injectKeyEvent(keyCode: Int, actionDown: Boolean, metaState: Int = 0) {
+        val eventTime = SystemClock.uptimeMillis()
+        val event =
+            KeyEvent.obtain(
+                eventTime,
+                eventTime,
+                if (actionDown) KeyEvent.ACTION_DOWN else MotionEvent.ACTION_UP,
+                keyCode,
+                /* repeat= */ 0,
+                metaState,
+                KeyCharacterMap.VIRTUAL_KEYBOARD,
+                /* scancode= */ 0,
+                /* flags= */ 0,
+                InputDevice.SOURCE_KEYBOARD,
+                /* characters =*/ null,
+            )
+        executeOnLauncher { it.dispatchKeyEvent(event) }
+        event.recycle()
+    }
+
+    fun startAppFast(packageName: String) {
+        val intent = targetContext().packageManager.getLaunchIntentForPackage(packageName)!!
+        intent.addCategory(Intent.CATEGORY_LAUNCHER)
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+        targetContext().startActivity(intent)
+        UiDevice.getInstance(getInstrumentation()).waitForIdle()
+    }
+
+    fun freezeAllApps() = executeOnLauncher {
+        it.appsView.appsStore.enableDeferUpdates(DEFER_UPDATES_TEST)
+    }
+
+    fun executeShellCommand(cmd: String) =
+        UiDevice.getInstance(getInstrumentation()).executeShellCommand(cmd)
+}
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
deleted file mode 100644
index 50bc32e..0000000
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.android.launcher3.util;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.launcher3.tapl.LauncherInstrumentation;
-
-import org.junit.Assert;
-
-import java.util.function.Supplier;
-
-/**
- * A utility class for waiting for a condition to be true.
- */
-public class Wait {
-
-    private static final long DEFAULT_SLEEP_MS = 200;
-
-    public static void atMost(String message, Condition condition, long timeout,
-            LauncherInstrumentation launcher) {
-        atMost(() -> message, condition, timeout, DEFAULT_SLEEP_MS, launcher);
-    }
-
-    public static void atMost(Supplier<String> message, Condition condition, long timeout,
-            LauncherInstrumentation launcher) {
-        atMost(message, condition, timeout, DEFAULT_SLEEP_MS, launcher);
-    }
-
-    public static void atMost(Supplier<String> message, Condition condition, long timeout,
-            long sleepMillis,
-            LauncherInstrumentation launcher) {
-        final long startTime = SystemClock.uptimeMillis();
-        long endTime = startTime + timeout;
-        Log.d("Wait", "atMost: " + startTime + " - " + endTime);
-        while (SystemClock.uptimeMillis() < endTime) {
-            try {
-                if (condition.isTrue()) {
-                    return;
-                }
-            } catch (Throwable t) {
-                throw new RuntimeException(t);
-            }
-            SystemClock.sleep(sleepMillis);
-        }
-
-        // Check once more before returning false.
-        try {
-            if (condition.isTrue()) {
-                return;
-            }
-        } catch (Throwable t) {
-            throw new RuntimeException(t);
-        }
-        Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
-        launcher.checkForAnomaly(false, false);
-        Assert.fail(message.get());
-    }
-
-    /**
-     * Interface representing a generic condition
-     */
-    public interface Condition {
-
-        boolean isTrue() throws Throwable;
-    }
-}
diff --git a/tests/src/com/android/launcher3/util/Wait.kt b/tests/src/com/android/launcher3/util/Wait.kt
new file mode 100644
index 0000000..1e5af54
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/Wait.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.os.SystemClock
+import android.util.Log
+import com.android.launcher3.tapl.LauncherInstrumentation
+import java.util.function.Supplier
+import org.junit.Assert
+
+/** A utility class for waiting for a condition to be true. */
+object Wait {
+    private const val DEFAULT_SLEEP_MS: Long = 200
+
+    @JvmStatic
+    @JvmOverloads
+    fun atMost(
+        message: String,
+        condition: Condition,
+        launcherInstrumentation: LauncherInstrumentation? = null,
+        timeout: Long = TestUtil.DEFAULT_UI_TIMEOUT,
+    ) {
+        atMost({ message }, condition, launcherInstrumentation, timeout)
+    }
+
+    @JvmStatic
+    @JvmOverloads
+    fun atMost(
+        message: Supplier<String>,
+        condition: Condition,
+        launcherInstrumentation: LauncherInstrumentation? = null,
+        timeout: Long = TestUtil.DEFAULT_UI_TIMEOUT,
+    ) {
+        val startTime = SystemClock.uptimeMillis()
+        val endTime = startTime + timeout
+        Log.d("Wait", "atMost: $startTime - $endTime")
+        while (SystemClock.uptimeMillis() < endTime) {
+            try {
+                if (condition.isTrue()) {
+                    return
+                }
+            } catch (t: Throwable) {
+                throw RuntimeException(t)
+            }
+            SystemClock.sleep(DEFAULT_SLEEP_MS)
+        }
+
+        // Check once more before returning false.
+        try {
+            if (condition.isTrue()) {
+                return
+            }
+        } catch (t: Throwable) {
+            throw RuntimeException(t)
+        }
+        Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis())
+        launcherInstrumentation?.checkForAnomaly(false, false)
+        Assert.fail(message.get())
+    }
+
+    /** Interface representing a generic condition */
+    fun interface Condition {
+
+        @Throws(Throwable::class) fun isTrue(): Boolean
+    }
+}