1 /*
2  * Copyright (C) 2021 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.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
19 
20 import android.app.prediction.AppTarget;
21 import android.content.ComponentName;
22 import android.text.TextUtils;
23 
24 import com.android.launcher3.LauncherAppState;
25 import com.android.launcher3.config.FeatureFlags;
26 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
27 import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
28 import com.android.launcher3.util.ComponentKey;
29 import com.android.launcher3.util.PackageUserKey;
30 import com.android.launcher3.widget.PendingAddWidgetInfo;
31 
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.stream.Collectors;
36 
37 /** Task to update model as a result of predicted widgets update */
38 public final class WidgetsPredictionUpdateTask extends BaseModelUpdateTask {
39     private final PredictorState mPredictorState;
40     private final List<AppTarget> mTargets;
41 
WidgetsPredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets)42     WidgetsPredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets) {
43         mPredictorState = predictorState;
44         mTargets = targets;
45     }
46 
47     /**
48      * Uses the app predication result to infer widgets that the user may want to use.
49      *
50      * <p>The algorithm uses the app prediction ranking to create a widgets ranking which only
51      * includes one widget per app and excludes widgets that have already been added to the
52      * workspace.
53      */
54     @Override
execute(LauncherAppState appState, BgDataModel dataModel, AllAppsList apps)55     public void execute(LauncherAppState appState, BgDataModel dataModel, AllAppsList apps) {
56         Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
57                 widget -> new ComponentKey(widget.providerName, widget.user)).collect(
58                 Collectors.toSet());
59         Map<PackageUserKey, List<WidgetItem>> allWidgets =
60                 dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
61 
62         FixedContainerItems fixedContainerItems = mPredictorState.items;
63         fixedContainerItems.items.clear();
64 
65         if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
66             for (AppTarget app : mTargets) {
67                 PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(),
68                         app.getUser());
69                 if (allWidgets.containsKey(packageUserKey)) {
70                     List<WidgetItem> notAddedWidgets = allWidgets.get(packageUserKey).stream()
71                             .filter(item ->
72                                     !widgetsInWorkspace.contains(
73                                             new ComponentKey(item.componentName, item.user)))
74                             .collect(Collectors.toList());
75                     if (notAddedWidgets.size() > 0) {
76                         // Even an apps have more than one widgets, we only include one widget.
77                         fixedContainerItems.items.add(
78                                 new PendingAddWidgetInfo(
79                                         notAddedWidgets.get(0).widgetInfo,
80                                         CONTAINER_WIDGETS_PREDICTION));
81                     }
82                 }
83             }
84         } else {
85             Map<ComponentKey, WidgetItem> widgetItems =
86                     allWidgets.values().stream().flatMap(List::stream).distinct()
87                             .collect(Collectors.toMap(widget -> (ComponentKey) widget,
88                                     widget -> widget));
89             for (AppTarget app : mTargets) {
90                 if (TextUtils.isEmpty(app.getClassName())) {
91                     continue;
92                 }
93                 ComponentKey targetWidget = new ComponentKey(
94                         new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
95                 if (widgetItems.containsKey(targetWidget)) {
96                     fixedContainerItems.items.add(
97                             new PendingAddWidgetInfo(widgetItems.get(
98                                     targetWidget).widgetInfo,
99                                     CONTAINER_WIDGETS_PREDICTION));
100                 }
101             }
102         }
103         bindExtraContainerItems(fixedContainerItems);
104 
105         // Don't store widgets prediction to disk because it is not used frequently.
106     }
107 }
108