Revert^2 "Restore Archived Pinned Shortcuts instead of removing them."

This reverts commit f5cb1b664b.

Reason for revert: adding test fix and reverting

Change-Id: I2fc8dc83259b2ee7992ac2291df6ee4246a2e9d5
This commit is contained in:
Charlie Anderson
2025-02-07 06:57:29 -08:00
parent 126566a096
commit ceb401c3e7
7 changed files with 252 additions and 71 deletions
@@ -46,6 +46,7 @@ import androidx.annotation.Nullable;
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 @@ public class BgDataModel {
}
/**
* 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 @@ public class BgDataModel {
|| !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 @@ public class BgDataModel {
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) {
@@ -24,6 +24,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SH
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 @@ public class LoaderCursor extends CursorWrapper {
* 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 @@ public class LoaderCursor extends CursorWrapper {
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 @@ public class LoaderCursor extends CursorWrapper {
info.contentDescription = mIconCache.getUserBadgedLabel(info.title, info.user);
info.itemType = itemType;
info.status = restoreFlag;
if (isArchived) info.runtimeStatusFlags |= FLAG_ARCHIVED;
return info;
}
@@ -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 androidx.annotation.NonNull;
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 @@ public class PackageUpdatedTask implements ModelUpdateTask {
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 @@ public class PackageUpdatedTask implements ModelUpdateTask {
|| 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 @@ public class PackageUpdatedTask implements ModelUpdateTask {
*/
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 @@ public class PackageUpdatedTask implements ModelUpdateTask {
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";
@@ -17,6 +17,7 @@ package com.android.launcher3.model
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 @@ class ShortcutsChangedTask(
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 @@ class ShortcutsChangedTask(
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
@@ -194,27 +194,36 @@ class WorkspaceItemProcessor(
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 @@ class WorkspaceItemProcessor(
}
}
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 @@ class WorkspaceItemProcessor(
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)
@@ -24,8 +24,12 @@ import android.content.pm.LauncherApps
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 com.google.common.truth.Truth.assertThat
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 @@ import org.mockito.kotlin.whenever
@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
@@ -131,7 +138,8 @@ class ShortcutsChangedTaskTest {
}
@Test
fun `When installed unpinned shortcut is found then remove from workspace`() {
@DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_SHORTCUTS)
fun `When installed unpinned shortcut is found with Flag off then remove from workspace`() {
// Given
shortcuts =
listOf(
@@ -162,6 +170,37 @@ class ShortcutsChangedTaskTest {
)
}
@Test
@EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_SHORTCUTS)
fun `When installed unpinned shortcut is found with Flag on then keep in workspace`() {
// Given
shortcuts =
listOf(
mock<ShortcutInfo>().apply {
whenever(isPinned).thenReturn(false)
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 = false
}
)
.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(mockAppState.iconCache)
.getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWai))
}
@Test
fun `When shortcut app is uninstalled then skip handling`() {
// Given
@@ -192,7 +231,8 @@ class ShortcutsChangedTaskTest {
}
@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 +262,8 @@ class ShortcutsChangedTaskTest {
}
@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 +351,34 @@ class ShortcutsChangedTaskTest {
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())
}
}
@@ -18,16 +18,19 @@ package com.android.launcher3.model
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.ItemInfo
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.android.launcher3.widget.WidgetInflater
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.Mockito.verify
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 @@ import org.mockito.kotlin.whenever
@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 @@ class WorkspaceItemProcessorTest {
@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 @@ class WorkspaceItemProcessorTest {
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 @@ class WorkspaceItemProcessorTest {
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 @@ class WorkspaceItemProcessorTest {
fun `When app has null target package then mark deleted`() {
// Given
intent.apply {
mIntent.apply {
component = null
`package` = null
}
@@ -264,8 +272,8 @@ class WorkspaceItemProcessorTest {
// Given
mComponentName = ComponentName("", "")
intent.component = mComponentName
intent.`package` = ""
mIntent.component = mComponentName
mIntent.`package` = ""
// When
itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
@@ -298,15 +306,14 @@ class WorkspaceItemProcessorTest {
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 @@ class WorkspaceItemProcessorTest {
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 @@ class WorkspaceItemProcessorTest {
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 @@ class WorkspaceItemProcessorTest {
@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 @@ class WorkspaceItemProcessorTest {
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()
@@ -392,6 +398,67 @@ class WorkspaceItemProcessorTest {
verify(mockCursor).checkAndAddItem(any(), any(), anyOrNull())
}
@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
@@ -406,8 +473,9 @@ class WorkspaceItemProcessorTest {
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 @@ class WorkspaceItemProcessorTest {
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 @@ class WorkspaceItemProcessorTest {
}
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 @@ class WorkspaceItemProcessorTest {
itemProcessorUnderTest.processItem()
// Then
verify(mockCursor).checkAndAddItem(any(), any(), anyOrNull())
verify(mockCursor).checkAndAddItem(any(), eq(mockBgDataModel), eq(null))
}
@Test