1 /*
2  * Copyright (C) 2020 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 com.android.launcher3.EncryptionType.ENCRYPTED;
19 import static com.android.launcher3.LauncherPrefs.nonRestorableItem;
20 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
21 import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
22 
23 import android.app.prediction.AppTarget;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.pm.LauncherActivityInfo;
27 import android.content.pm.LauncherApps;
28 import android.content.pm.ShortcutInfo;
29 import android.os.UserHandle;
30 
31 import androidx.annotation.NonNull;
32 
33 import com.android.launcher3.ConstantItem;
34 import com.android.launcher3.LauncherAppState;
35 import com.android.launcher3.LauncherModel.ModelUpdateTask;
36 import com.android.launcher3.LauncherPrefs;
37 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
38 import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
39 import com.android.launcher3.model.data.AppInfo;
40 import com.android.launcher3.model.data.ItemInfo;
41 import com.android.launcher3.model.data.WorkspaceItemInfo;
42 
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.Set;
46 import java.util.stream.Collectors;
47 
48 /**
49  * Task to update model as a result of predicted apps update
50  */
51 public class PredictionUpdateTask implements ModelUpdateTask {
52 
53     public static final ConstantItem<Boolean> LAST_PREDICTION_ENABLED =
54             nonRestorableItem("last_prediction_enabled_state", true, ENCRYPTED);
55 
56     private final List<AppTarget> mTargets;
57     private final PredictorState mPredictorState;
58 
PredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets)59     PredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets) {
60         mPredictorState = predictorState;
61         mTargets = targets;
62     }
63 
64     @Override
execute(@onNull ModelTaskController taskController, @NonNull BgDataModel dataModel, @NonNull AllAppsList apps)65     public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
66             @NonNull AllAppsList apps) {
67         LauncherAppState app = taskController.getApp();
68         Context context = app.getContext();
69 
70         // TODO: remove this
71         LauncherPrefs.get(context).put(LAST_PREDICTION_ENABLED, !mTargets.isEmpty());
72 
73         Set<UserHandle> usersForChangedShortcuts =
74                 dataModel.extraItems.get(mPredictorState.containerId).items.stream()
75                         .filter(info -> info.itemType == ITEM_TYPE_DEEP_SHORTCUT)
76                         .map(info -> info.user)
77                         .collect(Collectors.toSet());
78 
79         List<ItemInfo> items = new ArrayList<>(mTargets.size());
80         for (AppTarget target : mTargets) {
81             WorkspaceItemInfo itemInfo;
82             ShortcutInfo si = target.getShortcutInfo();
83             if (si != null) {
84                 usersForChangedShortcuts.add(si.getUserHandle());
85                 itemInfo = new WorkspaceItemInfo(si, context);
86                 app.getIconCache().getShortcutIcon(itemInfo, si);
87             } else {
88                 String className = target.getClassName();
89                 if (COMPONENT_CLASS_MARKER.equals(className)) {
90                     // TODO: Implement this
91                     continue;
92                 }
93                 ComponentName cn = new ComponentName(target.getPackageName(), className);
94                 UserHandle user = target.getUser();
95                 itemInfo = apps.data.stream()
96                         .filter(info -> user.equals(info.user) && cn.equals(info.componentName))
97                         .map(ai -> {
98                             app.getIconCache().getTitleAndIcon(ai, false);
99                             return ai.makeWorkspaceItem(context);
100                         })
101                         .findAny()
102                         .orElseGet(() -> {
103                             LauncherActivityInfo lai = context.getSystemService(LauncherApps.class)
104                                     .resolveActivity(AppInfo.makeLaunchIntent(cn), user);
105                             if (lai == null) {
106                                 return null;
107                             }
108                             AppInfo ai = new AppInfo(context, lai, user);
109                             app.getIconCache().getTitleAndIcon(ai, lai, false);
110                             return ai.makeWorkspaceItem(context);
111                         });
112 
113                 if (itemInfo == null) {
114                     continue;
115                 }
116             }
117 
118             itemInfo.container = mPredictorState.containerId;
119             items.add(itemInfo);
120         }
121 
122         FixedContainerItems fci = new FixedContainerItems(mPredictorState.containerId, items);
123         dataModel.extraItems.put(fci.containerId, fci);
124         taskController.bindExtraContainerItems(fci);
125         usersForChangedShortcuts.forEach(
126                 u -> dataModel.updateShortcutPinnedState(app.getContext(), u));
127 
128         // Save to disk
129         mPredictorState.storage.write(context, fci.items);
130     }
131 }
132