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