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.Intent;
22import android.content.pm.ActivityInfo;
23import android.content.pm.PackageManager;
24import android.content.pm.ResolveInfo;
25
26import com.android.launcher3.compat.LauncherActivityInfoCompat;
27import com.android.launcher3.compat.LauncherAppsCompat;
28import com.android.launcher3.compat.UserHandleCompat;
29
30import java.util.ArrayList;
31import java.util.List;
32
33
34/**
35 * Stores the list of all applications for the all apps view.
36 */
37class AllAppsList {
38    public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
39
40    /** The list off all apps. */
41    public ArrayList<AppInfo> data =
42            new ArrayList<AppInfo>(DEFAULT_APPLICATIONS_NUMBER);
43    /** The list of apps that have been added since the last notify() call. */
44    public ArrayList<AppInfo> added =
45            new ArrayList<AppInfo>(DEFAULT_APPLICATIONS_NUMBER);
46    /** The list of apps that have been removed since the last notify() call. */
47    public ArrayList<AppInfo> removed = new ArrayList<AppInfo>();
48    /** The list of apps that have been modified since the last notify() call. */
49    public ArrayList<AppInfo> modified = new ArrayList<AppInfo>();
50
51    private IconCache mIconCache;
52
53    private AppFilter mAppFilter;
54
55    /**
56     * Boring constructor.
57     */
58    public AllAppsList(IconCache iconCache, AppFilter appFilter) {
59        mIconCache = iconCache;
60        mAppFilter = appFilter;
61    }
62
63    /**
64     * Add the supplied ApplicationInfo objects to the list, and enqueue it into the
65     * list to broadcast when notify() is called.
66     *
67     * If the app is already in the list, doesn't add it.
68     */
69    public void add(AppInfo info) {
70        if (mAppFilter != null && !mAppFilter.shouldShowApp(info.componentName)) {
71            return;
72        }
73        if (findActivity(data, info.componentName, info.user)) {
74            return;
75        }
76        data.add(info);
77        added.add(info);
78    }
79
80    public void clear() {
81        data.clear();
82        // TODO: do we clear these too?
83        added.clear();
84        removed.clear();
85        modified.clear();
86    }
87
88    public int size() {
89        return data.size();
90    }
91
92    public AppInfo get(int index) {
93        return data.get(index);
94    }
95
96    /**
97     * Add the icons for the supplied apk called packageName.
98     */
99    public void addPackage(Context context, String packageName, UserHandleCompat user) {
100        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
101        final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
102                user);
103
104        if (matches.size() > 0) {
105            for (LauncherActivityInfoCompat info : matches) {
106                add(new AppInfo(context, info, user, mIconCache, null));
107            }
108        }
109    }
110
111    /**
112     * Remove the apps for the given apk identified by packageName.
113     */
114    public void removePackage(String packageName, UserHandleCompat user) {
115        final List<AppInfo> data = this.data;
116        for (int i = data.size() - 1; i >= 0; i--) {
117            AppInfo info = data.get(i);
118            final ComponentName component = info.intent.getComponent();
119            if (info.user.equals(user) && packageName.equals(component.getPackageName())) {
120                removed.add(info);
121                data.remove(i);
122            }
123        }
124        mIconCache.remove(packageName, user);
125    }
126
127    /**
128     * Add and remove icons for this package which has been updated.
129     */
130    public void updatePackage(Context context, String packageName, UserHandleCompat user) {
131        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
132        final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
133                user);
134        if (matches.size() > 0) {
135            // Find disabled/removed activities and remove them from data and add them
136            // to the removed list.
137            for (int i = data.size() - 1; i >= 0; i--) {
138                final AppInfo applicationInfo = data.get(i);
139                final ComponentName component = applicationInfo.intent.getComponent();
140                if (user.equals(applicationInfo.user)
141                        && packageName.equals(component.getPackageName())) {
142                    if (!findActivity(matches, component)) {
143                        removed.add(applicationInfo);
144                        mIconCache.remove(component, user);
145                        data.remove(i);
146                    }
147                }
148            }
149
150            // Find enabled activities and add them to the adapter
151            // Also updates existing activities with new labels/icons
152            int count = matches.size();
153            for (int i = 0; i < count; i++) {
154                final LauncherActivityInfoCompat info = matches.get(i);
155                AppInfo applicationInfo = findApplicationInfoLocked(
156                        info.getComponentName().getPackageName(), user,
157                        info.getComponentName().getClassName());
158                if (applicationInfo == null) {
159                    add(new AppInfo(context, info, user, mIconCache, null));
160                } else {
161                    mIconCache.remove(applicationInfo.componentName, user);
162                    mIconCache.getTitleAndIcon(applicationInfo, info, null);
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