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 androidx.annotation.NonNull; 22 import androidx.annotation.UiThread; 23 24 import com.android.launcher3.LauncherSettings.Favorites; 25 import com.android.launcher3.model.BgDataModel; 26 import com.android.launcher3.model.BgDataModel.FixedContainerItems; 27 import com.android.launcher3.model.data.AppInfo; 28 import com.android.launcher3.model.data.ItemInfo; 29 import com.android.launcher3.util.ComponentKey; 30 import com.android.launcher3.util.IntArray; 31 import com.android.launcher3.util.IntSet; 32 import com.android.launcher3.util.ItemInfoMatcher; 33 import com.android.launcher3.util.LauncherBindableItemsContainer; 34 import com.android.launcher3.util.PackageUserKey; 35 import com.android.launcher3.util.Preconditions; 36 import com.android.quickstep.util.GroupTask; 37 38 import java.io.PrintWriter; 39 import java.util.ArrayList; 40 import java.util.Collections; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.function.Predicate; 46 47 /** 48 * Launcher model Callbacks for rendering taskbar. 49 */ 50 public class TaskbarModelCallbacks implements 51 BgDataModel.Callbacks, LauncherBindableItemsContainer { 52 53 private final SparseArray<ItemInfo> mHotseatItems = new SparseArray<>(); 54 private List<ItemInfo> mPredictedItems = Collections.emptyList(); 55 56 private final TaskbarActivityContext mContext; 57 private final TaskbarView mContainer; 58 59 // Initialized in init. 60 protected TaskbarControllers mControllers; 61 62 // Used to defer any UI updates during the SUW unstash animation. 63 private boolean mDeferUpdatesForSUW; 64 private Runnable mDeferredUpdates; 65 private boolean mBindingItems = false; 66 TaskbarModelCallbacks( TaskbarActivityContext context, TaskbarView container)67 public TaskbarModelCallbacks( 68 TaskbarActivityContext context, TaskbarView container) { 69 mContext = context; 70 mContainer = container; 71 } 72 init(TaskbarControllers controllers)73 public void init(TaskbarControllers controllers) { 74 mControllers = controllers; 75 } 76 77 @Override startBinding()78 public void startBinding() { 79 mBindingItems = true; 80 mHotseatItems.clear(); 81 mPredictedItems = Collections.emptyList(); 82 } 83 84 @Override finishBindingItems(IntSet pagesBoundFirst)85 public void finishBindingItems(IntSet pagesBoundFirst) { 86 mBindingItems = false; 87 commitItemsToUI(); 88 } 89 90 @Override bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated)91 public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, 92 ArrayList<ItemInfo> addAnimated) { 93 boolean add1 = handleItemsAdded(addNotAnimated); 94 boolean add2 = handleItemsAdded(addAnimated); 95 if (add1 || add2) { 96 commitItemsToUI(); 97 } 98 } 99 100 @Override bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons)101 public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { 102 if (handleItemsAdded(shortcuts)) { 103 commitItemsToUI(); 104 } 105 } 106 handleItemsAdded(List<ItemInfo> items)107 private boolean handleItemsAdded(List<ItemInfo> items) { 108 boolean modified = false; 109 for (ItemInfo item : items) { 110 if (item.container == Favorites.CONTAINER_HOTSEAT) { 111 mHotseatItems.put(item.screenId, item); 112 modified = true; 113 } 114 } 115 return modified; 116 } 117 118 @Override bindItemsUpdated(Set<ItemInfo> updates)119 public void bindItemsUpdated(Set<ItemInfo> updates) { 120 updateContainerItems(updates, mContext); 121 } 122 123 @Override mapOverItems(@onNull ItemOperator op)124 public View mapOverItems(@NonNull ItemOperator op) { 125 final int itemCount = mContainer.getChildCount(); 126 for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) { 127 View item = mContainer.getChildAt(itemIdx); 128 if (item.getTag() instanceof ItemInfo itemInfo && op.evaluate(itemInfo, item)) { 129 return item; 130 } 131 } 132 return null; 133 } 134 135 @Override bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher)136 public void bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher) { 137 if (handleItemsRemoved(matcher)) { 138 commitItemsToUI(); 139 } 140 } 141 handleItemsRemoved(Predicate<ItemInfo> matcher)142 private boolean handleItemsRemoved(Predicate<ItemInfo> matcher) { 143 boolean modified = false; 144 for (int i = mHotseatItems.size() - 1; i >= 0; i--) { 145 if (matcher.test(mHotseatItems.valueAt(i))) { 146 modified = true; 147 mHotseatItems.removeAt(i); 148 } 149 } 150 return modified; 151 } 152 153 @Override bindItemsModified(List<ItemInfo> items)154 public void bindItemsModified(List<ItemInfo> items) { 155 boolean removed = handleItemsRemoved(ItemInfoMatcher.ofItems(items)); 156 boolean added = handleItemsAdded(items); 157 if (removed || added) { 158 commitItemsToUI(); 159 } 160 } 161 162 @Override bindExtraContainerItems(FixedContainerItems item)163 public void bindExtraContainerItems(FixedContainerItems item) { 164 if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) { 165 mPredictedItems = item.items; 166 commitItemsToUI(); 167 } else if (item.containerId == Favorites.CONTAINER_PREDICTION) { 168 mControllers.taskbarAllAppsController.setPredictedApps(item.items); 169 } 170 } 171 commitItemsToUI()172 private void commitItemsToUI() { 173 if (mBindingItems) { 174 return; 175 } 176 177 ItemInfo[] hotseatItemInfos = 178 new ItemInfo[mContext.getDeviceProfile().numShownHotseatIcons]; 179 int predictionSize = mPredictedItems.size(); 180 int predictionNextIndex = 0; 181 182 for (int i = 0; i < hotseatItemInfos.length; i++) { 183 hotseatItemInfos[i] = mHotseatItems.get(i); 184 if (hotseatItemInfos[i] == null && predictionNextIndex < predictionSize) { 185 hotseatItemInfos[i] = mPredictedItems.get(predictionNextIndex); 186 hotseatItemInfos[i].screenId = i; 187 predictionNextIndex++; 188 } 189 } 190 191 final TaskbarRecentAppsController recentAppsController = 192 mControllers.taskbarRecentAppsController; 193 hotseatItemInfos = recentAppsController.updateHotseatItemInfos(hotseatItemInfos); 194 195 if (mDeferUpdatesForSUW) { 196 ItemInfo[] finalHotseatItemInfos = hotseatItemInfos; 197 mDeferredUpdates = () -> 198 commitHotseatItemUpdates(finalHotseatItemInfos, 199 recentAppsController.getShownTasks()); 200 } else { 201 commitHotseatItemUpdates(hotseatItemInfos, recentAppsController.getShownTasks()); 202 } 203 } 204 commitHotseatItemUpdates( ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks)205 private void commitHotseatItemUpdates( 206 ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) { 207 mContainer.updateItems(hotseatItemInfos, recentTasks); 208 mControllers.taskbarViewController.updateIconViewsRunningStates(); 209 mControllers.taskbarPopupController.setHotseatInfosList(mHotseatItems); 210 } 211 212 /** 213 * This is used to defer UI updates after SUW builds the unstash animation. 214 * @param defer if true, defers updates to the UI 215 * if false, posts updates (if any) to the UI 216 */ setDeferUpdatesForSUW(boolean defer)217 public void setDeferUpdatesForSUW(boolean defer) { 218 mDeferUpdatesForSUW = defer; 219 220 if (!mDeferUpdatesForSUW) { 221 if (mDeferredUpdates != null) { 222 mContainer.post(mDeferredUpdates); 223 mDeferredUpdates = null; 224 } 225 } 226 } 227 228 /** Called when there's a change in running apps to update the UI. */ commitRunningAppsToUI()229 public void commitRunningAppsToUI() { 230 commitItemsToUI(); 231 } 232 233 @Override bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy)234 public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) { 235 mControllers.taskbarPopupController.setDeepShortcutMap(deepShortcutMapCopy); 236 } 237 238 @UiThread 239 @Override bindAllApplications(AppInfo[] apps, int flags, Map<PackageUserKey, Integer> packageUserKeytoUidMap)240 public void bindAllApplications(AppInfo[] apps, int flags, 241 Map<PackageUserKey, Integer> packageUserKeytoUidMap) { 242 Preconditions.assertUIThread(); 243 mControllers.taskbarAllAppsController.setApps(apps, flags, packageUserKeytoUidMap); 244 mControllers.taskbarPopupController.setApps(apps); 245 } 246 dumpLogs(String prefix, PrintWriter pw)247 protected void dumpLogs(String prefix, PrintWriter pw) { 248 pw.println(prefix + "TaskbarModelCallbacks:"); 249 250 pw.println(String.format("%s\thotseat items count=%s", prefix, mHotseatItems.size())); 251 if (mPredictedItems != null) { 252 pw.println( 253 String.format("%s\tpredicted items count=%s", prefix, mPredictedItems.size())); 254 } 255 pw.println(String.format("%s\tmDeferUpdatesForSUW=%b", prefix, mDeferUpdatesForSUW)); 256 pw.println(String.format("%s\tupdates pending=%b", prefix, (mDeferredUpdates != null))); 257 } 258 } 259