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