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:
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
+117
-49
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user