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
17package com.android.launcher3;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.pm.LauncherActivityInfo;
22import android.os.UserHandle;
23
24import com.android.launcher3.compat.LauncherAppsCompat;
25import com.android.launcher3.util.FlagOp;
26import com.android.launcher3.util.ItemInfoMatcher;
27
28import java.util.ArrayList;
29import java.util.HashSet;
30import java.util.List;
31
32
33/**
34 * Stores the list of all applications for the all apps view.
35 */
36public 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     */
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     */
68    public void add(AppInfo info, LauncherActivityInfo activityInfo) {
69        if (!mAppFilter.shouldShowApp(info.componentName)) {
70            return;
71        }
72        if (findActivity(data, info.componentName, info.user)) {
73            return;
74        }
75        mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
76
77        data.add(info);
78        added.add(info);
79    }
80
81    public void clear() {
82        data.clear();
83        // TODO: do we clear these too?
84        added.clear();
85        removed.clear();
86        modified.clear();
87    }
88
89    public int size() {
90        return data.size();
91    }
92
93    public AppInfo get(int index) {
94        return data.get(index);
95    }
96
97    /**
98     * Add the icons for the supplied apk called packageName.
99     */
100    public void addPackage(Context context, String packageName, UserHandle user) {
101        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
102        final List<LauncherActivityInfo> matches = launcherApps.getActivityList(packageName,
103                user);
104
105        for (LauncherActivityInfo info : matches) {
106            add(new AppInfo(context, info, user), info);
107        }
108    }
109
110    /**
111     * Remove the apps for the given apk identified by packageName.
112     */
113    public void removePackage(String packageName, UserHandle user) {
114        final List<AppInfo> data = this.data;
115        for (int i = data.size() - 1; i >= 0; i--) {
116            AppInfo info = data.get(i);
117            if (info.user.equals(user) && packageName.equals(info.componentName.getPackageName())) {
118                removed.add(info);
119                data.remove(i);
120            }
121        }
122    }
123
124    /**
125     * Updates the disabled flags of apps matching {@param matcher} based on {@param op}.
126     */
127    public void updateDisabledFlags(ItemInfoMatcher matcher, FlagOp op) {
128        final List<AppInfo> data = this.data;
129        for (int i = data.size() - 1; i >= 0; i--) {
130            AppInfo info = data.get(i);
131            if (matcher.matches(info, info.componentName)) {
132                info.isDisabled = op.apply(info.isDisabled);
133                modified.add(info);
134            }
135        }
136    }
137
138    public void updateIconsAndLabels(HashSet<String> packages, UserHandle 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     */
151    public void updatePackage(Context context, String packageName, UserHandle user) {
152        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
153        final List<LauncherActivityInfo> 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                if (user.equals(applicationInfo.user)
161                        && packageName.equals(applicationInfo.componentName.getPackageName())) {
162                    if (!findActivity(matches, applicationInfo.componentName)) {
163                        removed.add(applicationInfo);
164                        data.remove(i);
165                    }
166                }
167            }
168
169            // Find enabled activities and add them to the adapter
170            // Also updates existing activities with new labels/icons
171            for (final LauncherActivityInfo info : matches) {
172                AppInfo applicationInfo = findApplicationInfoLocked(
173                        info.getComponentName().getPackageName(), user,
174                        info.getComponentName().getClassName());
175                if (applicationInfo == null) {
176                    add(new AppInfo(context, info, user), info);
177                } else {
178                    mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
179                    modified.add(applicationInfo);
180                }
181            }
182        } else {
183            // Remove all data for this package.
184            for (int i = data.size() - 1; i >= 0; i--) {
185                final AppInfo applicationInfo = data.get(i);
186                if (user.equals(applicationInfo.user)
187                        && packageName.equals(applicationInfo.componentName.getPackageName())) {
188                    removed.add(applicationInfo);
189                    mIconCache.remove(applicationInfo.componentName, user);
190                    data.remove(i);
191                }
192            }
193        }
194    }
195
196
197    /**
198     * Returns whether <em>apps</em> contains <em>component</em>.
199     */
200    private static boolean findActivity(List<LauncherActivityInfo> apps,
201            ComponentName component) {
202        for (LauncherActivityInfo info : apps) {
203            if (info.getComponentName().equals(component)) {
204                return true;
205            }
206        }
207        return false;
208    }
209
210    /**
211     * Returns whether <em>apps</em> contains <em>component</em>.
212     */
213    private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component,
214            UserHandle user) {
215        final int N = apps.size();
216        for (int i = 0; i < N; i++) {
217            final AppInfo info = apps.get(i);
218            if (info.user.equals(user) && info.componentName.equals(component)) {
219                return true;
220            }
221        }
222        return false;
223    }
224
225    /**
226     * Find an ApplicationInfo object for the given packageName and className.
227     */
228    private AppInfo findApplicationInfoLocked(String packageName, UserHandle user,
229            String className) {
230        for (AppInfo info: data) {
231            if (user.equals(info.user) && packageName.equals(info.componentName.getPackageName())
232                    && className.equals(info.componentName.getClassName())) {
233                return info;
234            }
235        }
236        return null;
237    }
238}
239