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.Flags.enableCategorizedWidgetSuggestions;
19 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
20 
21 import android.app.prediction.AppTarget;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.text.TextUtils;
25 
26 import androidx.annotation.NonNull;
27 
28 import com.android.launcher3.LauncherModel.ModelUpdateTask;
29 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
30 import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
31 import com.android.launcher3.model.data.ItemInfo;
32 import com.android.launcher3.util.ComponentKey;
33 import com.android.launcher3.widget.PendingAddWidgetInfo;
34 import com.android.launcher3.widget.picker.WidgetRecommendationCategoryProvider;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.function.Predicate;
41 import java.util.stream.Collectors;
42 
43 /** Task to update model as a result of predicted widgets update */
44 public final class WidgetsPredictionUpdateTask implements ModelUpdateTask {
45     private final PredictorState mPredictorState;
46     private final List<AppTarget> mTargets;
47 
WidgetsPredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets)48     WidgetsPredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets) {
49         mPredictorState = predictorState;
50         mTargets = targets;
51     }
52 
53     /**
54      * Uses the app predication result to infer widgets that the user may want to use.
55      *
56      * <p>The algorithm uses the app prediction ranking to create a widgets ranking which only
57      * includes one widget per app and excludes widgets that have already been added to the
58      * workspace.
59      */
60     @Override
execute(@onNull ModelTaskController taskController, @NonNull BgDataModel dataModel, @NonNull AllAppsList apps)61     public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
62             @NonNull AllAppsList apps) {
63         Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
64                 widget -> new ComponentKey(widget.providerName, widget.user)).collect(
65                 Collectors.toSet());
66         Predicate<WidgetItem> notOnWorkspace = w -> !widgetsInWorkspace.contains(w);
67         Map<ComponentKey, WidgetItem> allWidgets =
68                 dataModel.widgetsModel.getAllWidgetComponentsWithoutShortcuts();
69 
70         List<WidgetItem> servicePredictedItems = new ArrayList<>();
71 
72         for (AppTarget app : mTargets) {
73             ComponentKey componentKey = new ComponentKey(
74                     new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
75             WidgetItem widget = allWidgets.get(componentKey);
76             if (widget == null) {
77                 continue;
78             }
79             String className = app.getClassName();
80             if (!TextUtils.isEmpty(className)) {
81                 if (notOnWorkspace.test(widget)) {
82                     servicePredictedItems.add(widget);
83                 }
84             }
85         }
86 
87         List<ItemInfo> items;
88         if (enableCategorizedWidgetSuggestions()) {
89             Context context = taskController.getApp().getContext();
90             WidgetRecommendationCategoryProvider categoryProvider =
91                     WidgetRecommendationCategoryProvider.newInstance(context);
92             items = servicePredictedItems.stream()
93                     .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
94                             categoryProvider.getWidgetRecommendationCategory(context, it)))
95                     .collect(Collectors.toList());
96         } else {
97             items = servicePredictedItems.stream()
98                     .map(it -> new PendingAddWidgetInfo(it.widgetInfo,
99                             CONTAINER_WIDGETS_PREDICTION)).collect(
100                             Collectors.toList());
101         }
102         FixedContainerItems fixedContainerItems =
103                 new FixedContainerItems(mPredictorState.containerId, items);
104 
105         dataModel.extraItems.put(mPredictorState.containerId, fixedContainerItems);
106         taskController.bindExtraContainerItems(fixedContainerItems);
107 
108         // Don't store widgets prediction to disk because it is not used frequently.
109     }
110 }
111