blob: da41f149964c1cd927237ff03ce9389f1706f0c1 [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);
177 adapter.add(makeAndCacheApplicationInfo(packageManager, cache, info));
178 }
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) {
242 updateAndCacheApplicationInfo(packageManager, info, applicationInfo);
243 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,
255 ApplicationInfo applicationInfo) {
256
257 updateApplicationInfoTitleAndIcon(packageManager, info, applicationInfo);
258
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(),
329 mAppInfoCache, info));
330 changed = true;
331 } else {
332 updateAndCacheApplicationInfo(launcher.getPackageManager(), info, applicationInfo);
333 changed = true;
334 }
335 }
336
337 for (ApplicationInfo info : toAdd) {
338 adapter.setNotifyOnChange(false);
339 adapter.add(info);
340 }
341
342 return changed;
343 }
344
345 private boolean removeDisabledActivities(String packageName, List<ResolveInfo> matches,
346 ApplicationsAdapter adapter) {
347
348 final List<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>();
349 final int count = adapter.getCount();
350
351 boolean changed = false;
352
353 for (int i = 0; i < count; i++) {
354 final ApplicationInfo applicationInfo = adapter.getItem(i);
355 final Intent intent = applicationInfo.intent;
356 final ComponentName component = intent.getComponent();
357 if (packageName.equals(component.getPackageName())) {
358 if (!findIntent(matches, component)) {
359 toRemove.add(applicationInfo);
360 changed = true;
361 }
362 }
363 }
364
365 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
366 for (ApplicationInfo info : toRemove) {
367 adapter.setNotifyOnChange(false);
368 adapter.remove(info);
369 cache.remove(info.intent.getComponent());
370 }
371
372 return changed;
373 }
374
375 private static ApplicationInfo findIntent(ApplicationsAdapter adapter, String packageName,
376 String name) {
377
378 final int count = adapter.getCount();
379 for (int i = 0; i < count; i++) {
380 final ApplicationInfo applicationInfo = adapter.getItem(i);
381 final Intent intent = applicationInfo.intent;
382 final ComponentName component = intent.getComponent();
383 if (packageName.equals(component.getPackageName()) &&
384 name.equals(component.getClassName())) {
385 return applicationInfo;
386 }
387 }
388
389 return null;
390 }
391
392 private static boolean findIntent(List<ResolveInfo> apps, ComponentName component) {
393 final String className = component.getClassName();
394 for (ResolveInfo info : apps) {
395 final ActivityInfo activityInfo = info.activityInfo;
396 if (activityInfo.name.equals(className)) {
397 return true;
398 }
399 }
400 return false;
401 }
402
403 Drawable getApplicationInfoIcon(PackageManager manager, ApplicationInfo info) {
404 final ResolveInfo resolveInfo = manager.resolveActivity(info.intent, 0);
405 if (resolveInfo == null) {
406 return null;
407 }
408
409 ComponentName componentName = new ComponentName(
410 resolveInfo.activityInfo.applicationInfo.packageName,
411 resolveInfo.activityInfo.name);
412 ApplicationInfo application = mAppInfoCache.get(componentName);
413
414 if (application == null) {
415 return resolveInfo.activityInfo.loadIcon(manager);
416 }
417
418 return application.icon;
419 }
420
421 private static ApplicationInfo makeAndCacheApplicationInfo(PackageManager manager,
422 HashMap<ComponentName, ApplicationInfo> appInfoCache, ResolveInfo info) {
423
424 ComponentName componentName = new ComponentName(
425 info.activityInfo.applicationInfo.packageName,
426 info.activityInfo.name);
427 ApplicationInfo application = appInfoCache.get(componentName);
428
429 if (application == null) {
430 application = new ApplicationInfo();
431 application.container = ItemInfo.NO_ID;
432
433 updateApplicationInfoTitleAndIcon(manager, info, application);
434
435 application.setActivity(componentName,
436 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
437
438 appInfoCache.put(componentName, application);
439 }
440
441 return application;
442 }
443
444 private static void updateApplicationInfoTitleAndIcon(PackageManager manager, ResolveInfo info,
445 ApplicationInfo application) {
446
447 application.title = info.loadLabel(manager);
448 if (application.title == null) {
449 application.title = info.activityInfo.name;
450 }
451
452 application.icon = info.activityInfo.loadIcon(manager);
453 application.filtered = false;
454 }
455
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800456 private class ApplicationsLoader implements Runnable {
457 private final WeakReference<Launcher> mLauncher;
458
459 private volatile boolean mStopped;
460 private volatile boolean mRunning;
461
462 ApplicationsLoader(Launcher launcher) {
463 mLauncher = new WeakReference<Launcher>(launcher);
464 }
465
466 void stop() {
467 mStopped = true;
468 }
469
470 boolean isRunning() {
471 return mRunning;
472 }
473
474 public void run() {
475 mRunning = true;
476
477 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
478
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700479 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800480 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
481
482 final Launcher launcher = mLauncher.get();
483 final PackageManager manager = launcher.getPackageManager();
484 final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
485
486 if (apps != null && !mStopped) {
487 final int count = apps.size();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700488 // Can be set to null on the UI thread by the unbind() method
489 // Do not access without checking for null first
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800490 final ApplicationsAdapter applicationList = mApplicationsAdapter;
491
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700492 ChangeNotifier action = new ChangeNotifier(applicationList, true);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700493 final HashMap<ComponentName, ApplicationInfo> appInfoCache = mAppInfoCache;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800494
495 for (int i = 0; i < count && !mStopped; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800496 ResolveInfo info = apps.get(i);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700497 ApplicationInfo application =
498 makeAndCacheApplicationInfo(manager, appInfoCache, info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800499
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700500 if (action.add(application) && !mStopped) {
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700501 launcher.runOnUiThread(action);
502 action = new ChangeNotifier(applicationList, false);
503 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800504 }
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700505
506 launcher.runOnUiThread(action);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800507 }
508
509 if (!mStopped) {
510 mApplicationsLoaded = true;
511 }
512 mRunning = false;
513 }
514 }
515
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700516 private static class ChangeNotifier implements Runnable {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800517 private final ApplicationsAdapter mApplicationList;
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700518 private final ArrayList<ApplicationInfo> mBuffer;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800519
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700520 private boolean mFirst = true;
521
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700522 ChangeNotifier(ApplicationsAdapter applicationList, boolean first) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800523 mApplicationList = applicationList;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700524 mFirst = first;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800525 mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
526 }
527
528 public void run() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800529 final ApplicationsAdapter applicationList = mApplicationList;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700530 // Can be set to null on the UI thread by the unbind() method
531 if (applicationList == null) return;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800532
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700533 if (mFirst) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800534 applicationList.setNotifyOnChange(false);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700535 applicationList.clear();
536 mFirst = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800537 }
538
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700539 final ArrayList<ApplicationInfo> buffer = mBuffer;
540 final int count = buffer.size();
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700541
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700542 for (int i = 0; i < count; i++) {
543 applicationList.setNotifyOnChange(false);
544 applicationList.add(buffer.get(i));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700545 }
546
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700547 buffer.clear();
548
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700549 applicationList.sort(new ApplicationInfoComparator());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800550 applicationList.notifyDataSetChanged();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800551 }
552
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700553 boolean add(ApplicationInfo application) {
554 final ArrayList<ApplicationInfo> buffer = mBuffer;
555 buffer.add(application);
556 return buffer.size() >= UI_NOTIFICATION_RATE;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800557 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700558 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800559
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700560 private static class ApplicationInfoComparator implements Comparator<ApplicationInfo> {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700561 public final int compare(ApplicationInfo a, ApplicationInfo b) {
562 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800563 }
564 }
565
566 boolean isDesktopLoaded() {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700567 return mDesktopItems != null && mDesktopAppWidgets != null && mDesktopItemsLoaded;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800568 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700569
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800570 /**
571 * Loads all of the items on the desktop, in folders, or in the dock.
572 * These can be apps, shortcuts or widgets
573 */
574 void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
575 boolean loadApplications) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700576 if (DEBUG_LOADERS) d(LOG_TAG, "loading user items");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800577
578 if (isLaunching && isDesktopLoaded()) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700579 if (DEBUG_LOADERS) d(LOG_TAG, " --> items loaded, return");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800580 if (loadApplications) startApplicationsLoader(launcher);
581 // We have already loaded our data from the DB
582 launcher.onDesktopItemsLoaded();
583 return;
584 }
585
586 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
Romain Guyfedc4fc2009-03-27 20:48:20 -0700587 if (DEBUG_LOADERS) d(LOG_TAG, " --> stopping workspace loader");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800588 mDesktopItemsLoader.stop();
589 // Wait for the currently running thread to finish, this can take a little
590 // time but it should be well below the timeout limit
591 try {
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700592 mDesktopLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800593 } catch (InterruptedException e) {
594 // Empty
595 }
Romain Guyfedc4fc2009-03-27 20:48:20 -0700596
597 // If the thread we are interrupting was tasked to load the list of
598 // applications make sure we keep that information in the new loader
599 // spawned below
600 // note: we don't apply this to localeChanged because the thread can
601 // only be stopped *after* the localeChanged handling has occured
602 loadApplications = mDesktopItemsLoader.mLoadApplications;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800603 }
604
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700605 if (DEBUG_LOADERS) d(LOG_TAG, " --> starting workspace loader");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800606 mDesktopItemsLoaded = false;
607 mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700608 mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
609 mDesktopLoaderThread.start();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800610 }
611
612 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
613 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
614 new String[] { LauncherSettings.Favorites.ID, LauncherSettings.Favorites.TITLE,
615 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
616 null, null, null);
617
618 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
619 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
620 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
621 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
622
623 // boolean changed = false;
624
625 try {
626 while (c.moveToNext()) {
627 try {
628 if (c.getInt(itemTypeIndex) !=
629 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
630 continue;
631 }
632
633 final String intentUri = c.getString(intentIndex);
634 if (intentUri != null) {
635 final Intent shortcut = Intent.getIntent(intentUri);
636 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
637 final ComponentName name = shortcut.getComponent();
638 if (name != null) {
639 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
640 final String title = c.getString(titleIndex);
641 String label = getLabel(manager, activityInfo);
642
643 if (title == null || !title.equals(label)) {
644 final ContentValues values = new ContentValues();
645 values.put(LauncherSettings.Favorites.TITLE, label);
646
Romain Guyfedc4fc2009-03-27 20:48:20 -0700647 resolver.update(
648 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800649 values, "_id=?",
650 new String[] { String.valueOf(c.getLong(idIndex)) });
651
652 // changed = true;
653 }
654 }
655 }
656 }
657 } catch (URISyntaxException e) {
658 // Ignore
659 } catch (PackageManager.NameNotFoundException e) {
660 // Ignore
661 }
662 }
663 } finally {
664 c.close();
665 }
666
667 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
668 }
669
670 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
671 String label = activityInfo.loadLabel(manager).toString();
672 if (label == null) {
673 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
674 if (label == null) {
675 label = activityInfo.name;
676 }
677 }
678 return label;
679 }
680
681 private class DesktopItemsLoader implements Runnable {
682 private volatile boolean mStopped;
683 private volatile boolean mRunning;
684
685 private final WeakReference<Launcher> mLauncher;
686 private final boolean mLocaleChanged;
687 private final boolean mLoadApplications;
688
689 DesktopItemsLoader(Launcher launcher, boolean localeChanged, boolean loadApplications) {
690 mLoadApplications = loadApplications;
691 mLauncher = new WeakReference<Launcher>(launcher);
692 mLocaleChanged = localeChanged;
693 }
694
695 void stop() {
696 mStopped = true;
697 }
698
699 boolean isRunning() {
700 return mRunning;
701 }
702
703 public void run() {
704 mRunning = true;
705
706 final Launcher launcher = mLauncher.get();
707 final ContentResolver contentResolver = launcher.getContentResolver();
708 final PackageManager manager = launcher.getPackageManager();
709
710 if (mLocaleChanged) {
711 updateShortcutLabels(contentResolver, manager);
712 }
713
714 mDesktopItems = new ArrayList<ItemInfo>();
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700715 mDesktopAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800716 mFolders = new HashMap<Long, FolderInfo>();
717
718 final ArrayList<ItemInfo> desktopItems = mDesktopItems;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700719 final ArrayList<LauncherAppWidgetInfo> desktopAppWidgets = mDesktopAppWidgets;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800720
721 final Cursor c = contentResolver.query(
722 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
723
724 try {
725 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
726 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
727 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
728 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
729 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
730 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
731 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
732 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
733 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700734 final int appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWIDGET_ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800735 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
736 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
737 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
738 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
739 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
740 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
741 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
742
743 ApplicationInfo info;
744 String intentDescription;
745 Widget widgetInfo;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700746 LauncherAppWidgetInfo appWidgetInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800747 int container;
748 long id;
749 Intent intent;
750
751 final HashMap<Long, FolderInfo> folders = mFolders;
752
753 while (!mStopped && c.moveToNext()) {
754 try {
755 int itemType = c.getInt(itemTypeIndex);
756
757 switch (itemType) {
758 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
759 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
760 intentDescription = c.getString(intentIndex);
761 try {
762 intent = Intent.getIntent(intentDescription);
763 } catch (java.net.URISyntaxException e) {
764 continue;
765 }
766
767 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
768 info = getApplicationInfo(manager, intent);
769 } else {
770 info = getApplicationInfoShortcut(c, launcher, iconTypeIndex,
771 iconPackageIndex, iconResourceIndex, iconIndex);
772 }
773
774 if (info == null) {
775 info = new ApplicationInfo();
776 info.icon = manager.getDefaultActivityIcon();
777 }
778
779 if (info != null) {
780 info.title = c.getString(titleIndex);
781 info.intent = intent;
782
783 info.id = c.getLong(idIndex);
784 container = c.getInt(containerIndex);
785 info.container = container;
786 info.screen = c.getInt(screenIndex);
787 info.cellX = c.getInt(cellXIndex);
788 info.cellY = c.getInt(cellYIndex);
789
790 switch (container) {
791 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
792 desktopItems.add(info);
793 break;
794 default:
795 // Item is in a user folder
796 UserFolderInfo folderInfo =
797 findOrMakeUserFolder(folders, container);
798 folderInfo.add(info);
799 break;
800 }
801 }
802 break;
803 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
804
805 id = c.getLong(idIndex);
806 UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id);
807
808 folderInfo.title = c.getString(titleIndex);
809
810 folderInfo.id = id;
811 container = c.getInt(containerIndex);
812 folderInfo.container = container;
813 folderInfo.screen = c.getInt(screenIndex);
814 folderInfo.cellX = c.getInt(cellXIndex);
815 folderInfo.cellY = c.getInt(cellYIndex);
816
817 switch (container) {
818 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
819 desktopItems.add(folderInfo);
820 break;
821 }
822 break;
823 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
824
825 id = c.getLong(idIndex);
826 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id);
827
828 intentDescription = c.getString(intentIndex);
829 intent = null;
830 if (intentDescription != null) {
831 try {
832 intent = Intent.getIntent(intentDescription);
833 } catch (java.net.URISyntaxException e) {
834 // Ignore, a live folder might not have a base intent
835 }
836 }
837
838 liveFolderInfo.title = c.getString(titleIndex);
839 liveFolderInfo.id = id;
840 container = c.getInt(containerIndex);
841 liveFolderInfo.container = container;
842 liveFolderInfo.screen = c.getInt(screenIndex);
843 liveFolderInfo.cellX = c.getInt(cellXIndex);
844 liveFolderInfo.cellY = c.getInt(cellYIndex);
845 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
846 liveFolderInfo.baseIntent = intent;
847 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
848
849 loadLiveFolderIcon(launcher, c, iconTypeIndex, iconPackageIndex,
850 iconResourceIndex, liveFolderInfo);
851
852 switch (container) {
853 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
854 desktopItems.add(liveFolderInfo);
855 break;
856 }
857 break;
858 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
859 widgetInfo = Widget.makeSearch();
860
861 container = c.getInt(containerIndex);
862 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700863 e(Launcher.LOG_TAG, "Widget found where container "
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800864 + "!= CONTAINER_DESKTOP ignoring!");
865 continue;
866 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700867
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800868 widgetInfo.id = c.getLong(idIndex);
869 widgetInfo.screen = c.getInt(screenIndex);
870 widgetInfo.container = container;
871 widgetInfo.cellX = c.getInt(cellXIndex);
872 widgetInfo.cellY = c.getInt(cellYIndex);
873
874 desktopItems.add(widgetInfo);
875 break;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700876 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
877 // Read all Launcher-specific widget details
878 int appWidgetId = c.getInt(appWidgetIdIndex);
879 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
880 appWidgetInfo.id = c.getLong(idIndex);
881 appWidgetInfo.screen = c.getInt(screenIndex);
882 appWidgetInfo.cellX = c.getInt(cellXIndex);
883 appWidgetInfo.cellY = c.getInt(cellYIndex);
884 appWidgetInfo.spanX = c.getInt(spanXIndex);
885 appWidgetInfo.spanY = c.getInt(spanYIndex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800886
887 container = c.getInt(containerIndex);
888 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700889 e(Launcher.LOG_TAG, "Widget found where container "
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800890 + "!= CONTAINER_DESKTOP -- ignoring!");
891 continue;
892 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700893 appWidgetInfo.container = c.getInt(containerIndex);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700894
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700895 desktopAppWidgets.add(appWidgetInfo);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800896 break;
897 }
898 } catch (Exception e) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700899 w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800900 }
901 }
902 } finally {
903 c.close();
904 }
905
906 if (!mStopped) {
907 launcher.runOnUiThread(new Runnable() {
908 public void run() {
909 launcher.onDesktopItemsLoaded();
910 }
911 });
912 if (mLoadApplications) startApplicationsLoader(launcher);
913 }
914
915 if (!mStopped) {
916 mDesktopItemsLoaded = true;
917 }
918 mRunning = false;
919 }
920 }
921
922 private static void loadLiveFolderIcon(Launcher launcher, Cursor c, int iconTypeIndex,
923 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
924
925 int iconType = c.getInt(iconTypeIndex);
926 switch (iconType) {
927 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
928 String packageName = c.getString(iconPackageIndex);
929 String resourceName = c.getString(iconResourceIndex);
930 PackageManager packageManager = launcher.getPackageManager();
931 try {
932 Resources resources = packageManager.getResourcesForApplication(packageName);
933 final int id = resources.getIdentifier(resourceName, null, null);
934 liveFolderInfo.icon = resources.getDrawable(id);
935 } catch (Exception e) {
936 liveFolderInfo.icon =
937 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
938 }
939 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
940 liveFolderInfo.iconResource.packageName = packageName;
941 liveFolderInfo.iconResource.resourceName = resourceName;
942 break;
943 default:
944 liveFolderInfo.icon =
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700945 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800946 }
947 }
948
949 /**
950 * Finds the user folder defined by the specified id.
951 *
952 * @param id The id of the folder to look for.
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700953 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800954 * @return A UserFolderInfo if the folder exists or null otherwise.
955 */
956 FolderInfo findFolderById(long id) {
957 return mFolders.get(id);
958 }
959
960 void addFolder(FolderInfo info) {
961 mFolders.put(info.id, info);
962 }
963
964 /**
965 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
966 * new one.
967 */
968 private UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
969 // See if a placeholder was created for us already
970 FolderInfo folderInfo = folders.get(id);
971 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
972 // No placeholder -- create a new instance
973 folderInfo = new UserFolderInfo();
974 folders.put(id, folderInfo);
975 }
976 return (UserFolderInfo) folderInfo;
977 }
978
979 /**
980 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
981 * new one.
982 */
983 private LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
984 // See if a placeholder was created for us already
985 FolderInfo folderInfo = folders.get(id);
986 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
987 // No placeholder -- create a new instance
988 folderInfo = new LiveFolderInfo();
989 folders.put(id, folderInfo);
990 }
991 return (LiveFolderInfo) folderInfo;
992 }
993
994 /**
995 * Remove the callback for the cached drawables or we leak the previous
996 * Home screen on orientation change.
997 */
998 void unbind() {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700999 // Interrupt the applications loader before setting the adapter to null
1000 stopAndWaitForApplicationsLoader();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001001 mApplicationsAdapter = null;
1002 unbindAppDrawables(mApplications);
1003 unbindDrawables(mDesktopItems);
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001004 unbindAppWidgetHostViews(mDesktopAppWidgets);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001005 unbindCachedIconDrawables();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001006 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001007
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001008 /**
1009 * Remove the callback for the cached drawables or we leak the previous
1010 * Home screen on orientation change.
1011 */
1012 private void unbindDrawables(ArrayList<ItemInfo> desktopItems) {
1013 if (desktopItems != null) {
1014 final int count = desktopItems.size();
1015 for (int i = 0; i < count; i++) {
1016 ItemInfo item = desktopItems.get(i);
1017 switch (item.itemType) {
1018 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1019 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1020 ((ApplicationInfo)item).icon.setCallback(null);
1021 break;
1022 }
1023 }
1024 }
1025 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001026
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001027 /**
1028 * Remove the callback for the cached drawables or we leak the previous
1029 * Home screen on orientation change.
1030 */
1031 private void unbindAppDrawables(ArrayList<ApplicationInfo> applications) {
1032 if (applications != null) {
1033 final int count = applications.size();
1034 for (int i = 0; i < count; i++) {
1035 applications.get(i).icon.setCallback(null);
1036 }
1037 }
1038 }
1039
1040 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001041 * Remove any {@link LauncherAppWidgetHostView} references in our widgets.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001042 */
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001043 private void unbindAppWidgetHostViews(ArrayList<LauncherAppWidgetInfo> appWidgets) {
1044 if (appWidgets != null) {
1045 final int count = appWidgets.size();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001046 for (int i = 0; i < count; i++) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001047 LauncherAppWidgetInfo launcherInfo = appWidgets.get(i);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001048 launcherInfo.hostView = null;
1049 }
1050 }
1051 }
1052
1053 /**
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001054 * Remove the callback for the cached drawables or we leak the previous
1055 * Home screen on orientation change.
1056 */
1057 private void unbindCachedIconDrawables() {
1058 for (ApplicationInfo appInfo : mAppInfoCache.values()) {
1059 appInfo.icon.setCallback(null);
1060 }
1061 }
1062
1063 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001064 * @return The current list of applications
1065 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001066 ApplicationsAdapter getApplicationsAdapter() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001067 return mApplicationsAdapter;
1068 }
1069
1070 /**
1071 * @return The current list of desktop items
1072 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001073 ArrayList<ItemInfo> getDesktopItems() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001074 return mDesktopItems;
1075 }
1076
1077 /**
1078 * @return The current list of desktop items
1079 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001080 ArrayList<LauncherAppWidgetInfo> getDesktopAppWidgets() {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001081 return mDesktopAppWidgets;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001082 }
1083
1084 /**
1085 * Add an item to the desktop
1086 * @param info
1087 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001088 void addDesktopItem(ItemInfo info) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001089 // TODO: write to DB; also check that folder has been added to folders list
1090 mDesktopItems.add(info);
1091 }
1092
1093 /**
1094 * Remove an item from the desktop
1095 * @param info
1096 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001097 void removeDesktopItem(ItemInfo info) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001098 // TODO: write to DB; figure out if we should remove folder from folders list
1099 mDesktopItems.remove(info);
1100 }
1101
1102 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001103 * Add a widget to the desktop
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001104 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001105 void addDesktopAppWidget(LauncherAppWidgetInfo info) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001106 mDesktopAppWidgets.add(info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001107 }
1108
1109 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001110 * Remove a widget from the desktop
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001111 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001112 void removeDesktopAppWidget(LauncherAppWidgetInfo info) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001113 mDesktopAppWidgets.remove(info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001114 }
1115
1116 /**
1117 * Make an ApplicationInfo object for an application
1118 */
1119 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) {
1120 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1121
1122 if (resolveInfo == null) {
1123 return null;
1124 }
1125
1126 final ApplicationInfo info = new ApplicationInfo();
1127 final ActivityInfo activityInfo = resolveInfo.activityInfo;
1128 info.icon = activityInfo.loadIcon(manager);
1129 if (info.title == null || info.title.length() == 0) {
1130 info.title = activityInfo.loadLabel(manager);
1131 }
1132 if (info.title == null) {
1133 info.title = "";
1134 }
1135 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1136 return info;
1137 }
1138
1139 /**
1140 * Make an ApplicationInfo object for a sortcut
1141 */
1142 private ApplicationInfo getApplicationInfoShortcut(Cursor c, Launcher launcher,
1143 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
1144
1145 final ApplicationInfo info = new ApplicationInfo();
1146 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
1147
1148 int iconType = c.getInt(iconTypeIndex);
1149 switch (iconType) {
1150 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1151 String packageName = c.getString(iconPackageIndex);
1152 String resourceName = c.getString(iconResourceIndex);
1153 PackageManager packageManager = launcher.getPackageManager();
1154 try {
1155 Resources resources = packageManager.getResourcesForApplication(packageName);
1156 final int id = resources.getIdentifier(resourceName, null, null);
1157 info.icon = resources.getDrawable(id);
1158 } catch (Exception e) {
1159 info.icon = packageManager.getDefaultActivityIcon();
1160 }
1161 info.iconResource = new Intent.ShortcutIconResource();
1162 info.iconResource.packageName = packageName;
1163 info.iconResource.resourceName = resourceName;
1164 info.customIcon = false;
1165 break;
1166 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
1167 byte[] data = c.getBlob(iconIndex);
1168 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
1169 info.icon = new FastBitmapDrawable(
1170 Utilities.createBitmapThumbnail(bitmap, launcher));
1171 info.filtered = true;
1172 info.customIcon = true;
1173 break;
1174 default:
1175 info.icon = launcher.getPackageManager().getDefaultActivityIcon();
1176 info.customIcon = false;
1177 break;
1178 }
1179 return info;
1180 }
1181
1182 /**
1183 * Remove an item from the in-memory represention of a user folder. Does not change the DB.
1184 */
1185 void removeUserFolderItem(UserFolderInfo folder, ItemInfo info) {
1186 //noinspection SuspiciousMethodCalls
1187 folder.contents.remove(info);
1188 }
1189
1190 /**
1191 * Removes a UserFolder from the in-memory list of folders. Does not change the DB.
1192 * @param userFolderInfo
1193 */
1194 void removeUserFolder(UserFolderInfo userFolderInfo) {
1195 mFolders.remove(userFolderInfo.id);
1196 }
1197
1198 /**
1199 * Adds an item to the DB if it was not created previously, or move it to a new
1200 * <container, screen, cellX, cellY>
1201 */
1202 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
1203 int screen, int cellX, int cellY) {
1204 if (item.container == ItemInfo.NO_ID) {
1205 // From all apps
1206 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
1207 } else {
1208 // From somewhere else
1209 moveItemInDatabase(context, item, container, screen, cellX, cellY);
1210 }
1211 }
1212
1213 /**
1214 * Move an item in the DB to a new <container, screen, cellX, cellY>
1215 */
1216 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
1217 int cellX, int cellY) {
1218 item.container = container;
1219 item.screen = screen;
1220 item.cellX = cellX;
1221 item.cellY = cellY;
1222
1223 final ContentValues values = new ContentValues();
1224 final ContentResolver cr = context.getContentResolver();
1225
1226 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
1227 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
1228 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
1229 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
1230
1231 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
1232 }
1233
1234 /**
1235 * Returns true if the shortcuts already exists in the database.
1236 * we identify a shortcut by its title and intent.
1237 */
1238 static boolean shortcutExists(Context context, String title, Intent intent) {
1239 final ContentResolver cr = context.getContentResolver();
1240 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
1241 new String[] { "title", "intent" }, "title=? and intent=?",
1242 new String[] { title, intent.toURI() }, null);
1243 boolean result = false;
1244 try {
1245 result = c.moveToFirst();
1246 } finally {
1247 c.close();
1248 }
1249 return result;
1250 }
1251
1252 FolderInfo getFolderById(Context context, long id) {
1253 final ContentResolver cr = context.getContentResolver();
1254 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
Jeffrey Sharkey591d6d72009-03-27 19:45:21 -07001255 "_id=? and (itemType=? or itemType=?)",
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001256 new String[] { String.valueOf(id),
1257 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
1258 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
1259
1260 try {
1261 if (c.moveToFirst()) {
1262 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1263 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1264 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1265 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1266 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1267 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1268
1269 FolderInfo folderInfo = null;
1270 switch (c.getInt(itemTypeIndex)) {
1271 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1272 folderInfo = findOrMakeUserFolder(mFolders, id);
1273 break;
1274 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
1275 folderInfo = findOrMakeLiveFolder(mFolders, id);
1276 break;
1277 }
1278
1279 folderInfo.title = c.getString(titleIndex);
1280 folderInfo.id = id;
1281 folderInfo.container = c.getInt(containerIndex);
1282 folderInfo.screen = c.getInt(screenIndex);
1283 folderInfo.cellX = c.getInt(cellXIndex);
1284 folderInfo.cellY = c.getInt(cellYIndex);
1285
1286 return folderInfo;
1287 }
1288 } finally {
1289 c.close();
1290 }
1291
1292 return null;
1293 }
1294
1295 /**
1296 * Add an item to the database in a specified container. Sets the container, screen, cellX and
1297 * cellY fields of the item. Also assigns an ID to the item.
1298 */
1299 static void addItemToDatabase(Context context, ItemInfo item, long container,
1300 int screen, int cellX, int cellY, boolean notify) {
1301 item.container = container;
1302 item.screen = screen;
1303 item.cellX = cellX;
1304 item.cellY = cellY;
1305
1306 final ContentValues values = new ContentValues();
1307 final ContentResolver cr = context.getContentResolver();
1308
1309 item.onAddToDatabase(values);
1310
1311 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
1312 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
1313
1314 if (result != null) {
1315 item.id = Integer.parseInt(result.getPathSegments().get(1));
1316 }
1317 }
1318
1319 /**
1320 * Update an item to the database in a specified container.
1321 */
1322 static void updateItemInDatabase(Context context, ItemInfo item) {
1323 final ContentValues values = new ContentValues();
1324 final ContentResolver cr = context.getContentResolver();
1325
1326 item.onAddToDatabase(values);
1327
1328 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
1329 }
1330
1331 /**
1332 * Removes the specified item from the database
1333 * @param context
1334 * @param item
1335 */
1336 static void deleteItemFromDatabase(Context context, ItemInfo item) {
1337 final ContentResolver cr = context.getContentResolver();
1338
1339 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
1340 }
1341
1342
1343 /**
1344 * Remove the contents of the specified folder from the database
1345 */
1346 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
1347 final ContentResolver cr = context.getContentResolver();
1348
1349 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
1350 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
1351 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
1352 }
1353}