Add multi-instance state to item infos
- Add legacy resource for supported multi-instance apps that matches the current SystemUI resource of the same name, and will be removed as apps migrate to the V manifest property to declare multi-instance support - Load the multi-instance state from PackageManager when the db is first loaded or when packages are updated - The multi-instance check is then used to determine if an app pair can be saved (ie. whether the action can be shown) Bug: 323112914 Test: atest NexusLauncherTests Change-Id: I565b4bee4ab5f7040910306b1fd60a4fc3bf9a1c
This commit is contained in:
@@ -73,6 +73,7 @@ import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
import com.android.launcher3.util.ApiWrapper;
|
||||
import com.android.launcher3.util.Executors;
|
||||
import com.android.launcher3.util.IntSparseArrayMap;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.PersistedItemArray;
|
||||
import com.android.quickstep.logging.SettingsChangeLogger;
|
||||
import com.android.quickstep.logging.StatsLogCompatManager;
|
||||
@@ -150,7 +151,8 @@ public class QuickstepModelDelegate extends ModelDelegate {
|
||||
// TODO: Implement caching and preloading
|
||||
|
||||
WorkspaceItemFactory factory =
|
||||
new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, numColumns, state.containerId);
|
||||
new WorkspaceItemFactory(mApp, ums, mPmHelper, pinnedShortcuts, numColumns,
|
||||
state.containerId);
|
||||
FixedContainerItems fci = new FixedContainerItems(state.containerId,
|
||||
state.storage.read(mApp.getContext(), factory, ums.allUsers::get));
|
||||
if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
|
||||
@@ -530,6 +532,7 @@ public class QuickstepModelDelegate extends ModelDelegate {
|
||||
|
||||
private final LauncherAppState mAppState;
|
||||
private final UserManagerState mUMS;
|
||||
private final PackageManagerHelper mPmHelper;
|
||||
private final Map<ShortcutKey, ShortcutInfo> mPinnedShortcuts;
|
||||
private final int mMaxCount;
|
||||
private final int mContainer;
|
||||
@@ -537,9 +540,11 @@ public class QuickstepModelDelegate extends ModelDelegate {
|
||||
private int mReadCount = 0;
|
||||
|
||||
protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums,
|
||||
Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount, int container) {
|
||||
PackageManagerHelper pmHelper, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts,
|
||||
int maxCount, int container) {
|
||||
mAppState = appState;
|
||||
mUMS = ums;
|
||||
mPmHelper = pmHelper;
|
||||
mPinnedShortcuts = pinnedShortcuts;
|
||||
mMaxCount = maxCount;
|
||||
mContainer = container;
|
||||
@@ -563,6 +568,7 @@ public class QuickstepModelDelegate extends ModelDelegate {
|
||||
lai,
|
||||
UserCache.INSTANCE.get(mAppState.getContext()).getUserInfo(user),
|
||||
ApiWrapper.INSTANCE.get(mAppState.getContext()),
|
||||
mPmHelper,
|
||||
mUMS.isUserQuiet(user));
|
||||
info.container = mContainer;
|
||||
mAppState.getIconCache().getTitleAndIcon(info, lai, false);
|
||||
|
||||
@@ -335,24 +335,15 @@ public interface TaskShortcutFactory {
|
||||
recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
|
||||
boolean shouldShowActionsButtonInstead =
|
||||
isLargeTileFocusedTask && isInExpectedScrollPosition;
|
||||
boolean hasUnpinnableApp = taskView.getTaskContainers().stream()
|
||||
.anyMatch(att -> att != null && att.getItemInfo() != null
|
||||
&& ((att.getItemInfo().runtimeStatusFlags
|
||||
& ItemInfoWithIcon.FLAG_NOT_PINNABLE) != 0));
|
||||
|
||||
// No "save app pair" menu item if:
|
||||
// - app pairs feature is not enabled
|
||||
// - we are in 3p launcher
|
||||
// - the task in question is a single task
|
||||
// - at least one app in app pair is unpinnable
|
||||
// - the Overview Actions Button should be visible
|
||||
// - the task is not a GroupedTaskView
|
||||
if (!FeatureFlags.enableAppPairs()
|
||||
|| !recentsView.supportsAppPairs()
|
||||
|| !taskView.containsMultipleTasks()
|
||||
|| hasUnpinnableApp
|
||||
// - the task view is not a valid save-able split pair
|
||||
if (!recentsView.supportsAppPairs()
|
||||
|| shouldShowActionsButtonInstead
|
||||
|| !(taskView instanceof GroupedTaskView)) {
|
||||
|| !recentsView.getSplitSelectController().getAppPairsController()
|
||||
.canSaveAppPair(taskView)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_TASKBAR;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_LAUNCH;
|
||||
import static com.android.launcher3.model.data.AppInfo.PACKAGE_KEY_COMPARATOR;
|
||||
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SUPPORTS_MULTI_INSTANCE;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
@@ -32,34 +33,38 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.isPersisten
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.jank.Cuj;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||
import com.android.launcher3.allapps.AllAppsStore;
|
||||
import com.android.launcher3.apppairs.AppPairIcon;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.logging.InstanceId;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.AppPairInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.taskbar.TaskbarActivityContext;
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.quickstep.TaskUtils;
|
||||
import com.android.quickstep.TopTaskTracker;
|
||||
import com.android.quickstep.views.GroupedTaskView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
@@ -100,6 +105,55 @@ public class AppPairsController {
|
||||
mContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified GroupedTaskView can be saved as an app pair.
|
||||
*/
|
||||
public boolean canSaveAppPair(TaskView taskView) {
|
||||
if (mContext == null) {
|
||||
// Can ignore as the activity is already destroyed
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disallow saving app pairs if:
|
||||
// - app pairs feature is not enabled
|
||||
// - the task in question is a single task
|
||||
// - at least one app in app pair is unpinnable
|
||||
// - the task is not a GroupedTaskView
|
||||
// - both tasks in the GroupedTaskView are from the same app and the app does not
|
||||
// support multi-instance
|
||||
boolean hasUnpinnableApp = taskView.getTaskContainers().stream()
|
||||
.anyMatch(att -> att != null && att.getItemInfo() != null
|
||||
&& ((att.getItemInfo().runtimeStatusFlags
|
||||
& ItemInfoWithIcon.FLAG_NOT_PINNABLE) != 0));
|
||||
if (!FeatureFlags.enableAppPairs()
|
||||
|| !taskView.containsMultipleTasks()
|
||||
|| hasUnpinnableApp
|
||||
|| !(taskView instanceof GroupedTaskView)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GroupedTaskView gtv = (GroupedTaskView) taskView;
|
||||
List<TaskView.TaskContainer> containers = gtv.getTaskContainers();
|
||||
ComponentKey taskKey1 = TaskUtils.getLaunchComponentKeyForTask(
|
||||
containers.get(0).getTask().key);
|
||||
ComponentKey taskKey2 = TaskUtils.getLaunchComponentKeyForTask(
|
||||
containers.get(1).getTask().key);
|
||||
AppInfo app1 = resolveAppInfoByComponent(taskKey1);
|
||||
AppInfo app2 = resolveAppInfoByComponent(taskKey2);
|
||||
|
||||
if (app1 == null || app2 == null) {
|
||||
// Disallow saving app pairs for apps that don't have a front-door in Launcher
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PackageManagerHelper.isSameAppForMultiInstance(app1, app2)) {
|
||||
if (!app1.supportsMultiInstance() || !app2.supportsMultiInstance()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new app pair ItemInfo and adds it to the workspace.
|
||||
* <br>
|
||||
@@ -119,31 +173,23 @@ public class AppPairsController {
|
||||
List<TaskView.TaskContainer> containers = gtv.getTaskContainers();
|
||||
WorkspaceItemInfo recentsInfo1 = containers.get(0).getItemInfo();
|
||||
WorkspaceItemInfo recentsInfo2 = containers.get(1).getItemInfo();
|
||||
WorkspaceItemInfo app1 = lookupLaunchableItem(recentsInfo1.getComponentKey());
|
||||
WorkspaceItemInfo app2 = lookupLaunchableItem(recentsInfo2.getComponentKey());
|
||||
WorkspaceItemInfo app1 = resolveAppPairWorkspaceInfo(recentsInfo1);
|
||||
WorkspaceItemInfo app2 = resolveAppPairWorkspaceInfo(recentsInfo2);
|
||||
|
||||
// If app lookup fails, use the WorkspaceItemInfo that we have, but try to override default
|
||||
// intent with one from PackageManager.
|
||||
if (app1 == null) {
|
||||
Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo1.title
|
||||
+ " failed. Falling back to the WorkspaceItemInfo from Recents.");
|
||||
app1 = convertRecentsItemToAppItem(recentsInfo1);
|
||||
if (app1 == null || app2 == null) {
|
||||
// This shouldn't happen if canSaveAppPair() is called above, but log an error and do
|
||||
// not create the app pair if the workspace items can't be resolved
|
||||
Log.w(TAG, "Failed to save app pair due to invalid apps ("
|
||||
+ "app1=" + recentsInfo1.getComponentKey().componentName
|
||||
+ " app2=" + recentsInfo2.getComponentKey().componentName + ")");
|
||||
return;
|
||||
}
|
||||
if (app2 == null) {
|
||||
Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo2.title
|
||||
+ " failed. Falling back to the WorkspaceItemInfo from Recents.");
|
||||
app2 = convertRecentsItemToAppItem(recentsInfo2);
|
||||
}
|
||||
|
||||
// WorkspaceItemProcessor won't process these new ItemInfos until the next launcher restart,
|
||||
// so update some flags now.
|
||||
updateWorkspaceItemFlags(app1);
|
||||
updateWorkspaceItemFlags(app2);
|
||||
|
||||
@PersistentSnapPosition int snapPosition = gtv.getSnapPosition();
|
||||
if (!isPersistentSnapPosition(snapPosition)) {
|
||||
// if we received an illegal snap position, log an error and do not create the app pair.
|
||||
Log.wtf(TAG, "tried to save an app pair with illegal snapPosition " + snapPosition);
|
||||
// If we received an illegal snap position, log an error and do not create the app pair
|
||||
Log.wtf(TAG, "Tried to save an app pair with illegal snapPosition "
|
||||
+ snapPosition);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -228,68 +274,39 @@ public class AppPairsController {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an AppInfo associated with the app for the given ComponentKey, or null if no such
|
||||
* package exists in the AllAppsStore.
|
||||
*/
|
||||
@Nullable
|
||||
private AppInfo resolveAppInfoByComponent(@NonNull ComponentKey key) {
|
||||
AllAppsStore appsStore = ActivityContext.lookupContext(mContext)
|
||||
.getAppsView().getAppsStore();
|
||||
|
||||
// First look up the app info in order of:
|
||||
// - The exact activity for the recent task
|
||||
// - The first(?) loaded activity from the package
|
||||
AppInfo appInfo = appsStore.getApp(key);
|
||||
if (appInfo == null) {
|
||||
appInfo = appsStore.getApp(key, PACKAGE_KEY_COMPARATOR);
|
||||
}
|
||||
return appInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new launchable WorkspaceItemInfo of itemType=ITEM_TYPE_APPLICATION by looking the
|
||||
* ComponentKey up in the AllAppsStore. If no app is found, attempts a lookup by package
|
||||
* instead. If that lookup fails, returns null.
|
||||
*/
|
||||
@Nullable
|
||||
private WorkspaceItemInfo lookupLaunchableItem(@Nullable ComponentKey key) {
|
||||
if (key == null) {
|
||||
private WorkspaceItemInfo resolveAppPairWorkspaceInfo(
|
||||
@NonNull WorkspaceItemInfo recentTaskInfo) {
|
||||
// ComponentKey should never be null (see TaskView#getItemInfo)
|
||||
AppInfo appInfo = resolveAppInfoByComponent(recentTaskInfo.getComponentKey());
|
||||
if (appInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AllAppsStore appsStore = ActivityContext.lookupContext(mContext)
|
||||
.getAppsView().getAppsStore();
|
||||
|
||||
// Lookup by ComponentKey
|
||||
AppInfo appInfo = appsStore.getApp(key);
|
||||
if (appInfo == null) {
|
||||
// Lookup by package
|
||||
appInfo = appsStore.getApp(key, PACKAGE_KEY_COMPARATOR);
|
||||
}
|
||||
|
||||
return appInfo != null ? appInfo.makeWorkspaceItem(mContext) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates flags for newly created WorkspaceItemInfos.
|
||||
*/
|
||||
private void updateWorkspaceItemFlags(WorkspaceItemInfo wii) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
ActivityInfo ai = null;
|
||||
try {
|
||||
ai = pm.getActivityInfo(wii.getTargetComponent(), 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, "PackageManager lookup failed.");
|
||||
}
|
||||
|
||||
if (ai != null) {
|
||||
wii.setNonResizeable(ai.resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a WorkspaceItemInfo of itemType=ITEM_TYPE_TASK (from a Recents task) to a new
|
||||
* WorkspaceItemInfo of itemType=ITEM_TYPE_APPLICATION.
|
||||
*/
|
||||
private WorkspaceItemInfo convertRecentsItemToAppItem(WorkspaceItemInfo recentsItem) {
|
||||
if (recentsItem.itemType != LauncherSettings.Favorites.ITEM_TYPE_TASK) {
|
||||
Log.w(TAG, "Expected ItemInfo of type ITEM_TYPE_TASK, but received "
|
||||
+ recentsItem.itemType);
|
||||
}
|
||||
|
||||
WorkspaceItemInfo launchableItem = recentsItem.clone();
|
||||
PackageManager p = mContext.getPackageManager();
|
||||
Intent launchIntent = p.getLaunchIntentForPackage(recentsItem.getTargetPackage());
|
||||
Log.w(TAG, "Initial intent from Recents: " + launchableItem.intent + "\n"
|
||||
+ "Intent from PackageManager: " + launchIntent);
|
||||
if (launchIntent != null) {
|
||||
// If lookup from PackageManager fails, just use the existing intent
|
||||
launchableItem.intent = launchIntent;
|
||||
}
|
||||
launchableItem.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
return launchableItem;
|
||||
return appInfo.makeWorkspaceItem(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.launcher3.util.NavigationMode;
|
||||
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
|
||||
import com.android.quickstep.util.AppPairsController;
|
||||
import com.android.quickstep.util.LayoutUtils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -139,6 +140,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
protected DeviceProfile mDp;
|
||||
private final Rect mTaskSize = new Rect();
|
||||
private boolean mIsGroupedTask = false;
|
||||
private boolean mCanSaveAppPair = false;
|
||||
|
||||
public OverviewActionsView(Context context) {
|
||||
this(context, null);
|
||||
@@ -245,9 +247,12 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
* Updates a batch of flags to hide and show actions buttons when a grouped task (split screen)
|
||||
* is focused.
|
||||
* @param isGroupedTask True if the focused task is a grouped task.
|
||||
* @param canSaveAppPair True if the focused task is a grouped task and can be saved as an app
|
||||
* pair.
|
||||
*/
|
||||
public void updateForGroupedTask(boolean isGroupedTask) {
|
||||
public void updateForGroupedTask(boolean isGroupedTask, boolean canSaveAppPair) {
|
||||
mIsGroupedTask = isGroupedTask;
|
||||
mCanSaveAppPair = canSaveAppPair;
|
||||
updateActionButtonsVisibility();
|
||||
}
|
||||
|
||||
@@ -264,7 +269,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
private void updateActionButtonsVisibility() {
|
||||
assert mDp != null;
|
||||
boolean showSingleTaskActions = !mIsGroupedTask;
|
||||
boolean showGroupActions = mIsGroupedTask && mDp.isTablet;
|
||||
boolean showGroupActions = mIsGroupedTask && mDp.isTablet && mCanSaveAppPair;
|
||||
getActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showSingleTaskActions ? 1 : 0);
|
||||
getGroupActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showGroupActions ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -4011,7 +4011,9 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
|
||||
* * Device is large screen
|
||||
*/
|
||||
private void updateCurrentTaskActionsVisibility() {
|
||||
boolean isCurrentSplit = getCurrentPageTaskView() instanceof GroupedTaskView;
|
||||
TaskView taskView = getCurrentPageTaskView();
|
||||
boolean isCurrentSplit = taskView instanceof GroupedTaskView;
|
||||
GroupedTaskView groupedTaskView = isCurrentSplit ? (GroupedTaskView) taskView : null;
|
||||
// Update flags to see if entire actions bar should be hidden.
|
||||
if (!FeatureFlags.enableAppPairs()) {
|
||||
mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SCREEN, isCurrentSplit);
|
||||
@@ -4019,9 +4021,11 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
|
||||
mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SELECT_ACTIVE, isSplitSelectionActive());
|
||||
// Update flags to see if actions bar should show buttons for a single task or a pair of
|
||||
// tasks.
|
||||
mActionsView.updateForGroupedTask(isCurrentSplit);
|
||||
boolean canSaveAppPair = isCurrentSplit && supportsAppPairs() &&
|
||||
getSplitSelectController().getAppPairsController().canSaveAppPair(groupedTaskView);
|
||||
mActionsView.updateForGroupedTask(isCurrentSplit, canSaveAppPair);
|
||||
|
||||
boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
|
||||
boolean isCurrentDesktop = taskView instanceof DesktopTaskView;
|
||||
mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
|
||||
}
|
||||
|
||||
|
||||
@@ -223,4 +223,11 @@
|
||||
<string-array name="skip_private_profile_shortcut_packages" translatable="false">
|
||||
<item>com.android.settings</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Legacy list of components supporting multiple instances.
|
||||
DO NOT ADD TO THIS LIST. Apps should use the PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
|
||||
property to declare multi-instance support in V+. This resource should match the resource
|
||||
of the same name in SystemUI. -->
|
||||
<string-array name="config_appsSupportMultiInstancesSplit">
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.launcher3.pm.InstallSessionTracker;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.util.LockedUserState;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.RunnableList;
|
||||
import com.android.launcher3.util.SafeCloseable;
|
||||
@@ -181,7 +182,7 @@ public class LauncherAppState implements SafeCloseable {
|
||||
mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
|
||||
iconCacheFileName, mIconProvider);
|
||||
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
|
||||
iconCacheFileName != null);
|
||||
PackageManagerHelper.INSTANCE.get(context), iconCacheFileName != null);
|
||||
mOnTerminateCallback.add(mIconCache::close);
|
||||
mOnTerminateCallback.add(mModel::destroy);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,8 @@ public class LauncherModel implements InstallSessionTracker.Callback {
|
||||
@NonNull
|
||||
private final LauncherAppState mApp;
|
||||
@NonNull
|
||||
private final PackageManagerHelper mPmHelper;
|
||||
@NonNull
|
||||
private final ModelDbController mModelDbController;
|
||||
@NonNull
|
||||
private final Object mLock = new Object();
|
||||
@@ -152,12 +154,13 @@ public class LauncherModel implements InstallSessionTracker.Callback {
|
||||
|
||||
LauncherModel(@NonNull final Context context, @NonNull final LauncherAppState app,
|
||||
@NonNull final IconCache iconCache, @NonNull final AppFilter appFilter,
|
||||
final boolean isPrimaryInstance) {
|
||||
@NonNull final PackageManagerHelper pmHelper, final boolean isPrimaryInstance) {
|
||||
mApp = app;
|
||||
mPmHelper = pmHelper;
|
||||
mModelDbController = new ModelDbController(context);
|
||||
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
|
||||
mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
|
||||
isPrimaryInstance);
|
||||
mModelDelegate = ModelDelegate.newInstance(context, app, mPmHelper, mBgAllAppsList,
|
||||
mBgDataModel, isPrimaryInstance);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -132,6 +132,10 @@ public final class Utilities {
|
||||
@ChecksSdkIntAtLeast(api = VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "U")
|
||||
public static final boolean ATLEAST_U = Build.VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE;
|
||||
|
||||
@ChecksSdkIntAtLeast(api = VERSION_CODES.VANILLA_ICE_CREAM, codename = "V")
|
||||
public static final boolean ATLEAST_V = Build.VERSION.SDK_INT
|
||||
>= VERSION_CODES.VANILLA_ICE_CREAM;
|
||||
|
||||
/**
|
||||
* Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
|
||||
*/
|
||||
|
||||
@@ -301,6 +301,7 @@ public class AllAppsList {
|
||||
Context context, String packageName, UserHandle user) {
|
||||
final ApiWrapper apiWrapper = ApiWrapper.INSTANCE.get(context);
|
||||
final UserCache userCache = UserCache.getInstance(context);
|
||||
final PackageManagerHelper pmHelper = PackageManagerHelper.INSTANCE.get(context);
|
||||
final List<LauncherActivityInfo> matches = context.getSystemService(LauncherApps.class)
|
||||
.getActivityList(packageName, user);
|
||||
if (matches.size() > 0) {
|
||||
@@ -330,7 +331,7 @@ public class AllAppsList {
|
||||
applicationInfo.sectionName = mIndex.computeSectionName(applicationInfo.title);
|
||||
applicationInfo.intent = launchIntent;
|
||||
AppInfo.updateRuntimeFlagsForActivityTarget(applicationInfo, info,
|
||||
userCache.getUserInfo(user), apiWrapper);
|
||||
userCache.getUserInfo(user), apiWrapper, pmHelper);
|
||||
mDataChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ 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.PackageManagerHelper;
|
||||
import com.android.launcher3.util.UserIconInfo;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
@@ -73,6 +74,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||
|
||||
private final LauncherAppState mApp;
|
||||
private final Context mContext;
|
||||
private final PackageManagerHelper mPmHelper;
|
||||
private final IconCache mIconCache;
|
||||
private final InvariantDeviceProfile mIDP;
|
||||
private final @Nullable LauncherRestoreEventLogger mRestoreEventLogger;
|
||||
@@ -114,6 +116,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||
public int restoreFlag;
|
||||
|
||||
public LoaderCursor(Cursor cursor, LauncherAppState app, UserManagerState userManagerState,
|
||||
PackageManagerHelper pmHelper,
|
||||
@Nullable LauncherRestoreEventLogger restoreEventLogger) {
|
||||
super(cursor);
|
||||
|
||||
@@ -121,6 +124,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||
allUsers = userManagerState.allUsers;
|
||||
mContext = app.getContext();
|
||||
mIconCache = app.getIconCache();
|
||||
mPmHelper = pmHelper;
|
||||
mIDP = app.getInvariantDeviceProfile();
|
||||
mRestoreEventLogger = restoreEventLogger;
|
||||
|
||||
@@ -368,7 +372,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||
|
||||
if (mActivityInfo != null) {
|
||||
AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo, userIconInfo,
|
||||
ApiWrapper.INSTANCE.get(mContext));
|
||||
ApiWrapper.INSTANCE.get(mContext), mPmHelper);
|
||||
}
|
||||
|
||||
// from the db
|
||||
|
||||
@@ -435,7 +435,8 @@ public class LoaderTask implements Runnable {
|
||||
mShortcutKeyToPinnedShortcuts = new HashMap<>();
|
||||
final LoaderCursor c = new LoaderCursor(
|
||||
dbController.query(TABLE_NAME, null, selection, null, null),
|
||||
mApp, mUserManagerState, mIsRestoreFromBackup ? restoreEventLogger : null);
|
||||
mApp, mUserManagerState, mPmHelper,
|
||||
mIsRestoreFromBackup ? restoreEventLogger : null);
|
||||
final Bundle extras = c.getExtras();
|
||||
mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME);
|
||||
try {
|
||||
@@ -697,7 +698,7 @@ public class LoaderTask implements Runnable {
|
||||
for (int i = 0; i < apps.size(); i++) {
|
||||
LauncherActivityInfo app = apps.get(i);
|
||||
AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user),
|
||||
ApiWrapper.INSTANCE.get(mApp.getContext()), quietMode);
|
||||
ApiWrapper.INSTANCE.get(mApp.getContext()), mPmHelper, quietMode);
|
||||
if (Flags.enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
|
||||
// For archived apps, include progress info in case there is a pending
|
||||
// install session post restart of device.
|
||||
|
||||
@@ -26,6 +26,7 @@ import androidx.annotation.WorkerThread;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -41,15 +42,16 @@ public class ModelDelegate implements ResourceBasedOverride {
|
||||
* Creates and initializes a new instance of the delegate
|
||||
*/
|
||||
public static ModelDelegate newInstance(
|
||||
Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel,
|
||||
boolean isPrimaryInstance) {
|
||||
Context context, LauncherAppState app, PackageManagerHelper pmHelper,
|
||||
AllAppsList appsList, BgDataModel dataModel, boolean isPrimaryInstance) {
|
||||
ModelDelegate delegate = Overrides.getObject(
|
||||
ModelDelegate.class, context, R.string.model_delegate_class);
|
||||
delegate.init(app, appsList, dataModel, isPrimaryInstance);
|
||||
delegate.init(app, pmHelper, appsList, dataModel, isPrimaryInstance);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
protected final Context mContext;
|
||||
protected PackageManagerHelper mPmHelper;
|
||||
protected LauncherAppState mApp;
|
||||
protected AllAppsList mAppsList;
|
||||
protected BgDataModel mDataModel;
|
||||
@@ -62,9 +64,10 @@ public class ModelDelegate implements ResourceBasedOverride {
|
||||
/**
|
||||
* Initializes the object with the given params.
|
||||
*/
|
||||
private void init(LauncherAppState app, AllAppsList appsList,
|
||||
private void init(LauncherAppState app, PackageManagerHelper pmHelper, AllAppsList appsList,
|
||||
BgDataModel dataModel, boolean isPrimaryInstance) {
|
||||
this.mApp = app;
|
||||
this.mPmHelper = pmHelper;
|
||||
this.mAppsList = appsList;
|
||||
this.mDataModel = dataModel;
|
||||
this.mIsPrimaryInstance = isPrimaryInstance;
|
||||
|
||||
@@ -126,8 +126,8 @@ public class PackageUpdatedTask implements ModelUpdateTask {
|
||||
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
|
||||
appsList.removePackage(packages[i], mUser);
|
||||
}
|
||||
activitiesLists.put(
|
||||
packages[i], appsList.addPackage(context, packages[i], mUser));
|
||||
activitiesLists.put(packages[i],
|
||||
appsList.addPackage(context, packages[i], mUser));
|
||||
}
|
||||
flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
|
||||
break;
|
||||
@@ -138,8 +138,8 @@ public class PackageUpdatedTask implements ModelUpdateTask {
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
|
||||
iconCache.updateIconsForPkg(packages[i], mUser);
|
||||
activitiesLists.put(
|
||||
packages[i], appsList.updatePackage(context, packages[i], mUser));
|
||||
activitiesLists.put(packages[i],
|
||||
appsList.updatePackage(context, packages[i], mUser));
|
||||
}
|
||||
}
|
||||
// Since package was just updated, the target must be available now.
|
||||
@@ -269,6 +269,8 @@ public class PackageUpdatedTask implements ModelUpdateTask {
|
||||
if (isNewApkAvailable) {
|
||||
List<LauncherActivityInfo> activities = activitiesLists.get(
|
||||
packageName);
|
||||
// TODO: See if we can migrate this to
|
||||
// AppInfo#updateRuntimeFlagsForActivityTarget
|
||||
si.setProgressLevel(
|
||||
activities == null || activities.isEmpty()
|
||||
? 100
|
||||
@@ -399,7 +401,8 @@ public class PackageUpdatedTask implements ModelUpdateTask {
|
||||
return false;
|
||||
}
|
||||
// Try to find the best match activity.
|
||||
Intent intent = new PackageManagerHelper(context).getAppLaunchIntent(packageName, mUser);
|
||||
Intent intent = PackageManagerHelper.INSTANCE.get(context)
|
||||
.getAppLaunchIntent(packageName, mUser);
|
||||
if (intent != null) {
|
||||
si.intent = intent;
|
||||
si.status = WorkspaceItemInfo.DEFAULT;
|
||||
|
||||
@@ -336,7 +336,8 @@ class WorkspaceItemProcessor(
|
||||
info,
|
||||
activityInfo,
|
||||
userCache.getUserInfo(c.user),
|
||||
ApiWrapper.INSTANCE[app.context]
|
||||
ApiWrapper.INSTANCE[app.context],
|
||||
pmHelper
|
||||
)
|
||||
}
|
||||
if (
|
||||
|
||||
@@ -90,12 +90,12 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {
|
||||
*/
|
||||
public AppInfo(Context context, LauncherActivityInfo info, UserHandle user) {
|
||||
this(info, UserCache.INSTANCE.get(context).getUserInfo(user),
|
||||
ApiWrapper.INSTANCE.get(context),
|
||||
ApiWrapper.INSTANCE.get(context), PackageManagerHelper.INSTANCE.get(context),
|
||||
context.getSystemService(UserManager.class).isQuietModeEnabled(user));
|
||||
}
|
||||
|
||||
public AppInfo(LauncherActivityInfo info, UserIconInfo userIconInfo,
|
||||
ApiWrapper apiWrapper, boolean quietModeEnabled) {
|
||||
ApiWrapper apiWrapper, PackageManagerHelper pmHelper, boolean quietModeEnabled) {
|
||||
this.componentName = info.getComponentName();
|
||||
this.container = CONTAINER_ALL_APPS;
|
||||
this.user = userIconInfo.user;
|
||||
@@ -105,7 +105,7 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {
|
||||
runtimeStatusFlags |= FLAG_DISABLED_QUIET_USER;
|
||||
}
|
||||
uid = info.getApplicationInfo().uid;
|
||||
updateRuntimeFlagsForActivityTarget(this, info, userIconInfo, apiWrapper);
|
||||
updateRuntimeFlagsForActivityTarget(this, info, userIconInfo, apiWrapper, pmHelper);
|
||||
}
|
||||
|
||||
public AppInfo(AppInfo info) {
|
||||
@@ -184,7 +184,7 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {
|
||||
*/
|
||||
public static boolean updateRuntimeFlagsForActivityTarget(
|
||||
ItemInfoWithIcon info, LauncherActivityInfo lai, UserIconInfo userIconInfo,
|
||||
ApiWrapper apiWrapper) {
|
||||
ApiWrapper apiWrapper, PackageManagerHelper pmHelper) {
|
||||
final int oldProgressLevel = info.getProgressLevel();
|
||||
final int oldRuntimeStatusFlags = info.runtimeStatusFlags;
|
||||
ApplicationInfo appInfo = lai.getApplicationInfo();
|
||||
@@ -216,6 +216,8 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {
|
||||
PackageManagerHelper.getLoadingProgress(lai),
|
||||
PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
|
||||
info.setNonResizeable(apiWrapper.isNonResizeableActivity(lai));
|
||||
info.setSupportsMultiInstance(
|
||||
pmHelper.supportsMultiInstance(lai.getComponentName()));
|
||||
return (oldProgressLevel != info.getProgressLevel())
|
||||
|| (oldRuntimeStatusFlags != info.runtimeStatusFlags);
|
||||
}
|
||||
|
||||
@@ -126,6 +126,11 @@ public abstract class ItemInfoWithIcon extends ItemInfo {
|
||||
*/
|
||||
public static final int FLAG_NOT_RESIZEABLE = 1 << 15;
|
||||
|
||||
/**
|
||||
* Flag indicating whether the package related to the item & user supports multiple instances.
|
||||
*/
|
||||
public static final int FLAG_SUPPORTS_MULTI_INSTANCE = 1 << 16;
|
||||
|
||||
/**
|
||||
* Status associated with the system state of the underlying item. This is calculated every
|
||||
* time a new info is created and not persisted on the disk.
|
||||
@@ -251,6 +256,24 @@ public abstract class ItemInfoWithIcon extends ItemInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this app info supports multi-instance.
|
||||
*/
|
||||
protected void setSupportsMultiInstance(boolean supportsMultiInstance) {
|
||||
if (supportsMultiInstance) {
|
||||
runtimeStatusFlags |= FLAG_SUPPORTS_MULTI_INSTANCE;
|
||||
} else {
|
||||
runtimeStatusFlags &= ~FLAG_SUPPORTS_MULTI_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this app info supports multi-instance.
|
||||
*/
|
||||
public boolean supportsMultiInstance() {
|
||||
return (runtimeStatusFlags & FLAG_SUPPORTS_MULTI_INSTANCE) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this app info is non-resizeable.
|
||||
*/
|
||||
@@ -301,4 +324,11 @@ public abstract class ItemInfoWithIcon extends ItemInfo {
|
||||
drawable.setIsDisabled(isDisabled());
|
||||
return drawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String dumpProperties() {
|
||||
return super.dumpProperties()
|
||||
+ " supportsMultiInstance=" + supportsMultiInstance()
|
||||
+ " nonResizeable=" + isNonResizeable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
|
||||
/**
|
||||
* A set of utility methods for Launcher DB used for DB updates and migration.
|
||||
@@ -107,9 +108,11 @@ public class LauncherDbUtils {
|
||||
Cursor c = db.query(
|
||||
Favorites.TABLE_NAME, null, "itemType = 1", null, null, null, null);
|
||||
UserManagerState ums = new UserManagerState();
|
||||
PackageManagerHelper pmHelper = PackageManagerHelper.INSTANCE.get(context);
|
||||
ums.init(UserCache.INSTANCE.get(context),
|
||||
context.getSystemService(UserManager.class));
|
||||
LoaderCursor lc = new LoaderCursor(c, LauncherAppState.getInstance(context), ums, null);
|
||||
LoaderCursor lc = new LoaderCursor(c, LauncherAppState.getInstance(context), ums, pmHelper,
|
||||
null);
|
||||
IntSet deletedShortcuts = new IntSet();
|
||||
|
||||
while (lc.moveToNext()) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.launcher3.util;
|
||||
|
||||
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
|
||||
import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
@@ -73,10 +74,14 @@ public class PackageManagerHelper implements SafeCloseable{
|
||||
@NonNull
|
||||
private final LauncherApps mLauncherApps;
|
||||
|
||||
private final String[] mLegacyMultiInstanceSupportedApps;
|
||||
|
||||
public PackageManagerHelper(@NonNull final Context context) {
|
||||
mContext = context;
|
||||
mPm = context.getPackageManager();
|
||||
mLauncherApps = Objects.requireNonNull(context.getSystemService(LauncherApps.class));
|
||||
mLegacyMultiInstanceSupportedApps = mContext.getResources().getStringArray(
|
||||
R.array.config_appsSupportMultiInstancesSplit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,11 +164,23 @@ public class PackageManagerHelper implements SafeCloseable{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred launch activity intent for a given package.
|
||||
*/
|
||||
@Nullable
|
||||
public Intent getAppLaunchIntent(@Nullable final String pkg, @NonNull final UserHandle user) {
|
||||
LauncherActivityInfo info = getAppLaunchInfo(pkg, user);
|
||||
return info != null ? AppInfo.makeLaunchIntent(info) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred launch activity for a given package.
|
||||
*/
|
||||
@Nullable
|
||||
public LauncherActivityInfo getAppLaunchInfo(@Nullable final String pkg,
|
||||
@NonNull final UserHandle user) {
|
||||
List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user);
|
||||
return activities.isEmpty() ? null :
|
||||
AppInfo.makeLaunchIntent(activities.get(0));
|
||||
return activities.isEmpty() ? null : activities.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -285,4 +302,47 @@ public class PackageManagerHelper implements SafeCloseable{
|
||||
return (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 || (
|
||||
Flags.enableSupportForArchiving() && info.isArchived);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given component or its application has the multi-instance property set.
|
||||
*/
|
||||
public boolean supportsMultiInstance(@NonNull ComponentName component) {
|
||||
// Check the legacy hardcoded allowlist first
|
||||
for (String pkg : mLegacyMultiInstanceSupportedApps) {
|
||||
if (pkg.equals(component.getPackageName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check app multi-instance properties after V
|
||||
if (!Utilities.ATLEAST_V) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if the component has the multi-instance property
|
||||
return mPm.getProperty(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, component)
|
||||
.getBoolean();
|
||||
} catch (PackageManager.NameNotFoundException e1) {
|
||||
try {
|
||||
// Check if the application has the multi-instance property
|
||||
return mPm.getProperty(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI,
|
||||
component.getPackageName())
|
||||
.getBoolean();
|
||||
} catch (PackageManager.NameNotFoundException e2) {
|
||||
// Fall through
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two apps should be considered the same for multi-instance purposes, which
|
||||
* requires additional checks to ensure they can be started as multiple instances.
|
||||
*/
|
||||
public static boolean isSameAppForMultiInstance(@NonNull ItemInfo app1,
|
||||
@NonNull ItemInfo app2) {
|
||||
return app1.getTargetPackage().equals(app2.getTargetPackage())
|
||||
&& app1.user.equals(app2.user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,5 +412,9 @@
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
tools:node="remove" />
|
||||
|
||||
<property
|
||||
android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
|
||||
android:value="true"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -79,6 +79,7 @@ public class LoaderCursorTest {
|
||||
|
||||
private LauncherModelHelper mModelHelper;
|
||||
private LauncherAppState mApp;
|
||||
private PackageManagerHelper mPmHelper;
|
||||
|
||||
private MatrixCursor mCursor;
|
||||
private InvariantDeviceProfile mIDP;
|
||||
@@ -92,6 +93,7 @@ public class LoaderCursorTest {
|
||||
mContext = mModelHelper.sandboxContext;
|
||||
mIDP = InvariantDeviceProfile.INSTANCE.get(mContext);
|
||||
mApp = LauncherAppState.getInstance(mContext);
|
||||
mPmHelper = PackageManagerHelper.INSTANCE.get(mContext);
|
||||
|
||||
mCursor = new MatrixCursor(new String[] {
|
||||
ICON, TITLE, _ID, CONTAINER, ITEM_TYPE,
|
||||
@@ -101,7 +103,7 @@ public class LoaderCursorTest {
|
||||
});
|
||||
|
||||
UserManagerState ums = new UserManagerState();
|
||||
mLoaderCursor = new LoaderCursor(mCursor, mApp, ums, null);
|
||||
mLoaderCursor = new LoaderCursor(mCursor, mApp, ums, mPmHelper, null);
|
||||
ums.allUsers.put(0, Process.myUserHandle());
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -63,6 +64,8 @@ public final class PackageManagerHelperTest {
|
||||
mContext = mock(Context.class);
|
||||
mLauncherApps = mock(LauncherApps.class);
|
||||
when(mContext.getSystemService(eq(LauncherApps.class))).thenReturn(mLauncherApps);
|
||||
when(mContext.getResources()).thenReturn(
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext().getResources());
|
||||
mPackageManagerHelper = new PackageManagerHelper(mContext);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user