content: /*
   * 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 com.android.launcher3.util.PackageUserKey;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import static android.os.Process.myUserHandle;
  
  /**
   * 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<PackageUserKey, 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<PackageUserKey, SessionInfo> sessionInfoForPackage) {
          MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
          for (Map.Entry<PackageUserKey, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
              if (myUserHandle().equals(entry.getKey().mUser)) {
                  packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
                          entry.getKey().mPackageName);
              }
          }
          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)
                  .putStringArrayListExtra(FOLDER_ITEM_EXTRA, new ArrayList<>(folderItems))
                  .putStringArrayListExtra(WORKSPACE_ITEM_EXTRA, new ArrayList<>(workspaceItems))
                  .putStringArrayListExtra(HOTSEAT_ITEM_EXTRA, new ArrayList<>(hotseatItems))
                  .putStringArrayListExtra(WIDGET_ITEM_EXTRA, new ArrayList<>(widgetItems))
                  .putExtra(VERIFICATION_TOKEN_EXTRA, PendingIntent.getActivity(context, 0,
                          new Intent(), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE)));
      }
  
      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);
          }
      }
  }
  