1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.model; 17 18 import static android.app.PendingIntent.FLAG_IMMUTABLE; 19 import static android.app.PendingIntent.FLAG_ONE_SHOT; 20 import static android.os.Process.myUserHandle; 21 22 import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle; 23 24 import static java.util.stream.Collectors.groupingBy; 25 import static java.util.stream.Collectors.mapping; 26 27 import android.app.PendingIntent; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.pm.PackageInstaller.SessionInfo; 31 import android.os.UserHandle; 32 import android.util.Log; 33 34 import com.android.launcher3.LauncherSettings; 35 import com.android.launcher3.model.data.FolderInfo; 36 import com.android.launcher3.model.data.ItemInfo; 37 import com.android.launcher3.model.data.LauncherAppWidgetInfo; 38 import com.android.launcher3.util.PackageUserKey; 39 40 import java.util.ArrayList; 41 import java.util.HashMap; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Set; 45 import java.util.stream.Collectors; 46 47 /** 48 * Helper class to send broadcasts to package installers that have: 49 * - Items on the first screen 50 * - Items with an active install session 51 * 52 * The packages are broken down by: folder items, workspace items, hotseat items, and widgets. 53 * 54 * Package installers only receive data for items that they are installing. 55 */ 56 public class FirstScreenBroadcast { 57 58 private static final String TAG = "FirstScreenBroadcast"; 59 private static final boolean DEBUG = false; 60 61 private static final String ACTION_FIRST_SCREEN_ACTIVE_INSTALLS 62 = "com.android.launcher3.action.FIRST_SCREEN_ACTIVE_INSTALLS"; 63 64 private static final String FOLDER_ITEM_EXTRA = "folderItem"; 65 private static final String WORKSPACE_ITEM_EXTRA = "workspaceItem"; 66 private static final String HOTSEAT_ITEM_EXTRA = "hotseatItem"; 67 private static final String WIDGET_ITEM_EXTRA = "widgetItem"; 68 69 private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken"; 70 71 private final HashMap<PackageUserKey, SessionInfo> mSessionInfoForPackage; 72 FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage)73 public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) { 74 mSessionInfoForPackage = sessionInfoForPackage; 75 } 76 77 /** 78 * Sends a broadcast to all package installers that have items with active sessions on the users 79 * first screen. 80 */ sendBroadcasts(Context context, List<ItemInfo> firstScreenItems)81 public void sendBroadcasts(Context context, List<ItemInfo> firstScreenItems) { 82 UserHandle myUser = myUserHandle(); 83 mSessionInfoForPackage 84 .values() 85 .stream() 86 .filter(info -> myUser.equals(getUserHandle(info))) 87 .collect(groupingBy(SessionInfo::getInstallerPackageName, 88 mapping(SessionInfo::getAppPackageName, Collectors.toSet()))) 89 .forEach((installer, packages) -> 90 sendBroadcastToInstaller(context, installer, packages, firstScreenItems)); 91 } 92 93 /** 94 * @param installerPackageName Package name of the package installer. 95 * @param packages List of packages with active sessions for this package installer. 96 * @param firstScreenItems List of items on the first screen. 97 */ sendBroadcastToInstaller(Context context, String installerPackageName, Set<String> packages, List<ItemInfo> firstScreenItems)98 private void sendBroadcastToInstaller(Context context, String installerPackageName, 99 Set<String> packages, List<ItemInfo> firstScreenItems) { 100 Set<String> folderItems = new HashSet<>(); 101 Set<String> workspaceItems = new HashSet<>(); 102 Set<String> hotseatItems = new HashSet<>(); 103 Set<String> widgetItems = new HashSet<>(); 104 105 for (ItemInfo info : firstScreenItems) { 106 if (info instanceof FolderInfo) { 107 FolderInfo folderInfo = (FolderInfo) info; 108 String folderItemInfoPackage; 109 for (ItemInfo folderItemInfo : folderInfo.contents) { 110 folderItemInfoPackage = getPackageName(folderItemInfo); 111 if (folderItemInfoPackage != null 112 && packages.contains(folderItemInfoPackage)) { 113 folderItems.add(folderItemInfoPackage); 114 } 115 } 116 } 117 118 String packageName = getPackageName(info); 119 if (packageName == null || !packages.contains(packageName)) { 120 continue; 121 } 122 if (info instanceof LauncherAppWidgetInfo) { 123 widgetItems.add(packageName); 124 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 125 hotseatItems.add(packageName); 126 } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 127 workspaceItems.add(packageName); 128 } 129 } 130 131 if (DEBUG) { 132 printList(installerPackageName, "Folder item", folderItems); 133 printList(installerPackageName, "Workspace item", workspaceItems); 134 printList(installerPackageName, "Hotseat item", hotseatItems); 135 printList(installerPackageName, "Widget item", widgetItems); 136 } 137 138 context.sendBroadcast(new Intent(ACTION_FIRST_SCREEN_ACTIVE_INSTALLS) 139 .setPackage(installerPackageName) 140 .putStringArrayListExtra(FOLDER_ITEM_EXTRA, new ArrayList<>(folderItems)) 141 .putStringArrayListExtra(WORKSPACE_ITEM_EXTRA, new ArrayList<>(workspaceItems)) 142 .putStringArrayListExtra(HOTSEAT_ITEM_EXTRA, new ArrayList<>(hotseatItems)) 143 .putStringArrayListExtra(WIDGET_ITEM_EXTRA, new ArrayList<>(widgetItems)) 144 .putExtra(VERIFICATION_TOKEN_EXTRA, PendingIntent.getActivity(context, 0, 145 new Intent(), FLAG_ONE_SHOT | FLAG_IMMUTABLE))); 146 } 147 getPackageName(ItemInfo info)148 private static String getPackageName(ItemInfo info) { 149 String packageName = null; 150 if (info instanceof LauncherAppWidgetInfo) { 151 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info; 152 if (widgetInfo.providerName != null) { 153 packageName = widgetInfo.providerName.getPackageName(); 154 } 155 } else if (info.getTargetComponent() != null){ 156 packageName = info.getTargetComponent().getPackageName(); 157 } 158 return packageName; 159 } 160 printList(String packageInstaller, String label, Set<String> packages)161 private static void printList(String packageInstaller, String label, Set<String> packages) { 162 for (String pkg : packages) { 163 Log.d(TAG, packageInstaller + ":" + label + ":" + pkg); 164 } 165 } 166 } 167