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.launcher2;
18
19import java.util.ArrayList;
20import java.util.List;
21
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.pm.LauncherActivityInfo;
25import android.content.pm.LauncherApps;
26import android.os.UserHandle;
27
28/**
29 * Stores the list of all applications for the all apps view.
30 */
31class AllAppsList {
32    public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
33
34    /** The list off all apps. */
35    public ArrayList<ApplicationInfo> data =
36            new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
37    /** The list of apps that have been added since the last notify() call. */
38    public ArrayList<ApplicationInfo> added =
39            new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
40    /** The list of apps that have been removed since the last notify() call. */
41    public ArrayList<ApplicationInfo> removed = new ArrayList<ApplicationInfo>();
42    /** The list of apps that have been modified since the last notify() call. */
43    public ArrayList<ApplicationInfo> modified = new ArrayList<ApplicationInfo>();
44
45    private IconCache mIconCache;
46
47    /**
48     * Boring constructor.
49     */
50    public AllAppsList(IconCache iconCache) {
51        mIconCache = iconCache;
52    }
53
54    /**
55     * Add the supplied ApplicationInfo objects to the list, and enqueue it into the
56     * list to broadcast when notify() is called.
57     *
58     * If the app is already in the list, doesn't add it.
59     */
60    public void add(ApplicationInfo info) {
61        if (findActivity(data, info.componentName, info.user)) {
62            return;
63        }
64        data.add(info);
65        added.add(info);
66    }
67
68    public void clear() {
69        data.clear();
70        // TODO: do we clear these too?
71        added.clear();
72        removed.clear();
73        modified.clear();
74    }
75
76    public int size() {
77        return data.size();
78    }
79
80    public ApplicationInfo get(int index) {
81        return data.get(index);
82    }
83
84    /**
85     * Add the icons for the supplied apk called packageName.
86     */
87    public void addPackage(Context context, String packageName, UserHandle user) {
88        LauncherApps launcherApps = (LauncherApps)
89                context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
90        final List<LauncherActivityInfo> matches = launcherApps.getActivityList(packageName,
91                user);
92
93        for (LauncherActivityInfo info : matches) {
94            add(new ApplicationInfo(info, user, mIconCache, null));
95        }
96    }
97
98    /**
99     * Remove the apps for the given apk identified by packageName.
100     */
101    public void removePackage(String packageName, UserHandle user) {
102        final List<ApplicationInfo> data = this.data;
103        for (int i = data.size() - 1; i >= 0; i--) {
104            ApplicationInfo info = data.get(i);
105            final ComponentName component = info.intent.getComponent();
106            if (info.user.equals(user) && packageName.equals(component.getPackageName())) {
107                removed.add(info);
108                data.remove(i);
109            }
110        }
111        // This is more aggressive than it needs to be.
112        mIconCache.flush();
113    }
114
115    /**
116     * Add and remove icons for this package which has been updated.
117     */
118    public void updatePackage(Context context, String packageName, UserHandle user) {
119        LauncherApps launcherApps = (LauncherApps)
120                context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
121        final List<LauncherActivityInfo> matches = launcherApps.getActivityList(packageName,
122                user);
123        if (matches.size() > 0) {
124            // Find disabled/removed activities and remove them from data and add them
125            // to the removed list.
126            for (int i = data.size() - 1; i >= 0; i--) {
127                final ApplicationInfo applicationInfo = data.get(i);
128                final ComponentName component = applicationInfo.intent.getComponent();
129                if (user.equals(applicationInfo.user)
130                        && packageName.equals(component.getPackageName())) {
131                    if (!findActivity(matches, component, user)) {
132                        removed.add(applicationInfo);
133                        mIconCache.remove(component, user);
134                        data.remove(i);
135                    }
136                }
137            }
138
139            // Find enabled activities and add them to the adapter
140            // Also updates existing activities with new labels/icons
141            int count = matches.size();
142            for (int i = 0; i < count; i++) {
143                final LauncherActivityInfo info = matches.get(i);
144                ApplicationInfo applicationInfo = findApplicationInfoLocked(
145                        info.getComponentName().getPackageName(),
146                        info.getComponentName().getClassName(),
147                        user);
148                if (applicationInfo == null) {
149                    add(new ApplicationInfo(info, user,
150                            mIconCache, null));
151                } else {
152                    mIconCache.remove(applicationInfo.componentName, user);
153                    mIconCache.getTitleAndIcon(applicationInfo, info, null);
154                    modified.add(applicationInfo);
155                }
156            }
157        } else {
158            // Remove all data for this package.
159            for (int i = data.size() - 1; i >= 0; i--) {
160                final ApplicationInfo applicationInfo = data.get(i);
161                final ComponentName component = applicationInfo.intent.getComponent();
162                if (user.equals(applicationInfo.user)
163                        && packageName.equals(component.getPackageName())) {
164                    removed.add(applicationInfo);
165                    mIconCache.remove(component, user);
166                    data.remove(i);
167                }
168            }
169        }
170    }
171
172    /**
173     * Returns whether <em>apps</em> contains <em>component</em> for a specific
174     * user profile.
175     */
176    private static boolean findActivity(List<LauncherActivityInfo> apps, ComponentName component,
177            UserHandle user) {
178        for (LauncherActivityInfo info : apps) {
179            if (info.getUser().equals(user)
180                    && info.getComponentName().equals(component)) {
181                return true;
182            }
183        }
184        return false;
185    }
186
187    /**
188     * Returns whether <em>apps</em> contains <em>component</em> for a specific
189     * user profile.
190     */
191    private static boolean findActivity(ArrayList<ApplicationInfo> apps, ComponentName component,
192            UserHandle user) {
193        final int N = apps.size();
194        for (int i = 0; i < N; i++) {
195            final ApplicationInfo info = apps.get(i);
196            if (info.user.equals(user) && info.componentName.equals(component)) {
197                return true;
198            }
199        }
200        return false;
201    }
202
203    /**
204     * Find an ApplicationInfo object for the given packageName, className and
205     * user profile.
206     */
207    private ApplicationInfo findApplicationInfoLocked(String packageName, String className,
208            UserHandle user) {
209        for (ApplicationInfo info: data) {
210            final ComponentName component = info.intent.getComponent();
211            if (user.equals(info.user) && packageName.equals(component.getPackageName())
212                    && className.equals(component.getClassName())) {
213                return info;
214            }
215        }
216        return null;
217    }
218}
219