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 import androidx.recyclerview.widget.RecyclerView.RecycledViewPool; 31 32 import com.android.launcher3.BubbleTextView; 33 import com.android.launcher3.model.data.AppInfo; 34 import com.android.launcher3.model.data.ItemInfo; 35 import com.android.launcher3.recyclerview.AllAppsRecyclerViewPool; 36 import com.android.launcher3.util.ComponentKey; 37 import com.android.launcher3.util.PackageUserKey; 38 import com.android.launcher3.views.ActivityContext; 39 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collections; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.concurrent.CopyOnWriteArrayList; 46 import java.util.function.Consumer; 47 import java.util.function.Predicate; 48 49 /** 50 * A utility class to maintain the collection of all apps. 51 * 52 * @param <T> The type of the context. 53 */ 54 public class AllAppsStore<T extends Context & ActivityContext> { 55 56 // Defer updates flag used to defer all apps updates to the next draw. 57 public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0; 58 // Defer updates flag used to defer all apps updates by a test's request. 59 public static final int DEFER_UPDATES_TEST = 1 << 1; 60 61 private PackageUserKey mTempKey = new PackageUserKey(null, null); 62 private AppInfo mTempInfo = new AppInfo(); 63 64 private @NonNull AppInfo[] mApps = EMPTY_ARRAY; 65 66 private final List<OnUpdateListener> mUpdateListeners = new CopyOnWriteArrayList<>(); 67 private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>(); 68 private Map<PackageUserKey, Integer> mPackageUserKeytoUidMap = Collections.emptyMap(); 69 private int mModelFlags; 70 private int mDeferUpdatesFlags = 0; 71 private boolean mUpdatePending = false; 72 private final AllAppsRecyclerViewPool mAllAppsRecyclerViewPool = new AllAppsRecyclerViewPool(); 73 74 private final T mContext; 75 getApps()76 public AppInfo[] getApps() { 77 return mApps; 78 } 79 AllAppsStore(@onNull T context)80 public AllAppsStore(@NonNull T context) { 81 mContext = context; 82 } 83 84 /** 85 * Sets the current set of apps and sets mapping for {@link PackageUserKey} to Uid for 86 * the current set of apps. 87 */ setApps(@ullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map)88 public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map) { 89 mApps = apps == null ? EMPTY_ARRAY : apps; 90 mModelFlags = flags; 91 notifyUpdate(); 92 mPackageUserKeytoUidMap = map; 93 // Preinflate all apps RV when apps has changed, which can happen after unlocking screen, 94 // rotating screen, or downloading/upgrading apps. 95 if (ENABLE_ALL_APPS_RV_PREINFLATION.get()) { 96 mAllAppsRecyclerViewPool.preInflateAllAppsViewHolders(mContext); 97 } 98 } 99 getRecyclerViewPool()100 RecycledViewPool getRecyclerViewPool() { 101 return mAllAppsRecyclerViewPool; 102 } 103 104 /** 105 * Look up for Uid using package name and user handle for the current set of apps. 106 */ lookUpForUid(String packageName, UserHandle user)107 public int lookUpForUid(String packageName, UserHandle user) { 108 return mPackageUserKeytoUidMap.getOrDefault(new PackageUserKey(packageName, user), -1); 109 } 110 111 /** 112 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_ENABLED 113 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_HAS_SHORTCUT_PERMISSION 114 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION 115 */ hasModelFlag(int mask)116 public boolean hasModelFlag(int mask) { 117 return (mModelFlags & mask) != 0; 118 } 119 120 /** 121 * Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise 122 * null. 123 */ 124 @Nullable getApp(ComponentKey key)125 public AppInfo getApp(ComponentKey key) { 126 mTempInfo.componentName = key.componentName; 127 mTempInfo.user = key.user; 128 int index = Arrays.binarySearch(mApps, mTempInfo, COMPONENT_KEY_COMPARATOR); 129 return index < 0 ? null : mApps[index]; 130 } 131 enableDeferUpdates(int flag)132 public void enableDeferUpdates(int flag) { 133 mDeferUpdatesFlags |= flag; 134 } 135 disableDeferUpdates(int flag)136 public void disableDeferUpdates(int flag) { 137 mDeferUpdatesFlags &= ~flag; 138 if (mDeferUpdatesFlags == 0 && mUpdatePending) { 139 notifyUpdate(); 140 mUpdatePending = false; 141 } 142 } 143 disableDeferUpdatesSilently(int flag)144 public void disableDeferUpdatesSilently(int flag) { 145 mDeferUpdatesFlags &= ~flag; 146 } 147 getDeferUpdatesFlags()148 public int getDeferUpdatesFlags() { 149 return mDeferUpdatesFlags; 150 } 151 notifyUpdate()152 private void notifyUpdate() { 153 if (mDeferUpdatesFlags != 0) { 154 mUpdatePending = true; 155 return; 156 } 157 for (OnUpdateListener listener : mUpdateListeners) { 158 listener.onAppsUpdated(); 159 } 160 } 161 addUpdateListener(OnUpdateListener listener)162 public void addUpdateListener(OnUpdateListener listener) { 163 mUpdateListeners.add(listener); 164 } 165 removeUpdateListener(OnUpdateListener listener)166 public void removeUpdateListener(OnUpdateListener listener) { 167 mUpdateListeners.remove(listener); 168 } 169 registerIconContainer(ViewGroup container)170 public void registerIconContainer(ViewGroup container) { 171 if (container != null && !mIconContainers.contains(container)) { 172 mIconContainers.add(container); 173 } 174 } 175 unregisterIconContainer(ViewGroup container)176 public void unregisterIconContainer(ViewGroup container) { 177 mIconContainers.remove(container); 178 } 179 updateNotificationDots(Predicate<PackageUserKey> updatedDots)180 public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) { 181 updateAllIcons((child) -> { 182 if (child.getTag() instanceof ItemInfo) { 183 ItemInfo info = (ItemInfo) child.getTag(); 184 if (mTempKey.updateFromItemInfo(info) && updatedDots.test(mTempKey)) { 185 child.applyDotState(info, true /* animate */); 186 } 187 } 188 }); 189 } 190 191 /** 192 * Sets the AppInfo's associated icon's progress bar. 193 * 194 * If this app is installed and supports incremental downloads, the progress bar will be updated 195 * the app's total download progress. Otherwise, the progress bar will be updated to the app's 196 * installation progress. 197 * 198 * If this app is fully downloaded, the app icon will be reapplied. 199 */ updateProgressBar(AppInfo app)200 public void updateProgressBar(AppInfo app) { 201 updateAllIcons((child) -> { 202 if (child.getTag() == app) { 203 if ((app.runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) == 0) { 204 child.applyFromApplicationInfo(app); 205 } else { 206 child.applyProgressLevel(); 207 } 208 } 209 }); 210 } 211 updateAllIcons(Consumer<BubbleTextView> action)212 private void updateAllIcons(Consumer<BubbleTextView> action) { 213 for (int i = mIconContainers.size() - 1; i >= 0; i--) { 214 ViewGroup parent = mIconContainers.get(i); 215 int childCount = parent.getChildCount(); 216 217 for (int j = 0; j < childCount; j++) { 218 View child = parent.getChildAt(j); 219 if (child instanceof BubbleTextView) { 220 action.accept((BubbleTextView) child); 221 } 222 } 223 } 224 } 225 226 public interface OnUpdateListener { onAppsUpdated()227 void onAppsUpdated(); 228 } 229 } 230