1 /* 2 * Copyright (C) 2018 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.allapps; 17 18 import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_RV_PREINFLATION; 19 import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR; 20 import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; 21 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK; 22 23 import android.content.Context; 24 import android.os.UserHandle; 25 import android.view.View; 26 import android.view.ViewGroup; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 31 import com.android.launcher3.BubbleTextView; 32 import com.android.launcher3.model.data.AppInfo; 33 import com.android.launcher3.model.data.ItemInfo; 34 import com.android.launcher3.recyclerview.AllAppsRecyclerViewPool; 35 import com.android.launcher3.util.ComponentKey; 36 import com.android.launcher3.util.PackageUserKey; 37 import com.android.launcher3.views.ActivityContext; 38 39 import java.io.PrintWriter; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collections; 43 import java.util.Comparator; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.concurrent.CopyOnWriteArrayList; 47 import java.util.function.Consumer; 48 import java.util.function.Predicate; 49 50 /** 51 * A utility class to maintain the collection of all apps. 52 * 53 * @param <T> The type of the context. 54 */ 55 public class AllAppsStore<T extends Context & ActivityContext> { 56 57 // Defer updates flag used to defer all apps updates to the next draw. 58 public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0; 59 // Defer updates flag used to defer all apps updates by a test's request. 60 public static final int DEFER_UPDATES_TEST = 1 << 1; 61 62 private PackageUserKey mTempKey = new PackageUserKey(null, null); 63 private AppInfo mTempInfo = new AppInfo(); 64 65 private @NonNull AppInfo[] mApps = EMPTY_ARRAY; 66 67 private final List<OnUpdateListener> mUpdateListeners = new CopyOnWriteArrayList<>(); 68 private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>(); 69 private Map<PackageUserKey, Integer> mPackageUserKeytoUidMap = Collections.emptyMap(); 70 private int mModelFlags; 71 private int mDeferUpdatesFlags = 0; 72 private boolean mUpdatePending = false; 73 private final AllAppsRecyclerViewPool mAllAppsRecyclerViewPool = new AllAppsRecyclerViewPool(); 74 75 private final T mContext; 76 getApps()77 public AppInfo[] getApps() { 78 return mApps; 79 } 80 AllAppsStore(@onNull T context)81 public AllAppsStore(@NonNull T context) { 82 mContext = context; 83 } 84 85 /** 86 * Calling {@link #setApps(AppInfo[], int, Map, boolean)} with shouldPreinflate set to 87 * {@code true}. This method should be called in launcher (not for taskbar). 88 */ setApps(@ullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map)89 public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map) { 90 setApps(apps, flags, map, /* shouldPreinflate= */ true); 91 } 92 93 /** 94 * Sets the current set of apps and sets mapping for {@link PackageUserKey} to Uid for 95 * the current set of apps. 96 * 97 * <p> Note that shouldPreinflate param should be set to {@code false} for taskbar, because 98 * this method is too late to preinflate all apps, as user will open all apps in the frame 99 * 100 * <p>Param: apps are required to be sorted using the comparator COMPONENT_KEY_COMPARATOR 101 * in order to enable binary search on the mApps store 102 */ setApps(@ullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map, boolean shouldPreinflate)103 public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map, 104 boolean shouldPreinflate) { 105 mApps = apps == null ? EMPTY_ARRAY : apps; 106 mModelFlags = flags; 107 notifyUpdate(); 108 mPackageUserKeytoUidMap = map; 109 // Preinflate all apps RV when apps has changed, which can happen after unlocking screen, 110 // rotating screen, or downloading/upgrading apps. 111 if (shouldPreinflate && ENABLE_ALL_APPS_RV_PREINFLATION.get()) { 112 mAllAppsRecyclerViewPool.preInflateAllAppsViewHolders(mContext); 113 } 114 } 115 getRecyclerViewPool()116 AllAppsRecyclerViewPool getRecyclerViewPool() { 117 return mAllAppsRecyclerViewPool; 118 } 119 120 /** 121 * Look up for Uid using package name and user handle for the current set of apps. 122 */ lookUpForUid(String packageName, UserHandle user)123 public int lookUpForUid(String packageName, UserHandle user) { 124 return mPackageUserKeytoUidMap.getOrDefault(new PackageUserKey(packageName, user), -1); 125 } 126 127 /** 128 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_ENABLED 129 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_HAS_SHORTCUT_PERMISSION 130 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION 131 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_WORK_PROFILE_QUIET_MODE_ENABLED 132 * @see 133 * com.android.launcher3.model.BgDataModel.Callbacks#FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED 134 */ hasModelFlag(int mask)135 public boolean hasModelFlag(int mask) { 136 return (mModelFlags & mask) != 0; 137 } 138 139 /** 140 * Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise 141 * null. 142 * 143 * Uses {@link AppInfo#COMPONENT_KEY_COMPARATOR} as a default comparator. 144 */ 145 @Nullable getApp(ComponentKey key)146 public AppInfo getApp(ComponentKey key) { 147 return getApp(key, COMPONENT_KEY_COMPARATOR); 148 } 149 150 /** 151 * Generic version of {@link #getApp(ComponentKey)} that allows comparator to be specified. 152 */ 153 @Nullable getApp(ComponentKey key, Comparator<AppInfo> comparator)154 public AppInfo getApp(ComponentKey key, Comparator<AppInfo> comparator) { 155 mTempInfo.componentName = key.componentName; 156 mTempInfo.user = key.user; 157 int index = Arrays.binarySearch(mApps, mTempInfo, comparator); 158 return index < 0 ? null : mApps[index]; 159 } 160 enableDeferUpdates(int flag)161 public void enableDeferUpdates(int flag) { 162 mDeferUpdatesFlags |= flag; 163 } 164 disableDeferUpdates(int flag)165 public void disableDeferUpdates(int flag) { 166 mDeferUpdatesFlags &= ~flag; 167 if (mDeferUpdatesFlags == 0 && mUpdatePending) { 168 notifyUpdate(); 169 mUpdatePending = false; 170 } 171 } 172 disableDeferUpdatesSilently(int flag)173 public void disableDeferUpdatesSilently(int flag) { 174 mDeferUpdatesFlags &= ~flag; 175 } 176 getDeferUpdatesFlags()177 public int getDeferUpdatesFlags() { 178 return mDeferUpdatesFlags; 179 } 180 notifyUpdate()181 private void notifyUpdate() { 182 if (mDeferUpdatesFlags != 0) { 183 mUpdatePending = true; 184 return; 185 } 186 for (OnUpdateListener listener : mUpdateListeners) { 187 listener.onAppsUpdated(); 188 } 189 } 190 addUpdateListener(OnUpdateListener listener)191 public void addUpdateListener(OnUpdateListener listener) { 192 mUpdateListeners.add(listener); 193 } 194 removeUpdateListener(OnUpdateListener listener)195 public void removeUpdateListener(OnUpdateListener listener) { 196 mUpdateListeners.remove(listener); 197 } 198 registerIconContainer(ViewGroup container)199 public void registerIconContainer(ViewGroup container) { 200 if (container != null && !mIconContainers.contains(container)) { 201 mIconContainers.add(container); 202 } 203 } 204 unregisterIconContainer(ViewGroup container)205 public void unregisterIconContainer(ViewGroup container) { 206 mIconContainers.remove(container); 207 } 208 updateNotificationDots(Predicate<PackageUserKey> updatedDots)209 public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) { 210 updateAllIcons((child) -> { 211 if (child.getTag() instanceof ItemInfo) { 212 ItemInfo info = (ItemInfo) child.getTag(); 213 if (mTempKey.updateFromItemInfo(info) && updatedDots.test(mTempKey)) { 214 child.applyDotState(info, true /* animate */); 215 } 216 } 217 }); 218 } 219 220 /** 221 * Sets the AppInfo's associated icon's progress bar. 222 * 223 * If this app is installed and supports incremental downloads, the progress bar will be updated 224 * the app's total download progress. Otherwise, the progress bar will be updated to the app's 225 * installation progress. 226 * 227 * If this app is fully downloaded, the app icon will be reapplied. 228 */ updateProgressBar(AppInfo app)229 public void updateProgressBar(AppInfo app) { 230 updateAllIcons((child) -> { 231 if (child.getTag() == app) { 232 if ((app.runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) == 0) { 233 child.applyFromApplicationInfo(app); 234 } else { 235 child.applyProgressLevel(); 236 } 237 } 238 }); 239 } 240 updateAllIcons(Consumer<BubbleTextView> action)241 private void updateAllIcons(Consumer<BubbleTextView> action) { 242 for (int i = mIconContainers.size() - 1; i >= 0; i--) { 243 ViewGroup parent = mIconContainers.get(i); 244 int childCount = parent.getChildCount(); 245 246 for (int j = 0; j < childCount; j++) { 247 View child = parent.getChildAt(j); 248 if (child instanceof BubbleTextView) { 249 action.accept((BubbleTextView) child); 250 } 251 } 252 } 253 } 254 255 public interface OnUpdateListener { onAppsUpdated()256 void onAppsUpdated(); 257 } 258 259 /** Generate a dumpsys for each app package name and position in the apps list */ dump(String prefix, PrintWriter writer)260 public void dump(String prefix, PrintWriter writer) { 261 writer.println(prefix + "\tAllAppsStore Apps[] size: " + mApps.length); 262 for (int i = 0; i < mApps.length; i++) { 263 writer.println(String.format("%s\tPackage index and name: %d/%s", prefix, i, 264 mApps[i].componentName.getPackageName())); 265 } 266 } 267 } 268