按照客户需求添加了无抽屉模式,也就是把所有应用图标都添加到Workspace中去,虽说实现了该需求,但是留下了部分bug,正好我们可以在实现需求和解决bug的过程中不断学习~ 1 无抽屉模式时安装应用,应用图标没有添加到桌面,以及 adb shell pm hide(unhide) com.android.settings ,unhide后图标也没有恢复到桌面 解决方案如下:
--- a/packages/apps/Launcher3/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/packages/apps/Launcher3/src/com/android/launcher3/SessionCommitReceiver.java
@@ -66,7 +66,7 @@ public class SessionCommitReceiver extends BroadcastReceiver {
InstallSessionHelper packageInstallerCompat = InstallSessionHelper.INSTANCE.get(context);
if (TextUtils.isEmpty(info.getAppPackageName())
- || info.getInstallReason() != PackageManager.INSTALL_REASON_USER
+ || (info.getInstallReason() != PackageManager.INSTALL_REASON_USER && info.getInstallReason() != PackageManager.INSTALL_REASON_UNKNOWN)
|| packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) {
packageInstallerCompat.removePromiseIconId(info.getSessionId());
return;
原理是:之前只有PackageManager.INSTALL_REASON_USER
的应用才能添加应用图标到桌面,现在我们添加PackageManager.INSTALL_REASON_UNKNOWN
的应用 (adb install(unhide),手动安装应用都是INSTALL_REASON_UNKNOWN) 2 HomeSettings 修复无抽屉模式安装拥有多个图标的应用时只添加了一个图标 这个就需要梳理下代码逻辑的大概流程了 首先是SessionCommitReceiver 收到SESSION_COMMITTED 广播
<!-- Intent received when a session is committed -->
<receiver
android:name=com.android.launcher3.SessionCommitReceiver
android:exported=true>
<intent-filter>
<action android:name=android.content.pm.action.SESSION_COMMITTED />
</intent-filter>
</receiver>
在做了一定的筛选之后,调用如下方法
ItemInstallQueue.INSTANCE.get(context)
.queueItem(info.getAppPackageName(), user);
public void queueItem(String packageName, UserHandle userHandle) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle));//插眼
}
private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
final Exception stackTrace = new Exception();
// Queue the item up for adding if launcher has not loaded properly yet
MODEL_EXECUTOR.post(() -> {
//插眼,getItemInfo方法很重要,这里后面有用而且可以学下如何创建WorkspaceItemInfo
Pair<ItemInfo, Object> itemInfo = info.getItemInfo(mContext);
if (itemInfo == null) {
FileLog.d(LOG,
Adding PendingInstallShortcutInfo with no attached info to queue.,
stackTrace);
} else {
FileLog.d(LOG,
Adding PendingInstallShortcutInfo to queue. Attached info:
+ itemInfo.first,
stackTrace);
}
addToQueue(info);
});
flushInstallQueue();
}
@WorkerThread
private void addToQueue(PendingInstallShortcutInfo info) {
ensureQueueLoaded();
if (!mItems.contains(info)) {
mItems.add(info);
mStorage.write(mContext, mItems);
}
}
@WorkerThread
private void flushQueueInBackground() {
Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
if (launcher == null) {
// Launcher not loaded
return;
}
ensureQueueLoaded();
if (mItems.isEmpty()) {
return;
}
List<Pair<ItemInfo, Object>> installQueue = mItems.stream()
.map(info -> info.getItemInfo(mContext))
.collect(Collectors.toList());
// Add the items and clear queue
if (!installQueue.isEmpty()) {
// add log
launcher.getModel().addAndBindAddedWorkspaceItems(installQueue);
}
mItems.clear();
mStorage.getFile(mContext).delete();
}
也就是代码流程为 queueItem -》 queuePendingShortcutInfo -》addToQueue(info); -》 flushInstallQueue(); 在 flushInstallQueue()方法中调用LauncherModel.java的addAndBindAddedWorkspaceItems(installQueue);
方法
/**
* Adds the provided items to the workspace.
*/
public void addAndBindAddedWorkspaceItems(
@NonNull final List<Pair<ItemInfo, Object>> itemList) {
for (Callbacks cb : getCallbacks()) {
cb.preAddApps();
}
enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
}
AddWorkspaceItemsTask.java是非常重要的核心类,仅做核心代码展示
// Add the shortcut to the db
getModelWriter().addItemToDatabase(itemInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
coords[1], coords[2]);
// Save the WorkspaceItemInfo for binding in the workspace
addedItemsFinal.add(itemInfo);
// log bitmap and label
FileLog.d(LOG, Adding item info to workspace: + itemInfo);
if (!addedItemsFinal.isEmpty()) {
scheduleCallbackTask(new CallbackTask() {
@Override
public void execute(@NonNull Callbacks callbacks) {
final ArrayList<ItemInfo> addAnimated = new ArrayList<>();
final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
if (!addedItemsFinal.isEmpty()) {
ItemInfo info = addedItemsFinal.get(addedItemsFinal.size() - 1);
int lastScreenId = info.screenId;
for (ItemInfo i : addedItemsFinal) {
if (i.screenId == lastScreenId) {
addAnimated.add(i);
} else {
addNotAnimated.add(i);
}
}
}
callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
addNotAnimated, addAnimated);
}
});
}
InstallSessionTracker installSessionTracker =
InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
InstallSessionTracker.java监听安装进度,在LauncherAppState.java 和InstallSessionHelper.java中进行注册
其中
@Override
public void onFinished(final int sessionId, final boolean success) {
InstallSessionHelper helper = mWeakHelper.get();
Callback callback = mWeakCallback.get();
if (callback == null || helper == null) {
return;
}
// For a finished session, we can't get the session info. So use the
// packageName from our local cache.
SparseArray<PackageUserKey> activeSessions = getActiveSessionMap(helper);
PackageUserKey key = activeSessions.get(sessionId);
activeSessions.remove(sessionId);
if (key != null && key.mPackageName != null) {
String packageName = key.mPackageName;
PackageInstallInfo info = PackageInstallInfo.fromState(
success ? STATUS_INSTALLED : STATUS_FAILED,
packageName, key.mUser);
callback.onPackageStateChanged(info);
if (!success && helper.promiseIconAddedForId(sessionId)) {
callback.onSessionFailure(packageName, key.mUser);
// If it is successful, the id is removed in the the package added flow.
helper.removePromiseIconId(sessionId);
}
}
}
callback.onPackageStateChanged(info); callback是LaunchModel
@Override
public void onPackageStateChanged(@NonNull final PackageInstallInfo installInfo) {
enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
}
PackageInstallStateChangedTask.java也很核心
if (mInstallInfo.state == PackageInstallInfo.STATUS_INSTALLED) {
try {
// For instant apps we do not get package-add. Use setting events to update
// any pinned icons.
ApplicationInfo ai = app.getContext()
.getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user);//---------
}
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
// Ignore install success events as they are handled by Package add events.
return;
}
synchronized (apps) {
List<AppInfo> updatedAppInfos = apps.updatePromiseInstallInfo(mInstallInfo);
if (!updatedAppInfos.isEmpty()) {
for (AppInfo appInfo : updatedAppInfos) {
scheduleCallbackTask(c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
}
}
bindApplicationsIfNeeded();//-----
}
synchronized (dataModel) {
final HashSet<ItemInfo> updates = new HashSet<>();
dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
if (si.hasPromiseIconUi()
&& mInstallInfo.packageName.equals(si.getTargetPackage())) {
si.setProgressLevel(mInstallInfo);
updates.add(si);
}
});
for (LauncherAppWidgetInfo widget : dataModel.appWidgets) {
if (widget.providerName.getPackageName().equals(mInstallInfo.packageName)) {
widget.installProgress = mInstallInfo.progress;
updates.add(widget); //-----
}
}
if (!updates.isEmpty()) {
scheduleCallbackTask(callbacks -> callbacks.bindRestoreItemsChange(updates));
}
}
}
其中app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user);
LauncherModel.java:
@Override
public void onPackageAdded(@NonNull final String packageName, @NonNull final UserHandle user) {
int op = PackageUpdatedTask.OP_ADD;
android.util.Log.e("ninini", " new PackageUpdatedTask(OP_ADD");
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
PackageUpdatedTask :
switch (mOp) {
case OP_ADD: {
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
appsList.removePackage(packages[i], mUser);
}
activitiesLists.put(
packages[i], appsList.addPackage(context, packages[i], mUser));
}
flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
}
appsList.addPackage,appsList是AllAppsList
/**
* Add the icons for the supplied apk called packageName.
*/
public List<LauncherActivityInfo> addPackage(
Context context, String packageName, UserHandle user) {
List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
.getActivityList(packageName, user);
//在这里,遍历LauncherActivityInfo,然后安装应用时把图标添加到了AppList中
for (LauncherActivityInfo info : activities) {
add(new AppInfo(context, info, user), info);
}
return activities;
}
/**
* Add the supplied ApplicationInfo objects to the list, and enqueue it into the
* list to broadcast when notify() is called.
*
* If the app is already in the list, doesn't add it.
*/
public void add(AppInfo info, LauncherActivityInfo activityInfo) {
add(info, activityInfo, true);
}
public void add(AppInfo info, LauncherActivityInfo activityInfo, boolean loadIcon) {
if (!mAppFilter.shouldShowApp(info.componentName)) {
return;
}
if (findAppInfo(info.componentName, info.user) != null) {
return;
}
if (loadIcon) {
mIconCache.getTitleAndIcon(info, activityInfo, false /* useLowResIcon */);
info.sectionName = mIndex.computeSectionName(info.title);
} else {
info.title = "";
}
data.add(info);
mDataChanged = true;
}
修改方案如下
@@ -172,6 +174,17 @@ public class ItemInstallQueue {
*/
public void queueItem(String packageName, UserHandle userHandle) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle));
+
+ if(MultiModeController.isSingleLayerMode(mContext)){
+ List<LauncherActivityInfo> activities = mContext.getSystemService(LauncherApps.class)
+ .getActivityList(packageName, userHandle);
+ android.util.Log.e("queuePendingShortcutInfo ", activities.size()+"~" );
+ if(null != activities && activities.size() > 1){
+ for (int i = 1; i < activities.size(); i++) {
+ queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle,i));
+ }
+ }
+ }
}