From 5ab0d896c6636276976e75cbe549db8eb1232ef3 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Mon, 26 Mar 2018 17:30:50 -0700 Subject: [PATCH] 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 --- .../compat/PackageInstallerCompat.java | 2 +- .../compat/PackageInstallerCompatVL.java | 6 +- .../launcher3/model/FirstScreenBroadcast.java | 164 ++++++++++++++++++ .../launcher3/model/LoaderResults.java | 2 +- .../android/launcher3/model/LoaderTask.java | 41 ++++- 5 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 src/com/android/launcher3/model/FirstScreenBroadcast.java diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java index 112cca5402..3270ba2005 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompat.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java @@ -45,7 +45,7 @@ public abstract class PackageInstallerCompat { /** * @return a map of active installs to their progress */ - public abstract HashMap updateAndGetActiveSessionCache(); + public abstract HashMap updateAndGetActiveSessionCache(); public abstract void onStop(); diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index 1ffd3da014..dd17916f0d 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -59,13 +59,13 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { } @Override - public HashMap updateAndGetActiveSessionCache() { - HashMap activePackages = new HashMap<>(); + public HashMap updateAndGetActiveSessionCache() { + HashMap 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 0000000000..2736509ca6 --- /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 mPackagesForInstaller; + + public FirstScreenBroadcast(HashMap 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 getPackagesForInstaller( + HashMap sessionInfoForPackage) { + MultiHashMap packagesForInstaller = new MultiHashMap<>(); + for (Map.Entry 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 firstScreenItems) { + for (Map.Entry> 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 packages, List firstScreenItems) { + Set folderItems = new HashSet<>(); + Set workspaceItems = new HashSet<>(); + Set hotseatItems = new HashSet<>(); + Set 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 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 5d4a352994..0fd9b735ed 100644 --- a/src/com/android/launcher3/model/LoaderResults.java +++ b/src/com/android/launcher3/model/LoaderResults.java @@ -209,7 +209,7 @@ public class LoaderResults { /** Filters the set of items who are directly or indirectly (via another container) on the * specified screen. */ - private void filterCurrentWorkspaceItems(long currentScreenId, + public static void filterCurrentWorkspaceItems(long currentScreenId, ArrayList allWorkspaceItems, ArrayList currentScreenItems, ArrayList otherScreenItems) { diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 9d1ff83168..06da843f7f 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_LOCKED_USER; 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.Intent; 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 @@ public class LoaderTask implements Runnable { private final AllAppsList mBgAllAppsList; private final BgDataModel mBgDataModel; + private FirstScreenBroadcast mFirstScreenBroadcast; + private final LoaderResults mResults; private final LauncherAppsCompat mLauncherApps; @@ -134,6 +138,22 @@ public class LoaderTask implements Runnable { } } + private void sendFirstScreenActiveInstallsBroadcast() { + ArrayList firstScreenItems = new ArrayList<>(); + + ArrayList 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 @@ public class LoaderTask implements Runnable { 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 @@ public class LoaderTask implements Runnable { synchronized (mBgDataModel) { mBgDataModel.clear(); - final HashMap installingPkgs = + final HashMap installingPkgs = mPackageInstaller.updateAndGetActiveSessionCache(); + mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context)); Map shortcutKeyToPinnedShortcuts = new HashMap<>(); @@ -511,11 +536,11 @@ public class LoaderTask implements Runnable { } 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 @@ public class LoaderTask implements Runnable { 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.