Merge "Restore Archived Pinned Shortcuts instead of removing them." into main
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index ddc775d..eab28b7 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -46,6 +46,7 @@
 import com.android.launcher3.BuildConfig;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.CollectionInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -210,7 +211,7 @@
     }
 
     /**
-     * Updates the deep shortucts state in system to match out internal model, pinning any missing
+     * Updates the deep shortcuts state in system to match out internal model, pinning any missing
      * shortcuts and unpinning any extra shortcuts.
      */
     public void updateShortcutPinnedState(Context context) {
@@ -266,6 +267,8 @@
                     || !systemShortcuts.containsAll(modelShortcuts)) {
                 // Update system state for this package
                 try {
+                    FileLog.d(TAG, "updateShortcutPinnedState:"
+                            + " Pinning Shortcuts: " + entry.getKey() + ": " + modelShortcuts);
                     context.getSystemService(LauncherApps.class).pinShortcuts(
                             entry.getKey(), new ArrayList<>(modelShortcuts), user);
                 } catch (SecurityException | IllegalStateException e) {
@@ -278,6 +281,9 @@
         systemMap.keySet().forEach(packageName -> {
             // Update system state
             try {
+                FileLog.d(TAG, "updateShortcutPinnedState:"
+                        + " Unpinning extra Shortcuts for package: " + packageName
+                        + ": " + systemMap.get(packageName));
                 context.getSystemService(LauncherApps.class).pinShortcuts(
                         packageName, Collections.emptyList(), user);
             } catch (SecurityException | IllegalStateException e) {
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 6a8d86b..bd8c36b 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -24,6 +24,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
 import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
 import static com.android.launcher3.icons.cache.CacheLookupFlag.DEFAULT_LOOKUP_FLAG;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
 
 import android.content.ComponentName;
 import android.content.ContentValues;
@@ -307,7 +308,7 @@
      * Make an WorkspaceItemInfo object for a restored application or shortcut item that points
      * to a package that is not yet installed on the system.
      */
-    public WorkspaceItemInfo getRestoredItemInfo(Intent intent) {
+    public WorkspaceItemInfo getRestoredItemInfo(Intent intent, boolean isArchived) {
         final WorkspaceItemInfo info = new WorkspaceItemInfo();
         info.user = user;
         info.intent = intent;
@@ -317,7 +318,7 @@
             mIconCache.getTitleAndIcon(info, DEFAULT_LOOKUP_FLAG);
         }
 
-        if (hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORED_ICON)) {
+        if (hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORED_ICON) || isArchived) {
             String title = getTitle();
             if (!TextUtils.isEmpty(title)) {
                 info.title = Utilities.trim(title);
@@ -333,6 +334,7 @@
         info.contentDescription = mIconCache.getUserBadgedLabel(info.title, info.user);
         info.itemType = itemType;
         info.status = restoreFlag;
+        if (isArchived) info.runtimeStatusFlags |= FLAG_ARCHIVED;
         return info;
     }
 
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 6bef292..d1eceb9 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.model;
 
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED;
@@ -39,7 +40,6 @@
 import com.android.launcher3.Flags;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.IconCache;
@@ -238,19 +238,22 @@
                         if (itemInfo.isPromise() && isNewApkAvailable) {
                             boolean isTargetValid = !cn.getClassName().equals(
                                     IconCache.EMPTY_CLASS_NAME);
-                            if (itemInfo.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                            if (itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
                                 List<ShortcutInfo> shortcut =
                                         new ShortcutRequest(context, mUser)
                                                 .forPackage(cn.getPackageName(),
                                                         itemInfo.getDeepShortcutId())
                                                 .query(ShortcutRequest.PINNED);
-                                if (shortcut.isEmpty()) {
+                                if (shortcut.isEmpty()
+                                        && !(Flags.restoreArchivedShortcuts()
+                                            && !itemInfo.isArchived())
+                                ) {
                                     isTargetValid = false;
                                     if (DEBUG) {
                                         Log.d(TAG, "Pinned Shortcut not found for updated"
                                                 + " package=" + itemInfo.getTargetPackage());
                                     }
-                                } else {
+                                } else if (!shortcut.isEmpty()) {
                                     if (DEBUG) {
                                         Log.d(TAG, "Found pinned shortcut for updated"
                                                 + " package=" + itemInfo.getTargetPackage()
@@ -269,7 +272,7 @@
                                     || itemInfo.isArchived())) {
                                 if (updateWorkspaceItemIntent(context, itemInfo, packageName)) {
                                     infoUpdated = true;
-                                } else if (itemInfo.hasPromiseIconUi()) {
+                                } else if (shouldRemoveRestoredShortcut(itemInfo)) {
                                     removedShortcuts.add(itemInfo.id);
                                     if (DEBUG) {
                                         FileLog.w(TAG, "Removing restored shortcut promise icon"
@@ -436,7 +439,7 @@
      */
     private boolean updateWorkspaceItemIntent(Context context,
             WorkspaceItemInfo si, String packageName) {
-        if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+        if (si.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
             // Do not update intent for deep shortcuts as they contain additional information
             // about the shortcut.
             return false;
@@ -452,6 +455,15 @@
         return false;
     }
 
+    private boolean shouldRemoveRestoredShortcut(WorkspaceItemInfo itemInfo) {
+        if (itemInfo.hasPromiseIconUi() && !Flags.restoreArchivedShortcuts()) {
+            return true;
+        }
+        return Flags.restoreArchivedShortcuts()
+                && !itemInfo.isArchived()
+                && itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT;
+    }
+
     private String getOpString() {
         return switch (mOp) {
             case OP_NONE -> "NONE";
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.kt b/src/com/android/launcher3/model/ShortcutsChangedTask.kt
index 2e4f75f..56e9e43 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.kt
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.kt
@@ -17,6 +17,7 @@
 
 import android.content.pm.ShortcutInfo
 import android.os.UserHandle
+import com.android.launcher3.Flags
 import com.android.launcher3.LauncherModel.ModelUpdateTask
 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
 import com.android.launcher3.icons.CacheableShortcutInfo
@@ -59,8 +60,11 @@
             val infoWrapper = ApplicationInfoWrapper(context, packageName, user)
             if (shortcuts.isEmpty()) {
                 // Verify that the app is indeed installed.
-                if (!infoWrapper.isInstalled() && !infoWrapper.isArchived()) {
-                    // App is not installed or archived, ignoring package events
+                if (
+                    (!infoWrapper.isInstalled() && !infoWrapper.isArchived()) ||
+                        (Flags.restoreArchivedShortcuts() && infoWrapper.isArchived())
+                ) {
+                    // App is not installed or is archived, ignoring package events
                     return
                 }
             }
@@ -75,7 +79,7 @@
             val nonPinnedIds: MutableSet<String> = HashSet(allLauncherKnownIds)
             val updatedWorkspaceItemInfos = ArrayList<WorkspaceItemInfo>()
             for (fullDetails in shortcuts) {
-                if (!fullDetails.isPinned) {
+                if (!fullDetails.isPinned && !Flags.restoreArchivedShortcuts()) {
                     continue
                 }
                 val shortcutId = fullDetails.id
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 90f11a3..3919eb7 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -194,27 +194,36 @@
         if (intent.`package` == null) {
             intent.`package` = targetPkg
         }
+        val isPreArchived = appInfoWrapper.isArchived() && c.restoreFlag != 0
+
         // else if cn == null => can't infer much, leave it
         // else if !validPkg => could be restored icon or missing sd-card
         when {
-            !TextUtils.isEmpty(targetPkg) && !validTarget -> {
+            !TextUtils.isEmpty(targetPkg) && (!validTarget || isPreArchived) -> {
                 // Points to a valid app (superset of cn != null) but the apk
                 // is not available.
                 when {
-                    c.restoreFlag != 0 -> {
+                    c.restoreFlag != 0 || isPreArchived -> {
                         // Package is not yet available but might be
                         // installed later.
-                        FileLog.d(TAG, "package not yet restored: $targetPkg")
+                        FileLog.d(
+                            TAG,
+                            "package not yet restored: $targetPkg, itemType=${c.itemType}" +
+                                "isPreArchived=$isPreArchived, restoreFlag=${c.restoreFlag}",
+                        )
                         tempPackageKey.update(targetPkg, c.user)
                         when {
                             c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED) -> {
                                 // Restore has started once.
                             }
-                            installingPkgs.containsKey(tempPackageKey) -> {
+                            installingPkgs.containsKey(tempPackageKey) || isPreArchived -> {
                                 // App restore has started. Update the flag
                                 c.restoreFlag =
                                     c.restoreFlag or WorkspaceItemInfo.FLAG_RESTORE_STARTED
-                                FileLog.d(TAG, "restore started for installing app: $targetPkg")
+                                FileLog.d(
+                                    TAG,
+                                    "restore started for installing app: $targetPkg, itemType=${c.itemType}",
+                                )
                                 c.updater().put(Favorites.RESTORED, c.restoreFlag).commit()
                             }
                             else -> {
@@ -253,9 +262,18 @@
             }
         }
         if (c.restoreFlag and WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI != 0) {
+            FileLog.d(
+                TAG,
+                "restore flag set AND WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI != 0, setting valid target to false: $targetPkg, itemType=${c.itemType}, restoreFlag=${c.restoreFlag}",
+            )
             validTarget = false
         }
-        if (validTarget) {
+        if (validTarget && !isPreArchived) {
+            FileLog.d(
+                TAG,
+                "valid target true, marking restored: $targetPkg," +
+                    " itemType=${c.itemType}, restoreFlag=${c.restoreFlag}",
+            )
             // The shortcut points to a valid target (either no target
             // or something which is ready to be used)
             c.markRestored()
@@ -265,7 +283,7 @@
         when {
             c.restoreFlag != 0 -> {
                 // Already verified above that user is same as default user
-                info = c.getRestoredItemInfo(intent)
+                info = c.getRestoredItemInfo(intent, isPreArchived)
             }
             c.itemType == Favorites.ITEM_TYPE_APPLICATION ->
                 info = c.getAppShortcutInfo(intent, allowMissingTarget, useLowResIcon, false)
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt
index fb6d038..010159c 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt
@@ -24,8 +24,12 @@
 import android.content.pm.ShortcutInfo
 import android.os.Process.myUserHandle
 import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.launcher3.Flags
 import com.android.launcher3.LauncherAppState
 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
 import com.android.launcher3.icons.BitmapInfo
@@ -42,6 +46,7 @@
 import java.util.function.Predicate
 import org.junit.After
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
@@ -55,6 +60,8 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ShortcutsChangedTaskTest {
+    @get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
     private lateinit var shortcutsChangedTask: ShortcutsChangedTask
     private lateinit var modelHelper: LauncherModelHelper
     private lateinit var context: SandboxModelContext
@@ -192,7 +199,8 @@
     }
 
     @Test
-    fun `When archived pinned shortcut is found then keep in workspace`() {
+    @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_SHORTCUTS)
+    fun `When archived pinned shortcut is found with flag off then keep in workspace`() {
         // Given
         shortcuts =
             listOf(
@@ -222,7 +230,8 @@
     }
 
     @Test
-    fun `When archived unpinned shortcut is found then keep in workspace`() {
+    @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_SHORTCUTS)
+    fun `When archived unpinned shortcut is found with flag off then keep in workspace`() {
         // Given
         shortcuts =
             listOf(
@@ -310,4 +319,34 @@
         assertThat(modelHelper.bgDataModel.deepShortcutMap).doesNotContainKey(expectedKey)
         verify(mockTaskController, times(0)).bindDeepShortcuts(eq(modelHelper.bgDataModel))
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_SHORTCUTS)
+    fun `When restoring archived shortcut with flag on then skip handling`() {
+        // Given
+        shortcuts =
+            listOf(
+                mock<ShortcutInfo>().apply {
+                    whenever(isPinned).thenReturn(true)
+                    whenever(id).thenReturn(expectedShortcutId)
+                }
+            )
+        val items: IntSparseArrayMap<ItemInfo> = modelHelper.bgDataModel.itemsIdMap
+        items.put(expectedWai.id, expectedWai)
+        doReturn(
+                ApplicationInfo().apply {
+                    enabled = true
+                    flags = flags or FLAG_INSTALLED
+                    isArchived = true
+                }
+            )
+            .whenever(launcherApps)
+            .getApplicationInfo(eq(expectedPackage), any(), eq(user))
+        doReturn(shortcuts).whenever(launcherApps).getShortcuts(any(), eq(user))
+        // When
+        shortcutsChangedTask.execute(mockTaskController, modelHelper.bgDataModel, mockAllApps)
+        // Then
+        verify(mockTaskController, times(0)).deleteAndBindComponentsRemoved(any(), any())
+        verify(mockTaskController, times(0)).bindUpdatedWorkspaceItems(any())
+    }
 }
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index da87dfc..7a403e1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -18,16 +18,19 @@
 
 import android.appwidget.AppWidgetProviderInfo
 import android.content.ComponentName
-import android.content.Context
 import android.content.Intent
+import android.content.pm.ApplicationInfo
 import android.content.pm.LauncherApps
 import android.content.pm.PackageInstaller
 import android.content.pm.ShortcutInfo
 import android.os.Process
 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.util.LongSparseArray
-import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.Flags
 import com.android.launcher3.LauncherAppState
 import com.android.launcher3.LauncherSettings.Favorites
 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
@@ -44,9 +47,14 @@
 import com.android.launcher3.model.data.LauncherAppWidgetInfo
 import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_UI_NOT_READY
 import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_RESTORED_ICON
+import com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_RESTORE_STARTED
 import com.android.launcher3.pm.UserCache
 import com.android.launcher3.shortcuts.ShortcutKey
 import com.android.launcher3.util.ComponentKey
+import com.android.launcher3.util.ContentWriter
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
 import com.android.launcher3.util.PackageManagerHelper
 import com.android.launcher3.util.PackageUserKey
 import com.android.launcher3.util.UserIconInfo
@@ -55,6 +63,7 @@
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -66,6 +75,7 @@
 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.mock
 import org.mockito.kotlin.whenever
@@ -73,20 +83,23 @@
 @RunWith(AndroidJUnit4::class)
 class WorkspaceItemProcessorTest {
 
+    @get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
     @Mock private lateinit var mockIconRequestInfo: IconRequestInfo<WorkspaceItemInfo>
     @Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
     @Mock private lateinit var mockBgDataModel: BgDataModel
-    @Mock private lateinit var mockContext: Context
     @Mock private lateinit var mockAppState: LauncherAppState
     @Mock private lateinit var mockPmHelper: PackageManagerHelper
-    @Mock private lateinit var mockLauncherApps: LauncherApps
     @Mock private lateinit var mockCursor: LoaderCursor
     @Mock private lateinit var mockUserCache: UserCache
     @Mock private lateinit var mockUserManagerState: UserManagerState
     @Mock private lateinit var mockWidgetInflater: WidgetInflater
 
-    private var intent: Intent = Intent()
-    private var mUserHandle: UserHandle = UserHandle(0)
+    lateinit var mModelHelper: LauncherModelHelper
+    lateinit var mContext: SandboxModelContext
+    lateinit var mLauncherApps: LauncherApps
+    private var mIntent: Intent = Intent()
+    private var mUserHandle: UserHandle = Process.myUserHandle()
     private var mIconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>> = mutableListOf()
     private var mComponentName: ComponentName = ComponentName("package", "class")
     private var mUnlockedUsersArray: LongSparseArray<Boolean> = LongSparseArray()
@@ -101,40 +114,35 @@
 
     @Before
     fun setup() {
-        mUserHandle = UserHandle(0)
+        mModelHelper = LauncherModelHelper()
+        mContext = mModelHelper.sandboxContext
+        mLauncherApps =
+            mContext.spyService(LauncherApps::class.java).apply {
+                doReturn(true).whenever(this).isPackageEnabled("package", mUserHandle)
+                doReturn(true).whenever(this).isActivityEnabled(mComponentName, mUserHandle)
+            }
+        mUserHandle = Process.myUserHandle()
         mockIconRequestInfo = mock<IconRequestInfo<WorkspaceItemInfo>>()
         mockWorkspaceInfo = mock<WorkspaceItemInfo>()
         mockBgDataModel = mock<BgDataModel>()
         mComponentName = ComponentName("package", "class")
         mUnlockedUsersArray = LongSparseArray<Boolean>(1).apply { put(101, true) }
-        intent =
+        mIntent =
             Intent().apply {
                 component = mComponentName
                 `package` = "pkg"
                 putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "")
             }
-        mockLauncherApps =
-            mock<LauncherApps>().apply {
-                whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
-                whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true)
-            }
-        mockContext =
-            mock<Context>().apply {
-                whenever(packageManager).thenReturn(mock())
-                whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
-                whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
-                whenever(getSystemService(LauncherApps::class.java)).thenReturn(mockLauncherApps)
-            }
         mockAppState =
             mock<LauncherAppState>().apply {
-                whenever(context).thenReturn(mockContext)
+                whenever(context).thenReturn(mContext)
                 whenever(iconCache).thenReturn(mock())
                 whenever(iconCache.getShortcutIcon(any(), any(), any())).then {}
             }
         mockPmHelper =
             mock<PackageManagerHelper>().apply {
                 whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
-                    .thenReturn(intent)
+                    .thenReturn(mIntent)
             }
         mockCursor =
             mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
@@ -143,9 +151,9 @@
                 id = 1
                 restoreFlag = 1
                 serialNumber = 101
-                whenever(parseIntent()).thenReturn(intent)
+                whenever(parseIntent()).thenReturn(mIntent)
                 whenever(markRestored()).doAnswer { restoreFlag = 0 }
-                whenever(updater().put(Favorites.INTENT, intent.toUri(0)).commit()).thenReturn(1)
+                whenever(updater().put(Favorites.INTENT, mIntent.toUri(0)).commit()).thenReturn(1)
                 whenever(getAppShortcutInfo(any(), any(), any(), any()))
                     .thenReturn(mockWorkspaceInfo)
                 whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
@@ -177,7 +185,7 @@
         memoryLogger: LoaderMemoryLogger? = null,
         userCache: UserCache = mockUserCache,
         userManagerState: UserManagerState = mockUserManagerState,
-        launcherApps: LauncherApps = mockLauncherApps,
+        launcherApps: LauncherApps = mLauncherApps,
         shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = mKeyToPinnedShortcutsMap,
         app: LauncherAppState = mockAppState,
         bgDataModel: BgDataModel = mockBgDataModel,
@@ -244,7 +252,7 @@
     fun `When app has null target package then mark deleted`() {
 
         // Given
-        intent.apply {
+        mIntent.apply {
             component = null
             `package` = null
         }
@@ -264,8 +272,8 @@
 
         // Given
         mComponentName = ComponentName("", "")
-        intent.component = mComponentName
-        intent.`package` = ""
+        mIntent.component = mComponentName
+        mIntent.`package` = ""
 
         // When
         itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
@@ -298,15 +306,14 @@
     fun `When fallback Activity found for app then mark restored`() {
 
         // Given
-        mockLauncherApps =
-            mock<LauncherApps>().apply {
-                whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
-                whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(false)
-            }
+        mLauncherApps.apply {
+            whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
+            whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(false)
+        }
         mockPmHelper =
             mock<PackageManagerHelper>().apply {
                 whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
-                    .thenReturn(intent)
+                    .thenReturn(mIntent)
             }
 
         // When
@@ -317,7 +324,7 @@
         assertWithMessage("item restoreFlag should be set to 0")
             .that(mockCursor.restoreFlag)
             .isEqualTo(0)
-        verify(mockCursor.updater().put(Favorites.INTENT, intent.toUri(0))).commit()
+        verify(mockCursor.updater().put(Favorites.INTENT, mIntent.toUri(0))).commit()
         assertThat(mIconRequestInfos).containsExactly(mockIconRequestInfo)
         verify(mockCursor).checkAndAddItem(mockWorkspaceInfo, mockBgDataModel, null)
     }
@@ -326,11 +333,10 @@
     fun `When app with disabled activity and no fallback found then mark deleted`() {
 
         // Given
-        mockLauncherApps =
-            mock<LauncherApps>().apply {
-                whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
-                whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(false)
-            }
+        mLauncherApps.apply {
+            whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
+            whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(false)
+        }
         mockPmHelper =
             mock<PackageManagerHelper>().apply {
                 whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
@@ -358,11 +364,11 @@
 
     @Test
     fun `When valid Pinned Deep Shortcut then mark restored`() {
-
         // Given
         mockCursor.itemType = ITEM_TYPE_DEEP_SHORTCUT
         val expectedShortcutInfo =
             mock<ShortcutInfo>().apply {
+                whenever(userHandle).thenReturn(mUserHandle)
                 whenever(id).thenReturn("")
                 whenever(`package`).thenReturn("")
                 whenever(activity).thenReturn(mock())
@@ -372,7 +378,7 @@
                 whenever(disabledReason).thenReturn(0)
                 whenever(persons).thenReturn(EMPTY_PERSON_ARRAY)
             }
-        val shortcutKey = ShortcutKey.fromIntent(intent, mockCursor.user)
+        val shortcutKey = ShortcutKey.fromIntent(mIntent, mockCursor.user)
         mKeyToPinnedShortcutsMap[shortcutKey] = expectedShortcutInfo
         mIconRequestInfos = mutableListOf()
 
@@ -393,6 +399,67 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_SHORTCUTS)
+    fun `When Archived Deep Shortcut with flag on then mark restored`() {
+        // Given
+        val mockContentWriter: ContentWriter = mock()
+        val mockAppInfo: ApplicationInfo =
+            mock<ApplicationInfo>().apply {
+                isArchived = true
+                enabled = true
+            }
+        val expectedRestoreFlag = FLAG_RESTORED_ICON or FLAG_RESTORE_STARTED
+        doReturn(mockAppInfo).whenever(mLauncherApps).getApplicationInfo(any(), any(), any())
+        whenever(mockContentWriter.put(Favorites.RESTORED, expectedRestoreFlag))
+            .thenReturn(mockContentWriter)
+        whenever(mockContentWriter.commit()).thenReturn(1)
+        mockCursor.apply {
+            itemType = ITEM_TYPE_DEEP_SHORTCUT
+            restoreFlag = restoreFlag or FLAG_RESTORED_ICON
+            whenever(updater()).thenReturn(mockContentWriter)
+        }
+        mIconRequestInfos = mutableListOf()
+
+        // When
+        itemProcessorUnderTest =
+            createWorkspaceItemProcessorUnderTest(allDeepShortcuts = mAllDeepShortcuts)
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        assertThat(mockCursor.restoreFlag and FLAG_RESTORED_ICON).isEqualTo(FLAG_RESTORED_ICON)
+        assertThat(mockCursor.restoreFlag and FLAG_RESTORE_STARTED).isEqualTo(FLAG_RESTORE_STARTED)
+        assertThat(mIconRequestInfos).isNotEmpty()
+        assertThat(mAllDeepShortcuts).isEmpty()
+        verify(mockContentWriter).put(Favorites.RESTORED, expectedRestoreFlag)
+        verify(mockCursor).checkAndAddItem(any(), eq(mockBgDataModel), eq(null))
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_SHORTCUTS)
+    fun `When Archived Deep Shortcut with flag off then remove`() {
+        // Given
+        mockCursor.itemType = ITEM_TYPE_DEEP_SHORTCUT
+        mIconRequestInfos = mutableListOf()
+
+        // When
+        itemProcessorUnderTest =
+            createWorkspaceItemProcessorUnderTest(allDeepShortcuts = mAllDeepShortcuts)
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        assertWithMessage("item restoreFlag should be set to 0")
+            .that(mockCursor.restoreFlag)
+            .isEqualTo(0)
+        assertThat(mIconRequestInfos).isEmpty()
+        assertThat(mAllDeepShortcuts).isEmpty()
+        verify(mockCursor)
+            .markDeleted(
+                "Pinned shortcut not found from request. package=pkg, user=UserHandle{0}",
+                "shortcut_not_found",
+            )
+    }
+
+    @Test
     fun `When Pinned Deep Shortcut is not stored in ShortcutManager re-query by Shortcut ID`() {
         // Given
         mockCursor.itemType = ITEM_TYPE_DEEP_SHORTCUT
@@ -406,8 +473,9 @@
                 whenever(disabledMessage).thenReturn("")
                 whenever(disabledReason).thenReturn(0)
                 whenever(persons).thenReturn(EMPTY_PERSON_ARRAY)
+                whenever(userHandle).thenReturn(mUserHandle)
             }
-        whenever(mockLauncherApps.getShortcuts(any(), any())).thenReturn(listOf(si))
+        doReturn(listOf(si)).whenever(mLauncherApps).getShortcuts(any(), any())
         mKeyToPinnedShortcutsMap.clear()
         mIconRequestInfos = mutableListOf()
 
@@ -417,12 +485,12 @@
         itemProcessorUnderTest.processItem()
 
         // Then
-        verify(mockLauncherApps).getShortcuts(any(), any())
+        verify(mLauncherApps).getShortcuts(any(), any())
         assertWithMessage("item restoreFlag should be set to 0")
             .that(mockCursor.restoreFlag)
             .isEqualTo(0)
         verify(mockCursor).markRestored()
-        verify(mockCursor).checkAndAddItem(any(), any(), anyOrNull())
+        verify(mockCursor).checkAndAddItem(any(), any(), eq(null))
     }
 
     @Test
@@ -469,11 +537,11 @@
             }
         mIconRequestInfos = mutableListOf()
         // Make sure shortcuts map has expected key from expected package
-        intent.`package` = mComponentName.packageName
-        val shortcutKey = ShortcutKey.fromIntent(intent, mockCursor.user)
+        mIntent.`package` = mComponentName.packageName
+        val shortcutKey = ShortcutKey.fromIntent(mIntent, mockCursor.user)
         mKeyToPinnedShortcutsMap[shortcutKey] = expectedShortcutInfo
         // set intent package back to null to test scenario
-        intent.`package` = null
+        mIntent.`package` = null
 
         // When
         itemProcessorUnderTest =
@@ -656,7 +724,7 @@
         itemProcessorUnderTest.processItem()
 
         // Then
-        verify(mockCursor).checkAndAddItem(any(), any(), anyOrNull())
+        verify(mockCursor).checkAndAddItem(any(), eq(mockBgDataModel), eq(null))
     }
 
     @Test