Send directed broadcast to package installers that have active sessions.
For each installer, we send a broadcast that includes package names of items that:
* Are being installed by that installer
* Are on the first screen
* Have an active install session
The packages are seperated by:
* Folder items
* Workspace items
* Hotseat items
* Widgets
Bug: 74355094
Change-Id: I573ed6b3b84314ef244486fcf8354bebdff8bbdf
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 112cca5..3270ba2 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -45,7 +45,7 @@
/**
* @return a map of active installs to their progress
*/
- public abstract HashMap<String, Integer> updateAndGetActiveSessionCache();
+ public abstract HashMap<String, PackageInstaller.SessionInfo> updateAndGetActiveSessionCache();
public abstract void onStop();
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 1ffd3da..dd17916 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -59,13 +59,13 @@
}
@Override
- public HashMap<String, Integer> updateAndGetActiveSessionCache() {
- HashMap<String, Integer> activePackages = new HashMap<>();
+ public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
+ HashMap<String, SessionInfo> activePackages = new HashMap<>();
UserHandle user = Process.myUserHandle();
for (SessionInfo info : getAllVerifiedSessions()) {
addSessionInfoToCache(info, user);
if (info.getAppPackageName() != null) {
- activePackages.put(info.getAppPackageName(), (int) (info.getProgress() * 100));
+ activePackages.put(info.getAppPackageName(), info);
mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
}
}
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
new file mode 100644
index 0000000..2736509
--- /dev/null
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.util.Log;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.util.MultiHashMap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper class to send broadcasts to package installers that have:
+ * - Items on the first screen
+ * - Items with an active install session
+ *
+ * The packages are broken down by: folder items, workspace items, hotseat items, and widgets.
+ *
+ * Package installers only receive data for items that they are installing.
+ */
+public class FirstScreenBroadcast {
+
+ private static final String TAG = "FirstScreenBroadcast";
+ private static final boolean DEBUG = false;
+
+ private static final String ACTION_FIRST_SCREEN_ACTIVE_INSTALLS
+ = "com.android.launcher3.action.FIRST_SCREEN_ACTIVE_INSTALLS";
+
+ private static final String FOLDER_ITEM_EXTRA = "folderItem";
+ private static final String WORKSPACE_ITEM_EXTRA = "workspaceItem";
+ private static final String HOTSEAT_ITEM_EXTRA = "hotseatItem";
+ private static final String WIDGET_ITEM_EXTRA = "widgetItem";
+
+ private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
+
+ private final MultiHashMap<String, String> mPackagesForInstaller;
+
+ public FirstScreenBroadcast(HashMap<String, SessionInfo> sessionInfoForPackage) {
+ mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage);
+ }
+
+ /**
+ * @return Map where the key is the package name of the installer, and the value is a list
+ * of packages with active sessions for that installer.
+ */
+ private MultiHashMap<String, String> getPackagesForInstaller(
+ HashMap<String, SessionInfo> sessionInfoForPackage) {
+ MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
+ for (Map.Entry<String, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
+ packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
+ entry.getKey());
+ }
+ return packagesForInstaller;
+ }
+
+ /**
+ * Sends a broadcast to all package installers that have items with active sessions on the users
+ * first screen.
+ */
+ public void sendBroadcasts(Context context, List<ItemInfo> firstScreenItems) {
+ for (Map.Entry<String, ArrayList<String>> entry : mPackagesForInstaller.entrySet()) {
+ sendBroadcastToInstaller(context, entry.getKey(), entry.getValue(), firstScreenItems);
+ }
+ }
+
+ /**
+ * @param installerPackageName Package name of the package installer.
+ * @param packages List of packages with active sessions for this package installer.
+ * @param firstScreenItems List of items on the first screen.
+ */
+ private void sendBroadcastToInstaller(Context context, String installerPackageName,
+ List<String> packages, List<ItemInfo> firstScreenItems) {
+ Set<String> folderItems = new HashSet<>();
+ Set<String> workspaceItems = new HashSet<>();
+ Set<String> hotseatItems = new HashSet<>();
+ Set<String> widgetItems = new HashSet<>();
+
+ for (ItemInfo info : firstScreenItems) {
+ if (info instanceof FolderInfo) {
+ FolderInfo folderInfo = (FolderInfo) info;
+ String folderItemInfoPackage;
+ for (ItemInfo folderItemInfo : folderInfo.contents) {
+ folderItemInfoPackage = getPackageName(folderItemInfo);
+ if (folderItemInfoPackage != null
+ && packages.contains(folderItemInfoPackage)) {
+ folderItems.add(folderItemInfoPackage);
+ }
+ }
+ }
+
+ String packageName = getPackageName(info);
+ if (packageName == null || !packages.contains(packageName)) {
+ continue;
+ }
+ if (info instanceof LauncherAppWidgetInfo) {
+ widgetItems.add(packageName);
+ } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ hotseatItems.add(packageName);
+ } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ workspaceItems.add(packageName);
+ }
+ }
+
+ if (DEBUG) {
+ printList(installerPackageName, "Folder item", folderItems);
+ printList(installerPackageName, "Workspace item", workspaceItems);
+ printList(installerPackageName, "Hotseat item", hotseatItems);
+ printList(installerPackageName, "Widget item", widgetItems);
+ }
+
+ context.sendBroadcast(new Intent(ACTION_FIRST_SCREEN_ACTIVE_INSTALLS)
+ .setPackage(installerPackageName)
+ .putExtra(FOLDER_ITEM_EXTRA, folderItems.toArray())
+ .putExtra(WORKSPACE_ITEM_EXTRA, workspaceItems.toArray())
+ .putExtra(HOTSEAT_ITEM_EXTRA, hotseatItems.toArray())
+ .putExtra(WIDGET_ITEM_EXTRA, widgetItems.toArray())
+ .putExtra(VERIFICATION_TOKEN_EXTRA, PendingIntent.getActivity(context, 0,
+ new Intent(), PendingIntent.FLAG_ONE_SHOT)));
+ }
+
+ private static String getPackageName(ItemInfo info) {
+ String packageName = null;
+ if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
+ if (widgetInfo.providerName != null) {
+ packageName = widgetInfo.providerName.getPackageName();
+ }
+ } else if (info.getTargetComponent() != null){
+ packageName = info.getTargetComponent().getPackageName();
+ }
+ return packageName;
+ }
+
+ private static void printList(String packageInstaller, String label, Set<String> packages) {
+ for (String pkg : packages) {
+ Log.d(TAG, packageInstaller + ":" + label + ":" + pkg);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 5d4a352..0fd9b73 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -209,7 +209,7 @@
/** Filters the set of items who are directly or indirectly (via another container) on the
* specified screen. */
- private <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId,
+ public static <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId,
ArrayList<T> allWorkspaceItems,
ArrayList<T> currentScreenItems,
ArrayList<T> otherScreenItems) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 9d1ff83..06da843 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -29,6 +30,7 @@
import android.content.IntentFilter;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Process;
@@ -92,6 +94,8 @@
private final AllAppsList mBgAllAppsList;
private final BgDataModel mBgDataModel;
+ private FirstScreenBroadcast mFirstScreenBroadcast;
+
private final LoaderResults mResults;
private final LauncherAppsCompat mLauncherApps;
@@ -134,6 +138,22 @@
}
}
+ private void sendFirstScreenActiveInstallsBroadcast() {
+ ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
+
+ ArrayList<ItemInfo> allItems = new ArrayList<>();
+ synchronized (mBgDataModel) {
+ allItems.addAll(mBgDataModel.workspaceItems);
+ allItems.addAll(mBgDataModel.appWidgets);
+ }
+ long firstScreen = mBgDataModel.workspaceScreens.isEmpty()
+ ? -1 // In this case, we can still look at the items in the hotseat.
+ : mBgDataModel.workspaceScreens.get(0);
+ filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems,
+ new ArrayList<>() /* otherScreenItems are ignored */);
+ mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
+ }
+
public void run() {
synchronized (this) {
// Skip fast if we are already stopped.
@@ -151,6 +171,10 @@
TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
mResults.bindWorkspace();
+ // Notify the installer packages of packages with active installs on the first screen.
+ TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");
+ sendFirstScreenActiveInstallsBroadcast();
+
// Take a break
TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
waitForIdle();
@@ -242,8 +266,9 @@
synchronized (mBgDataModel) {
mBgDataModel.clear();
- final HashMap<String, Integer> installingPkgs =
+ final HashMap<String, SessionInfo> installingPkgs =
mPackageInstaller.updateAndGetActiveSessionCache();
+ mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
@@ -511,11 +536,11 @@
}
if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
- Integer progress = installingPkgs.get(targetPkg);
- if (progress != null) {
- info.setInstallProgress(progress);
- } else {
+ SessionInfo si = installingPkgs.get(targetPkg);
+ if (si == null) {
info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+ } else {
+ info.setInstallProgress((int) (si.getProgress() * 100));
}
}
@@ -605,7 +630,11 @@
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
component);
appWidgetInfo.restoreStatus = c.restoreFlag;
- Integer installProgress = installingPkgs.get(component.getPackageName());
+ SessionInfo si =
+ installingPkgs.get(component.getPackageName());
+ Integer installProgress = si == null
+ ? null
+ : (int) (si.getProgress() * 100);
if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
// Restore has started once.