1 /*
2  * Copyright (C) 2016 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 android.content.Context;
19 import android.content.pm.ShortcutInfo;
20 import android.os.UserHandle;
21 
22 import com.android.launcher3.LauncherAppState;
23 import com.android.launcher3.LauncherSettings;
24 import com.android.launcher3.model.data.WorkspaceItemInfo;
25 import com.android.launcher3.shortcuts.ShortcutKey;
26 import com.android.launcher3.shortcuts.ShortcutRequest;
27 import com.android.launcher3.util.ItemInfoMatcher;
28 import com.android.launcher3.util.PackageManagerHelper;
29 
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Set;
34 import java.util.stream.Collectors;
35 
36 /**
37  * Handles changes due to shortcut manager updates (deep shortcut changes)
38  */
39 public class ShortcutsChangedTask extends BaseModelUpdateTask {
40 
41     private final String mPackageName;
42     private final List<ShortcutInfo> mShortcuts;
43     private final UserHandle mUser;
44     private final boolean mUpdateIdMap;
45 
ShortcutsChangedTask(String packageName, List<ShortcutInfo> shortcuts, UserHandle user, boolean updateIdMap)46     public ShortcutsChangedTask(String packageName, List<ShortcutInfo> shortcuts,
47             UserHandle user, boolean updateIdMap) {
48         mPackageName = packageName;
49         mShortcuts = shortcuts;
50         mUser = user;
51         mUpdateIdMap = updateIdMap;
52     }
53 
54     @Override
execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps)55     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
56         final Context context = app.getContext();
57         // Find WorkspaceItemInfo's that have changed on the workspace.
58         ArrayList<WorkspaceItemInfo> matchingWorkspaceItems = new ArrayList<>();
59 
60         synchronized (dataModel) {
61             dataModel.forAllWorkspaceItemInfos(mUser, si -> {
62                 if ((si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
63                         && mPackageName.equals(si.getIntent().getPackage())) {
64                     matchingWorkspaceItems.add(si);
65                 }
66             });
67         }
68 
69         if (!matchingWorkspaceItems.isEmpty()) {
70             if (mShortcuts.isEmpty()) {
71                 // Verify that the app is indeed installed.
72                 if (!new PackageManagerHelper(app.getContext())
73                         .isAppInstalled(mPackageName, mUser)) {
74                     // App is not installed, ignoring package events
75                     return;
76                 }
77             }
78             // Update the workspace to reflect the changes to updated shortcuts residing on it.
79             List<String> allLauncherKnownIds = matchingWorkspaceItems.stream()
80                     .map(WorkspaceItemInfo::getDeepShortcutId)
81                     .distinct()
82                     .collect(Collectors.toList());
83             List<ShortcutInfo> shortcuts = new ShortcutRequest(context, mUser)
84                     .forPackage(mPackageName, allLauncherKnownIds)
85                     .query(ShortcutRequest.ALL);
86 
87             Set<String> nonPinnedIds = new HashSet<>(allLauncherKnownIds);
88             ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
89             for (ShortcutInfo fullDetails : shortcuts) {
90                 if (!fullDetails.isPinned()) {
91                     continue;
92                 }
93 
94                 String sid = fullDetails.getId();
95                 nonPinnedIds.remove(sid);
96                 matchingWorkspaceItems
97                         .stream()
98                         .filter(itemInfo -> sid.equals(itemInfo.getDeepShortcutId()))
99                         .forEach(workspaceItemInfo -> {
100                             workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
101                             app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails);
102                             updatedWorkspaceItemInfos.add(workspaceItemInfo);
103                         });
104             }
105 
106             bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
107             if (!nonPinnedIds.isEmpty()) {
108                 deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(
109                         nonPinnedIds.stream()
110                                 .map(id -> new ShortcutKey(mPackageName, mUser, id))
111                                 .collect(Collectors.toSet())));
112             }
113         }
114 
115         if (mUpdateIdMap) {
116             // Update the deep shortcut map if the list of ids has changed for an activity.
117             dataModel.updateDeepShortcutCounts(mPackageName, mUser, mShortcuts);
118             bindDeepShortcuts(dataModel);
119         }
120     }
121 }
122