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