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.model.data.AppInfo.COMPONENT_KEY_COMPARATOR; 19 import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; 20 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK; 21 import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; 22 23 import android.util.Log; 24 import android.view.View; 25 import android.view.ViewGroup; 26 27 import androidx.annotation.Nullable; 28 29 import com.android.launcher3.BubbleTextView; 30 import com.android.launcher3.model.data.AppInfo; 31 import com.android.launcher3.model.data.ItemInfo; 32 import com.android.launcher3.testing.shared.TestProtocol; 33 import com.android.launcher3.util.ComponentKey; 34 import com.android.launcher3.util.PackageUserKey; 35 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 import java.util.concurrent.CopyOnWriteArrayList; 40 import java.util.function.Consumer; 41 import java.util.function.Predicate; 42 43 /** 44 * A utility class to maintain the collection of all apps. 45 */ 46 public class AllAppsStore { 47 48 // Defer updates flag used to defer all apps updates to the next draw. 49 public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0; 50 // Defer updates flag used to defer all apps updates by a test's request. 51 public static final int DEFER_UPDATES_TEST = 1 << 1; 52 53 private PackageUserKey mTempKey = new PackageUserKey(null, null); 54 private AppInfo mTempInfo = new AppInfo(); 55 56 private AppInfo[] mApps = EMPTY_ARRAY; 57 58 private final List<OnUpdateListener> mUpdateListeners = new CopyOnWriteArrayList<>(); 59 private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>(); 60 private int mModelFlags; 61 62 private int mDeferUpdatesFlags = 0; 63 private boolean mUpdatePending = false; 64 getApps()65 public AppInfo[] getApps() { 66 return mApps; 67 } 68 69 /** 70 * Sets the current set of apps. 71 */ setApps(AppInfo[] apps, int flags)72 public void setApps(AppInfo[] apps, int flags) { 73 mApps = apps; 74 mModelFlags = flags; 75 notifyUpdate(); 76 } 77 78 /** 79 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_ENABLED 80 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_HAS_SHORTCUT_PERMISSION 81 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION 82 */ hasModelFlag(int mask)83 public boolean hasModelFlag(int mask) { 84 return (mModelFlags & mask) != 0; 85 } 86 87 /** 88 * Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise 89 * null. 90 */ 91 @Nullable getApp(ComponentKey key)92 public AppInfo getApp(ComponentKey key) { 93 mTempInfo.componentName = key.componentName; 94 mTempInfo.user = key.user; 95 int index = Arrays.binarySearch(mApps, mTempInfo, COMPONENT_KEY_COMPARATOR); 96 return index < 0 ? null : mApps[index]; 97 } 98 enableDeferUpdates(int flag)99 public void enableDeferUpdates(int flag) { 100 mDeferUpdatesFlags |= flag; 101 } 102 disableDeferUpdates(int flag)103 public void disableDeferUpdates(int flag) { 104 mDeferUpdatesFlags &= ~flag; 105 if (mDeferUpdatesFlags == 0 && mUpdatePending) { 106 notifyUpdate(); 107 mUpdatePending = false; 108 } 109 } 110 disableDeferUpdatesSilently(int flag)111 public void disableDeferUpdatesSilently(int flag) { 112 mDeferUpdatesFlags &= ~flag; 113 } 114 getDeferUpdatesFlags()115 public int getDeferUpdatesFlags() { 116 return mDeferUpdatesFlags; 117 } 118 notifyUpdate()119 private void notifyUpdate() { 120 if (mDeferUpdatesFlags != 0) { 121 mUpdatePending = true; 122 return; 123 } 124 for (OnUpdateListener listener : mUpdateListeners) { 125 if (TestProtocol.sDebugTracing) { 126 Log.d(WORK_TAB_MISSING, "AllAppsStore#notifyUpdate listener: " + listener); 127 } 128 listener.onAppsUpdated(); 129 } 130 } 131 addUpdateListener(OnUpdateListener listener)132 public void addUpdateListener(OnUpdateListener listener) { 133 mUpdateListeners.add(listener); 134 } 135 removeUpdateListener(OnUpdateListener listener)136 public void removeUpdateListener(OnUpdateListener listener) { 137 mUpdateListeners.remove(listener); 138 } 139 registerIconContainer(ViewGroup container)140 public void registerIconContainer(ViewGroup container) { 141 if (container != null && !mIconContainers.contains(container)) { 142 mIconContainers.add(container); 143 } 144 } 145 unregisterIconContainer(ViewGroup container)146 public void unregisterIconContainer(ViewGroup container) { 147 mIconContainers.remove(container); 148 } 149 updateNotificationDots(Predicate<PackageUserKey> updatedDots)150 public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) { 151 updateAllIcons((child) -> { 152 if (child.getTag() instanceof ItemInfo) { 153 ItemInfo info = (ItemInfo) child.getTag(); 154 if (mTempKey.updateFromItemInfo(info) && updatedDots.test(mTempKey)) { 155 child.applyDotState(info, true /* animate */); 156 } 157 } 158 }); 159 } 160 161 /** 162 * Sets the AppInfo's associated icon's progress bar. 163 * 164 * If this app is installed and supports incremental downloads, the progress bar will be updated 165 * the app's total download progress. Otherwise, the progress bar will be updated to the app's 166 * installation progress. 167 * 168 * If this app is fully downloaded, the app icon will be reapplied. 169 */ updateProgressBar(AppInfo app)170 public void updateProgressBar(AppInfo app) { 171 updateAllIcons((child) -> { 172 if (child.getTag() == app) { 173 if ((app.runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) == 0) { 174 child.applyFromApplicationInfo(app); 175 } else { 176 child.applyProgressLevel(); 177 } 178 } 179 }); 180 } 181 updateAllIcons(Consumer<BubbleTextView> action)182 private void updateAllIcons(Consumer<BubbleTextView> action) { 183 for (int i = mIconContainers.size() - 1; i >= 0; i--) { 184 ViewGroup parent = mIconContainers.get(i); 185 int childCount = parent.getChildCount(); 186 187 for (int j = 0; j < childCount; j++) { 188 View child = parent.getChildAt(j); 189 if (child instanceof BubbleTextView) { 190 action.accept((BubbleTextView) child); 191 } 192 } 193 } 194 } 195 196 public interface OnUpdateListener { onAppsUpdated()197 void onAppsUpdated(); 198 } 199 } 200