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.AppInfo.COMPONENT_KEY_COMPARATOR;
19 import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
20 
21 import android.view.View;
22 import android.view.ViewGroup;
23 
24 import com.android.launcher3.AppInfo;
25 import com.android.launcher3.BubbleTextView;
26 import com.android.launcher3.ItemInfo;
27 import com.android.launcher3.PromiseAppInfo;
28 import com.android.launcher3.util.ComponentKey;
29 import com.android.launcher3.util.PackageUserKey;
30 
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.function.Consumer;
35 import java.util.function.Predicate;
36 
37 /**
38  * A utility class to maintain the collection of all apps.
39  */
40 public class AllAppsStore {
41 
42     // Defer updates flag used to defer all apps updates to the next draw.
43     public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0;
44     // Defer updates flag used to defer all apps updates by a test's request.
45     public static final int DEFER_UPDATES_TEST = 1 << 1;
46 
47     private PackageUserKey mTempKey = new PackageUserKey(null, null);
48     private AppInfo mTempInfo = new AppInfo();
49 
50     private AppInfo[] mApps = EMPTY_ARRAY;
51 
52     private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
53     private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
54 
55     private int mDeferUpdatesFlags = 0;
56     private boolean mUpdatePending = false;
57 
getApps()58     public AppInfo[] getApps() {
59         return mApps;
60     }
61 
62     /**
63      * Sets the current set of apps.
64      */
setApps(AppInfo[] apps)65     public void setApps(AppInfo[] apps) {
66         mApps = apps;
67         notifyUpdate();
68     }
69 
getApp(ComponentKey key)70     public AppInfo getApp(ComponentKey key) {
71         mTempInfo.componentName = key.componentName;
72         mTempInfo.user = key.user;
73         int index = Arrays.binarySearch(mApps, mTempInfo, COMPONENT_KEY_COMPARATOR);
74         return index < 0 ? null : mApps[index];
75     }
76 
enableDeferUpdates(int flag)77     public void enableDeferUpdates(int flag) {
78         mDeferUpdatesFlags |= flag;
79     }
80 
disableDeferUpdates(int flag)81     public void disableDeferUpdates(int flag) {
82         mDeferUpdatesFlags &= ~flag;
83         if (mDeferUpdatesFlags == 0 && mUpdatePending) {
84             notifyUpdate();
85             mUpdatePending = false;
86         }
87     }
88 
disableDeferUpdatesSilently(int flag)89     public void disableDeferUpdatesSilently(int flag) {
90         mDeferUpdatesFlags &= ~flag;
91     }
92 
getDeferUpdatesFlags()93     public int getDeferUpdatesFlags() {
94         return mDeferUpdatesFlags;
95     }
96 
notifyUpdate()97     private void notifyUpdate() {
98         if (mDeferUpdatesFlags != 0) {
99             mUpdatePending = true;
100             return;
101         }
102         int count = mUpdateListeners.size();
103         for (int i = 0; i < count; i++) {
104             mUpdateListeners.get(i).onAppsUpdated();
105         }
106     }
107 
addUpdateListener(OnUpdateListener listener)108     public void addUpdateListener(OnUpdateListener listener) {
109         mUpdateListeners.add(listener);
110     }
111 
removeUpdateListener(OnUpdateListener listener)112     public void removeUpdateListener(OnUpdateListener listener) {
113         mUpdateListeners.remove(listener);
114     }
115 
registerIconContainer(ViewGroup container)116     public void registerIconContainer(ViewGroup container) {
117         if (container != null) {
118             mIconContainers.add(container);
119         }
120     }
121 
unregisterIconContainer(ViewGroup container)122     public void unregisterIconContainer(ViewGroup container) {
123         mIconContainers.remove(container);
124     }
125 
updateNotificationDots(Predicate<PackageUserKey> updatedDots)126     public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
127         updateAllIcons((child) -> {
128             if (child.getTag() instanceof ItemInfo) {
129                 ItemInfo info = (ItemInfo) child.getTag();
130                 if (mTempKey.updateFromItemInfo(info) && updatedDots.test(mTempKey)) {
131                     child.applyDotState(info, true /* animate */);
132                 }
133             }
134         });
135     }
136 
updatePromiseAppProgress(PromiseAppInfo app)137     public void updatePromiseAppProgress(PromiseAppInfo app) {
138         updateAllIcons((child) -> {
139             if (child.getTag() == app) {
140                 child.applyProgressLevel(app.level);
141             }
142         });
143     }
144 
updateAllIcons(Consumer<BubbleTextView> action)145     private void updateAllIcons(Consumer<BubbleTextView> action) {
146         for (int i = mIconContainers.size() - 1; i >= 0; i--) {
147             ViewGroup parent = mIconContainers.get(i);
148             int childCount = parent.getChildCount();
149 
150             for (int j = 0; j < childCount; j++) {
151                 View child = parent.getChildAt(j);
152                 if (child instanceof BubbleTextView) {
153                     action.accept((BubbleTextView) child);
154                 }
155             }
156         }
157     }
158 
159     public interface OnUpdateListener {
onAppsUpdated()160         void onAppsUpdated();
161     }
162 }
163