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.taskbar;
17 
18 import android.util.SparseArray;
19 import android.view.View;
20 
21 import com.android.launcher3.LauncherSettings.Favorites;
22 import com.android.launcher3.model.BgDataModel;
23 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
24 import com.android.launcher3.model.data.ItemInfo;
25 import com.android.launcher3.model.data.WorkspaceItemInfo;
26 import com.android.launcher3.util.ComponentKey;
27 import com.android.launcher3.util.IntArray;
28 import com.android.launcher3.util.IntSet;
29 import com.android.launcher3.util.ItemInfoMatcher;
30 import com.android.launcher3.util.LauncherBindableItemsContainer;
31 
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.List;
37 
38 /**
39  * Launcher model Callbacks for rendering taskbar.
40  */
41 public class TaskbarModelCallbacks implements
42         BgDataModel.Callbacks, LauncherBindableItemsContainer {
43 
44     private final SparseArray<ItemInfo> mHotseatItems = new SparseArray<>();
45     private List<ItemInfo> mPredictedItems = Collections.emptyList();
46 
47     private final TaskbarActivityContext mContext;
48     private final TaskbarView mContainer;
49 
50     // Initialized in init.
51     private TaskbarControllers mControllers;
52 
53     private boolean mBindInProgress = false;
54 
TaskbarModelCallbacks( TaskbarActivityContext context, TaskbarView container)55     public TaskbarModelCallbacks(
56             TaskbarActivityContext context, TaskbarView container) {
57         mContext = context;
58         mContainer = container;
59     }
60 
init(TaskbarControllers controllers)61     public void init(TaskbarControllers controllers) {
62         mControllers = controllers;
63     }
64 
65     @Override
startBinding()66     public void startBinding() {
67         mBindInProgress = true;
68         mHotseatItems.clear();
69         mPredictedItems = Collections.emptyList();
70     }
71 
72     @Override
finishBindingItems(IntSet pagesBoundFirst)73     public void finishBindingItems(IntSet pagesBoundFirst) {
74         mBindInProgress = false;
75         commitItemsToUI();
76     }
77 
78     @Override
bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated)79     public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated,
80             ArrayList<ItemInfo> addAnimated) {
81         boolean add1 = handleItemsAdded(addNotAnimated);
82         boolean add2 = handleItemsAdded(addAnimated);
83         if (add1 || add2) {
84             commitItemsToUI();
85         }
86     }
87 
88     @Override
bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons)89     public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) {
90         if (handleItemsAdded(shortcuts)) {
91             commitItemsToUI();
92         }
93     }
94 
handleItemsAdded(List<ItemInfo> items)95     private boolean handleItemsAdded(List<ItemInfo> items) {
96         boolean modified = false;
97         for (ItemInfo item : items) {
98             if (item.container == Favorites.CONTAINER_HOTSEAT) {
99                 mHotseatItems.put(item.screenId, item);
100                 modified = true;
101             }
102         }
103         return modified;
104     }
105 
106 
107     @Override
bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated)108     public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
109         updateWorkspaceItems(updated, mContext);
110     }
111 
112     @Override
bindRestoreItemsChange(HashSet<ItemInfo> updates)113     public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
114         updateRestoreItems(updates, mContext);
115     }
116 
117     @Override
mapOverItems(ItemOperator op)118     public void mapOverItems(ItemOperator op) {
119         final int itemCount = mContainer.getChildCount();
120         for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
121             View item = mContainer.getChildAt(itemIdx);
122             if (op.evaluate((ItemInfo) item.getTag(), item)) {
123                 return;
124             }
125         }
126     }
127 
128     @Override
bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher)129     public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) {
130         if (handleItemsRemoved(matcher)) {
131             commitItemsToUI();
132         }
133     }
134 
handleItemsRemoved(ItemInfoMatcher matcher)135     private boolean handleItemsRemoved(ItemInfoMatcher matcher) {
136         boolean modified = false;
137         for (int i = mHotseatItems.size() - 1; i >= 0; i--) {
138             if (matcher.matchesInfo(mHotseatItems.valueAt(i))) {
139                 modified = true;
140                 mHotseatItems.removeAt(i);
141             }
142         }
143         return modified;
144     }
145 
146     @Override
bindItemsModified(List<ItemInfo> items)147     public void bindItemsModified(List<ItemInfo> items) {
148         boolean removed = handleItemsRemoved(ItemInfoMatcher.ofItems(items));
149         boolean added = handleItemsAdded(items);
150         if (removed || added) {
151             commitItemsToUI();
152         }
153     }
154 
155     @Override
bindExtraContainerItems(FixedContainerItems item)156     public void bindExtraContainerItems(FixedContainerItems item) {
157         if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
158             mPredictedItems = item.items;
159             commitItemsToUI();
160         }
161     }
162 
commitItemsToUI()163     private void commitItemsToUI() {
164         if (mBindInProgress) {
165             return;
166         }
167 
168         ItemInfo[] hotseatItemInfos =
169                 new ItemInfo[mContext.getDeviceProfile().numShownHotseatIcons];
170         int predictionSize = mPredictedItems.size();
171         int predictionNextIndex = 0;
172 
173         boolean isHotseatEmpty = true;
174         for (int i = 0; i < hotseatItemInfos.length; i++) {
175             hotseatItemInfos[i] = mHotseatItems.get(i);
176             if (hotseatItemInfos[i] == null && predictionNextIndex < predictionSize) {
177                 hotseatItemInfos[i] = mPredictedItems.get(predictionNextIndex);
178                 hotseatItemInfos[i].screenId = i;
179                 predictionNextIndex++;
180             }
181             if (hotseatItemInfos[i] != null) {
182                 isHotseatEmpty = false;
183             }
184         }
185         mContainer.updateHotseatItems(hotseatItemInfos);
186 
187         final boolean finalIsHotseatEmpty = isHotseatEmpty;
188         mControllers.runAfterInit(() -> {
189             mControllers.taskbarStashController.updateStateForFlag(
190                     TaskbarStashController.FLAG_STASHED_IN_APP_EMPTY, finalIsHotseatEmpty);
191             mControllers.taskbarStashController.applyState();
192         });
193     }
194 
195     @Override
bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy)196     public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
197         mControllers.taskbarPopupController.setDeepShortcutMap(deepShortcutMapCopy);
198     }
199 }
200