1 /*
2  * Copyright (C) 2008 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 
17 package com.android.launcher3;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 
22 import com.android.launcher3.compat.LauncherActivityInfoCompat;
23 import com.android.launcher3.compat.LauncherAppsCompat;
24 import com.android.launcher3.compat.UserHandleCompat;
25 import com.android.launcher3.util.FlagOp;
26 import com.android.launcher3.util.StringFilter;
27 
28 import java.util.ArrayList;
29 import java.util.HashSet;
30 import java.util.List;
31 
32 
33 /**
34  * Stores the list of all applications for the all apps view.
35  */
36 class AllAppsList {
37     public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
38 
39     /** The list off all apps. */
40     public ArrayList<AppInfo> data =
41             new ArrayList<AppInfo>(DEFAULT_APPLICATIONS_NUMBER);
42     /** The list of apps that have been added since the last notify() call. */
43     public ArrayList<AppInfo> added =
44             new ArrayList<AppInfo>(DEFAULT_APPLICATIONS_NUMBER);
45     /** The list of apps that have been removed since the last notify() call. */
46     public ArrayList<AppInfo> removed = new ArrayList<AppInfo>();
47     /** The list of apps that have been modified since the last notify() call. */
48     public ArrayList<AppInfo> modified = new ArrayList<AppInfo>();
49 
50     private IconCache mIconCache;
51 
52     private AppFilter mAppFilter;
53 
54     /**
55      * Boring constructor.
56      */
AllAppsList(IconCache iconCache, AppFilter appFilter)57     public AllAppsList(IconCache iconCache, AppFilter appFilter) {
58         mIconCache = iconCache;
59         mAppFilter = appFilter;
60     }
61 
62     /**
63      * Add the supplied ApplicationInfo objects to the list, and enqueue it into the
64      * list to broadcast when notify() is called.
65      *
66      * If the app is already in the list, doesn't add it.
67      */
add(AppInfo info)68     public void add(AppInfo info) {
69         if (mAppFilter != null && !mAppFilter.shouldShowApp(info.componentName)) {
70             return;
71         }
72         if (findActivity(data, info.componentName, info.user)) {
73             return;
74         }
75         data.add(info);
76         added.add(info);
77     }
78 
clear()79     public void clear() {
80         data.clear();
81         // TODO: do we clear these too?
82         added.clear();
83         removed.clear();
84         modified.clear();
85     }
86 
size()87     public int size() {
88         return data.size();
89     }
90 
get(int index)91     public AppInfo get(int index) {
92         return data.get(index);
93     }
94 
95     /**
96      * Add the icons for the supplied apk called packageName.
97      */
addPackage(Context context, String packageName, UserHandleCompat user)98     public void addPackage(Context context, String packageName, UserHandleCompat user) {
99         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
100         final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
101                 user);
102 
103         for (LauncherActivityInfoCompat info : matches) {
104             add(new AppInfo(context, info, user, mIconCache));
105         }
106     }
107 
108     /**
109      * Remove the apps for the given apk identified by packageName.
110      */
removePackage(String packageName, UserHandleCompat user)111     public void removePackage(String packageName, UserHandleCompat user) {
112         final List<AppInfo> data = this.data;
113         for (int i = data.size() - 1; i >= 0; i--) {
114             AppInfo info = data.get(i);
115             final ComponentName component = info.intent.getComponent();
116             if (info.user.equals(user) && packageName.equals(component.getPackageName())) {
117                 removed.add(info);
118                 data.remove(i);
119             }
120         }
121     }
122 
123     /**
124      * Updates the apps for the given packageName and user based on {@param op}.
125      */
updatePackageFlags(StringFilter pkgFilter, UserHandleCompat user, FlagOp op)126     public void updatePackageFlags(StringFilter pkgFilter, UserHandleCompat user, FlagOp op) {
127         final List<AppInfo> data = this.data;
128         for (int i = data.size() - 1; i >= 0; i--) {
129             AppInfo info = data.get(i);
130             final ComponentName component = info.intent.getComponent();
131             if (info.user.equals(user) && pkgFilter.matches(component.getPackageName())) {
132                 info.isDisabled = op.apply(info.isDisabled);
133                 modified.add(info);
134             }
135         }
136     }
137 
updateIconsAndLabels(HashSet<String> packages, UserHandleCompat user, ArrayList<AppInfo> outUpdates)138     public void updateIconsAndLabels(HashSet<String> packages, UserHandleCompat user,
139             ArrayList<AppInfo> outUpdates) {
140         for (AppInfo info : data) {
141             if (info.user.equals(user) && packages.contains(info.componentName.getPackageName())) {
142                 mIconCache.updateTitleAndIcon(info);
143                 outUpdates.add(info);
144             }
145         }
146     }
147 
148     /**
149      * Add and remove icons for this package which has been updated.
150      */
updatePackage(Context context, String packageName, UserHandleCompat user)151     public void updatePackage(Context context, String packageName, UserHandleCompat user) {
152         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
153         final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
154                 user);
155         if (matches.size() > 0) {
156             // Find disabled/removed activities and remove them from data and add them
157             // to the removed list.
158             for (int i = data.size() - 1; i >= 0; i--) {
159                 final AppInfo applicationInfo = data.get(i);
160                 final ComponentName component = applicationInfo.intent.getComponent();
161                 if (user.equals(applicationInfo.user)
162                         && packageName.equals(component.getPackageName())) {
163                     if (!findActivity(matches, component)) {
164                         removed.add(applicationInfo);
165                         data.remove(i);
166                     }
167                 }
168             }
169 
170             // Find enabled activities and add them to the adapter
171             // Also updates existing activities with new labels/icons
172             for (final LauncherActivityInfoCompat info : matches) {
173                 AppInfo applicationInfo = findApplicationInfoLocked(
174                         info.getComponentName().getPackageName(), user,
175                         info.getComponentName().getClassName());
176                 if (applicationInfo == null) {
177                     add(new AppInfo(context, info, user, mIconCache));
178                 } else {
179                     mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
180                     modified.add(applicationInfo);
181                 }
182             }
183         } else {
184             // Remove all data for this package.
185             for (int i = data.size() - 1; i >= 0; i--) {
186                 final AppInfo applicationInfo = data.get(i);
187                 final ComponentName component = applicationInfo.intent.getComponent();
188                 if (user.equals(applicationInfo.user)
189                         && packageName.equals(component.getPackageName())) {
190                     removed.add(applicationInfo);
191                     mIconCache.remove(component, user);
192                     data.remove(i);
193                 }
194             }
195         }
196     }
197 
198 
199     /**
200      * Returns whether <em>apps</em> contains <em>component</em>.
201      */
findActivity(List<LauncherActivityInfoCompat> apps, ComponentName component)202     private static boolean findActivity(List<LauncherActivityInfoCompat> apps,
203             ComponentName component) {
204         for (LauncherActivityInfoCompat info : apps) {
205             if (info.getComponentName().equals(component)) {
206                 return true;
207             }
208         }
209         return false;
210     }
211 
212     /**
213      * Query the launcher apps service for whether the supplied package has
214      * MAIN/LAUNCHER activities in the supplied package.
215      */
packageHasActivities(Context context, String packageName, UserHandleCompat user)216     static boolean packageHasActivities(Context context, String packageName,
217             UserHandleCompat user) {
218         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
219         return launcherApps.getActivityList(packageName, user).size() > 0;
220     }
221 
222     /**
223      * Returns whether <em>apps</em> contains <em>component</em>.
224      */
findActivity(ArrayList<AppInfo> apps, ComponentName component, UserHandleCompat user)225     private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component,
226             UserHandleCompat user) {
227         final int N = apps.size();
228         for (int i = 0; i < N; i++) {
229             final AppInfo info = apps.get(i);
230             if (info.user.equals(user) && info.componentName.equals(component)) {
231                 return true;
232             }
233         }
234         return false;
235     }
236 
237     /**
238      * Find an ApplicationInfo object for the given packageName and className.
239      */
findApplicationInfoLocked(String packageName, UserHandleCompat user, String className)240     private AppInfo findApplicationInfoLocked(String packageName, UserHandleCompat user,
241             String className) {
242         for (AppInfo info: data) {
243             final ComponentName component = info.intent.getComponent();
244             if (user.equals(info.user) && packageName.equals(component.getPackageName())
245                     && className.equals(component.getClassName())) {
246                 return info;
247             }
248         }
249         return null;
250     }
251 }
252