blob: 271f9f47e07bc01a3ada8d27eeeca7d313d09519 [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
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.launcher;
18
19import android.content.ComponentName;
20import android.content.ContentResolver;
21import android.content.ContentValues;
22import android.content.Intent;
23import android.content.Context;
24import android.content.pm.ActivityInfo;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.content.res.Resources;
28import android.database.Cursor;
29import android.graphics.Bitmap;
30import android.graphics.BitmapFactory;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070031import android.graphics.drawable.Drawable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.net.Uri;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070033import static android.util.Log.*;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.os.Process;
35
36import java.util.ArrayList;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import java.util.HashMap;
38import java.util.List;
39import java.util.Comparator;
40import java.lang.ref.WeakReference;
41import java.text.Collator;
42import java.net.URISyntaxException;
43
44/**
45 * Maintains in-memory state of the Launcher. It is expected that there should be only one
46 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070047 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080048 */
49public class LauncherModel {
Romain Guy829f56a2009-03-27 16:58:13 -070050 static final boolean DEBUG_LOADERS = true;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070051 static final String LOG_TAG = "HomeLoaders";
52
The Android Open Source Project31dd5032009-03-03 19:32:27 -080053 private static final int UI_NOTIFICATION_RATE = 4;
54 private static final int DEFAULT_APPLICATIONS_NUMBER = 42;
55 private static final long APPLICATION_NOT_RESPONDING_TIMEOUT = 5000;
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070056 private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057
The Android Open Source Project7376fae2009-03-11 12:11:58 -070058 private static final Collator sCollator = Collator.getInstance();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059
60 private boolean mApplicationsLoaded;
61 private boolean mDesktopItemsLoaded;
62
63 private ArrayList<ItemInfo> mDesktopItems;
The Android Open Source Project7376fae2009-03-11 12:11:58 -070064 private ArrayList<LauncherAppWidgetInfo> mDesktopAppWidgets;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065 private HashMap<Long, FolderInfo> mFolders;
66
67 private ArrayList<ApplicationInfo> mApplications;
68 private ApplicationsAdapter mApplicationsAdapter;
69 private ApplicationsLoader mApplicationsLoader;
70 private DesktopItemsLoader mDesktopItemsLoader;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -070071 private Thread mApplicationsLoaderThread;
72 private Thread mDesktopLoaderThread;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080073
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070074 private final HashMap<ComponentName, ApplicationInfo> mAppInfoCache =
75 new HashMap<ComponentName, ApplicationInfo>(INITIAL_ICON_CACHE_CAPACITY);
76
The Android Open Source Projectca9475f2009-03-13 13:04:24 -070077 synchronized void abortLoaders() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -080078 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
79 mApplicationsLoader.stop();
80 mApplicationsLoaded = false;
81 }
The Android Open Source Projectca9475f2009-03-13 13:04:24 -070082
The Android Open Source Project31dd5032009-03-03 19:32:27 -080083 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
84 mDesktopItemsLoader.stop();
85 mDesktopItemsLoaded = false;
86 }
87 }
88
89 /**
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070090 * Drop our cache of components to their lables & icons. We do
91 * this from Launcher when applications are added/removed. It's a
92 * bit overkill, but it's a rare operation anyway.
93 */
94 synchronized void dropApplicationCache() {
95 mAppInfoCache.clear();
96 }
97
98 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -080099 * Loads the list of installed applications in mApplications.
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700100 *
101 * @return true if the applications loader must be started
102 * (see startApplicationsLoader()), false otherwise.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800103 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700104 synchronized boolean loadApplications(boolean isLaunching, Launcher launcher,
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700105 boolean localeChanged) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700106
107 if (DEBUG_LOADERS) d(LOG_TAG, "load applications");
108
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800109 if (isLaunching && mApplicationsLoaded && !localeChanged) {
110 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700111 if (DEBUG_LOADERS) d(LOG_TAG, " --> applications loaded, return");
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700112 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113 }
114
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700115 stopAndWaitForApplicationsLoader();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700116
117 if (localeChanged) {
118 dropApplicationCache();
119 }
120
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800121 if (mApplicationsAdapter == null || isLaunching || localeChanged) {
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700122 mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
123 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800124 }
125
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800126 mApplicationsLoaded = false;
127
128 if (!isLaunching) {
129 startApplicationsLoader(launcher);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700130 return false;
131 }
132
133 return true;
134 }
135
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700136 private synchronized void stopAndWaitForApplicationsLoader() {
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700137 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700138 if (DEBUG_LOADERS) d(LOG_TAG, " --> wait for applications loader");
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700139
140 mApplicationsLoader.stop();
141 // Wait for the currently running thread to finish, this can take a little
142 // time but it should be well below the timeout limit
143 try {
144 mApplicationsLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
145 } catch (InterruptedException e) {
146 // EMpty
147 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800148 }
149 }
150
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700151 private synchronized void startApplicationsLoader(Launcher launcher) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700152 if (DEBUG_LOADERS) d(LOG_TAG, " --> starting applications loader");
153
154 stopAndWaitForApplicationsLoader();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700155
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800156 mApplicationsLoader = new ApplicationsLoader(launcher);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700157 mApplicationsLoaderThread = new Thread(mApplicationsLoader, "Applications Loader");
158 mApplicationsLoaderThread.start();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800159 }
160
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700161 synchronized void addPackage(Launcher launcher, String packageName) {
162 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
163 startApplicationsLoader(launcher);
164 return;
165 }
166
167 if (packageName != null && packageName.length() > 0) {
168 final PackageManager packageManager = launcher.getPackageManager();
169 final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName);
170
171 if (matches.size() > 0) {
172 final ApplicationsAdapter adapter = mApplicationsAdapter;
173 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
174
175 for (ResolveInfo info : matches) {
176 adapter.setNotifyOnChange(false);
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700177 adapter.add(makeAndCacheApplicationInfo(packageManager, cache, info, launcher));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700178 }
179
180 adapter.sort(new ApplicationInfoComparator());
181 adapter.notifyDataSetChanged();
182 }
183 }
184 }
185
186 synchronized void removePackage(Launcher launcher, String packageName) {
187 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
188 dropApplicationCache(); // TODO: this could be optimized
189 startApplicationsLoader(launcher);
190 return;
191 }
192
193 if (packageName != null && packageName.length() > 0) {
194 final ApplicationsAdapter adapter = mApplicationsAdapter;
195
196 final List<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>();
197 final int count = adapter.getCount();
198
199 for (int i = 0; i < count; i++) {
200 final ApplicationInfo applicationInfo = adapter.getItem(i);
201 final Intent intent = applicationInfo.intent;
202 final ComponentName component = intent.getComponent();
203 if (packageName.equals(component.getPackageName())) {
204 toRemove.add(applicationInfo);
205 }
206 }
207
208 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
209 for (ApplicationInfo info : toRemove) {
210 adapter.setNotifyOnChange(false);
211 adapter.remove(info);
212 cache.remove(info.intent.getComponent());
213 }
214
215 if (toRemove.size() > 0) {
216 adapter.sort(new ApplicationInfoComparator());
217 adapter.notifyDataSetChanged();
218 }
219 }
220 }
221
222 synchronized void updatePackage(Launcher launcher, String packageName) {
223 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
224 startApplicationsLoader(launcher);
225 return;
226 }
227
228 if (packageName != null && packageName.length() > 0) {
229 final PackageManager packageManager = launcher.getPackageManager();
230 final ApplicationsAdapter adapter = mApplicationsAdapter;
231
232 final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName);
233 final int count = matches.size();
234
235 boolean changed = false;
236
237 for (int i = 0; i < count; i++) {
238 final ResolveInfo info = matches.get(i);
239 final ApplicationInfo applicationInfo = findIntent(adapter,
240 info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
241 if (applicationInfo != null) {
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700242 updateAndCacheApplicationInfo(packageManager, info, applicationInfo, launcher);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700243 changed = true;
244 }
245 }
246
247 if (changed) {
248 adapter.sort(new ApplicationInfoComparator());
249 adapter.notifyDataSetChanged();
250 }
251 }
252 }
253
254 private void updateAndCacheApplicationInfo(PackageManager packageManager, ResolveInfo info,
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700255 ApplicationInfo applicationInfo, Context context) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700256
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700257 updateApplicationInfoTitleAndIcon(packageManager, info, applicationInfo, context);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700258
259 ComponentName componentName = new ComponentName(
260 info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
261 mAppInfoCache.put(componentName, applicationInfo);
262 }
263
264 synchronized void syncPackage(Launcher launcher, String packageName) {
265 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
266 startApplicationsLoader(launcher);
267 return;
268 }
269
270 if (packageName != null && packageName.length() > 0) {
271 final PackageManager packageManager = launcher.getPackageManager();
272 final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName);
273
274 if (matches.size() > 0) {
275 final ApplicationsAdapter adapter = mApplicationsAdapter;
276
277 // Find disabled activities and remove them from the adapter
278 boolean removed = removeDisabledActivities(packageName, matches, adapter);
279 // Find enable activities and add them to the adapter
280 // Also updates existing activities with new labels/icons
281 boolean added = addEnabledAndUpdateActivities(matches, adapter, launcher);
282
283 if (added || removed) {
284 adapter.sort(new ApplicationInfoComparator());
285 adapter.notifyDataSetChanged();
286 }
287 }
288 }
289 }
290
291 private static List<ResolveInfo> findActivitiesForPackage(PackageManager packageManager,
292 String packageName) {
293
294 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
295 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
296
297 final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
298 final List<ResolveInfo> matches = new ArrayList<ResolveInfo>();
299
300 if (apps != null) {
301 // Find all activities that match the packageName
302 int count = apps.size();
303 for (int i = 0; i < count; i++) {
304 final ResolveInfo info = apps.get(i);
305 final ActivityInfo activityInfo = info.activityInfo;
306 if (packageName.equals(activityInfo.packageName)) {
307 matches.add(info);
308 }
309 }
310 }
311
312 return matches;
313 }
314
315 private boolean addEnabledAndUpdateActivities(List<ResolveInfo> matches,
316 ApplicationsAdapter adapter, Launcher launcher) {
317
318 final List<ApplicationInfo> toAdd = new ArrayList<ApplicationInfo>();
319 final int count = matches.size();
320
321 boolean changed = false;
322
323 for (int i = 0; i < count; i++) {
324 final ResolveInfo info = matches.get(i);
325 final ApplicationInfo applicationInfo = findIntent(adapter,
326 info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
327 if (applicationInfo == null) {
328 toAdd.add(makeAndCacheApplicationInfo(launcher.getPackageManager(),
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700329 mAppInfoCache, info, launcher));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700330 changed = true;
331 } else {
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700332 updateAndCacheApplicationInfo(
333 launcher.getPackageManager(), info, applicationInfo, launcher);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700334 changed = true;
335 }
336 }
337
338 for (ApplicationInfo info : toAdd) {
339 adapter.setNotifyOnChange(false);
340 adapter.add(info);
341 }
342
343 return changed;
344 }
345
346 private boolean removeDisabledActivities(String packageName, List<ResolveInfo> matches,
347 ApplicationsAdapter adapter) {
348
349 final List<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>();
350 final int count = adapter.getCount();
351
352 boolean changed = false;
353
354 for (int i = 0; i < count; i++) {
355 final ApplicationInfo applicationInfo = adapter.getItem(i);
356 final Intent intent = applicationInfo.intent;
357 final ComponentName component = intent.getComponent();
358 if (packageName.equals(component.getPackageName())) {
359 if (!findIntent(matches, component)) {
360 toRemove.add(applicationInfo);
361 changed = true;
362 }
363 }
364 }
365
366 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
367 for (ApplicationInfo info : toRemove) {
368 adapter.setNotifyOnChange(false);
369 adapter.remove(info);
370 cache.remove(info.intent.getComponent());
371 }
372
373 return changed;
374 }
375
376 private static ApplicationInfo findIntent(ApplicationsAdapter adapter, String packageName,
377 String name) {
378
379 final int count = adapter.getCount();
380 for (int i = 0; i < count; i++) {
381 final ApplicationInfo applicationInfo = adapter.getItem(i);
382 final Intent intent = applicationInfo.intent;
383 final ComponentName component = intent.getComponent();
384 if (packageName.equals(component.getPackageName()) &&
385 name.equals(component.getClassName())) {
386 return applicationInfo;
387 }
388 }
389
390 return null;
391 }
392
393 private static boolean findIntent(List<ResolveInfo> apps, ComponentName component) {
394 final String className = component.getClassName();
395 for (ResolveInfo info : apps) {
396 final ActivityInfo activityInfo = info.activityInfo;
397 if (activityInfo.name.equals(className)) {
398 return true;
399 }
400 }
401 return false;
402 }
403
404 Drawable getApplicationInfoIcon(PackageManager manager, ApplicationInfo info) {
405 final ResolveInfo resolveInfo = manager.resolveActivity(info.intent, 0);
406 if (resolveInfo == null) {
407 return null;
408 }
409
410 ComponentName componentName = new ComponentName(
411 resolveInfo.activityInfo.applicationInfo.packageName,
412 resolveInfo.activityInfo.name);
413 ApplicationInfo application = mAppInfoCache.get(componentName);
414
415 if (application == null) {
416 return resolveInfo.activityInfo.loadIcon(manager);
417 }
418
419 return application.icon;
420 }
421
422 private static ApplicationInfo makeAndCacheApplicationInfo(PackageManager manager,
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700423 HashMap<ComponentName, ApplicationInfo> appInfoCache, ResolveInfo info,
424 Context context) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700425
426 ComponentName componentName = new ComponentName(
427 info.activityInfo.applicationInfo.packageName,
428 info.activityInfo.name);
429 ApplicationInfo application = appInfoCache.get(componentName);
430
431 if (application == null) {
432 application = new ApplicationInfo();
433 application.container = ItemInfo.NO_ID;
434
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700435 updateApplicationInfoTitleAndIcon(manager, info, application, context);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700436
437 application.setActivity(componentName,
438 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
439
440 appInfoCache.put(componentName, application);
441 }
442
443 return application;
444 }
445
446 private static void updateApplicationInfoTitleAndIcon(PackageManager manager, ResolveInfo info,
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700447 ApplicationInfo application, Context context) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700448
449 application.title = info.loadLabel(manager);
450 if (application.title == null) {
451 application.title = info.activityInfo.name;
452 }
453
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700454 application.icon =
455 Utilities.createIconThumbnail(info.activityInfo.loadIcon(manager), context);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700456 application.filtered = false;
457 }
458
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800459 private class ApplicationsLoader implements Runnable {
460 private final WeakReference<Launcher> mLauncher;
461
462 private volatile boolean mStopped;
463 private volatile boolean mRunning;
464
465 ApplicationsLoader(Launcher launcher) {
466 mLauncher = new WeakReference<Launcher>(launcher);
467 }
468
469 void stop() {
470 mStopped = true;
471 }
472
473 boolean isRunning() {
474 return mRunning;
475 }
476
477 public void run() {
478 mRunning = true;
479
480 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
481
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700482 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800483 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
484
485 final Launcher launcher = mLauncher.get();
486 final PackageManager manager = launcher.getPackageManager();
487 final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
488
489 if (apps != null && !mStopped) {
490 final int count = apps.size();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700491 // Can be set to null on the UI thread by the unbind() method
492 // Do not access without checking for null first
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800493 final ApplicationsAdapter applicationList = mApplicationsAdapter;
494
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700495 ChangeNotifier action = new ChangeNotifier(applicationList, true);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700496 final HashMap<ComponentName, ApplicationInfo> appInfoCache = mAppInfoCache;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800497
498 for (int i = 0; i < count && !mStopped; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800499 ResolveInfo info = apps.get(i);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700500 ApplicationInfo application =
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700501 makeAndCacheApplicationInfo(manager, appInfoCache, info, launcher);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800502
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700503 if (action.add(application) && !mStopped) {
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700504 launcher.runOnUiThread(action);
505 action = new ChangeNotifier(applicationList, false);
506 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800507 }
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700508
509 launcher.runOnUiThread(action);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800510 }
511
512 if (!mStopped) {
513 mApplicationsLoaded = true;
514 }
515 mRunning = false;
516 }
517 }
518
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700519 private static class ChangeNotifier implements Runnable {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800520 private final ApplicationsAdapter mApplicationList;
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700521 private final ArrayList<ApplicationInfo> mBuffer;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800522
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700523 private boolean mFirst = true;
524
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700525 ChangeNotifier(ApplicationsAdapter applicationList, boolean first) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800526 mApplicationList = applicationList;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700527 mFirst = first;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800528 mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
529 }
530
531 public void run() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800532 final ApplicationsAdapter applicationList = mApplicationList;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700533 // Can be set to null on the UI thread by the unbind() method
534 if (applicationList == null) return;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800535
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700536 if (mFirst) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800537 applicationList.setNotifyOnChange(false);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700538 applicationList.clear();
539 mFirst = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800540 }
541
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700542 final ArrayList<ApplicationInfo> buffer = mBuffer;
543 final int count = buffer.size();
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700544
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700545 for (int i = 0; i < count; i++) {
546 applicationList.setNotifyOnChange(false);
547 applicationList.add(buffer.get(i));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700548 }
549
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700550 buffer.clear();
551
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700552 applicationList.sort(new ApplicationInfoComparator());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800553 applicationList.notifyDataSetChanged();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800554 }
555
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700556 boolean add(ApplicationInfo application) {
557 final ArrayList<ApplicationInfo> buffer = mBuffer;
558 buffer.add(application);
559 return buffer.size() >= UI_NOTIFICATION_RATE;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800560 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700561 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800562
Romain Guy73b979d2009-06-09 12:57:21 -0700563 static class ApplicationInfoComparator implements Comparator<ApplicationInfo> {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700564 public final int compare(ApplicationInfo a, ApplicationInfo b) {
565 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800566 }
567 }
568
569 boolean isDesktopLoaded() {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700570 return mDesktopItems != null && mDesktopAppWidgets != null && mDesktopItemsLoaded;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800571 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700572
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800573 /**
574 * Loads all of the items on the desktop, in folders, or in the dock.
575 * These can be apps, shortcuts or widgets
576 */
577 void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
578 boolean loadApplications) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700579 if (DEBUG_LOADERS) d(LOG_TAG, "loading user items");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800580
581 if (isLaunching && isDesktopLoaded()) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700582 if (DEBUG_LOADERS) d(LOG_TAG, " --> items loaded, return");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800583 if (loadApplications) startApplicationsLoader(launcher);
584 // We have already loaded our data from the DB
585 launcher.onDesktopItemsLoaded();
586 return;
587 }
588
589 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
Romain Guyfedc4fc2009-03-27 20:48:20 -0700590 if (DEBUG_LOADERS) d(LOG_TAG, " --> stopping workspace loader");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800591 mDesktopItemsLoader.stop();
592 // Wait for the currently running thread to finish, this can take a little
593 // time but it should be well below the timeout limit
594 try {
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700595 mDesktopLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800596 } catch (InterruptedException e) {
597 // Empty
598 }
Romain Guyfedc4fc2009-03-27 20:48:20 -0700599
600 // If the thread we are interrupting was tasked to load the list of
601 // applications make sure we keep that information in the new loader
602 // spawned below
603 // note: we don't apply this to localeChanged because the thread can
604 // only be stopped *after* the localeChanged handling has occured
605 loadApplications = mDesktopItemsLoader.mLoadApplications;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800606 }
607
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700608 if (DEBUG_LOADERS) d(LOG_TAG, " --> starting workspace loader");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800609 mDesktopItemsLoaded = false;
610 mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700611 mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
612 mDesktopLoaderThread.start();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800613 }
614
615 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
616 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
Romain Guy73b979d2009-06-09 12:57:21 -0700617 new String[] { LauncherSettings.Favorites._ID, LauncherSettings.Favorites.TITLE,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800618 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
619 null, null, null);
620
Romain Guy73b979d2009-06-09 12:57:21 -0700621 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800622 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
623 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
624 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
625
626 // boolean changed = false;
627
628 try {
629 while (c.moveToNext()) {
630 try {
631 if (c.getInt(itemTypeIndex) !=
632 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
633 continue;
634 }
635
636 final String intentUri = c.getString(intentIndex);
637 if (intentUri != null) {
638 final Intent shortcut = Intent.getIntent(intentUri);
639 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
640 final ComponentName name = shortcut.getComponent();
641 if (name != null) {
642 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
643 final String title = c.getString(titleIndex);
644 String label = getLabel(manager, activityInfo);
645
646 if (title == null || !title.equals(label)) {
647 final ContentValues values = new ContentValues();
648 values.put(LauncherSettings.Favorites.TITLE, label);
649
Romain Guyfedc4fc2009-03-27 20:48:20 -0700650 resolver.update(
651 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800652 values, "_id=?",
653 new String[] { String.valueOf(c.getLong(idIndex)) });
654
655 // changed = true;
656 }
657 }
658 }
659 }
660 } catch (URISyntaxException e) {
661 // Ignore
662 } catch (PackageManager.NameNotFoundException e) {
663 // Ignore
664 }
665 }
666 } finally {
667 c.close();
668 }
669
670 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
671 }
672
673 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
674 String label = activityInfo.loadLabel(manager).toString();
675 if (label == null) {
676 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
677 if (label == null) {
678 label = activityInfo.name;
679 }
680 }
681 return label;
682 }
683
684 private class DesktopItemsLoader implements Runnable {
685 private volatile boolean mStopped;
686 private volatile boolean mRunning;
687
688 private final WeakReference<Launcher> mLauncher;
689 private final boolean mLocaleChanged;
690 private final boolean mLoadApplications;
691
692 DesktopItemsLoader(Launcher launcher, boolean localeChanged, boolean loadApplications) {
693 mLoadApplications = loadApplications;
694 mLauncher = new WeakReference<Launcher>(launcher);
695 mLocaleChanged = localeChanged;
696 }
697
698 void stop() {
699 mStopped = true;
700 }
701
702 boolean isRunning() {
703 return mRunning;
704 }
705
706 public void run() {
707 mRunning = true;
708
709 final Launcher launcher = mLauncher.get();
710 final ContentResolver contentResolver = launcher.getContentResolver();
711 final PackageManager manager = launcher.getPackageManager();
712
713 if (mLocaleChanged) {
714 updateShortcutLabels(contentResolver, manager);
715 }
716
717 mDesktopItems = new ArrayList<ItemInfo>();
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700718 mDesktopAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800719 mFolders = new HashMap<Long, FolderInfo>();
720
721 final ArrayList<ItemInfo> desktopItems = mDesktopItems;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700722 final ArrayList<LauncherAppWidgetInfo> desktopAppWidgets = mDesktopAppWidgets;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800723
724 final Cursor c = contentResolver.query(
725 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
726
727 try {
Romain Guy73b979d2009-06-09 12:57:21 -0700728 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800729 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
730 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
731 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
732 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
733 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
734 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
735 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
736 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700737 final int appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWIDGET_ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800738 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
739 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
740 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
741 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
742 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
743 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
744 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
745
746 ApplicationInfo info;
747 String intentDescription;
748 Widget widgetInfo;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700749 LauncherAppWidgetInfo appWidgetInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800750 int container;
751 long id;
752 Intent intent;
753
754 final HashMap<Long, FolderInfo> folders = mFolders;
755
756 while (!mStopped && c.moveToNext()) {
757 try {
758 int itemType = c.getInt(itemTypeIndex);
759
760 switch (itemType) {
761 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
762 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
763 intentDescription = c.getString(intentIndex);
764 try {
765 intent = Intent.getIntent(intentDescription);
766 } catch (java.net.URISyntaxException e) {
767 continue;
768 }
769
770 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -0700771 info = getApplicationInfo(manager, intent, launcher);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800772 } else {
773 info = getApplicationInfoShortcut(c, launcher, iconTypeIndex,
774 iconPackageIndex, iconResourceIndex, iconIndex);
775 }
776
777 if (info == null) {
778 info = new ApplicationInfo();
779 info.icon = manager.getDefaultActivityIcon();
780 }
781
782 if (info != null) {
783 info.title = c.getString(titleIndex);
784 info.intent = intent;
785
786 info.id = c.getLong(idIndex);
787 container = c.getInt(containerIndex);
788 info.container = container;
789 info.screen = c.getInt(screenIndex);
790 info.cellX = c.getInt(cellXIndex);
791 info.cellY = c.getInt(cellYIndex);
792
793 switch (container) {
794 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
795 desktopItems.add(info);
796 break;
797 default:
798 // Item is in a user folder
799 UserFolderInfo folderInfo =
800 findOrMakeUserFolder(folders, container);
801 folderInfo.add(info);
802 break;
803 }
804 }
805 break;
806 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
807
808 id = c.getLong(idIndex);
809 UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id);
810
811 folderInfo.title = c.getString(titleIndex);
812
813 folderInfo.id = id;
814 container = c.getInt(containerIndex);
815 folderInfo.container = container;
816 folderInfo.screen = c.getInt(screenIndex);
817 folderInfo.cellX = c.getInt(cellXIndex);
818 folderInfo.cellY = c.getInt(cellYIndex);
819
820 switch (container) {
821 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
822 desktopItems.add(folderInfo);
823 break;
824 }
825 break;
826 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
827
828 id = c.getLong(idIndex);
829 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id);
830
831 intentDescription = c.getString(intentIndex);
832 intent = null;
833 if (intentDescription != null) {
834 try {
835 intent = Intent.getIntent(intentDescription);
836 } catch (java.net.URISyntaxException e) {
837 // Ignore, a live folder might not have a base intent
838 }
839 }
840
841 liveFolderInfo.title = c.getString(titleIndex);
842 liveFolderInfo.id = id;
843 container = c.getInt(containerIndex);
844 liveFolderInfo.container = container;
845 liveFolderInfo.screen = c.getInt(screenIndex);
846 liveFolderInfo.cellX = c.getInt(cellXIndex);
847 liveFolderInfo.cellY = c.getInt(cellYIndex);
848 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
849 liveFolderInfo.baseIntent = intent;
850 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
851
852 loadLiveFolderIcon(launcher, c, iconTypeIndex, iconPackageIndex,
853 iconResourceIndex, liveFolderInfo);
854
855 switch (container) {
856 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
857 desktopItems.add(liveFolderInfo);
858 break;
859 }
860 break;
861 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
862 widgetInfo = Widget.makeSearch();
863
864 container = c.getInt(containerIndex);
865 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700866 e(Launcher.LOG_TAG, "Widget found where container "
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800867 + "!= CONTAINER_DESKTOP ignoring!");
868 continue;
869 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700870
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800871 widgetInfo.id = c.getLong(idIndex);
872 widgetInfo.screen = c.getInt(screenIndex);
873 widgetInfo.container = container;
874 widgetInfo.cellX = c.getInt(cellXIndex);
875 widgetInfo.cellY = c.getInt(cellYIndex);
876
877 desktopItems.add(widgetInfo);
878 break;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700879 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
880 // Read all Launcher-specific widget details
881 int appWidgetId = c.getInt(appWidgetIdIndex);
882 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
883 appWidgetInfo.id = c.getLong(idIndex);
884 appWidgetInfo.screen = c.getInt(screenIndex);
885 appWidgetInfo.cellX = c.getInt(cellXIndex);
886 appWidgetInfo.cellY = c.getInt(cellYIndex);
887 appWidgetInfo.spanX = c.getInt(spanXIndex);
888 appWidgetInfo.spanY = c.getInt(spanYIndex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800889
890 container = c.getInt(containerIndex);
891 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700892 e(Launcher.LOG_TAG, "Widget found where container "
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800893 + "!= CONTAINER_DESKTOP -- ignoring!");
894 continue;
895 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700896 appWidgetInfo.container = c.getInt(containerIndex);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700897
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700898 desktopAppWidgets.add(appWidgetInfo);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800899 break;
900 }
901 } catch (Exception e) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700902 w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800903 }
904 }
905 } finally {
906 c.close();
907 }
908
909 if (!mStopped) {
910 launcher.runOnUiThread(new Runnable() {
911 public void run() {
912 launcher.onDesktopItemsLoaded();
913 }
914 });
915 if (mLoadApplications) startApplicationsLoader(launcher);
916 }
917
918 if (!mStopped) {
919 mDesktopItemsLoaded = true;
920 }
921 mRunning = false;
922 }
923 }
924
925 private static void loadLiveFolderIcon(Launcher launcher, Cursor c, int iconTypeIndex,
926 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
927
928 int iconType = c.getInt(iconTypeIndex);
929 switch (iconType) {
930 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
931 String packageName = c.getString(iconPackageIndex);
932 String resourceName = c.getString(iconResourceIndex);
933 PackageManager packageManager = launcher.getPackageManager();
934 try {
935 Resources resources = packageManager.getResourcesForApplication(packageName);
936 final int id = resources.getIdentifier(resourceName, null, null);
937 liveFolderInfo.icon = resources.getDrawable(id);
938 } catch (Exception e) {
939 liveFolderInfo.icon =
940 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
941 }
942 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
943 liveFolderInfo.iconResource.packageName = packageName;
944 liveFolderInfo.iconResource.resourceName = resourceName;
945 break;
946 default:
947 liveFolderInfo.icon =
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700948 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800949 }
950 }
951
952 /**
953 * Finds the user folder defined by the specified id.
954 *
955 * @param id The id of the folder to look for.
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700956 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800957 * @return A UserFolderInfo if the folder exists or null otherwise.
958 */
959 FolderInfo findFolderById(long id) {
960 return mFolders.get(id);
961 }
962
963 void addFolder(FolderInfo info) {
964 mFolders.put(info.id, info);
965 }
966
967 /**
968 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
969 * new one.
970 */
971 private UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
972 // See if a placeholder was created for us already
973 FolderInfo folderInfo = folders.get(id);
974 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
975 // No placeholder -- create a new instance
976 folderInfo = new UserFolderInfo();
977 folders.put(id, folderInfo);
978 }
979 return (UserFolderInfo) folderInfo;
980 }
981
982 /**
983 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
984 * new one.
985 */
986 private LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
987 // See if a placeholder was created for us already
988 FolderInfo folderInfo = folders.get(id);
989 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
990 // No placeholder -- create a new instance
991 folderInfo = new LiveFolderInfo();
992 folders.put(id, folderInfo);
993 }
994 return (LiveFolderInfo) folderInfo;
995 }
996
997 /**
998 * Remove the callback for the cached drawables or we leak the previous
999 * Home screen on orientation change.
1000 */
1001 void unbind() {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001002 // Interrupt the applications loader before setting the adapter to null
1003 stopAndWaitForApplicationsLoader();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001004 mApplicationsAdapter = null;
1005 unbindAppDrawables(mApplications);
1006 unbindDrawables(mDesktopItems);
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001007 unbindAppWidgetHostViews(mDesktopAppWidgets);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001008 unbindCachedIconDrawables();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001009 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001010
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001011 /**
1012 * Remove the callback for the cached drawables or we leak the previous
1013 * Home screen on orientation change.
1014 */
1015 private void unbindDrawables(ArrayList<ItemInfo> desktopItems) {
1016 if (desktopItems != null) {
1017 final int count = desktopItems.size();
1018 for (int i = 0; i < count; i++) {
1019 ItemInfo item = desktopItems.get(i);
1020 switch (item.itemType) {
1021 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1022 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1023 ((ApplicationInfo)item).icon.setCallback(null);
1024 break;
1025 }
1026 }
1027 }
1028 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001029
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001030 /**
1031 * Remove the callback for the cached drawables or we leak the previous
1032 * Home screen on orientation change.
1033 */
1034 private void unbindAppDrawables(ArrayList<ApplicationInfo> applications) {
1035 if (applications != null) {
1036 final int count = applications.size();
1037 for (int i = 0; i < count; i++) {
1038 applications.get(i).icon.setCallback(null);
1039 }
1040 }
1041 }
1042
1043 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001044 * Remove any {@link LauncherAppWidgetHostView} references in our widgets.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001045 */
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001046 private void unbindAppWidgetHostViews(ArrayList<LauncherAppWidgetInfo> appWidgets) {
1047 if (appWidgets != null) {
1048 final int count = appWidgets.size();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001049 for (int i = 0; i < count; i++) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001050 LauncherAppWidgetInfo launcherInfo = appWidgets.get(i);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001051 launcherInfo.hostView = null;
1052 }
1053 }
1054 }
1055
1056 /**
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001057 * Remove the callback for the cached drawables or we leak the previous
1058 * Home screen on orientation change.
1059 */
1060 private void unbindCachedIconDrawables() {
1061 for (ApplicationInfo appInfo : mAppInfoCache.values()) {
1062 appInfo.icon.setCallback(null);
1063 }
1064 }
1065
1066 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001067 * @return The current list of applications
1068 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001069 ApplicationsAdapter getApplicationsAdapter() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001070 return mApplicationsAdapter;
1071 }
1072
1073 /**
1074 * @return The current list of desktop items
1075 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001076 ArrayList<ItemInfo> getDesktopItems() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001077 return mDesktopItems;
1078 }
1079
1080 /**
1081 * @return The current list of desktop items
1082 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001083 ArrayList<LauncherAppWidgetInfo> getDesktopAppWidgets() {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001084 return mDesktopAppWidgets;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001085 }
1086
1087 /**
1088 * Add an item to the desktop
1089 * @param info
1090 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001091 void addDesktopItem(ItemInfo info) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001092 // TODO: write to DB; also check that folder has been added to folders list
1093 mDesktopItems.add(info);
1094 }
1095
1096 /**
1097 * Remove an item from the desktop
1098 * @param info
1099 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001100 void removeDesktopItem(ItemInfo info) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001101 // TODO: write to DB; figure out if we should remove folder from folders list
1102 mDesktopItems.remove(info);
1103 }
1104
1105 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001106 * Add a widget to the desktop
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001107 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001108 void addDesktopAppWidget(LauncherAppWidgetInfo info) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001109 mDesktopAppWidgets.add(info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001110 }
1111
1112 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001113 * Remove a widget from the desktop
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001114 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001115 void removeDesktopAppWidget(LauncherAppWidgetInfo info) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001116 mDesktopAppWidgets.remove(info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001117 }
1118
1119 /**
1120 * Make an ApplicationInfo object for an application
1121 */
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -07001122 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent,
1123 Context context) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001124 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1125
1126 if (resolveInfo == null) {
1127 return null;
1128 }
1129
1130 final ApplicationInfo info = new ApplicationInfo();
1131 final ActivityInfo activityInfo = resolveInfo.activityInfo;
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -07001132 info.icon = Utilities.createIconThumbnail(activityInfo.loadIcon(manager), context);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001133 if (info.title == null || info.title.length() == 0) {
1134 info.title = activityInfo.loadLabel(manager);
1135 }
1136 if (info.title == null) {
1137 info.title = "";
1138 }
1139 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1140 return info;
1141 }
1142
1143 /**
1144 * Make an ApplicationInfo object for a sortcut
1145 */
Romain Guy73b979d2009-06-09 12:57:21 -07001146 private ApplicationInfo getApplicationInfoShortcut(Cursor c, Context context,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001147 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
1148
1149 final ApplicationInfo info = new ApplicationInfo();
1150 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
1151
1152 int iconType = c.getInt(iconTypeIndex);
1153 switch (iconType) {
1154 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1155 String packageName = c.getString(iconPackageIndex);
1156 String resourceName = c.getString(iconResourceIndex);
Romain Guy73b979d2009-06-09 12:57:21 -07001157 PackageManager packageManager = context.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001158 try {
1159 Resources resources = packageManager.getResourcesForApplication(packageName);
1160 final int id = resources.getIdentifier(resourceName, null, null);
Romain Guy73b979d2009-06-09 12:57:21 -07001161 info.icon = Utilities.createIconThumbnail(resources.getDrawable(id), context);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001162 } catch (Exception e) {
1163 info.icon = packageManager.getDefaultActivityIcon();
1164 }
1165 info.iconResource = new Intent.ShortcutIconResource();
1166 info.iconResource.packageName = packageName;
1167 info.iconResource.resourceName = resourceName;
1168 info.customIcon = false;
1169 break;
1170 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
1171 byte[] data = c.getBlob(iconIndex);
Romain Guyc2ad7a62009-05-14 17:43:39 -07001172 try {
1173 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
1174 info.icon = new FastBitmapDrawable(
Romain Guy73b979d2009-06-09 12:57:21 -07001175 Utilities.createBitmapThumbnail(bitmap, context));
Romain Guyc2ad7a62009-05-14 17:43:39 -07001176 } catch (Exception e) {
Romain Guy73b979d2009-06-09 12:57:21 -07001177 packageManager = context.getPackageManager();
Romain Guyc2ad7a62009-05-14 17:43:39 -07001178 info.icon = packageManager.getDefaultActivityIcon();
1179 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001180 info.filtered = true;
1181 info.customIcon = true;
1182 break;
1183 default:
Romain Guy73b979d2009-06-09 12:57:21 -07001184 info.icon = context.getPackageManager().getDefaultActivityIcon();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001185 info.customIcon = false;
1186 break;
1187 }
1188 return info;
1189 }
1190
1191 /**
1192 * Remove an item from the in-memory represention of a user folder. Does not change the DB.
1193 */
1194 void removeUserFolderItem(UserFolderInfo folder, ItemInfo info) {
1195 //noinspection SuspiciousMethodCalls
1196 folder.contents.remove(info);
1197 }
1198
1199 /**
1200 * Removes a UserFolder from the in-memory list of folders. Does not change the DB.
1201 * @param userFolderInfo
1202 */
1203 void removeUserFolder(UserFolderInfo userFolderInfo) {
1204 mFolders.remove(userFolderInfo.id);
1205 }
1206
1207 /**
1208 * Adds an item to the DB if it was not created previously, or move it to a new
1209 * <container, screen, cellX, cellY>
1210 */
1211 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
1212 int screen, int cellX, int cellY) {
1213 if (item.container == ItemInfo.NO_ID) {
1214 // From all apps
1215 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
1216 } else {
1217 // From somewhere else
1218 moveItemInDatabase(context, item, container, screen, cellX, cellY);
1219 }
1220 }
1221
1222 /**
1223 * Move an item in the DB to a new <container, screen, cellX, cellY>
1224 */
1225 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
1226 int cellX, int cellY) {
1227 item.container = container;
1228 item.screen = screen;
1229 item.cellX = cellX;
1230 item.cellY = cellY;
1231
1232 final ContentValues values = new ContentValues();
1233 final ContentResolver cr = context.getContentResolver();
1234
1235 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
1236 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
1237 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
1238 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
1239
1240 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
1241 }
1242
1243 /**
1244 * Returns true if the shortcuts already exists in the database.
1245 * we identify a shortcut by its title and intent.
1246 */
1247 static boolean shortcutExists(Context context, String title, Intent intent) {
1248 final ContentResolver cr = context.getContentResolver();
1249 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
1250 new String[] { "title", "intent" }, "title=? and intent=?",
1251 new String[] { title, intent.toURI() }, null);
1252 boolean result = false;
1253 try {
1254 result = c.moveToFirst();
1255 } finally {
1256 c.close();
1257 }
1258 return result;
1259 }
1260
1261 FolderInfo getFolderById(Context context, long id) {
1262 final ContentResolver cr = context.getContentResolver();
1263 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
Jeffrey Sharkey591d6d72009-03-27 19:45:21 -07001264 "_id=? and (itemType=? or itemType=?)",
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001265 new String[] { String.valueOf(id),
1266 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
1267 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
1268
1269 try {
1270 if (c.moveToFirst()) {
1271 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1272 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1273 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1274 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1275 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1276 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1277
1278 FolderInfo folderInfo = null;
1279 switch (c.getInt(itemTypeIndex)) {
1280 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1281 folderInfo = findOrMakeUserFolder(mFolders, id);
1282 break;
1283 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
1284 folderInfo = findOrMakeLiveFolder(mFolders, id);
1285 break;
1286 }
1287
1288 folderInfo.title = c.getString(titleIndex);
1289 folderInfo.id = id;
1290 folderInfo.container = c.getInt(containerIndex);
1291 folderInfo.screen = c.getInt(screenIndex);
1292 folderInfo.cellX = c.getInt(cellXIndex);
1293 folderInfo.cellY = c.getInt(cellYIndex);
1294
1295 return folderInfo;
1296 }
1297 } finally {
1298 c.close();
1299 }
1300
1301 return null;
1302 }
1303
1304 /**
1305 * Add an item to the database in a specified container. Sets the container, screen, cellX and
1306 * cellY fields of the item. Also assigns an ID to the item.
1307 */
1308 static void addItemToDatabase(Context context, ItemInfo item, long container,
1309 int screen, int cellX, int cellY, boolean notify) {
1310 item.container = container;
1311 item.screen = screen;
1312 item.cellX = cellX;
1313 item.cellY = cellY;
1314
1315 final ContentValues values = new ContentValues();
1316 final ContentResolver cr = context.getContentResolver();
1317
1318 item.onAddToDatabase(values);
1319
1320 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
1321 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
1322
1323 if (result != null) {
1324 item.id = Integer.parseInt(result.getPathSegments().get(1));
1325 }
1326 }
1327
1328 /**
Romain Guy73b979d2009-06-09 12:57:21 -07001329 * Add an item to the database in a specified container. Sets the container, screen, cellX and
1330 * cellY fields of the item. Also assigns an ID to the item.
1331 */
1332 static boolean addGestureToDatabase(Context context, ItemInfo item, boolean notify) {
1333 final ContentValues values = new ContentValues();
1334 final ContentResolver cr = context.getContentResolver();
1335
1336 item.onAddToDatabase(values);
1337
1338 Uri result = cr.insert(notify ? LauncherSettings.Gestures.CONTENT_URI :
1339 LauncherSettings.Gestures.CONTENT_URI_NO_NOTIFICATION, values);
1340
1341 if (result != null) {
1342 item.id = Integer.parseInt(result.getPathSegments().get(1));
1343 }
1344
1345 return result != null;
1346 }
1347
1348 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001349 * Update an item to the database in a specified container.
1350 */
1351 static void updateItemInDatabase(Context context, ItemInfo item) {
1352 final ContentValues values = new ContentValues();
1353 final ContentResolver cr = context.getContentResolver();
1354
1355 item.onAddToDatabase(values);
1356
1357 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
1358 }
1359
1360 /**
1361 * Removes the specified item from the database
1362 * @param context
1363 * @param item
1364 */
1365 static void deleteItemFromDatabase(Context context, ItemInfo item) {
1366 final ContentResolver cr = context.getContentResolver();
1367
1368 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
1369 }
1370
1371
1372 /**
1373 * Remove the contents of the specified folder from the database
1374 */
1375 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
1376 final ContentResolver cr = context.getContentResolver();
1377
1378 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
1379 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
1380 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
1381 }
Romain Guy73b979d2009-06-09 12:57:21 -07001382
1383 static void deleteGestureFromDatabase(Context context, ItemInfo item) {
1384 final ContentResolver cr = context.getContentResolver();
1385
1386 cr.delete(LauncherSettings.Gestures.getContentUri(item.id, false), null, null);
1387 }
1388
1389 static void updateGestureInDatabase(Context context, ItemInfo item) {
1390 final ContentValues values = new ContentValues();
1391 final ContentResolver cr = context.getContentResolver();
1392
1393 item.onAddToDatabase(values);
1394
1395 cr.update(LauncherSettings.Gestures.getContentUri(item.id, false), values, null, null);
1396 }
1397
1398
1399 ApplicationInfo queryGesture(Context context, String id) {
1400 final ContentResolver contentResolver = context.getContentResolver();
1401 final PackageManager manager = context.getPackageManager();
1402 final Cursor c = contentResolver.query(
1403 LauncherSettings.Gestures.CONTENT_URI, null, LauncherSettings.Gestures._ID + "=?",
1404 new String[] { id }, null);
1405
1406 ApplicationInfo info = null;
1407
1408 try {
1409 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures._ID);
1410 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.INTENT);
1411 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.TITLE);
1412 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON_TYPE);
1413 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON);
1414 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON_PACKAGE);
1415 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON_RESOURCE);
1416 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ITEM_TYPE);
1417
1418 String intentDescription;
1419 Intent intent;
1420
1421 if (c.moveToNext()) {
1422 int itemType = c.getInt(itemTypeIndex);
1423
1424 switch (itemType) {
1425 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1426 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1427 intentDescription = c.getString(intentIndex);
1428 try {
1429 intent = Intent.getIntent(intentDescription);
1430 } catch (java.net.URISyntaxException e) {
1431 return null;
1432 }
1433
1434 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
1435 info = getApplicationInfo(manager, intent, context);
1436 } else {
1437 info = getApplicationInfoShortcut(c, context, iconTypeIndex,
1438 iconPackageIndex, iconResourceIndex, iconIndex);
1439 }
1440
1441 if (info == null) {
1442 info = new ApplicationInfo();
1443 info.icon = manager.getDefaultActivityIcon();
1444 }
1445
1446 info.isGesture = true;
1447 info.title = c.getString(titleIndex);
1448 info.intent = intent;
1449 info.id = c.getLong(idIndex);
1450
1451 break;
1452 }
1453 }
1454 } catch (Exception e) {
1455 w(LOG_TAG, "Could not load gesture with name " + id);
1456 } finally {
1457 c.close();
1458 }
1459
1460 return info;
1461 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001462}