blob: 87cb9ea0807e14c1372f57bbab1a30bb64d1dc60 [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 Projectbc219c32009-03-09 11:52:14 -070031import android.graphics.drawable.Drawable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.net.Uri;
33import android.util.Log;
34import android.os.Process;
35
36import java.util.ArrayList;
37import java.util.Collections;
38import java.util.HashMap;
39import java.util.List;
40import java.util.Comparator;
41import java.lang.ref.WeakReference;
42import java.text.Collator;
43import java.net.URISyntaxException;
44
45/**
46 * Maintains in-memory state of the Launcher. It is expected that there should be only one
47 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070048 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049 */
50public class LauncherModel {
51 private static final int UI_NOTIFICATION_RATE = 4;
52 private static final int DEFAULT_APPLICATIONS_NUMBER = 42;
53 private static final long APPLICATION_NOT_RESPONDING_TIMEOUT = 5000;
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070054 private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080055
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070056 private static final boolean DEBUG = false;
57
58 private 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;
64 private ArrayList<LauncherGadgetInfo> mDesktopGadgets;
65 private HashMap<Long, FolderInfo> mFolders;
66
67 private ArrayList<ApplicationInfo> mApplications;
68 private ApplicationsAdapter mApplicationsAdapter;
69 private ApplicationsLoader mApplicationsLoader;
70 private DesktopItemsLoader mDesktopItemsLoader;
71 private Thread mLoader;
72 private Thread mDesktopLoader;
73
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 Project31dd5032009-03-03 19:32:27 -080077 void abortLoaders() {
78 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
79 mApplicationsLoader.stop();
80 mApplicationsLoaded = false;
81 }
82 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
83 mDesktopItemsLoader.stop();
84 mDesktopItemsLoaded = false;
85 }
86 }
87
88 /**
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070089 * Drop our cache of components to their lables & icons. We do
90 * this from Launcher when applications are added/removed. It's a
91 * bit overkill, but it's a rare operation anyway.
92 */
93 synchronized void dropApplicationCache() {
94 mAppInfoCache.clear();
95 }
96
97 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -080098 * Loads the list of installed applications in mApplications.
99 */
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700100 synchronized void loadApplications(boolean isLaunching, Launcher launcher,
101 boolean localeChanged) {
102 if (localeChanged) {
103 dropApplicationCache();
104 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800105 if (isLaunching && mApplicationsLoaded && !localeChanged) {
106 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
107 return;
108 }
109
110 if (mApplicationsAdapter == null || isLaunching || localeChanged) {
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700111 mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
112 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113 }
114
115 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
116 mApplicationsLoader.stop();
117 // Wait for the currently running thread to finish, this can take a little
118 // time but it should be well below the timeout limit
119 try {
120 mLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
121 } catch (InterruptedException e) {
122 // Empty
123 }
124 }
125
126 mApplicationsLoaded = false;
127
128 if (!isLaunching) {
129 startApplicationsLoader(launcher);
130 }
131 }
132
133 private void startApplicationsLoader(Launcher launcher) {
134 mApplicationsLoader = new ApplicationsLoader(launcher);
135 mLoader = new Thread(mApplicationsLoader, "Applications Loader");
136 mLoader.start();
137 }
138
139 private class ApplicationsLoader implements Runnable {
140 private final WeakReference<Launcher> mLauncher;
141
142 private volatile boolean mStopped;
143 private volatile boolean mRunning;
144
145 ApplicationsLoader(Launcher launcher) {
146 mLauncher = new WeakReference<Launcher>(launcher);
147 }
148
149 void stop() {
150 mStopped = true;
151 }
152
153 boolean isRunning() {
154 return mRunning;
155 }
156
157 public void run() {
158 mRunning = true;
159
160 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
161
162 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
163 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
164
165 final Launcher launcher = mLauncher.get();
166 final PackageManager manager = launcher.getPackageManager();
167 final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
168
169 if (apps != null && !mStopped) {
170 final int count = apps.size();
171 final ApplicationsAdapter applicationList = mApplicationsAdapter;
172
173 ChangeNotifier action = new ChangeNotifier(applicationList);
174
175 for (int i = 0; i < count && !mStopped; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800176 ResolveInfo info = apps.get(i);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700177 ComponentName componentName = new ComponentName(
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800178 info.activityInfo.applicationInfo.packageName,
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700179 info.activityInfo.name);
180 ApplicationInfo application = mAppInfoCache.get(componentName);
181 if (application == null) {
182 application = new ApplicationInfo();
183 application.title = info.loadLabel(manager);
184 if (application.title == null) {
185 application.title = info.activityInfo.name;
186 }
187 application.setActivity(componentName,
188 Intent.FLAG_ACTIVITY_NEW_TASK |
189 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
190 application.container = ItemInfo.NO_ID;
191 application.icon = info.activityInfo.loadIcon(manager);
192 if (DEBUG) {
193 Log.d(Launcher.LOG_TAG, "Loaded ApplicationInfo for " + componentName);
194 }
195 mAppInfoCache.put(componentName, application);
196 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800197
198 action.add(application);
199 }
200
201 action.sort(new Comparator<ApplicationInfo>() {
202 public final int compare(ApplicationInfo a, ApplicationInfo b) {
203 return sCollator.compare(a.title.toString(), b.title.toString());
204 }
205 });
206
207 if (!mStopped) {
208 launcher.runOnUiThread(action);
209 }
210 }
211
212 if (!mStopped) {
213 mApplicationsLoaded = true;
214 }
215 mRunning = false;
216 }
217 }
218
219 private static class ChangeNotifier implements Runnable {
220 private final ApplicationsAdapter mApplicationList;
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700221 private final ArrayList<ApplicationInfo> mBuffer;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800222
223 ChangeNotifier(ApplicationsAdapter applicationList) {
224 mApplicationList = applicationList;
225 mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
226 }
227
228 public void run() {
229 final ArrayList<ApplicationInfo> buffer = mBuffer;
230 final ApplicationsAdapter applicationList = mApplicationList;
231 final int count = buffer.size();
232
233 applicationList.setNotifyOnChange(false);
234 applicationList.clear();
235 for (int i = 0; i < count; i++) {
236 applicationList.setNotifyOnChange(false);
237 applicationList.add(buffer.get(i));
238 }
239
240 applicationList.notifyDataSetChanged();
241 buffer.clear();
242 }
243
244 void add(ApplicationInfo application) {
245 mBuffer.add(application);
246 }
247
248 void sort(Comparator<ApplicationInfo> comparator) {
249 Collections.sort(mBuffer, comparator);
250 }
251 }
252
253 boolean isDesktopLoaded() {
254 return mDesktopItems != null && mDesktopGadgets != null && mDesktopItemsLoaded;
255 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700256
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800257 /**
258 * Loads all of the items on the desktop, in folders, or in the dock.
259 * These can be apps, shortcuts or widgets
260 */
261 void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
262 boolean loadApplications) {
263
264 if (isLaunching && isDesktopLoaded()) {
265 if (loadApplications) startApplicationsLoader(launcher);
266 // We have already loaded our data from the DB
267 launcher.onDesktopItemsLoaded();
268 return;
269 }
270
271 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
272 mDesktopItemsLoader.stop();
273 // Wait for the currently running thread to finish, this can take a little
274 // time but it should be well below the timeout limit
275 try {
276 mDesktopLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
277 } catch (InterruptedException e) {
278 // Empty
279 }
280 }
281
282 mDesktopItemsLoaded = false;
283 mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications);
284 mDesktopLoader = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
285 mDesktopLoader.start();
286 }
287
288 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
289 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
290 new String[] { LauncherSettings.Favorites.ID, LauncherSettings.Favorites.TITLE,
291 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
292 null, null, null);
293
294 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
295 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
296 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
297 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
298
299 // boolean changed = false;
300
301 try {
302 while (c.moveToNext()) {
303 try {
304 if (c.getInt(itemTypeIndex) !=
305 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
306 continue;
307 }
308
309 final String intentUri = c.getString(intentIndex);
310 if (intentUri != null) {
311 final Intent shortcut = Intent.getIntent(intentUri);
312 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
313 final ComponentName name = shortcut.getComponent();
314 if (name != null) {
315 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
316 final String title = c.getString(titleIndex);
317 String label = getLabel(manager, activityInfo);
318
319 if (title == null || !title.equals(label)) {
320 final ContentValues values = new ContentValues();
321 values.put(LauncherSettings.Favorites.TITLE, label);
322
323 resolver.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
324 values, "_id=?",
325 new String[] { String.valueOf(c.getLong(idIndex)) });
326
327 // changed = true;
328 }
329 }
330 }
331 }
332 } catch (URISyntaxException e) {
333 // Ignore
334 } catch (PackageManager.NameNotFoundException e) {
335 // Ignore
336 }
337 }
338 } finally {
339 c.close();
340 }
341
342 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
343 }
344
345 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
346 String label = activityInfo.loadLabel(manager).toString();
347 if (label == null) {
348 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
349 if (label == null) {
350 label = activityInfo.name;
351 }
352 }
353 return label;
354 }
355
356 private class DesktopItemsLoader implements Runnable {
357 private volatile boolean mStopped;
358 private volatile boolean mRunning;
359
360 private final WeakReference<Launcher> mLauncher;
361 private final boolean mLocaleChanged;
362 private final boolean mLoadApplications;
363
364 DesktopItemsLoader(Launcher launcher, boolean localeChanged, boolean loadApplications) {
365 mLoadApplications = loadApplications;
366 mLauncher = new WeakReference<Launcher>(launcher);
367 mLocaleChanged = localeChanged;
368 }
369
370 void stop() {
371 mStopped = true;
372 }
373
374 boolean isRunning() {
375 return mRunning;
376 }
377
378 public void run() {
379 mRunning = true;
380
381 final Launcher launcher = mLauncher.get();
382 final ContentResolver contentResolver = launcher.getContentResolver();
383 final PackageManager manager = launcher.getPackageManager();
384
385 if (mLocaleChanged) {
386 updateShortcutLabels(contentResolver, manager);
387 }
388
389 mDesktopItems = new ArrayList<ItemInfo>();
390 mDesktopGadgets = new ArrayList<LauncherGadgetInfo>();
391 mFolders = new HashMap<Long, FolderInfo>();
392
393 final ArrayList<ItemInfo> desktopItems = mDesktopItems;
394 final ArrayList<LauncherGadgetInfo> desktopGadgets = mDesktopGadgets;
395
396 final Cursor c = contentResolver.query(
397 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
398
399 try {
400 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
401 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
402 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
403 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
404 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
405 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
406 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
407 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
408 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
409 final int gadgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.GADGET_ID);
410 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
411 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
412 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
413 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
414 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
415 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
416 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
417
418 ApplicationInfo info;
419 String intentDescription;
420 Widget widgetInfo;
421 LauncherGadgetInfo gadgetInfo;
422 int container;
423 long id;
424 Intent intent;
425
426 final HashMap<Long, FolderInfo> folders = mFolders;
427
428 while (!mStopped && c.moveToNext()) {
429 try {
430 int itemType = c.getInt(itemTypeIndex);
431
432 switch (itemType) {
433 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
434 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
435 intentDescription = c.getString(intentIndex);
436 try {
437 intent = Intent.getIntent(intentDescription);
438 } catch (java.net.URISyntaxException e) {
439 continue;
440 }
441
442 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
443 info = getApplicationInfo(manager, intent);
444 } else {
445 info = getApplicationInfoShortcut(c, launcher, iconTypeIndex,
446 iconPackageIndex, iconResourceIndex, iconIndex);
447 }
448
449 if (info == null) {
450 info = new ApplicationInfo();
451 info.icon = manager.getDefaultActivityIcon();
452 }
453
454 if (info != null) {
455 info.title = c.getString(titleIndex);
456 info.intent = intent;
457
458 info.id = c.getLong(idIndex);
459 container = c.getInt(containerIndex);
460 info.container = container;
461 info.screen = c.getInt(screenIndex);
462 info.cellX = c.getInt(cellXIndex);
463 info.cellY = c.getInt(cellYIndex);
464
465 switch (container) {
466 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
467 desktopItems.add(info);
468 break;
469 default:
470 // Item is in a user folder
471 UserFolderInfo folderInfo =
472 findOrMakeUserFolder(folders, container);
473 folderInfo.add(info);
474 break;
475 }
476 }
477 break;
478 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
479
480 id = c.getLong(idIndex);
481 UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id);
482
483 folderInfo.title = c.getString(titleIndex);
484
485 folderInfo.id = id;
486 container = c.getInt(containerIndex);
487 folderInfo.container = container;
488 folderInfo.screen = c.getInt(screenIndex);
489 folderInfo.cellX = c.getInt(cellXIndex);
490 folderInfo.cellY = c.getInt(cellYIndex);
491
492 switch (container) {
493 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
494 desktopItems.add(folderInfo);
495 break;
496 }
497 break;
498 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
499
500 id = c.getLong(idIndex);
501 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id);
502
503 intentDescription = c.getString(intentIndex);
504 intent = null;
505 if (intentDescription != null) {
506 try {
507 intent = Intent.getIntent(intentDescription);
508 } catch (java.net.URISyntaxException e) {
509 // Ignore, a live folder might not have a base intent
510 }
511 }
512
513 liveFolderInfo.title = c.getString(titleIndex);
514 liveFolderInfo.id = id;
515 container = c.getInt(containerIndex);
516 liveFolderInfo.container = container;
517 liveFolderInfo.screen = c.getInt(screenIndex);
518 liveFolderInfo.cellX = c.getInt(cellXIndex);
519 liveFolderInfo.cellY = c.getInt(cellYIndex);
520 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
521 liveFolderInfo.baseIntent = intent;
522 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
523
524 loadLiveFolderIcon(launcher, c, iconTypeIndex, iconPackageIndex,
525 iconResourceIndex, liveFolderInfo);
526
527 switch (container) {
528 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
529 desktopItems.add(liveFolderInfo);
530 break;
531 }
532 break;
533 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
534 widgetInfo = Widget.makeSearch();
535
536 container = c.getInt(containerIndex);
537 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
538 Log.e(Launcher.LOG_TAG, "Widget found where container "
539 + "!= CONTAINER_DESKTOP ignoring!");
540 continue;
541 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700542
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800543 widgetInfo.id = c.getLong(idIndex);
544 widgetInfo.screen = c.getInt(screenIndex);
545 widgetInfo.container = container;
546 widgetInfo.cellX = c.getInt(cellXIndex);
547 widgetInfo.cellY = c.getInt(cellYIndex);
548
549 desktopItems.add(widgetInfo);
550 break;
551 case LauncherSettings.Favorites.ITEM_TYPE_GADGET:
552 // Read all Launcher-specific gadget details
553 int gadgetId = c.getInt(gadgetIdIndex);
554 gadgetInfo = new LauncherGadgetInfo(gadgetId);
555 gadgetInfo.id = c.getLong(idIndex);
556 gadgetInfo.screen = c.getInt(screenIndex);
557 gadgetInfo.cellX = c.getInt(cellXIndex);
558 gadgetInfo.cellY = c.getInt(cellYIndex);
559 gadgetInfo.spanX = c.getInt(spanXIndex);
560 gadgetInfo.spanY = c.getInt(spanYIndex);
561
562 container = c.getInt(containerIndex);
563 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
564 Log.e(Launcher.LOG_TAG, "Gadget found where container "
565 + "!= CONTAINER_DESKTOP -- ignoring!");
566 continue;
567 }
568 gadgetInfo.container = c.getInt(containerIndex);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700569
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800570 desktopGadgets.add(gadgetInfo);
571 break;
572 }
573 } catch (Exception e) {
574 Log.w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e);
575 }
576 }
577 } finally {
578 c.close();
579 }
580
581 if (!mStopped) {
582 launcher.runOnUiThread(new Runnable() {
583 public void run() {
584 launcher.onDesktopItemsLoaded();
585 }
586 });
587 if (mLoadApplications) startApplicationsLoader(launcher);
588 }
589
590 if (!mStopped) {
591 mDesktopItemsLoaded = true;
592 }
593 mRunning = false;
594 }
595 }
596
597 private static void loadLiveFolderIcon(Launcher launcher, Cursor c, int iconTypeIndex,
598 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
599
600 int iconType = c.getInt(iconTypeIndex);
601 switch (iconType) {
602 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
603 String packageName = c.getString(iconPackageIndex);
604 String resourceName = c.getString(iconResourceIndex);
605 PackageManager packageManager = launcher.getPackageManager();
606 try {
607 Resources resources = packageManager.getResourcesForApplication(packageName);
608 final int id = resources.getIdentifier(resourceName, null, null);
609 liveFolderInfo.icon = resources.getDrawable(id);
610 } catch (Exception e) {
611 liveFolderInfo.icon =
612 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
613 }
614 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
615 liveFolderInfo.iconResource.packageName = packageName;
616 liveFolderInfo.iconResource.resourceName = resourceName;
617 break;
618 default:
619 liveFolderInfo.icon =
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700620 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800621 }
622 }
623
624 /**
625 * Finds the user folder defined by the specified id.
626 *
627 * @param id The id of the folder to look for.
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700628 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800629 * @return A UserFolderInfo if the folder exists or null otherwise.
630 */
631 FolderInfo findFolderById(long id) {
632 return mFolders.get(id);
633 }
634
635 void addFolder(FolderInfo info) {
636 mFolders.put(info.id, info);
637 }
638
639 /**
640 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
641 * new one.
642 */
643 private UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
644 // See if a placeholder was created for us already
645 FolderInfo folderInfo = folders.get(id);
646 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
647 // No placeholder -- create a new instance
648 folderInfo = new UserFolderInfo();
649 folders.put(id, folderInfo);
650 }
651 return (UserFolderInfo) folderInfo;
652 }
653
654 /**
655 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
656 * new one.
657 */
658 private LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
659 // See if a placeholder was created for us already
660 FolderInfo folderInfo = folders.get(id);
661 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
662 // No placeholder -- create a new instance
663 folderInfo = new LiveFolderInfo();
664 folders.put(id, folderInfo);
665 }
666 return (LiveFolderInfo) folderInfo;
667 }
668
669 /**
670 * Remove the callback for the cached drawables or we leak the previous
671 * Home screen on orientation change.
672 */
673 void unbind() {
674 mApplicationsAdapter = null;
675 unbindAppDrawables(mApplications);
676 unbindDrawables(mDesktopItems);
677 unbindGadgetHostViews(mDesktopGadgets);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700678 unbindCachedIconDrawables();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800679 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700680
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800681 /**
682 * Remove the callback for the cached drawables or we leak the previous
683 * Home screen on orientation change.
684 */
685 private void unbindDrawables(ArrayList<ItemInfo> desktopItems) {
686 if (desktopItems != null) {
687 final int count = desktopItems.size();
688 for (int i = 0; i < count; i++) {
689 ItemInfo item = desktopItems.get(i);
690 switch (item.itemType) {
691 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
692 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
693 ((ApplicationInfo)item).icon.setCallback(null);
694 break;
695 }
696 }
697 }
698 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700699
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800700 /**
701 * Remove the callback for the cached drawables or we leak the previous
702 * Home screen on orientation change.
703 */
704 private void unbindAppDrawables(ArrayList<ApplicationInfo> applications) {
705 if (applications != null) {
706 final int count = applications.size();
707 for (int i = 0; i < count; i++) {
708 applications.get(i).icon.setCallback(null);
709 }
710 }
711 }
712
713 /**
714 * Remove any {@link LauncherGadgetHostView} references in our gadgets.
715 */
716 private void unbindGadgetHostViews(ArrayList<LauncherGadgetInfo> gadgets) {
717 if (gadgets != null) {
718 final int count = gadgets.size();
719 for (int i = 0; i < count; i++) {
720 LauncherGadgetInfo launcherInfo = gadgets.get(i);
721 launcherInfo.hostView = null;
722 }
723 }
724 }
725
726 /**
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700727 * Remove the callback for the cached drawables or we leak the previous
728 * Home screen on orientation change.
729 */
730 private void unbindCachedIconDrawables() {
731 for (ApplicationInfo appInfo : mAppInfoCache.values()) {
732 appInfo.icon.setCallback(null);
733 }
734 }
735
736 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800737 * @return The current list of applications
738 */
739 public ArrayList<ApplicationInfo> getApplications() {
740 return mApplications;
741 }
742
743 /**
744 * @return The current list of applications
745 */
746 public ApplicationsAdapter getApplicationsAdapter() {
747 return mApplicationsAdapter;
748 }
749
750 /**
751 * @return The current list of desktop items
752 */
753 public ArrayList<ItemInfo> getDesktopItems() {
754 return mDesktopItems;
755 }
756
757 /**
758 * @return The current list of desktop items
759 */
760 public ArrayList<LauncherGadgetInfo> getDesktopGadgets() {
761 return mDesktopGadgets;
762 }
763
764 /**
765 * Add an item to the desktop
766 * @param info
767 */
768 public void addDesktopItem(ItemInfo info) {
769 // TODO: write to DB; also check that folder has been added to folders list
770 mDesktopItems.add(info);
771 }
772
773 /**
774 * Remove an item from the desktop
775 * @param info
776 */
777 public void removeDesktopItem(ItemInfo info) {
778 // TODO: write to DB; figure out if we should remove folder from folders list
779 mDesktopItems.remove(info);
780 }
781
782 /**
783 * Add a gadget to the desktop
784 */
785 public void addDesktopGadget(LauncherGadgetInfo info) {
786 mDesktopGadgets.add(info);
787 }
788
789 /**
790 * Remove a gadget from the desktop
791 */
792 public void removeDesktopGadget(LauncherGadgetInfo info) {
793 mDesktopGadgets.remove(info);
794 }
795
796 /**
797 * Make an ApplicationInfo object for an application
798 */
799 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) {
800 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
801
802 if (resolveInfo == null) {
803 return null;
804 }
805
806 final ApplicationInfo info = new ApplicationInfo();
807 final ActivityInfo activityInfo = resolveInfo.activityInfo;
808 info.icon = activityInfo.loadIcon(manager);
809 if (info.title == null || info.title.length() == 0) {
810 info.title = activityInfo.loadLabel(manager);
811 }
812 if (info.title == null) {
813 info.title = "";
814 }
815 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
816 return info;
817 }
818
819 /**
820 * Make an ApplicationInfo object for a sortcut
821 */
822 private ApplicationInfo getApplicationInfoShortcut(Cursor c, Launcher launcher,
823 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
824
825 final ApplicationInfo info = new ApplicationInfo();
826 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
827
828 int iconType = c.getInt(iconTypeIndex);
829 switch (iconType) {
830 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
831 String packageName = c.getString(iconPackageIndex);
832 String resourceName = c.getString(iconResourceIndex);
833 PackageManager packageManager = launcher.getPackageManager();
834 try {
835 Resources resources = packageManager.getResourcesForApplication(packageName);
836 final int id = resources.getIdentifier(resourceName, null, null);
837 info.icon = resources.getDrawable(id);
838 } catch (Exception e) {
839 info.icon = packageManager.getDefaultActivityIcon();
840 }
841 info.iconResource = new Intent.ShortcutIconResource();
842 info.iconResource.packageName = packageName;
843 info.iconResource.resourceName = resourceName;
844 info.customIcon = false;
845 break;
846 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
847 byte[] data = c.getBlob(iconIndex);
848 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
849 info.icon = new FastBitmapDrawable(
850 Utilities.createBitmapThumbnail(bitmap, launcher));
851 info.filtered = true;
852 info.customIcon = true;
853 break;
854 default:
855 info.icon = launcher.getPackageManager().getDefaultActivityIcon();
856 info.customIcon = false;
857 break;
858 }
859 return info;
860 }
861
862 /**
863 * Remove an item from the in-memory represention of a user folder. Does not change the DB.
864 */
865 void removeUserFolderItem(UserFolderInfo folder, ItemInfo info) {
866 //noinspection SuspiciousMethodCalls
867 folder.contents.remove(info);
868 }
869
870 /**
871 * Removes a UserFolder from the in-memory list of folders. Does not change the DB.
872 * @param userFolderInfo
873 */
874 void removeUserFolder(UserFolderInfo userFolderInfo) {
875 mFolders.remove(userFolderInfo.id);
876 }
877
878 /**
879 * Adds an item to the DB if it was not created previously, or move it to a new
880 * <container, screen, cellX, cellY>
881 */
882 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
883 int screen, int cellX, int cellY) {
884 if (item.container == ItemInfo.NO_ID) {
885 // From all apps
886 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
887 } else {
888 // From somewhere else
889 moveItemInDatabase(context, item, container, screen, cellX, cellY);
890 }
891 }
892
893 /**
894 * Move an item in the DB to a new <container, screen, cellX, cellY>
895 */
896 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
897 int cellX, int cellY) {
898 item.container = container;
899 item.screen = screen;
900 item.cellX = cellX;
901 item.cellY = cellY;
902
903 final ContentValues values = new ContentValues();
904 final ContentResolver cr = context.getContentResolver();
905
906 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
907 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
908 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
909 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
910
911 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
912 }
913
914 /**
915 * Returns true if the shortcuts already exists in the database.
916 * we identify a shortcut by its title and intent.
917 */
918 static boolean shortcutExists(Context context, String title, Intent intent) {
919 final ContentResolver cr = context.getContentResolver();
920 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
921 new String[] { "title", "intent" }, "title=? and intent=?",
922 new String[] { title, intent.toURI() }, null);
923 boolean result = false;
924 try {
925 result = c.moveToFirst();
926 } finally {
927 c.close();
928 }
929 return result;
930 }
931
932 FolderInfo getFolderById(Context context, long id) {
933 final ContentResolver cr = context.getContentResolver();
934 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
935 "_id=? and itemType=? or itemType=?",
936 new String[] { String.valueOf(id),
937 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
938 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
939
940 try {
941 if (c.moveToFirst()) {
942 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
943 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
944 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
945 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
946 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
947 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
948
949 FolderInfo folderInfo = null;
950 switch (c.getInt(itemTypeIndex)) {
951 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
952 folderInfo = findOrMakeUserFolder(mFolders, id);
953 break;
954 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
955 folderInfo = findOrMakeLiveFolder(mFolders, id);
956 break;
957 }
958
959 folderInfo.title = c.getString(titleIndex);
960 folderInfo.id = id;
961 folderInfo.container = c.getInt(containerIndex);
962 folderInfo.screen = c.getInt(screenIndex);
963 folderInfo.cellX = c.getInt(cellXIndex);
964 folderInfo.cellY = c.getInt(cellYIndex);
965
966 return folderInfo;
967 }
968 } finally {
969 c.close();
970 }
971
972 return null;
973 }
974
975 /**
976 * Add an item to the database in a specified container. Sets the container, screen, cellX and
977 * cellY fields of the item. Also assigns an ID to the item.
978 */
979 static void addItemToDatabase(Context context, ItemInfo item, long container,
980 int screen, int cellX, int cellY, boolean notify) {
981 item.container = container;
982 item.screen = screen;
983 item.cellX = cellX;
984 item.cellY = cellY;
985
986 final ContentValues values = new ContentValues();
987 final ContentResolver cr = context.getContentResolver();
988
989 item.onAddToDatabase(values);
990
991 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
992 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
993
994 if (result != null) {
995 item.id = Integer.parseInt(result.getPathSegments().get(1));
996 }
997 }
998
999 /**
1000 * Update an item to the database in a specified container.
1001 */
1002 static void updateItemInDatabase(Context context, ItemInfo item) {
1003 final ContentValues values = new ContentValues();
1004 final ContentResolver cr = context.getContentResolver();
1005
1006 item.onAddToDatabase(values);
1007
1008 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
1009 }
1010
1011 /**
1012 * Removes the specified item from the database
1013 * @param context
1014 * @param item
1015 */
1016 static void deleteItemFromDatabase(Context context, ItemInfo item) {
1017 final ContentResolver cr = context.getContentResolver();
1018
1019 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
1020 }
1021
1022
1023 /**
1024 * Remove the contents of the specified folder from the database
1025 */
1026 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
1027 final ContentResolver cr = context.getContentResolver();
1028
1029 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
1030 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
1031 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
1032 }
1033}