blob: 0ef2a806b767142b28b2ff3b37f21f4ca16c355d [file] [log] [blame]
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001/*
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.graphics.drawable.BitmapDrawable;
32import android.net.Uri;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070033import android.util.Log;
34
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.HashMap;
38import java.util.List;
39import java.util.Comparator;
40import java.lang.ref.WeakReference;
41import java.text.Collator;
The Android Open Source Projectd097a182008-12-17 18:05:58 -080042import java.net.URISyntaxException;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070043
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
47 * for the Launcher
48 *
49 */
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;
54
55 private final Collator sCollator = Collator.getInstance();
56
57 private boolean mApplicationsLoaded;
58 private boolean mDesktopItemsLoaded;
59
60 private ArrayList<ItemInfo> mDesktopItems;
The Android Open Source Projectd097a182008-12-17 18:05:58 -080061 private HashMap<Long, FolderInfo> mFolders;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070062
63 private ArrayList<ApplicationInfo> mApplications;
64 private ApplicationsAdapter mApplicationsAdapter;
65 private ApplicationsLoader mApplicationsLoader;
66 private DesktopItemsLoader mDesktopItemsLoader;
67 private Thread mLoader;
68 private Thread mDesktopLoader;
69
70 void abortLoaders() {
71 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
72 mApplicationsLoader.stop();
73 mApplicationsLoaded = false;
74 }
75 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
76 mDesktopItemsLoader.stop();
77 mDesktopItemsLoaded = false;
78 }
79 }
80
81 /**
82 * Loads the list of installed applications in mApplications.
83 */
The Android Open Source Projectd097a182008-12-17 18:05:58 -080084 void loadApplications(boolean isLaunching, Launcher launcher, boolean localeChanged) {
85 if (isLaunching && mApplicationsLoaded && !localeChanged) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070086 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
87 return;
88 }
89
The Android Open Source Projectd097a182008-12-17 18:05:58 -080090 if (mApplicationsAdapter == null || isLaunching || localeChanged) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070091 mApplicationsAdapter = new ApplicationsAdapter(launcher,
92 mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER));
93 }
94
95 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
96 mApplicationsLoader.stop();
97 // Wait for the currently running thread to finish, this can take a little
98 // time but it should be well below the timeout limit
99 try {
100 mLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
101 } catch (InterruptedException e) {
102 // Empty
103 }
104 }
105
106 mApplicationsLoaded = false;
107 mApplicationsLoader = new ApplicationsLoader(launcher);
108 mLoader = new Thread(mApplicationsLoader, "Applications Loader");
109 mLoader.start();
110 }
111
112 private class ApplicationsLoader implements Runnable {
113 private final WeakReference<Launcher> mLauncher;
114
115 private volatile boolean mStopped;
116 private volatile boolean mRunning;
117
118 ApplicationsLoader(Launcher launcher) {
119 mLauncher = new WeakReference<Launcher>(launcher);
120 }
121
122 void stop() {
123 mStopped = true;
124 }
125
126 boolean isRunning() {
127 return mRunning;
128 }
129
130 public void run() {
131 mRunning = true;
132
133 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
134 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
135
136 final Launcher launcher = mLauncher.get();
137 final PackageManager manager = launcher.getPackageManager();
138 final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
139
140 if (apps != null && !mStopped) {
141 final int count = apps.size();
142 final ApplicationsAdapter applicationList = mApplicationsAdapter;
143
144 ChangeNotifier action = new ChangeNotifier(applicationList);
145
146 for (int i = 0; i < count && !mStopped; i++) {
147 ApplicationInfo application = new ApplicationInfo();
148 ResolveInfo info = apps.get(i);
149
150 application.title = info.loadLabel(manager);
151 if (application.title == null) {
152 application.title = info.activityInfo.name;
153 }
154 application.setActivity(new ComponentName(
155 info.activityInfo.applicationInfo.packageName,
156 info.activityInfo.name),
157 Intent.FLAG_ACTIVITY_NEW_TASK |
158 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
159 application.icon = info.activityInfo.loadIcon(manager);
160 application.container = ItemInfo.NO_ID;
161
162 action.add(application);
163 }
164
165 action.sort(new Comparator<ApplicationInfo>() {
166 public final int compare(ApplicationInfo a, ApplicationInfo b) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800167 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700168 }
169 });
170
171 if (!mStopped) {
172 launcher.runOnUiThread(action);
173 }
174 }
175
176 if (!mStopped) {
177 mApplicationsLoaded = true;
178 }
179 mRunning = false;
180 }
181 }
182
183 private static class ChangeNotifier implements Runnable {
184 private final ApplicationsAdapter mApplicationList;
185 private ArrayList<ApplicationInfo> mBuffer;
186
187 ChangeNotifier(ApplicationsAdapter applicationList) {
188 mApplicationList = applicationList;
189 mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
190 }
191
192 public void run() {
193 final ArrayList<ApplicationInfo> buffer = mBuffer;
194 final ApplicationsAdapter applicationList = mApplicationList;
195 final int count = buffer.size();
196
197 applicationList.clear();
198 for (int i = 0; i < count; i++) {
199 applicationList.setNotifyOnChange(false);
200 applicationList.add(buffer.get(i));
201 }
202
203 applicationList.notifyDataSetChanged();
204 buffer.clear();
205 }
206
207 void add(ApplicationInfo application) {
208 mBuffer.add(application);
209 }
210
211 void sort(Comparator<ApplicationInfo> comparator) {
212 Collections.sort(mBuffer, comparator);
213 }
214 }
215
216 boolean isDesktopLoaded() {
217 return mDesktopItems != null && mDesktopItemsLoaded;
218 }
219
220 /**
221 * Loads all of the items on the desktop, in folders, or in the dock.
222 * These can be apps, shortcuts or widgets
223 */
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800224 void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700225 if (isLaunching && mDesktopItems != null && mDesktopItemsLoaded) {
226 // We have already loaded our data from the DB
227 launcher.onDesktopItemsLoaded();
228 return;
229 }
230
231 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
232 mDesktopItemsLoader.stop();
233 // Wait for the currently running thread to finish, this can take a little
234 // time but it should be well below the timeout limit
235 try {
236 mDesktopLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
237 } catch (InterruptedException e) {
238 // Empty
239 }
240 }
241
242 mDesktopItemsLoaded = false;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800243 mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700244 mDesktopLoader = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
245 mDesktopLoader.start();
246 }
247
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800248 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
249 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
250 new String[] { LauncherSettings.Favorites.ID, LauncherSettings.Favorites.TITLE,
251 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
252 null, null, null);
253
254 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
255 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
256 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
257 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
258
259 // boolean changed = false;
260
261 try {
262 while (c.moveToNext()) {
263 try {
264 if (c.getInt(itemTypeIndex) != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
265 continue;
266 }
267
268 final String intentUri = c.getString(intentIndex);
269 if (intentUri != null) {
270 final Intent shortcut = Intent.getIntent(intentUri);
271 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
272 final ComponentName name = shortcut.getComponent();
273 if (name != null) {
274 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
275 final String title = c.getString(titleIndex);
276 String label = getLabel(manager, activityInfo);
277
278 if (title == null || !title.equals(label)) {
279 final ContentValues values = new ContentValues();
280 values.put(LauncherSettings.Favorites.TITLE, label);
281
282 resolver.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
283 values, "_id=?",
284 new String[] { String.valueOf(c.getLong(idIndex)) });
285
286 // changed = true;
287 }
288 }
289 }
290 }
291 } catch (URISyntaxException e) {
292 // Ignore
293 } catch (PackageManager.NameNotFoundException e) {
294 // Ignore
295 }
296 }
297 } finally {
298 c.close();
299 }
300
301 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
302 }
303
304 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
305 String label = activityInfo.loadLabel(manager).toString();
306 if (label == null) {
307 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
308 if (label == null) {
309 label = activityInfo.name;
310 }
311 }
312 return label;
313 }
314
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700315 private class DesktopItemsLoader implements Runnable {
316 private volatile boolean mStopped;
317 private volatile boolean mRunning;
318
319 private final WeakReference<Launcher> mLauncher;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800320 private boolean mLocaleChanged;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700321
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800322 DesktopItemsLoader(Launcher launcher, boolean localeChanged) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700323 mLauncher = new WeakReference<Launcher>(launcher);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800324 mLocaleChanged = localeChanged;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700325 }
326
327 void stop() {
328 mStopped = true;
329 }
330
331 boolean isRunning() {
332 return mRunning;
333 }
334
335 public void run() {
336 mRunning = true;
337
338 final Launcher launcher = mLauncher.get();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800339 final ContentResolver contentResolver = launcher.getContentResolver();
340 final PackageManager manager = launcher.getPackageManager();
341
342 if (mLocaleChanged) {
343 updateShortcutLabels(contentResolver, manager);
344 }
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700345
346 mDesktopItems = new ArrayList<ItemInfo>();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800347 mFolders = new HashMap<Long, FolderInfo>();
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700348
349 final ArrayList<ItemInfo> desktopItems = mDesktopItems;
350
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800351 final Cursor c = contentResolver.query(
352 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700353
354 try {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800355 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
356 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
357 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
358 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
359 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
360 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
361 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
362 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
363 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
364 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
365 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
366 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
367 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
368 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700369
370 ApplicationInfo info;
371 String intentDescription;
372 Widget widgetInfo = null;
373 int container;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800374 long id;
375 Intent intent;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700376
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800377 final HashMap<Long, FolderInfo> folders = mFolders;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700378
379 while (!mStopped && c.moveToNext()) {
380 try {
381 int itemType = c.getInt(itemTypeIndex);
382
383 switch (itemType) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800384 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
385 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700386 intentDescription = c.getString(intentIndex);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700387 try {
388 intent = Intent.getIntent(intentDescription);
389 } catch (java.net.URISyntaxException e) {
390 continue;
391 }
392
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800393 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700394 info = getApplicationInfo(manager, intent);
395 } else {
396 info = getApplicationInfoShortcut(c, launcher, iconTypeIndex,
397 iconPackageIndex, iconResourceIndex, iconIndex);
398 }
399
400 if (info == null) {
401 info = new ApplicationInfo();
402 info.icon = manager.getDefaultActivityIcon();
403 }
404
405 if (info != null) {
406 info.title = c.getString(titleIndex);
407 info.intent = intent;
408
409 info.id = c.getLong(idIndex);
410 container = c.getInt(containerIndex);
411 info.container = container;
412 info.screen = c.getInt(screenIndex);
413 info.cellX = c.getInt(cellXIndex);
414 info.cellY = c.getInt(cellYIndex);
415
416 switch (container) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800417 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700418 desktopItems.add(info);
419 break;
420 default:
421 // Item is in a user folder
422 UserFolderInfo folderInfo =
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800423 findOrMakeUserFolder(folders, container);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700424 folderInfo.add(info);
425 break;
426 }
427 }
428 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800429 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700430
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800431 id = c.getLong(idIndex);
432 UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700433
434 folderInfo.title = c.getString(titleIndex);
435
436 folderInfo.id = id;
437 container = c.getInt(containerIndex);
438 folderInfo.container = container;
439 folderInfo.screen = c.getInt(screenIndex);
440 folderInfo.cellX = c.getInt(cellXIndex);
441 folderInfo.cellY = c.getInt(cellYIndex);
442
443 switch (container) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800444 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
445 desktopItems.add(folderInfo);
446 break;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700447 }
448 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800449 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
450
451 id = c.getLong(idIndex);
452 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id);
453
454 intentDescription = c.getString(intentIndex);
455 intent = null;
456 if (intentDescription != null) {
457 try {
458 intent = Intent.getIntent(intentDescription);
459 } catch (java.net.URISyntaxException e) {
460 // Ignore, a live folder might not have a base intent
461 }
462 }
463
464 liveFolderInfo.title = c.getString(titleIndex);
465 liveFolderInfo.id = id;
466 container = c.getInt(containerIndex);
467 liveFolderInfo.container = container;
468 liveFolderInfo.screen = c.getInt(screenIndex);
469 liveFolderInfo.cellX = c.getInt(cellXIndex);
470 liveFolderInfo.cellY = c.getInt(cellYIndex);
471 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
472 liveFolderInfo.baseIntent = intent;
473 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
474
475 loadLiveFolderIcon(launcher, c, iconTypeIndex, iconPackageIndex,
476 iconResourceIndex, liveFolderInfo);
477
478 switch (container) {
479 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
480 desktopItems.add(liveFolderInfo);
481 break;
482 }
483 break;
484 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
485 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
486 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700487 switch (itemType) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800488 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700489 widgetInfo = Widget.makeClock();
490 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800491 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700492 widgetInfo = Widget.makeSearch();
493 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800494 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700495 widgetInfo = Widget.makePhotoFrame();
496 byte[] data = c.getBlob(iconIndex);
497 if (data != null) {
498 widgetInfo.photo =
499 BitmapFactory.decodeByteArray(data, 0, data.length);
500 }
501 break;
502 }
503
504 if (widgetInfo != null) {
505 container = c.getInt(containerIndex);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800506 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700507 Log.e(Launcher.LOG_TAG, "Widget found where container "
508 + "!= CONTAINER_DESKTOP -- ignoring!");
509 continue;
510 }
511 widgetInfo.id = c.getLong(idIndex);
512 widgetInfo.screen = c.getInt(screenIndex);
513 widgetInfo.container = container;
514 widgetInfo.cellX = c.getInt(cellXIndex);
515 widgetInfo.cellY = c.getInt(cellYIndex);
516
517 desktopItems.add(widgetInfo);
518 }
519 break;
520 }
521 } catch (Exception e) {
522 Log.w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e);
523 }
524 }
525 } finally {
526 c.close();
527 }
528
529 if (!mStopped) {
530 launcher.runOnUiThread(new Runnable() {
531 public void run() {
532 launcher.onDesktopItemsLoaded();
533 }
534 });
535 }
536
537 if (!mStopped) {
538 mDesktopItemsLoaded = true;
539 }
540 mRunning = false;
541 }
542 }
543
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800544 private static void loadLiveFolderIcon(Launcher launcher, Cursor c, int iconTypeIndex,
545 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
546
547 int iconType = c.getInt(iconTypeIndex);
548 switch (iconType) {
549 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
550 String packageName = c.getString(iconPackageIndex);
551 String resourceName = c.getString(iconResourceIndex);
552 PackageManager packageManager = launcher.getPackageManager();
553 try {
554 Resources resources = packageManager.getResourcesForApplication(packageName);
555 final int id = resources.getIdentifier(resourceName, null, null);
556 liveFolderInfo.icon = resources.getDrawable(id);
557 } catch (Exception e) {
558 liveFolderInfo.icon =
559 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
560 }
561 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
562 liveFolderInfo.iconResource.packageName = packageName;
563 liveFolderInfo.iconResource.resourceName = resourceName;
564 break;
565 default:
566 liveFolderInfo.icon =
567 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
568 }
569 }
570
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700571 /**
572 * Finds the user folder defined by the specified id.
573 *
574 * @param id The id of the folder to look for.
575 *
576 * @return A UserFolderInfo if the folder exists or null otherwise.
577 */
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800578 FolderInfo findFolderById(long id) {
579 return mFolders.get(id);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700580 }
581
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800582 void addFolder(FolderInfo info) {
583 mFolders.put(info.id, info);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700584 }
585
586 /**
587 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
588 * new one.
589 */
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800590 private UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700591 // See if a placeholder was created for us already
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800592 FolderInfo folderInfo = folders.get(id);
593 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700594 // No placeholder -- create a new instance
595 folderInfo = new UserFolderInfo();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800596 folders.put(id, folderInfo);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700597 }
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800598 return (UserFolderInfo) folderInfo;
599 }
600
601 /**
602 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
603 * new one.
604 */
605 private LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
606 // See if a placeholder was created for us already
607 FolderInfo folderInfo = folders.get(id);
608 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
609 // No placeholder -- create a new instance
610 folderInfo = new LiveFolderInfo();
611 folders.put(id, folderInfo);
612 }
613 return (LiveFolderInfo) folderInfo;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700614 }
615
616 /**
617 * Remove the callback for the cached drawables or we leak the previous
618 * Home screen on orientation change.
619 */
620 void unbind() {
621 mApplicationsAdapter = null;
622 unbindAppDrawables(mApplications);
623 unbindDrawables(mDesktopItems);
624 }
625
626 /**
627 * Remove the callback for the cached drawables or we leak the previous
628 * Home screen on orientation change.
629 */
630 private void unbindDrawables(ArrayList<ItemInfo> desktopItems) {
631 if (desktopItems != null) {
632 final int count = desktopItems.size();
633 for (int i = 0; i < count; i++) {
634 ItemInfo item = desktopItems.get(i);
635 switch (item.itemType) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800636 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
637 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700638 ((ApplicationInfo)item).icon.setCallback(null);
639 }
640 }
641 }
642 }
643
644 /**
645 * Remove the callback for the cached drawables or we leak the previous
646 * Home screen on orientation change.
647 */
648 private void unbindAppDrawables(ArrayList<ApplicationInfo> applications) {
649 if (applications != null) {
650 final int count = applications.size();
651 for (int i = 0; i < count; i++) {
652 applications.get(i).icon.setCallback(null);
653 }
654 }
655 }
656
657 /**
658 * @return The current list of applications
659 */
660 public ArrayList<ApplicationInfo> getApplications() {
661 return mApplications;
662 }
663
664 /**
665 * @return The current list of applications
666 */
667 public ApplicationsAdapter getApplicationsAdapter() {
668 return mApplicationsAdapter;
669 }
670
671 /**
672 * @return The current list of desktop items
673 */
674 public ArrayList<ItemInfo> getDesktopItems() {
675 return mDesktopItems;
676 }
677
678 /**
679 * Add an item to the desktop
680 * @param info
681 */
682 public void addDesktopItem(ItemInfo info) {
683 // TODO: write to DB; also check that folder has been added to folders list
684 mDesktopItems.add(info);
685 }
686
687 /**
688 * Remove an item from the desktop
689 * @param info
690 */
691 public void removeDesktopItem(ItemInfo info) {
692 // TODO: write to DB; figure out if we should remove folder from folders list
693 mDesktopItems.remove(info);
694 }
695
696 /**
697 * Make an ApplicationInfo object for an application
698 */
699 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) {
700 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
701
702 if (resolveInfo == null) {
703 return null;
704 }
705
706 final ApplicationInfo info = new ApplicationInfo();
707 final ActivityInfo activityInfo = resolveInfo.activityInfo;
708 info.icon = activityInfo.loadIcon(manager);
709 if (info.title == null || info.title.length() == 0) {
710 info.title = activityInfo.loadLabel(manager);
711 }
712 if (info.title == null) {
713 info.title = "";
714 }
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800715 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700716 return info;
717 }
718
719 /**
720 * Make an ApplicationInfo object for a sortcut
721 */
722 private ApplicationInfo getApplicationInfoShortcut(Cursor c, Launcher launcher,
723 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
724
725 final ApplicationInfo info = new ApplicationInfo();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800726 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700727
728 int iconType = c.getInt(iconTypeIndex);
729 switch (iconType) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800730 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700731 String packageName = c.getString(iconPackageIndex);
732 String resourceName = c.getString(iconResourceIndex);
733 PackageManager packageManager = launcher.getPackageManager();
734 try {
735 Resources resources = packageManager.getResourcesForApplication(packageName);
736 final int id = resources.getIdentifier(resourceName, null, null);
737 info.icon = resources.getDrawable(id);
738 } catch (Exception e) {
739 info.icon = packageManager.getDefaultActivityIcon();
740 }
741 info.iconResource = new Intent.ShortcutIconResource();
742 info.iconResource.packageName = packageName;
743 info.iconResource.resourceName = resourceName;
744 info.customIcon = false;
745 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800746 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700747 byte[] data = c.getBlob(iconIndex);
748 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800749 info.icon = new FastBitmapDrawable(
750 Utilities.createBitmapThumbnail(bitmap, launcher));
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700751 info.filtered = true;
752 info.customIcon = true;
753 break;
754 default:
755 info.icon = launcher.getPackageManager().getDefaultActivityIcon();
756 info.customIcon = false;
757 break;
758 }
759 return info;
760 }
761
762 /**
763 * Remove an item from the in-memory represention of a user folder. Does not change the DB.
764 */
765 void removeUserFolderItem(UserFolderInfo folder, ItemInfo info) {
766 //noinspection SuspiciousMethodCalls
767 folder.contents.remove(info);
768 }
769
770 /**
771 * Removes a UserFolder from the in-memory list of folders. Does not change the DB.
772 * @param userFolderInfo
773 */
774 void removeUserFolder(UserFolderInfo userFolderInfo) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800775 mFolders.remove(userFolderInfo.id);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700776 }
777
778 /**
779 * Adds an item to the DB if it was not created previously, or move it to a new
780 * <container, screen, cellX, cellY>
781 */
782 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
783 int screen, int cellX, int cellY) {
784 if (item.container == ItemInfo.NO_ID) {
785 // From all apps
786 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
787 } else {
788 // From somewhere else
789 moveItemInDatabase(context, item, container, screen, cellX, cellY);
790 }
791 }
792
793 /**
794 * Move an item in the DB to a new <container, screen, cellX, cellY>
795 */
796 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
797 int cellX, int cellY) {
798 item.container = container;
799 item.screen = screen;
800 item.cellX = cellX;
801 item.cellY = cellY;
802
803 final ContentValues values = new ContentValues();
804 final ContentResolver cr = context.getContentResolver();
805
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800806 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
807 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
808 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
809 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700810
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800811 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700812 }
813
814 /**
815 * Returns true if the shortcuts already exists in the database.
816 * we identify a shortcut by its title and intent.
817 */
818 static boolean shortcutExists(Context context, String title, Intent intent) {
819 final ContentResolver cr = context.getContentResolver();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800820 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700821 new String[] { "title", "intent" }, "title=? and intent=?",
822 new String[] { title, intent.toURI() }, null);
823 boolean result = false;
824 try {
825 result = c.moveToFirst();
826 } finally {
827 c.close();
828 }
829 return result;
830 }
831
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800832 FolderInfo getFolderById(Context context, long id) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700833 final ContentResolver cr = context.getContentResolver();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800834 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
835 "_id=? and itemType=? or itemType=?",
836 new String[] { String.valueOf(id),
837 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
838 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700839
840 try {
841 if (c.moveToFirst()) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800842 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
843 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
844 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
845 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
846 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
847 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700848
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800849 FolderInfo folderInfo = null;
850 switch (c.getInt(itemTypeIndex)) {
851 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
852 folderInfo = findOrMakeUserFolder(mFolders, id);
853 break;
854 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
855 folderInfo = findOrMakeLiveFolder(mFolders, id);
856 break;
857 }
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700858
859 folderInfo.title = c.getString(titleIndex);
860 folderInfo.id = id;
861 folderInfo.container = c.getInt(containerIndex);
862 folderInfo.screen = c.getInt(screenIndex);
863 folderInfo.cellX = c.getInt(cellXIndex);
864 folderInfo.cellY = c.getInt(cellYIndex);
865
866 return folderInfo;
867 }
868 } finally {
869 c.close();
870 }
871
872 return null;
873 }
874
875 static Widget getPhotoFrameInfo(Context context, int screen, int cellX, int cellY) {
876 final ContentResolver cr = context.getContentResolver();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800877 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700878 null, "screen=? and cellX=? and cellY=? and itemType=?",
879 new String[] { String.valueOf(screen), String.valueOf(cellX), String.valueOf(cellY),
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800880 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) }, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700881
882 try {
883 if (c.moveToFirst()) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800884 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
885 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
886 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
887 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
888 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700889
890 Widget widgetInfo = Widget.makePhotoFrame();
891 widgetInfo.id = c.getLong(idIndex);
892 widgetInfo.screen = c.getInt(screenIndex);
893 widgetInfo.container = c.getInt(containerIndex);
894 widgetInfo.cellX = c.getInt(cellXIndex);
895 widgetInfo.cellY = c.getInt(cellYIndex);
896
897 return widgetInfo;
898 }
899 } finally {
900 c.close();
901 }
902
903 return null;
904 }
905
906 /**
907 * Add an item to the database in a specified container. Sets the container, screen, cellX and
908 * cellY fields of the item. Also assigns an ID to the item.
909 */
910 static void addItemToDatabase(Context context, ItemInfo item, long container,
911 int screen, int cellX, int cellY, boolean notify) {
912 item.container = container;
913 item.screen = screen;
914 item.cellX = cellX;
915 item.cellY = cellY;
916
917 final ContentValues values = new ContentValues();
918 final ContentResolver cr = context.getContentResolver();
919
920 item.onAddToDatabase(values);
921
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800922 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
923 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700924
925 if (result != null) {
926 item.id = Integer.parseInt(result.getPathSegments().get(1));
927 }
928 }
929
930 /**
931 * Update an item to the database in a specified container.
932 */
933 static void updateItemInDatabase(Context context, ItemInfo item) {
934 final ContentValues values = new ContentValues();
935 final ContentResolver cr = context.getContentResolver();
936
937 item.onAddToDatabase(values);
938
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800939 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700940 }
941
942 /**
943 * Removes the specified item from the database
944 * @param context
945 * @param item
946 */
947 static void deleteItemFromDatabase(Context context, ItemInfo item) {
948 final ContentResolver cr = context.getContentResolver();
949
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800950 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700951 }
952
953
954 /**
955 * Remove the contents of the specified folder from the database
956 */
957 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
958 final ContentResolver cr = context.getContentResolver();
959
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800960 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
961 cr.delete(LauncherSettings.Favorites.CONTENT_URI, LauncherSettings.Favorites.CONTAINER + "=" + info.id,
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700962 null);
963 }
964}