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