From e274d97fe50dede76afc5e3d69ed58ef6d5d2e9d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 1 May 2023 16:55:59 -0700 Subject: [PATCH] Removing support for lagacy shortcuts > Addition/removal of shortcus is already removed. This just cleans up the unused code path Bug: 275875209 Test: Updated tests Flag: N/A Change-Id: I8ab7f57b693f996920e50e8beecafcffab5167e9 --- .../uioverrides/PredictedAppIcon.java | 1 - .../uioverrides/QuickstepLauncher.java | 3 - .../WidgetsPredicationUpdateTaskTest.java | 142 ++++--- src/com/android/launcher3/Launcher.java | 3 +- .../android/launcher3/LauncherAppState.java | 11 +- .../android/launcher3/LauncherSettings.java | 3 +- src/com/android/launcher3/Workspace.java | 6 +- src/com/android/launcher3/folder/Folder.java | 1 - .../android/launcher3/folder/FolderIcon.java | 1 - .../graphics/LauncherPreviewRenderer.java | 21 - .../model/AddWorkspaceItemsTask.java | 3 +- .../android/launcher3/model/BgDataModel.java | 2 - .../model/GridSizeMigrationUtil.java | 2 - .../launcher3/model/ItemInstallQueue.java | 1 - .../android/launcher3/model/LoaderCursor.java | 5 +- .../android/launcher3/model/LoaderTask.java | 6 +- .../android/launcher3/model/ModelWriter.java | 1 - .../launcher3/model/data/ItemInfo.java | 9 - .../model/data/WorkspaceItemInfo.java | 6 +- .../pm/ShortcutConfigActivityInfo.java | 2 +- src/com/android/launcher3/util/IntArray.java | 5 + .../util/MainThreadInitializedObject.java | 20 +- .../launcher3/views/ActivityContext.java | 65 +-- .../launcher3/views/OptionsPopupView.java | 1 - .../res/raw/cache_data_updated_task_data.txt | 28 -- ...package_install_state_change_task_data.txt | 24 -- .../widgets_predication_update_task_data.txt | 24 -- .../model/AbstractWorkspaceModelTest.kt | 68 ++-- .../model/AddWorkspaceItemsTaskTest.kt | 48 +-- .../model/CacheDataUpdatedTaskTest.java | 181 ++++----- .../model/GridSizeMigrationUtilTest.kt | 47 ++- .../launcher3/model/LoaderCursorTest.java | 6 +- .../PackageInstallStateChangedTaskTest.java | 81 +++- .../launcher3/util/DisplayControllerTest.kt | 5 +- .../launcher3/util/LauncherModelHelper.java | 373 +++++------------- .../com/android/launcher3/util/TestUtil.java | 25 ++ 36 files changed, 470 insertions(+), 760 deletions(-) delete mode 100644 tests/res/raw/cache_data_updated_task_data.txt delete mode 100644 tests/res/raw/package_install_state_change_task_data.txt delete mode 100644 tests/res/raw/widgets_predication_update_task_data.txt diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index a8b7698b57..b059cbdc6c 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -337,7 +337,6 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { if (getTag() instanceof WorkspaceItemInfo) { WorkspaceItemInfo info = (WorkspaceItemInfo) getTag(); isBadged = !Process.myUserHandle().equals(info.user) - || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index ef63b3bc7e..8ff0969fe5 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -25,7 +25,6 @@ import static com.android.launcher3.LauncherSettings.Animation.VIEW_BACKGROUND; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NO_OFFSET; @@ -283,7 +282,6 @@ public class QuickstepLauncher extends Launcher { if (mAllAppsPredictions != null && (info.itemType == ITEM_TYPE_APPLICATION - || info.itemType == ITEM_TYPE_SHORTCUT || info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) { int count = mAllAppsPredictions.items.size(); for (int i = 0; i < count; i++) { @@ -1162,7 +1160,6 @@ public class QuickstepLauncher extends Launcher { } switch (info.itemType) { case Favorites.ITEM_TYPE_APPLICATION: - case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_DEEP_SHORTCUT: case Favorites.ITEM_TYPE_APPWIDGET: // Fall through and continue if it's an app, shortcut, or widget diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java index 83341cb868..b12d98b8ce 100644 --- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java +++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java @@ -20,6 +20,7 @@ import static android.os.Process.myUserHandle; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo; import static com.google.common.truth.Truth.assertThat; @@ -40,11 +41,9 @@ import android.text.TextUtils; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.icons.ComponentWithLabel; -import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.QuickstepModelDelegate.PredictorState; +import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.LauncherModelHelper; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -53,8 +52,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.util.Arrays; import java.util.List; @@ -76,17 +73,9 @@ public final class WidgetsPredicationUpdateTaskTest { private LauncherModelHelper mModelHelper; private UserHandle mUserHandle; - @Mock - private IconCache mIconCache; - @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); - MockitoAnnotations.initMocks(this); - doAnswer(invocation -> { - ComponentWithLabel componentWithLabel = invocation.getArgument(0); - return componentWithLabel.getComponent().getShortClassName(); - }).when(mIconCache).getTitleNoCache(any()); mUserHandle = myUserHandle(); mApp1Provider1 = createAppWidgetProviderInfo( @@ -114,16 +103,12 @@ public final class WidgetsPredicationUpdateTaskTest { .collect(Collectors.toList()); }).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle())); - // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace. - mModelHelper.initializeData("widgets_predication_update_task_data"); - + LauncherLayoutBuilder builder = new LauncherLayoutBuilder() + .atWorkspace(0, 1, 2).putWidget("app4", "provider1", 1, 1) + .atWorkspace(0, 1, 3).putWidget("app5", "provider1", 1, 1); + mModelHelper.setupDefaultLayoutProvider(builder); MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get(); - MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update( - LauncherAppState.getInstance(mModelHelper.sandboxContext), - /* packageUser= */ null)); - - MODEL_EXECUTOR.submit(() -> { }).get(); - MAIN_EXECUTOR.submit(() -> { }).get(); + mModelHelper.loadModelSync(); } @After @@ -132,65 +117,72 @@ public final class WidgetsPredicationUpdateTaskTest { } @Test - public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() - throws Exception { - // WHEN newPredicationTask is executed with app predication of 5 apps. - AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1", - mUserHandle); - AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1", - mUserHandle); - AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className", - mUserHandle); - AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1", - mUserHandle); - AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1", - mUserHandle); - mModelHelper.executeTaskForTest( - newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1))) - .forEach(Runnable::run); + public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + // WHEN newPredicationTask is executed with app predication of 5 apps. + AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1", + mUserHandle); + AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1", + mUserHandle); + AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className", + mUserHandle); + AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1", + mUserHandle); + AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1", + mUserHandle); + mCallback.mRecommendedWidgets = null; + mModelHelper.getModel().enqueueModelUpdateTask( + newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1))); + runOnExecutorSync(MAIN_EXECUTOR, () -> { }); - // THEN only 2 widgets are returned because - // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are - // excluded from the result. - // 2. app3 doesn't have a widget. - // 3. only 1 widget is picked from app1 because we only want to promote one widget per app. - List recommendedWidgets = mCallback.mRecommendedWidgets.items - .stream() - .map(itemInfo -> (PendingAddWidgetInfo) itemInfo) - .collect(Collectors.toList()); - assertThat(recommendedWidgets).hasSize(2); - assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1); - assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1); + // THEN only 2 widgets are returned because + // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are + // excluded from the result. + // 2. app3 doesn't have a widget. + // 3. only 1 widget is picked from app1 because we only want to promote one widget per app. + List recommendedWidgets = mCallback.mRecommendedWidgets.items + .stream() + .map(itemInfo -> (PendingAddWidgetInfo) itemInfo) + .collect(Collectors.toList()); + assertThat(recommendedWidgets).hasSize(2); + assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1); + assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1); + }); } @Test - public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty() - throws Exception { + public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty() { + runOnExecutorSync(MODEL_EXECUTOR, () -> { - // Not installed widget - AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3", - mUserHandle); - // Not installed app - AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1", - mUserHandle); - // Workspace added widgets - AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1", - mUserHandle); - AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1", - mUserHandle); - mModelHelper.executeTaskForTest( - newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1))) - .forEach(Runnable::run); + // Not installed widget + AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3", + mUserHandle); + // Not installed app + AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1", + mUserHandle); + // Workspace added widgets + AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1", + mUserHandle); + AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1", + mUserHandle); - // THEN only 2 widgets are returned because the launcher only filters out non-exist widgets. - List recommendedWidgets = mCallback.mRecommendedWidgets.items - .stream() - .map(itemInfo -> (PendingAddWidgetInfo) itemInfo) - .collect(Collectors.toList()); - assertThat(recommendedWidgets).hasSize(2); - // Another widget from the same package - assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2); - assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1); + mCallback.mRecommendedWidgets = null; + mModelHelper.getModel().enqueueModelUpdateTask( + newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1))); + runOnExecutorSync(MAIN_EXECUTOR, () -> { }); + + // THEN only 2 widgets are returned because the launcher only filters out + // non-exist widgets. + List recommendedWidgets = mCallback.mRecommendedWidgets.items + .stream() + .map(itemInfo -> (PendingAddWidgetInfo) itemInfo) + .collect(Collectors.toList()); + assertThat(recommendedWidgets).hasSize(2); + // Another widget from the same package + assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2); + assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1); + }); } private void assertWidgetInfo( diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0b75c4509e..5af8e1e2e7 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1928,7 +1928,7 @@ public class Launcher extends StatefulActivity case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: addAppWidgetFromDrop((PendingAddWidgetInfo) info); break; - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: processShortcutFromDrop((PendingAddShortcutInfo) info); break; default: @@ -2435,7 +2435,6 @@ public class Launcher extends StatefulActivity final View view; switch (item.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: { WorkspaceItemInfo info = (WorkspaceItemInfo) item; view = createShortcut(info); diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 4d15ac7452..cc1c0f599c 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -104,6 +104,8 @@ public class LauncherAppState implements SafeCloseable { }); mContext.getSystemService(LauncherApps.class).registerCallback(mModel); + mOnTerminateCallback.add(() -> + mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel)); SimpleBroadcastReceiver modelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent); @@ -123,8 +125,9 @@ public class LauncherAppState implements SafeCloseable { mOnTerminateCallback.add(userChangeListener::close); LockedUserState.get(context).runOnUserUnlocked(() -> { - CustomWidgetManager.INSTANCE.get(mContext) - .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts); + CustomWidgetManager cwm = CustomWidgetManager.INSTANCE.get(mContext); + cwm.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts); + mOnTerminateCallback.add(() -> cwm.setWidgetRefreshCallback(null)); IconObserver observer = new IconObserver(); SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener( @@ -159,6 +162,7 @@ public class LauncherAppState implements SafeCloseable { mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext), iconCacheFileName != null); mOnTerminateCallback.add(mIconCache::close); + mOnTerminateCallback.add(mModel::destroy); } private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { @@ -180,9 +184,6 @@ public class LauncherAppState implements SafeCloseable { */ @Override public void close() { - mModel.destroy(); - mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel); - CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null); mOnTerminateCallback.executeAllAndDestroy(); } diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 7fda326ec2..2397429346 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -89,7 +89,9 @@ public class LauncherSettings { /** * The gesture is an application created shortcut + * @deprecated This is no longer supported. Use {@link #ITEM_TYPE_DEEP_SHORTCUT} instead */ + @Deprecated public static final int ITEM_TYPE_SHORTCUT = 1; /** @@ -213,7 +215,6 @@ public class LauncherSettings { public static final String itemTypeToString(int type) { switch(type) { case ITEM_TYPE_APPLICATION: return "APP"; - case ITEM_TYPE_SHORTCUT: return "SHORTCUT"; case ITEM_TYPE_FOLDER: return "FOLDER"; case ITEM_TYPE_APPWIDGET: return "WIDGET"; case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET"; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 73bb828317..dbf08944e0 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1840,7 +1840,6 @@ public class Workspace extends PagedView != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION); boolean willBecomeShortcut = (info.itemType == ITEM_TYPE_APPLICATION || - info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT); return (aboveShortcut && willBecomeShortcut); @@ -2759,7 +2758,7 @@ public class Workspace extends PagedView final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) info; boolean findNearestVacantCell = true; - if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { + if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY, cellLayout, mTargetCell); float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter( @@ -2832,8 +2831,7 @@ public class Workspace extends PagedView View view; switch (info.itemType) { - case ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION: if (info instanceof WorkspaceItemFactory) { diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 4ae54e69f1..f38cce19d7 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -881,7 +881,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo final ItemInfo item = d.dragInfo; final int itemType = item.itemType; return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || - itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)); } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 2c1100f86e..be643b31e2 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -260,7 +260,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel private boolean willAcceptItem(ItemInfo item) { final int itemType = item.itemType; return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || - itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) && item != mInfo && !mFolder.isOpen()); } diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index 47677eab8c..7241b17a9c 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -34,13 +34,9 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.content.ContextWrapper; -import android.content.Intent; import android.content.res.TypedArray; -import android.graphics.Color; import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.drawable.AdaptiveIconDrawable; -import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Handler; import android.os.Looper; @@ -78,8 +74,6 @@ import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.icons.BaseIconFactory; -import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.FixedContainerItems; @@ -183,7 +177,6 @@ public class LauncherPreviewRenderer extends ContextWrapper private final DeviceProfile mDp; private final DeviceProfile mDpOrig; private final Rect mInsets; - private final WorkspaceItemInfo mWorkspaceItemInfo; private final LayoutInflater mHomeElementInflater; private final InsettableFrameLayout mRootView; private final Hotseat mHotseat; @@ -221,19 +214,6 @@ public class LauncherPreviewRenderer extends ContextWrapper mDp.isTaskbarPresent ? 0 : currentWindowInsets.getSystemWindowInsetBottom()); mDp.updateInsets(mInsets); - BaseIconFactory iconFactory = - new BaseIconFactory(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize) { }; - BitmapInfo iconInfo = iconFactory.createBadgedIconBitmap( - new AdaptiveIconDrawable( - new ColorDrawable(Color.WHITE), - new ColorDrawable(Color.WHITE))); - - mWorkspaceItemInfo = new WorkspaceItemInfo(); - mWorkspaceItemInfo.bitmap = iconInfo; - mWorkspaceItemInfo.intent = new Intent(); - mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title = - context.getString(R.string.label_application); - mHomeElementInflater = LayoutInflater.from( new ContextThemeWrapper(this, R.style.HomeScreenElementTheme)); mHomeElementInflater.setFactory2(this); @@ -483,7 +463,6 @@ public class LauncherPreviewRenderer extends ContextWrapper for (ItemInfo itemInfo : currentWorkspaceItems) { switch (itemInfo.itemType) { case Favorites.ITEM_TYPE_APPLICATION: - case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_DEEP_SHORTCUT: inflateAndAddIcon((WorkspaceItemInfo) itemInfo); break; diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index 27d1f78fb5..5e86bd6b8a 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -91,8 +91,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { List filteredItems = new ArrayList<>(); for (Pair entry : mItemList) { ItemInfo item = entry.first; - if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || - item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { // Short-circuit this logic if the icon exists somewhere on the workspace if (shortcutExists(dataModel, item.getIntent(), item.user)) { continue; diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index 0861e9d51a..5b0da5bd6e 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -210,7 +210,6 @@ public class BgDataModel { // Fall through. } case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: workspaceItems.remove(item); break; case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: @@ -245,7 +244,6 @@ public class BgDataModel { break; case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP || item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { workspaceItems.add(item); diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java index 9a6cde63cd..9d16610410 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java +++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java @@ -455,7 +455,6 @@ public class GridSizeMigrationUtil { try { // calculate weight switch (entry.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: { entry.mIntent = c.getString(indexIntent); @@ -531,7 +530,6 @@ public class GridSizeMigrationUtil { try { // calculate weight switch (entry.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: { entry.mIntent = c.getString(indexIntent); diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java index fa0511cc56..9a3abd47fd 100644 --- a/src/com/android/launcher3/model/ItemInstallQueue.java +++ b/src/com/android/launcher3/model/ItemInstallQueue.java @@ -286,7 +286,6 @@ public class ItemInstallQueue { final WorkspaceItemInfo si = new WorkspaceItemInfo(); si.user = user; - si.itemType = ITEM_TYPE_APPLICATION; LauncherActivityInfo lai; boolean usePackageIcon = laiList.isEmpty(); diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 2054d930e6..33332f01ed 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -193,9 +193,7 @@ public class LoaderCursor extends CursorWrapper { public IconRequestInfo createIconRequestInfo( WorkspaceItemInfo wai, boolean useLowResIcon) { - byte[] iconBlob = itemType == Favorites.ITEM_TYPE_SHORTCUT - || itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT - || restoreFlag != 0 + byte[] iconBlob = itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0 ? getIconBlob() : null; return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon); @@ -347,7 +345,6 @@ public class LoaderCursor extends CursorWrapper { } final WorkspaceItemInfo info = new WorkspaceItemInfo(); - info.itemType = Favorites.ITEM_TYPE_APPLICATION; info.user = user; info.intent = newIntent; diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 1a8cf2491c..d2a81745a1 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -503,7 +503,6 @@ public class LoaderTask implements Runnable { boolean allowMissingTarget = false; switch (c.itemType) { - case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_APPLICATION: case Favorites.ITEM_TYPE_DEEP_SHORTCUT: Intent intent = c.parseIntent(); @@ -517,9 +516,8 @@ public class LoaderTask implements Runnable { ComponentName cn = intent.getComponent(); String targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); - if (TextUtils.isEmpty(targetPkg) - && c.itemType != Favorites.ITEM_TYPE_SHORTCUT) { - c.markDeleted("Only legacy shortcuts can have null package"); + if (TextUtils.isEmpty(targetPkg)) { + c.markDeleted("Shortcuts can't have null package"); return; } diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index ddb8b05839..f2afaebc57 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -498,7 +498,6 @@ public class ModelWriter { modelItem.container == Favorites.CONTAINER_HOTSEAT)) { switch (modelItem.itemType) { case Favorites.ITEM_TYPE_APPLICATION: - case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_DEEP_SHORTCUT: case Favorites.ITEM_TYPE_FOLDER: if (!mBgDataModel.workspaceItems.contains(modelItem)) { diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index bfb80b322e..1c682925a4 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -30,7 +30,6 @@ import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINE import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET; import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID; @@ -87,7 +86,6 @@ public class ItemInfo { /** * One of {@link Favorites#ITEM_TYPE_APPLICATION}, - * {@link Favorites#ITEM_TYPE_SHORTCUT}, * {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT} * {@link Favorites#ITEM_TYPE_FOLDER}, * {@link Favorites#ITEM_TYPE_APP_PAIR}, @@ -361,13 +359,6 @@ public class ItemInfo { }) .orElse(LauncherAtom.Shortcut.newBuilder())); break; - case ITEM_TYPE_SHORTCUT: - itemBuilder - .setShortcut(nullableComponent - .map(component -> LauncherAtom.Shortcut.newBuilder() - .setShortcutName(component.flattenToShortString())) - .orElse(LauncherAtom.Shortcut.newBuilder())); - break; case ITEM_TYPE_APPWIDGET: itemBuilder .setWidget(nullableComponent diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java index 01606d4611..3ce194dd81 100644 --- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java +++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java @@ -96,7 +96,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { public WorkspaceItemInfo() { - itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; + itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; } public WorkspaceItemInfo(WorkspaceItemInfo info) { @@ -205,8 +205,8 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { @Override public ComponentName getTargetComponent() { ComponentName cn = super.getTargetComponent(); - if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT || hasStatusFlag( - FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON))) { + if (cn == null && hasStatusFlag( + FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON)) { // Legacy shortcuts and promise icons with web UI may not have a componentName but just // a packageName. In that case create a empty componentName instead of adding additional // check everywhere. diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java index b24ee34502..06da8c55f8 100644 --- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java +++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java @@ -72,7 +72,7 @@ public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAn } public int getItemType() { - return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; + return LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; } @Override diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java index 1c787959e4..249824216a 100644 --- a/src/com/android/launcher3/util/IntArray.java +++ b/src/com/android/launcher3/util/IntArray.java @@ -250,6 +250,11 @@ public class IntArray implements Cloneable, Iterable { return b.toString(); } + @Override + public String toString() { + return "IntArray [" + toConcatString() + "]"; + } + public static IntArray fromConcatString(String concatString) { StringTokenizer tokenizer = new StringTokenizer(concatString, ","); int[] array = new int[tokenizer.countTokens()]; diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java index 0899a22ba7..1cb99944c0 100644 --- a/src/com/android/launcher3/util/MainThreadInitializedObject.java +++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java @@ -48,8 +48,8 @@ public class MainThreadInitializedObject { } public T get(Context context) { - if (context instanceof SandboxContext) { - return ((SandboxContext) context).getObject(this, mProvider); + if (context instanceof SandboxContext sc) { + return sc.getObject(this); } if (mValue == null) { @@ -131,24 +131,22 @@ public class MainThreadInitializedObject { * Find a cached object from mObjectMap if we have already created one. If not, generate * an object using the provider. */ - protected T getObject(MainThreadInitializedObject object, - ObjectProvider provider) { + protected T getObject(MainThreadInitializedObject object) { synchronized (mDestroyLock) { if (mDestroyed) { Log.e(TAG, "Static object access with a destroyed context"); } - T t = (T) mObjectMap.get(object); if (t != null) { return t; } if (Looper.myLooper() == Looper.getMainLooper()) { - t = createObject(provider); + t = createObject(object); // Check if we've explicitly allowed the object or if it's a SafeCloseable, // it will get destroyed in onDestroy() if (!mAllowedObjects.contains(object) && !(t instanceof SafeCloseable)) { - throw new IllegalStateException( - "Leaking unknown objects " + object + " " + provider + " " + t); + throw new IllegalStateException("Leaking unknown objects " + + object + " " + object.mProvider + " " + t); } mObjectMap.put(object, t); mOrderedObjects.add(t); @@ -157,15 +155,15 @@ public class MainThreadInitializedObject { } try { - return MAIN_EXECUTOR.submit(() -> getObject(object, provider)).get(); + return MAIN_EXECUTOR.submit(() -> getObject(object)).get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } @UiThread - protected T createObject(ObjectProvider provider) { - return provider.get(this); + protected T createObject(MainThreadInitializedObject object) { + return object.mProvider.get(this); } } } diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 515a2d81a5..3c8dfbb5b7 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -332,6 +332,12 @@ public interface ActivityContext { return null; } + boolean isShortcut = (item instanceof WorkspaceItemInfo) + && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT + && !((WorkspaceItemInfo) item).isPromise(); + if (isShortcut && GO_DISABLE_WIDGETS) { + return null; + } ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item) : makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON ? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */); @@ -343,13 +349,11 @@ public interface ActivityContext { intent.setSourceBounds(Utilities.getViewBounds(v)); } try { - boolean isShortcut = (item instanceof WorkspaceItemInfo) - && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT - || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) - && !((WorkspaceItemInfo) item).isPromise(); if (isShortcut) { - // Shortcuts need some special checks due to legacy reasons. - startShortcutIntentSafely(intent, optsBundle, item); + String id = ((WorkspaceItemInfo) item).getDeepShortcutId(); + String packageName = intent.getPackage(); + ((Context) this).getSystemService(LauncherApps.class).startShortcut( + packageName, id, intent.getSourceBounds(), optsBundle, user); } else if (user == null || user.equals(Process.myUserHandle())) { // Could be launching some bookkeeping activity context.startActivity(intent, optsBundle); @@ -424,55 +428,6 @@ public interface ActivityContext { return new ActivityOptionsWrapper(options, new RunnableList()); } - /** - * Safely launches an intent for a shortcut. - * - * @param intent Intent to start. - * @param optsBundle Optional launch arguments. - * @param info Shortcut information. - */ - default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { - try { - StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); - try { - // Temporarily disable deathPenalty on all default checks. For eg, shortcuts - // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure - // is enabled by default on NYC. - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll() - .penaltyLog().build()); - - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - String id = ((WorkspaceItemInfo) info).getDeepShortcutId(); - String packageName = intent.getPackage(); - startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user); - } else { - // Could be launching some bookkeeping activity - ((Context) this).startActivity(intent, optsBundle); - } - } finally { - StrictMode.setVmPolicy(oldPolicy); - } - } catch (SecurityException e) { - throw e; - } - } - - /** - * A wrapper around the platform method with Launcher specific checks. - */ - default void startShortcut(String packageName, String id, Rect sourceBounds, - Bundle startActivityOptions, UserHandle user) { - if (GO_DISABLE_WIDGETS) { - return; - } - try { - ((Context) this).getSystemService(LauncherApps.class).startShortcut(packageName, id, - sourceBounds, startActivityOptions, user); - } catch (SecurityException | IllegalStateException e) { - Log.e(TAG, "Failed to start shortcut", e); - } - } - default CellPosMapper getCellPosMapper() { return CellPosMapper.DEFAULT; } diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 4641e31b08..aebf752941 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -290,7 +290,6 @@ public class OptionsPopupView extends ArrowPopup static WorkspaceItemInfo placeholderInfo(Intent intent) { WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo(); placeholderInfo.intent = intent; - placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS; return placeholderInfo; } diff --git a/tests/res/raw/cache_data_updated_task_data.txt b/tests/res/raw/cache_data_updated_task_data.txt deleted file mode 100644 index 603dbe3f92..0000000000 --- a/tests/res/raw/cache_data_updated_task_data.txt +++ /dev/null @@ -1,28 +0,0 @@ -# Model data used by CacheDataUpdatedTaskTest - -classMap s com.android.launcher3.model.data.WorkspaceItemInfo - -# Items for the BgDataModel - -# App shortcuts -bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1 -bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2 -bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3 -bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4 - -# Auto install app shortcut -bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5 -bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6 - -# Custom shortcuts -bgItem s itemType=1 title=app1-shrt intent=component=app1/class3 id=7 -bgItem s itemType=1 title=app4-shrt intent=component=app4/class1 id=8 - -# Restored custom shortcut -bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=9 -bgItem s itemType=1 status=1 title=app5-shrt intent=component=app5/class1 id=10 - -allApps componentName=app1/class1 intent=component=app1/class1 -allApps componentName=app1/class2 intent=component=app1/class2 -allApps componentName=app2/class1 intent=component=app2/class1 -allApps componentName=app2/class2 intent=component=app2/class2 \ No newline at end of file diff --git a/tests/res/raw/package_install_state_change_task_data.txt b/tests/res/raw/package_install_state_change_task_data.txt deleted file mode 100644 index e82ea9d4d6..0000000000 --- a/tests/res/raw/package_install_state_change_task_data.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Model data used by PackageInstallStateChangeTaskTest - -classMap s com.android.launcher3.model.data.WorkspaceItemInfo -classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo - -# Items for the BgDataModel - -# App shortcuts -bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1 -bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2 -bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3 -bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4 - -# Promise icons for app3 -bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5 -bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6 -bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7 - -# Promise icon for app4 -bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8 - -# Widget -bgItem w providerName=app4/provider1 id=9 -bgItem w providerName=app5/provider1 id=10 \ No newline at end of file diff --git a/tests/res/raw/widgets_predication_update_task_data.txt b/tests/res/raw/widgets_predication_update_task_data.txt deleted file mode 100644 index 941d1954e2..0000000000 --- a/tests/res/raw/widgets_predication_update_task_data.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Model data used by WidgetsPredictionUpdateTasksTest - -classMap s com.android.launcher3.model.data.WorkspaceItemInfo -classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo - -# Items for the BgDataModel - -# App shortcuts -bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1 -bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2 -bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3 -bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4 - -# Promise icons for app3 -bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5 -bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6 -bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7 - -# Promise icon for app4 -bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8 - -# Widget -bgItem w providerName=app4/provider1 id=9 -bgItem w providerName=app5/provider1 id=10 \ No newline at end of file diff --git a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt index 03352fe234..98191fecf0 100644 --- a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt +++ b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt @@ -17,17 +17,18 @@ package com.android.launcher3.model import android.content.ComponentName import android.content.Context -import android.content.Intent import android.graphics.Rect import com.android.launcher3.InvariantDeviceProfile import com.android.launcher3.LauncherAppState -import com.android.launcher3.LauncherSettings +import com.android.launcher3.model.data.AppInfo import com.android.launcher3.model.data.WorkspaceItemInfo -import com.android.launcher3.util.ContentWriter import com.android.launcher3.util.GridOccupancy import com.android.launcher3.util.IntArray import com.android.launcher3.util.IntSparseArrayMap +import com.android.launcher3.util.LauncherLayoutBuilder import com.android.launcher3.util.LauncherModelHelper +import com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY +import com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE import java.util.UUID /** Base class for workspace related tests. */ @@ -38,6 +39,7 @@ abstract class AbstractWorkspaceModelTest { val nonEmptyScreenSpaces = listOf(Rect(1, 2, 3, 4)) } + protected lateinit var mLayoutBuilder: LauncherLayoutBuilder protected lateinit var mTargetContext: Context protected lateinit var mIdp: InvariantDeviceProfile protected lateinit var mAppState: LauncherAppState @@ -47,6 +49,7 @@ abstract class AbstractWorkspaceModelTest { protected lateinit var mScreenOccupancy: IntSparseArrayMap open fun setup() { + mLayoutBuilder = LauncherLayoutBuilder() mModelHelper = LauncherModelHelper() mTargetContext = mModelHelper.sandboxContext mIdp = InvariantDeviceProfile.INSTANCE[mTargetContext] @@ -64,10 +67,11 @@ abstract class AbstractWorkspaceModelTest { /** Sets up workspaces with the given screen IDs with some items and a 2x2 space. */ fun setupWorkspaces(screenIdsWithItems: List) { - var nextItemId = 1 - screenIdsWithItems.forEach { screenId -> - nextItemId = setupWorkspace(nextItemId, screenId, nonEmptyScreenSpaces) - } + screenIdsWithItems.forEach { screenId -> setupWorkspace(screenId, nonEmptyScreenSpaces) } + mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder) + mIdp.numRows = 5 + mIdp.numColumns = mIdp.numRows + mModelHelper.loadModelSync() } /** @@ -78,30 +82,23 @@ abstract class AbstractWorkspaceModelTest { screen1: List? = null, screen2: List? = null, screen3: List? = null, - ) = listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces) + ) { + listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces) + mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder) + mIdp.numRows = 5 + mIdp.numColumns = mIdp.numRows + mModelHelper.loadModelSync() + } private fun setupWithSpaces(workspaceSpaces: List?>) { - var nextItemId = 1 workspaceSpaces.forEachIndexed { screenId, spaces -> if (spaces != null) { - nextItemId = setupWorkspace(nextItemId, screenId, spaces) + setupWorkspace(screenId, spaces) } } } - private fun setupWorkspace(startId: Int, screenId: Int, spaces: List): Int { - return mModelHelper.executeSimpleTask { dataModel -> - writeWorkspaceWithSpaces(dataModel, startId, screenId, spaces) - } - } - - private fun writeWorkspaceWithSpaces( - bgDataModel: BgDataModel, - itemStartId: Int, - screenId: Int, - spaces: List, - ): Int { - var itemId = itemStartId + private fun setupWorkspace(screenId: Int, spaces: List) { val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows) occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true) spaces.forEach { spaceRect -> occupancy.markCells(spaceRect, false) } @@ -109,35 +106,22 @@ abstract class AbstractWorkspaceModelTest { mScreenOccupancy.append(screenId, occupancy) for (x in 0 until mIdp.numColumns) { for (y in 0 until mIdp.numRows) { - if (!occupancy.cells[x][y]) { - continue + if (occupancy.cells[x][y]) { + mLayoutBuilder.atWorkspace(x, y, screenId).putApp(TEST_PACKAGE, TEST_ACTIVITY) } - val info = getExistingItem() - info.id = itemId++ - info.screenId = screenId - info.cellX = x - info.cellY = y - info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP - bgDataModel.addItem(mTargetContext, info, false) - val writer = ContentWriter(mTargetContext) - info.writeToValues(writer) - writer.put(LauncherSettings.Favorites._ID, info.id) - mTargetContext.contentResolver.insert( - LauncherSettings.Favorites.CONTENT_URI, - writer.getValues(mTargetContext) - ) } } - return itemId } fun getExistingItem() = - WorkspaceItemInfo().apply { intent = Intent().setComponent(ComponentName("a", "b")) } + WorkspaceItemInfo().apply { + intent = AppInfo.makeLaunchIntent(ComponentName(TEST_PACKAGE, TEST_ACTIVITY)) + } fun getNewItem(): WorkspaceItemInfo { val itemPackage = UUID.randomUUID().toString() return WorkspaceItemInfo().apply { - intent = Intent().setComponent(ComponentName(itemPackage, itemPackage)) + intent = AppInfo.makeLaunchIntent(ComponentName(itemPackage, itemPackage)) } } } diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt index 6636b8a31a..115522721a 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt +++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt @@ -23,7 +23,7 @@ import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.util.Executors import com.android.launcher3.util.IntArray -import com.android.launcher3.util.IntSet +import com.android.launcher3.util.TestUtil.runOnExecutorSync import com.android.launcher3.util.any import com.android.launcher3.util.eq import com.android.launcher3.util.same @@ -32,8 +32,6 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -46,11 +44,7 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { - @Captor private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor> - - @Captor private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor> - - @Mock private lateinit var mDataModelCallbacks: BgDataModel.Callbacks + private lateinit var mDataModelCallbacks: MyCallbacks @Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder @@ -58,7 +52,7 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { override fun setup() { super.setup() MockitoAnnotations.initMocks(this) - whenever(mDataModelCallbacks.getPagesToBindSynchronously(any())).thenReturn(IntSet()) + mDataModelCallbacks = MyCallbacks() Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) } .get() } @@ -105,7 +99,7 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd) assertThat(addedItems.size).isEqualTo(0) - verifyZeroInteractions(mWorkspaceItemSpaceFinder, mDataModelCallbacks) + verifyZeroInteractions(mWorkspaceItemSpaceFinder) } @Test @@ -191,22 +185,14 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { ): List { setupWorkspaces(nonEmptyScreenIds) val task = newTask(*itemsToAdd) - var updateCount = 0 - mModelHelper.executeTaskForTest(task).forEach { - updateCount++ - it.run() - } val addedItems = mutableListOf() - if (updateCount > 0) { - verify(mDataModelCallbacks) - .bindAppsAdded( - any(), - mNotAnimatedItemArgumentCaptor.capture(), - mAnimatedItemArgumentCaptor.capture() - ) - addedItems.addAll(mAnimatedItemArgumentCaptor.value.map { AddedItem(it, true) }) - addedItems.addAll(mNotAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) }) + + runOnExecutorSync(Executors.MODEL_EXECUTOR) { + mDataModelCallbacks.addedItems.clear() + mModelHelper.model.enqueueModelUpdateTask(task) + runOnExecutorSync(Executors.MAIN_EXECUTOR) {} + addedItems.addAll(mDataModelCallbacks.addedItems) } return addedItems @@ -224,3 +210,17 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { } private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean) + +private class MyCallbacks : BgDataModel.Callbacks { + + val addedItems = mutableListOf() + + override fun bindAppsAdded( + newScreens: IntArray?, + addNotAnimated: ArrayList, + addAnimated: ArrayList + ) { + addedItems.addAll(addAnimated.map { AddedItem(it, true) }) + addedItems.addAll(addNotAnimated.map { AddedItem(it, false) }) + } +} diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java index f55b244c24..f7710522bb 100644 --- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java +++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java @@ -1,32 +1,31 @@ package com.android.launcher3.model; +import static android.os.Process.myUserHandle; + +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3; +import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; -import android.content.ComponentName; import android.content.Context; -import android.content.pm.PackageInfo; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Color; -import android.os.Process; -import android.os.UserHandle; -import android.os.UserManager; -import androidx.annotation.NonNull; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.launcher3.LauncherAppState; import com.android.launcher3.icons.BitmapInfo; -import com.android.launcher3.icons.IconCache; -import com.android.launcher3.icons.cache.CachingLogic; -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.LauncherModelHelper; +import com.android.launcher3.util.PackageUserKey; import org.junit.After; import org.junit.Before; @@ -35,6 +34,7 @@ import org.junit.runner.RunWith; import java.util.Arrays; import java.util.HashSet; +import java.util.List; /** * Tests for {@link CacheDataUpdatedTask} @@ -43,49 +43,40 @@ import java.util.HashSet; @RunWith(AndroidJUnit4.class) public class CacheDataUpdatedTaskTest { - private static final String NEW_LABEL_PREFIX = "new-label-"; + private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1"; + private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2"; private LauncherModelHelper mModelHelper; + private Context mContext; + + private int mSession1; @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); - mModelHelper.initializeData("cache_data_updated_task_data"); + mContext = mModelHelper.sandboxContext; + mSession1 = mModelHelper.createInstallerSession(PENDING_APP_1); + mModelHelper.createInstallerSession(PENDING_APP_2); - // Add placeholder entries in the cache to simulate update - Context context = mModelHelper.sandboxContext; - IconCache iconCache = LauncherAppState.getInstance(context).getIconCache(); - CachingLogic placeholderLogic = new CachingLogic() { - @Override - @NonNull - public ComponentName getComponent(@NonNull ItemInfo info) { - return info.getTargetComponent(); - } + LauncherLayoutBuilder builder = new LauncherLayoutBuilder() + .atHotseat(1).putFolder("MyFolder") + .addApp(TEST_PACKAGE, TEST_ACTIVITY) // 2 + .addApp(TEST_PACKAGE, TEST_ACTIVITY2) // 3 + .addApp(TEST_PACKAGE, TEST_ACTIVITY3) // 4 - @NonNull - @Override - public UserHandle getUser(@NonNull ItemInfo info) { - return info.user; - } + // Pending App 1 + .addApp(PENDING_APP_1, TEST_ACTIVITY) // 5 + .addApp(PENDING_APP_1, TEST_ACTIVITY2) // 6 + .addApp(PENDING_APP_1, TEST_ACTIVITY3) // 7 - @NonNull - @Override - public CharSequence getLabel(@NonNull ItemInfo info) { - return NEW_LABEL_PREFIX + info.id; - } - - @NonNull - @Override - public BitmapInfo loadIcon(@NonNull Context context, @NonNull ItemInfo info) { - return BitmapInfo.of(Bitmap.createBitmap(1, 1, Config.ARGB_8888), Color.RED); - } - }; - - UserManager um = context.getSystemService(UserManager.class); - for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) { - iconCache.addIconToDBAndMemCache(info, placeholderLogic, new PackageInfo(), - um.getSerialNumberForUser(info.user), true); - } + // Pending App 2 + .addApp(PENDING_APP_2, TEST_ACTIVITY) // 8 + .addApp(PENDING_APP_2, TEST_ACTIVITY2) // 9 + .addApp(PENDING_APP_2, TEST_ACTIVITY3) // 10 + .build(); + mModelHelper.setupDefaultLayoutProvider(builder); + mModelHelper.loadModelSync(); + assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size()); } @After @@ -94,27 +85,63 @@ public class CacheDataUpdatedTaskTest { } private CacheDataUpdatedTask newTask(int op, String... pkg) { - return new CacheDataUpdatedTask(op, Process.myUserHandle(), + return new CacheDataUpdatedTask(op, myUserHandle(), new HashSet<>(Arrays.asList(pkg))); } @Test - public void testCacheUpdate_update_apps() throws Exception { - // Clear all icons from apps list so that its easy to check what was updated - for (AppInfo info : mModelHelper.getAllAppsList().data) { - info.bitmap = BitmapInfo.LOW_RES_INFO; - } + public void testCacheUpdate_update_apps() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + // Clear all icons from apps list so that its easy to check what was updated + allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO); - mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1")); + mModelHelper.getModel().enqueueModelUpdateTask( + newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, TEST_PACKAGE)); - // Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7) - // is not updated - verifyUpdate(1, 2); + // Verify that only the app icons of TEST_PACKAGE (id 2, 3, 4) are updated. + verifyUpdate(2, 3, 4); + }); + } - // Verify that only app1 var updated in allAppsList - assertFalse(mModelHelper.getAllAppsList().data.isEmpty()); - for (AppInfo info : mModelHelper.getAllAppsList().data) { - if (info.componentName.getPackageName().equals("app1")) { + @Test + public void testSessionUpdate_ignores_normal_apps() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + // Clear all icons from apps list so that its easy to check what was updated + allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO); + + mModelHelper.getModel().enqueueModelUpdateTask( + newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, TEST_PACKAGE)); + + // TEST_PACKAGE has no restored shortcuts. Verify that nothing was updated. + verifyUpdate(); + }); + } + + @Test + public void testSessionUpdate_updates_pending_apps() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + LauncherAppState.getInstance(mContext).getIconCache().updateSessionCache( + new PackageUserKey(PENDING_APP_1, myUserHandle()), + mContext.getPackageManager().getPackageInstaller().getSessionInfo(mSession1)); + + // Clear all icons from apps list so that its easy to check what was updated + allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO); + + mModelHelper.getModel().enqueueModelUpdateTask( + newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, PENDING_APP_1)); + + // Only restored apps from PENDING_APP_1 (id 5, 6, 7) are updated + verifyUpdate(5, 6, 7); + }); + } + + private void verifyUpdate(int... idsUpdated) { + IntSet updates = IntSet.wrap(idsUpdated); + for (WorkspaceItemInfo info : allItems()) { + if (updates.contains(info.id)) { assertFalse(info.bitmap.isNullOrLowRes()); } else { assertTrue(info.bitmap.isNullOrLowRes()); @@ -122,33 +149,7 @@ public class CacheDataUpdatedTaskTest { } } - @Test - public void testSessionUpdate_ignores_normal_apps() throws Exception { - mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1")); - - // app1 has no restored shortcuts. Verify that nothing was updated. - verifyUpdate(); - } - - @Test - public void testSessionUpdate_updates_pending_apps() throws Exception { - mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3")); - - // app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were - // were updated - verifyUpdate(5, 6); - } - - private void verifyUpdate(Integer... idsUpdated) { - HashSet updates = new HashSet<>(Arrays.asList(idsUpdated)); - for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) { - if (updates.contains(info.id)) { - assertEquals(NEW_LABEL_PREFIX + info.id, info.title); - assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes()); - } else { - assertNotSame(NEW_LABEL_PREFIX + info.id, info.title); - assertTrue(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes()); - } - } + private List allItems() { + return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).contents; } } diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt index 3b480cac10..4fa5352a1c 100644 --- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt +++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt @@ -24,7 +24,6 @@ import android.graphics.Point import android.os.Process import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.InvariantDeviceProfile import com.android.launcher3.LauncherPrefs import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE @@ -108,8 +107,8 @@ class GridSizeMigrationUtilTest { fun testMigration() { // Src Hotseat icons addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) // Src grid icons // _ _ _ _ _ @@ -124,7 +123,7 @@ class GridSizeMigrationUtilTest { addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 3, testPackage9, 9, TMP_TABLE) // Dest hotseat icons - addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2) // Dest grid icons addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage10) @@ -219,8 +218,8 @@ class GridSizeMigrationUtilTest { // Hotseat items in grid A // 1 2 _ 3 4 addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) // Workspace items in grid A // _ _ _ _ _ @@ -235,7 +234,7 @@ class GridSizeMigrationUtilTest { // Hotseat items in grid B // 2 _ _ _ - addItem(ITEM_TYPE_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2) // Workspace items in grid B // _ _ _ _ // _ _ _ 10 @@ -291,7 +290,7 @@ class GridSizeMigrationUtilTest { null ) ?: throw IllegalStateException() - var locMap = parseLocMap(context, c) + var locMap = parseLocMap(c) // Expected items in grid B // _ _ _ _ // 5 6 7 8 @@ -348,7 +347,7 @@ class GridSizeMigrationUtilTest { null ) ?: throw IllegalStateException() - locMap = parseLocMap(context, c) + locMap = parseLocMap(c) // Expected workspace items in grid A // _ _ _ _ _ // _ _ _ _ 5 @@ -410,7 +409,7 @@ class GridSizeMigrationUtilTest { null ) ?: throw IllegalStateException() - locMap = parseLocMap(context, c) + locMap = parseLocMap(c) // Expected workspace items in grid B // _ _ _ _ // 5 6 _ 8 @@ -436,7 +435,7 @@ class GridSizeMigrationUtilTest { c.close() } - private fun parseLocMap(context: Context, c: Cursor): Map> { + private fun parseLocMap(c: Cursor): Map> { // Check workspace items val intentIndex = c.getColumnIndex(INTENT) val screenIndex = c.getColumnIndex(SCREEN) @@ -465,7 +464,16 @@ class GridSizeMigrationUtilTest { 1, TMP_TABLE ), - addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE), + addItem( + ITEM_TYPE_DEEP_SHORTCUT, + 1, + CONTAINER_HOTSEAT, + 0, + 0, + testPackage2, + 2, + TMP_TABLE + ), addItem( ITEM_TYPE_APPLICATION, 2, @@ -476,7 +484,16 @@ class GridSizeMigrationUtilTest { 3, TMP_TABLE ), - addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) + addItem( + ITEM_TYPE_DEEP_SHORTCUT, + 3, + CONTAINER_HOTSEAT, + 0, + 0, + testPackage4, + 4, + TMP_TABLE + ) ) val numSrcDatabaseHotseatIcons = srcHotseatItems.size idp.numDatabaseHotseatIcons = 6 @@ -532,9 +549,9 @@ class GridSizeMigrationUtilTest { @Test fun migrateFromLargerHotseat() { addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE) addItem(ITEM_TYPE_APPLICATION, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE) - addItem(ITEM_TYPE_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) + addItem(ITEM_TYPE_DEEP_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE) addItem(ITEM_TYPE_APPLICATION, 5, CONTAINER_HOTSEAT, 0, 0, testPackage5, 5, TMP_TABLE) idp.numDatabaseHotseatIcons = 4 diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java index 78812c0778..544ed6b01d 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -30,7 +30,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.ICON; import static com.android.launcher3.LauncherSettings.Favorites.INTENT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; import static com.android.launcher3.LauncherSettings.Favorites.OPTIONS; import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID; import static com.android.launcher3.LauncherSettings.Favorites.RANK; @@ -158,13 +158,13 @@ public class LoaderCursorTest { @Test public void loadSimpleShortcut() { - initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut"); + initCursor(ITEM_TYPE_DEEP_SHORTCUT, "my-shortcut"); assertTrue(mLoaderCursor.moveToNext()); WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem(); assertTrue(mApp.getIconCache().isDefaultIcon(info.bitmap, info.user)); assertEquals("my-shortcut", info.title); - assertEquals(ITEM_TYPE_SHORTCUT, info.itemType); + assertEquals(ITEM_TYPE_DEEP_SHORTCUT, info.itemType); } @Test diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java index 519191e251..4ba61ac315 100644 --- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java +++ b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java @@ -1,5 +1,12 @@ package com.android.launcher3.model; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2; +import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3; +import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; + import static org.junit.Assert.assertEquals; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -9,6 +16,8 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.PackageInstallInfo; +import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.LauncherModelHelper; import org.junit.After; @@ -16,9 +25,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.Arrays; -import java.util.HashSet; - /** * Tests for {@link PackageInstallStateChangedTask} */ @@ -26,12 +32,36 @@ import java.util.HashSet; @RunWith(AndroidJUnit4.class) public class PackageInstallStateChangedTaskTest { + private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1"; + private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2"; + private LauncherModelHelper mModelHelper; + private IntSet mDownloadingApps; @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); - mModelHelper.initializeData("package_install_state_change_task_data"); + mModelHelper.createInstallerSession(PENDING_APP_1); + mModelHelper.createInstallerSession(PENDING_APP_2); + + LauncherLayoutBuilder builder = new LauncherLayoutBuilder() + .atWorkspace(0, 0, 1).putApp(TEST_PACKAGE, TEST_ACTIVITY) // 1 + .atWorkspace(0, 0, 2).putApp(TEST_PACKAGE, TEST_ACTIVITY2) // 2 + .atWorkspace(0, 0, 3).putApp(TEST_PACKAGE, TEST_ACTIVITY3) // 3 + + .atWorkspace(0, 0, 4).putApp(PENDING_APP_1, TEST_ACTIVITY) // 4 + .atWorkspace(0, 0, 5).putApp(PENDING_APP_1, TEST_ACTIVITY2) // 5 + .atWorkspace(0, 0, 6).putApp(PENDING_APP_1, TEST_ACTIVITY3) // 6 + .atWorkspace(0, 0, 7).putWidget(PENDING_APP_1, "pending.widget", 1, 1) // 7 + + .atWorkspace(0, 0, 8).putApp(PENDING_APP_2, TEST_ACTIVITY) // 8 + .atWorkspace(0, 0, 9).putApp(PENDING_APP_2, TEST_ACTIVITY2) // 9 + .atWorkspace(0, 0, 10).putApp(PENDING_APP_2, TEST_ACTIVITY3); // 10 + + mDownloadingApps = IntSet.wrap(4, 5, 6, 7, 8, 9, 10); + mModelHelper.setupDefaultLayoutProvider(builder); + mModelHelper.loadModelSync(); + assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size()); } @After @@ -47,36 +77,45 @@ public class PackageInstallStateChangedTaskTest { } @Test - public void testSessionUpdate_ignore_installed() throws Exception { - mModelHelper.executeTaskForTest(newTask("app1", 30)); + public void testSessionUpdate_ignore_installed() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + mModelHelper.getModel().enqueueModelUpdateTask(newTask(TEST_PACKAGE, 30)); - // No shortcuts were updated - verifyProgressUpdate(0); + // No shortcuts were updated + verifyProgressUpdate(0); + }); } @Test - public void testSessionUpdate_shortcuts_updated() throws Exception { - mModelHelper.executeTaskForTest(newTask("app3", 30)); + public void testSessionUpdate_shortcuts_updated() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_1, 30)); - verifyProgressUpdate(30, 5, 6, 7); + verifyProgressUpdate(30, 4, 5, 6, 7); + }); } @Test - public void testSessionUpdate_widgets_updated() throws Exception { - mModelHelper.executeTaskForTest(newTask("app4", 30)); + public void testSessionUpdate_widgets_updated() { + // Run on model executor so that no other task runs in the middle. + runOnExecutorSync(MODEL_EXECUTOR, () -> { + mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_2, 30)); - verifyProgressUpdate(30, 8, 9); + verifyProgressUpdate(30, 8, 9, 10); + }); } - private void verifyProgressUpdate(int progress, Integer... idsUpdated) { - HashSet updates = new HashSet<>(Arrays.asList(idsUpdated)); + private void verifyProgressUpdate(int progress, int... idsUpdated) { + IntSet updates = IntSet.wrap(idsUpdated); for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) { - if (info instanceof WorkspaceItemInfo) { - assertEquals(updates.contains(info.id) ? progress: 100, - ((WorkspaceItemInfo) info).getProgressLevel()); + int expectedProgress = updates.contains(info.id) ? progress + : (mDownloadingApps.contains(info.id) ? 0 : 100); + if (info instanceof WorkspaceItemInfo wi) { + assertEquals(expectedProgress, wi.getProgressLevel()); } else { - assertEquals(updates.contains(info.id) ? progress: -1, - ((LauncherAppWidgetInfo) info).installProgress); + assertEquals(expectedProgress, ((LauncherAppWidgetInfo) info).installProgress); } } } diff --git a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt index 2f57634355..8e4e99812e 100644 --- a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt +++ b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt @@ -87,9 +87,8 @@ class DisplayControllerTest { @Before fun setUp() { MockitoAnnotations.initMocks(this) - whenever(context.getObject(eq(WindowManagerProxy.INSTANCE), any())) - .thenReturn(windowManagerProxy) - whenever(context.getObject(eq(LauncherPrefs.INSTANCE), any())).thenReturn(launcherPrefs) + whenever(context.getObject(eq(WindowManagerProxy.INSTANCE))).thenReturn(windowManagerProxy) + whenever(context.getObject(eq(LauncherPrefs.INSTANCE))).thenReturn(launcherPrefs) // Mock WindowManagerProxy val displayInfo = diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java index 976afcdd13..9dc04a14f2 100644 --- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java +++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java @@ -15,34 +15,31 @@ */ package com.android.launcher3.util; -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL; + import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.TestUtil.runOnExecutorSync; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; -import android.content.res.Resources; -import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Color; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.AutoCloseOutputStream; -import android.os.Process; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.util.ArrayMap; @@ -57,13 +54,10 @@ import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.ModelUpdateTask; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherProvider; -import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.AllAppsList; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ItemInstallQueue; -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.UserCache; import com.android.launcher3.testing.TestInformationProvider; @@ -72,37 +66,25 @@ import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext; import com.android.launcher3.util.window.WindowManagerProxy; import com.android.launcher3.widget.custom.CustomWidgetManager; -import org.mockito.ArgumentCaptor; - -import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; -import java.io.InputStreamReader; +import java.io.IOException; import java.io.OutputStreamWriter; -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.List; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; -import java.util.function.Function; /** * Utility class to help manage Launcher Model and related objects for test. */ public class LauncherModelHelper { - public static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP; - public static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT; - - public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; - public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; - public static final int NO__ICON = -1; - - public static final String TEST_PACKAGE = testContext().getPackageName(); + public static final String TEST_PACKAGE = getInstrumentation().getContext().getPackageName(); public static final String TEST_ACTIVITY = "com.android.launcher3.tests.Activity2"; + public static final String TEST_ACTIVITY2 = "com.android.launcher3.tests.Activity3"; + public static final String TEST_ACTIVITY3 = "com.android.launcher3.tests.Activity4"; // Authority for providing a test default-workspace-layout data. private static final String TEST_PROVIDER_AUTHORITY = @@ -110,39 +92,18 @@ public class LauncherModelHelper { private static final int DEFAULT_BITMAP_SIZE = 10; private static final int DEFAULT_GRID_SIZE = 4; - private final HashMap> mFieldCache = new HashMap<>(); - private final MockContentResolver mMockResolver = new MockContentResolver(); - public final TestLauncherProvider provider; public final SandboxModelContext sandboxContext; - public final long defaultProfileId; + private final RunnableList mDestroyTask = new RunnableList(); private BgDataModel mDataModel; - private AllAppsList mAllAppsList; public LauncherModelHelper() { - Context context = getApplicationContext(); - // System settings cache content provider. Ensure that they are statically initialized - Settings.Secure.getString(context.getContentResolver(), "test"); - Settings.System.getString(context.getContentResolver(), "test"); - Settings.Global.getString(context.getContentResolver(), "test"); - - provider = new TestLauncherProvider(); sandboxContext = new SandboxModelContext(); - defaultProfileId = UserCache.INSTANCE.get(sandboxContext) - .getSerialNumberForUser(Process.myUserHandle()); - setupProvider(LauncherProvider.AUTHORITY, provider); } public void setupProvider(String authority, ContentProvider provider) { - ProviderInfo providerInfo = new ProviderInfo(); - providerInfo.authority = authority; - providerInfo.applicationInfo = sandboxContext.getApplicationInfo(); - provider.attachInfo(sandboxContext, providerInfo); - mMockResolver.addProvider(providerInfo.authority, provider); - doReturn(providerInfo) - .when(sandboxContext.mPm) - .resolveContentProvider(eq(authority), anyInt()); + sandboxContext.setupProvider(authority, provider); } public LauncherModel getModel() { @@ -151,16 +112,35 @@ public class LauncherModelHelper { public synchronized BgDataModel getBgDataModel() { if (mDataModel == null) { - mDataModel = ReflectionHelpers.getField(getModel(), "mBgDataModel"); + getModel().enqueueModelUpdateTask(new ModelUpdateTask() { + @Override + public void init(@NonNull LauncherAppState app, @NonNull LauncherModel model, + @NonNull BgDataModel dataModel, @NonNull AllAppsList allAppsList, + @NonNull Executor uiExecutor) { + mDataModel = dataModel; + } + + @Override + public void run() { } + }); } return mDataModel; } - public synchronized AllAppsList getAllAppsList() { - if (mAllAppsList == null) { - mAllAppsList = ReflectionHelpers.getField(getModel(), "mBgAllAppsList"); - } - return mAllAppsList; + /** + * Creates a installer session for the provided package. + */ + public int createInstallerSession(String pkg) throws IOException { + SessionParams sp = new SessionParams(MODE_FULL_INSTALL); + sp.setAppPackageName(pkg); + Bitmap icon = Bitmap.createBitmap(100, 100, Config.ARGB_8888); + icon.eraseColor(Color.RED); + sp.setAppIcon(icon); + sp.setAppLabel(pkg); + PackageInstaller pi = sandboxContext.getPackageManager().getPackageInstaller(); + int sessionId = pi.createSession(sp); + mDestroyTask.add(() -> pi.abandonSession(sessionId)); + return sessionId; } public void destroy() { @@ -175,6 +155,8 @@ public class LauncherModelHelper { waitOrThrow(l1); sandboxContext.onDestroy(); l2.countDown(); + + mDestroyTask.executeAllAndDestroy(); } private void waitOrThrow(CountDownLatch latch) { @@ -185,184 +167,6 @@ public class LauncherModelHelper { } } - /** - * Synchronously executes the task and returns all the UI callbacks posted. - */ - public List executeTaskForTest(ModelUpdateTask task) throws Exception { - LauncherModel model = getModel(); - if (!model.isModelLoaded()) { - ReflectionHelpers.setField(model, "mModelLoaded", true); - } - Executor mockExecutor = mock(Executor.class); - model.enqueueModelUpdateTask(new ModelUpdateTask() { - @Override - public void init(@NonNull final LauncherAppState app, - @NonNull final LauncherModel model, @NonNull final BgDataModel dataModel, - @NonNull final AllAppsList allAppsList, @NonNull final Executor uiExecutor) { - task.init(app, model, dataModel, allAppsList, mockExecutor); - } - - @Override - public void run() { - task.run(); - } - }); - MODEL_EXECUTOR.submit(() -> null).get(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); - verify(mockExecutor, atLeast(0)).execute(captor.capture()); - return captor.getAllValues(); - } - - /** - * Synchronously executes a task on the model - */ - public T executeSimpleTask(Function task) throws Exception { - BgDataModel dataModel = getBgDataModel(); - return MODEL_EXECUTOR.submit(() -> task.apply(dataModel)).get(); - } - - /** - * Initializes mock data for the test. - */ - public void initializeData(String resourceName) throws Exception { - BgDataModel bgDataModel = getBgDataModel(); - AllAppsList allAppsList = getAllAppsList(); - - MODEL_EXECUTOR.submit(() -> { - // Copy apk from resources to a local file and install from there. - Resources resources = testContext().getResources(); - int resId = resources.getIdentifier( - resourceName, "raw", testContext().getPackageName()); - try (BufferedReader reader = new BufferedReader(new InputStreamReader( - resources.openRawResource(resId)))) { - String line; - HashMap classMap = new HashMap<>(); - while ((line = reader.readLine()) != null) { - line = line.trim(); - if (line.startsWith("#") || line.isEmpty()) { - continue; - } - String[] commands = line.split(" "); - switch (commands[0]) { - case "classMap": - classMap.put(commands[1], Class.forName(commands[2])); - break; - case "bgItem": - bgDataModel.addItem(sandboxContext, - (ItemInfo) initItem(classMap.get(commands[1]), commands, 2), - false); - break; - case "allApps": - allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null); - break; - } - } - } catch (Exception e) { - throw new RuntimeException(e); - } - }).get(); - } - - private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception { - HashMap cache = mFieldCache.get(clazz); - if (cache == null) { - cache = new HashMap<>(); - Class c = clazz; - while (c != null) { - for (Field f : c.getDeclaredFields()) { - f.setAccessible(true); - cache.put(f.getName(), f); - } - c = c.getSuperclass(); - } - mFieldCache.put(clazz, cache); - } - - Object item = clazz.newInstance(); - for (int i = startIndex; i < fieldDef.length; i++) { - String[] fieldData = fieldDef[i].split("=", 2); - Field f = cache.get(fieldData[0]); - Class type = f.getType(); - if (type == int.class || type == long.class) { - f.set(item, Integer.parseInt(fieldData[1])); - } else if (type == CharSequence.class || type == String.class) { - f.set(item, fieldData[1]); - } else if (type == Intent.class) { - if (!fieldData[1].startsWith("#Intent")) { - fieldData[1] = "#Intent;" + fieldData[1] + ";end"; - } - f.set(item, Intent.parseUri(fieldData[1], 0)); - } else if (type == ComponentName.class) { - f.set(item, ComponentName.unflattenFromString(fieldData[1])); - } else { - throw new Exception("Added parsing logic for " - + f.getName() + " of type " + f.getType()); - } - } - return item; - } - - public int addItem(int type, int screen, int container, int x, int y) { - return addItem(type, screen, container, x, y, defaultProfileId, TEST_PACKAGE); - } - - public int addItem(int type, int screen, int container, int x, int y, long profileId) { - return addItem(type, screen, container, x, y, profileId, TEST_PACKAGE); - } - - public int addItem(int type, int screen, int container, int x, int y, String packageName) { - return addItem(type, screen, container, x, y, defaultProfileId, packageName); - } - - public int addItem(int type, int screen, int container, int x, int y, String packageName, - int id, Uri contentUri) { - addItem(type, screen, container, x, y, defaultProfileId, packageName, id, contentUri); - return id; - } - - /** - * Adds a mock item in the DB. - * @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for - * folder (where the type represents the number of items in the folder). - */ - public int addItem(int type, int screen, int container, int x, int y, long profileId, - String packageName) { - int id = LauncherSettings.Settings.call(sandboxContext.getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_ITEM_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); - addItem(type, screen, container, x, y, profileId, packageName, id, CONTENT_URI); - return id; - } - - public void addItem(int type, int screen, int container, int x, int y, long profileId, - String packageName, int id, Uri contentUri) { - ContentValues values = new ContentValues(); - values.put(LauncherSettings.Favorites._ID, id); - values.put(LauncherSettings.Favorites.CONTAINER, container); - values.put(LauncherSettings.Favorites.SCREEN, screen); - values.put(LauncherSettings.Favorites.CELLX, x); - values.put(LauncherSettings.Favorites.CELLY, y); - values.put(LauncherSettings.Favorites.SPANX, 1); - values.put(LauncherSettings.Favorites.SPANY, 1); - values.put(LauncherSettings.Favorites.PROFILE_ID, profileId); - - if (type == APP_ICON || type == SHORTCUT) { - values.put(LauncherSettings.Favorites.ITEM_TYPE, type); - values.put(LauncherSettings.Favorites.INTENT, - new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0)); - } else { - values.put(LauncherSettings.Favorites.ITEM_TYPE, - LauncherSettings.Favorites.ITEM_TYPE_FOLDER); - // Add folder items. - for (int i = 0; i < type; i++) { - addItem(APP_ICON, 0, id, 0, 0, profileId); - } - } - - sandboxContext.getContentResolver().insert(contentUri, values); - } - /** * Sets up a mock provider to load the provided layout by default, next time the layout loads */ @@ -394,6 +198,9 @@ public class LauncherModelHelper { } }; setupProvider(TEST_PROVIDER_AUTHORITY, cp); + mDestroyTask.add(() -> runOnExecutorSync(MODEL_EXECUTOR, () -> + UiDevice.getInstance(getInstrumentation()).executeShellCommand( + "settings delete secure launcher3.layout.provider"))); return this; } @@ -402,46 +209,16 @@ public class LauncherModelHelper { */ public void loadModelSync() throws ExecutionException, InterruptedException { Callbacks mockCb = new Callbacks() { }; - Executors.MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get(); + MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get(); Executors.MODEL_EXECUTOR.submit(() -> { }).get(); - Executors.MAIN_EXECUTOR.submit(() -> { }).get(); - Executors.MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get(); + MAIN_EXECUTOR.submit(() -> { }).get(); + MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get(); } - /** - * An extension of LauncherProvider backed up by in-memory database. - */ - public static class TestLauncherProvider extends LauncherProvider { - - @Override - public boolean onCreate() { - return true; - } - - public SQLiteDatabase getDb() { - return getModelDbController().getDb(); - } - } - - public static boolean deleteContents(File dir) { - File[] files = dir.listFiles(); - boolean success = true; - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - success &= deleteContents(file); - } - if (!file.delete()) { - success = false; - } - } - } - return success; - } - - public class SandboxModelContext extends SandboxContext { + public static class SandboxModelContext extends SandboxContext { + private final MockContentResolver mMockResolver = new MockContentResolver(); private final ArrayMap mSpiedServices = new ArrayMap<>(); private final PackageManager mPm; private final File mDbDir; @@ -453,8 +230,31 @@ public class LauncherModelHelper { DisplayController.INSTANCE, CustomWidgetManager.INSTANCE, SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, LockedUserState.INSTANCE, ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE); + + // System settings cache content provider. Ensure that they are statically initialized + Settings.Secure.getString( + ApplicationProvider.getApplicationContext().getContentResolver(), "test"); + Settings.System.getString( + ApplicationProvider.getApplicationContext().getContentResolver(), "test"); + Settings.Global.getString( + ApplicationProvider.getApplicationContext().getContentResolver(), "test"); + mPm = spy(getBaseContext().getPackageManager()); mDbDir = new File(getCacheDir(), UUID.randomUUID().toString()); + setupProvider(LauncherProvider.AUTHORITY, new LauncherProvider() { + @Override + public boolean onCreate() { + return true; + } + }); + } + + @Override + protected T createObject(MainThreadInitializedObject object) { + if (object == LauncherAppState.INSTANCE) { + return (T) new LauncherAppState(this, null /* iconCacheFileName */); + } + return super.createObject(object); } public SandboxModelContext allow(MainThreadInitializedObject object) { @@ -505,9 +305,30 @@ public class LauncherModelHelper { mSpiedServices.put(name, result); return result; } - } - private static Context testContext() { - return getInstrumentation().getContext(); + public void setupProvider(String authority, ContentProvider provider) { + ProviderInfo providerInfo = new ProviderInfo(); + providerInfo.authority = authority; + providerInfo.applicationInfo = getApplicationInfo(); + provider.attachInfo(this, providerInfo); + mMockResolver.addProvider(providerInfo.authority, provider); + doReturn(providerInfo).when(mPm).resolveContentProvider(eq(authority), anyInt()); + } + + private static boolean deleteContents(File dir) { + File[] files = dir.listFiles(); + boolean success = true; + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + success &= deleteContents(file); + } + if (!file.delete()) { + success = false; + } + } + } + return success; + } } } diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java index 4981795fe5..e2c1fde069 100644 --- a/tests/src/com/android/launcher3/util/TestUtil.java +++ b/tests/src/com/android/launcher3/util/TestUtil.java @@ -53,6 +53,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.MessageDigest; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; import java.util.function.Predicate; import java.util.function.ToIntFunction; @@ -154,6 +155,30 @@ public class TestUtil { Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null); } + /** + * Utility method to run a task synchronously which converts any exceptions to RuntimeException + */ + public static void runOnExecutorSync(ExecutorService executor, UncheckedRunnable task) { + try { + executor.submit(() -> { + try { + task.run(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }).get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** Interface to indicate a runnable which can throw any exception. */ + public interface UncheckedRunnable { + /** Method to run the task */ + void run() throws Exception; + } + + private static class PackageInstallCheck extends LauncherApps.Callback implements AutoCloseable {