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