blob: 5d0179663b5485cf703732cf8a3873a6cf797c59 [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;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070031import android.net.Uri;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070032import android.util.Log;
The Android Open Source Project0e320b22009-01-09 17:51:25 -080033import android.os.Process;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070034
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
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070048 */
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;
The Android Open Source Projectd097a182008-12-17 18:05:58 -080060 private HashMap<Long, FolderInfo> mFolders;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070061
62 private ArrayList<ApplicationInfo> mApplications;
63 private ApplicationsAdapter mApplicationsAdapter;
64 private ApplicationsLoader mApplicationsLoader;
65 private DesktopItemsLoader mDesktopItemsLoader;
66 private Thread mLoader;
67 private Thread mDesktopLoader;
68
69 void abortLoaders() {
70 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
71 mApplicationsLoader.stop();
72 mApplicationsLoaded = false;
73 }
74 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
75 mDesktopItemsLoader.stop();
76 mDesktopItemsLoaded = false;
77 }
78 }
79
80 /**
81 * Loads the list of installed applications in mApplications.
82 */
The Android Open Source Projectd097a182008-12-17 18:05:58 -080083 void loadApplications(boolean isLaunching, Launcher launcher, boolean localeChanged) {
84 if (isLaunching && mApplicationsLoaded && !localeChanged) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070085 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
86 return;
87 }
88
The Android Open Source Projectd097a182008-12-17 18:05:58 -080089 if (mApplicationsAdapter == null || isLaunching || localeChanged) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070090 mApplicationsAdapter = new ApplicationsAdapter(launcher,
91 mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER));
92 }
93
94 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
95 mApplicationsLoader.stop();
96 // Wait for the currently running thread to finish, this can take a little
97 // time but it should be well below the timeout limit
98 try {
99 mLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
100 } catch (InterruptedException e) {
101 // Empty
102 }
103 }
104
105 mApplicationsLoaded = false;
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800106
107 if (!isLaunching) {
108 startApplicationsLoader(launcher);
109 }
110 }
111
112 private void startApplicationsLoader(Launcher launcher) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700113 mApplicationsLoader = new ApplicationsLoader(launcher);
114 mLoader = new Thread(mApplicationsLoader, "Applications Loader");
115 mLoader.start();
116 }
117
118 private class ApplicationsLoader implements Runnable {
119 private final WeakReference<Launcher> mLauncher;
120
121 private volatile boolean mStopped;
122 private volatile boolean mRunning;
123
124 ApplicationsLoader(Launcher launcher) {
125 mLauncher = new WeakReference<Launcher>(launcher);
126 }
127
128 void stop() {
129 mStopped = true;
130 }
131
132 boolean isRunning() {
133 return mRunning;
134 }
135
136 public void run() {
137 mRunning = true;
138
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800139 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
140
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700141 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
142 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
143
144 final Launcher launcher = mLauncher.get();
145 final PackageManager manager = launcher.getPackageManager();
146 final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
147
148 if (apps != null && !mStopped) {
149 final int count = apps.size();
150 final ApplicationsAdapter applicationList = mApplicationsAdapter;
151
152 ChangeNotifier action = new ChangeNotifier(applicationList);
153
154 for (int i = 0; i < count && !mStopped; i++) {
155 ApplicationInfo application = new ApplicationInfo();
156 ResolveInfo info = apps.get(i);
157
158 application.title = info.loadLabel(manager);
159 if (application.title == null) {
160 application.title = info.activityInfo.name;
161 }
162 application.setActivity(new ComponentName(
163 info.activityInfo.applicationInfo.packageName,
164 info.activityInfo.name),
165 Intent.FLAG_ACTIVITY_NEW_TASK |
166 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
167 application.icon = info.activityInfo.loadIcon(manager);
168 application.container = ItemInfo.NO_ID;
169
170 action.add(application);
171 }
172
173 action.sort(new Comparator<ApplicationInfo>() {
174 public final int compare(ApplicationInfo a, ApplicationInfo b) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800175 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700176 }
177 });
178
179 if (!mStopped) {
180 launcher.runOnUiThread(action);
181 }
182 }
183
184 if (!mStopped) {
185 mApplicationsLoaded = true;
186 }
187 mRunning = false;
188 }
189 }
190
191 private static class ChangeNotifier implements Runnable {
192 private final ApplicationsAdapter mApplicationList;
193 private ArrayList<ApplicationInfo> mBuffer;
194
195 ChangeNotifier(ApplicationsAdapter applicationList) {
196 mApplicationList = applicationList;
197 mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
198 }
199
200 public void run() {
201 final ArrayList<ApplicationInfo> buffer = mBuffer;
202 final ApplicationsAdapter applicationList = mApplicationList;
203 final int count = buffer.size();
204
205 applicationList.clear();
206 for (int i = 0; i < count; i++) {
207 applicationList.setNotifyOnChange(false);
208 applicationList.add(buffer.get(i));
209 }
210
211 applicationList.notifyDataSetChanged();
212 buffer.clear();
213 }
214
215 void add(ApplicationInfo application) {
216 mBuffer.add(application);
217 }
218
219 void sort(Comparator<ApplicationInfo> comparator) {
220 Collections.sort(mBuffer, comparator);
221 }
222 }
223
224 boolean isDesktopLoaded() {
225 return mDesktopItems != null && mDesktopItemsLoaded;
226 }
227
228 /**
229 * Loads all of the items on the desktop, in folders, or in the dock.
230 * These can be apps, shortcuts or widgets
231 */
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800232 void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
233 boolean loadApplications) {
234
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700235 if (isLaunching && mDesktopItems != null && mDesktopItemsLoaded) {
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800236 if (loadApplications) startApplicationsLoader(launcher);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700237 // We have already loaded our data from the DB
238 launcher.onDesktopItemsLoaded();
239 return;
240 }
241
242 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
243 mDesktopItemsLoader.stop();
244 // Wait for the currently running thread to finish, this can take a little
245 // time but it should be well below the timeout limit
246 try {
247 mDesktopLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
248 } catch (InterruptedException e) {
249 // Empty
250 }
251 }
252
253 mDesktopItemsLoaded = false;
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800254 mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700255 mDesktopLoader = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
256 mDesktopLoader.start();
257 }
258
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800259 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
260 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
261 new String[] { LauncherSettings.Favorites.ID, LauncherSettings.Favorites.TITLE,
262 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
263 null, null, null);
264
265 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
266 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
267 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
268 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
269
270 // boolean changed = false;
271
272 try {
273 while (c.moveToNext()) {
274 try {
The Android Open Source Project15a88802009-02-10 15:44:05 -0800275 if (c.getInt(itemTypeIndex) !=
276 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800277 continue;
278 }
279
280 final String intentUri = c.getString(intentIndex);
281 if (intentUri != null) {
282 final Intent shortcut = Intent.getIntent(intentUri);
283 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
284 final ComponentName name = shortcut.getComponent();
285 if (name != null) {
286 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
287 final String title = c.getString(titleIndex);
288 String label = getLabel(manager, activityInfo);
289
290 if (title == null || !title.equals(label)) {
291 final ContentValues values = new ContentValues();
292 values.put(LauncherSettings.Favorites.TITLE, label);
293
294 resolver.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
295 values, "_id=?",
296 new String[] { String.valueOf(c.getLong(idIndex)) });
297
298 // changed = true;
299 }
300 }
301 }
302 }
303 } catch (URISyntaxException e) {
304 // Ignore
305 } catch (PackageManager.NameNotFoundException e) {
306 // Ignore
307 }
308 }
309 } finally {
310 c.close();
311 }
312
313 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
314 }
315
316 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
317 String label = activityInfo.loadLabel(manager).toString();
318 if (label == null) {
319 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
320 if (label == null) {
321 label = activityInfo.name;
322 }
323 }
324 return label;
325 }
326
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700327 private class DesktopItemsLoader implements Runnable {
328 private volatile boolean mStopped;
329 private volatile boolean mRunning;
330
331 private final WeakReference<Launcher> mLauncher;
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800332 private final boolean mLocaleChanged;
333 private final boolean mLoadApplications;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700334
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800335 DesktopItemsLoader(Launcher launcher, boolean localeChanged, boolean loadApplications) {
336 mLoadApplications = loadApplications;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700337 mLauncher = new WeakReference<Launcher>(launcher);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800338 mLocaleChanged = localeChanged;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700339 }
340
341 void stop() {
342 mStopped = true;
343 }
344
345 boolean isRunning() {
346 return mRunning;
347 }
348
349 public void run() {
350 mRunning = true;
351
352 final Launcher launcher = mLauncher.get();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800353 final ContentResolver contentResolver = launcher.getContentResolver();
354 final PackageManager manager = launcher.getPackageManager();
355
356 if (mLocaleChanged) {
357 updateShortcutLabels(contentResolver, manager);
358 }
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700359
360 mDesktopItems = new ArrayList<ItemInfo>();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800361 mFolders = new HashMap<Long, FolderInfo>();
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700362
363 final ArrayList<ItemInfo> desktopItems = mDesktopItems;
364
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800365 final Cursor c = contentResolver.query(
366 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700367
368 try {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800369 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
370 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
371 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
372 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
373 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
374 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
375 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
376 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
377 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
The Android Open Source Project15a88802009-02-10 15:44:05 -0800378 final int gadgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.GADGET_ID);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800379 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
380 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
381 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project15a88802009-02-10 15:44:05 -0800382 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
383 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800384 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
385 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700386
387 ApplicationInfo info;
388 String intentDescription;
389 Widget widgetInfo = null;
The Android Open Source Project15a88802009-02-10 15:44:05 -0800390 LauncherGadgetInfo gadgetInfo = null;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700391 int container;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800392 long id;
393 Intent intent;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700394
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800395 final HashMap<Long, FolderInfo> folders = mFolders;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700396
397 while (!mStopped && c.moveToNext()) {
398 try {
399 int itemType = c.getInt(itemTypeIndex);
400
401 switch (itemType) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800402 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
403 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700404 intentDescription = c.getString(intentIndex);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700405 try {
406 intent = Intent.getIntent(intentDescription);
407 } catch (java.net.URISyntaxException e) {
408 continue;
409 }
410
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800411 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700412 info = getApplicationInfo(manager, intent);
413 } else {
414 info = getApplicationInfoShortcut(c, launcher, iconTypeIndex,
415 iconPackageIndex, iconResourceIndex, iconIndex);
416 }
417
418 if (info == null) {
419 info = new ApplicationInfo();
420 info.icon = manager.getDefaultActivityIcon();
421 }
422
423 if (info != null) {
424 info.title = c.getString(titleIndex);
425 info.intent = intent;
426
427 info.id = c.getLong(idIndex);
428 container = c.getInt(containerIndex);
429 info.container = container;
430 info.screen = c.getInt(screenIndex);
431 info.cellX = c.getInt(cellXIndex);
432 info.cellY = c.getInt(cellYIndex);
433
434 switch (container) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800435 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700436 desktopItems.add(info);
437 break;
438 default:
439 // Item is in a user folder
440 UserFolderInfo folderInfo =
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800441 findOrMakeUserFolder(folders, container);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700442 folderInfo.add(info);
443 break;
444 }
445 }
446 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800447 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700448
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800449 id = c.getLong(idIndex);
450 UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700451
452 folderInfo.title = c.getString(titleIndex);
453
454 folderInfo.id = id;
455 container = c.getInt(containerIndex);
456 folderInfo.container = container;
457 folderInfo.screen = c.getInt(screenIndex);
458 folderInfo.cellX = c.getInt(cellXIndex);
459 folderInfo.cellY = c.getInt(cellYIndex);
460
461 switch (container) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800462 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
463 desktopItems.add(folderInfo);
464 break;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700465 }
466 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800467 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
468
469 id = c.getLong(idIndex);
470 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id);
471
472 intentDescription = c.getString(intentIndex);
473 intent = null;
474 if (intentDescription != null) {
475 try {
476 intent = Intent.getIntent(intentDescription);
477 } catch (java.net.URISyntaxException e) {
478 // Ignore, a live folder might not have a base intent
479 }
480 }
481
482 liveFolderInfo.title = c.getString(titleIndex);
483 liveFolderInfo.id = id;
484 container = c.getInt(containerIndex);
485 liveFolderInfo.container = container;
486 liveFolderInfo.screen = c.getInt(screenIndex);
487 liveFolderInfo.cellX = c.getInt(cellXIndex);
488 liveFolderInfo.cellY = c.getInt(cellYIndex);
489 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
490 liveFolderInfo.baseIntent = intent;
491 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
492
493 loadLiveFolderIcon(launcher, c, iconTypeIndex, iconPackageIndex,
494 iconResourceIndex, liveFolderInfo);
495
496 switch (container) {
497 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
498 desktopItems.add(liveFolderInfo);
499 break;
500 }
501 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800502 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
The Android Open Source Project15a88802009-02-10 15:44:05 -0800503 widgetInfo = Widget.makeSearch();
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700504
The Android Open Source Project15a88802009-02-10 15:44:05 -0800505 container = c.getInt(containerIndex);
506 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
507 Log.e(Launcher.LOG_TAG, "Widget found where container "
508 + "!= CONTAINER_DESKTOP ignoring!");
509 continue;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700510 }
The Android Open Source Project15a88802009-02-10 15:44:05 -0800511
512 widgetInfo.id = c.getLong(idIndex);
513 widgetInfo.screen = c.getInt(screenIndex);
514 widgetInfo.container = container;
515 widgetInfo.cellX = c.getInt(cellXIndex);
516 widgetInfo.cellY = c.getInt(cellYIndex);
517
518 desktopItems.add(widgetInfo);
519 break;
520 case LauncherSettings.Favorites.ITEM_TYPE_GADGET:
521 // Read all Launcher-specific gadget details
522 int gadgetId = c.getInt(gadgetIdIndex);
523 gadgetInfo = new LauncherGadgetInfo(gadgetId);
524 gadgetInfo.id = c.getLong(idIndex);
525 gadgetInfo.screen = c.getInt(screenIndex);
526 gadgetInfo.cellX = c.getInt(cellXIndex);
527 gadgetInfo.cellY = c.getInt(cellYIndex);
528 gadgetInfo.spanX = c.getInt(spanXIndex);
529 gadgetInfo.spanY = c.getInt(spanYIndex);
530
531 container = c.getInt(containerIndex);
532 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
533 Log.e(Launcher.LOG_TAG, "Gadget found where container "
534 + "!= CONTAINER_DESKTOP -- ignoring!");
535 continue;
536 }
537 gadgetInfo.container = c.getInt(containerIndex);
538
539 desktopItems.add(gadgetInfo);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700540 break;
541 }
542 } catch (Exception e) {
543 Log.w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e);
544 }
545 }
546 } finally {
547 c.close();
548 }
549
550 if (!mStopped) {
551 launcher.runOnUiThread(new Runnable() {
552 public void run() {
553 launcher.onDesktopItemsLoaded();
554 }
555 });
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800556 if (mLoadApplications) startApplicationsLoader(launcher);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700557 }
558
559 if (!mStopped) {
560 mDesktopItemsLoaded = true;
561 }
562 mRunning = false;
563 }
564 }
565
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800566 private static void loadLiveFolderIcon(Launcher launcher, Cursor c, int iconTypeIndex,
567 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
568
569 int iconType = c.getInt(iconTypeIndex);
570 switch (iconType) {
571 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
572 String packageName = c.getString(iconPackageIndex);
573 String resourceName = c.getString(iconResourceIndex);
574 PackageManager packageManager = launcher.getPackageManager();
575 try {
576 Resources resources = packageManager.getResourcesForApplication(packageName);
577 final int id = resources.getIdentifier(resourceName, null, null);
578 liveFolderInfo.icon = resources.getDrawable(id);
579 } catch (Exception e) {
580 liveFolderInfo.icon =
581 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
582 }
583 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
584 liveFolderInfo.iconResource.packageName = packageName;
585 liveFolderInfo.iconResource.resourceName = resourceName;
586 break;
587 default:
588 liveFolderInfo.icon =
589 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
590 }
591 }
592
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700593 /**
594 * Finds the user folder defined by the specified id.
595 *
596 * @param id The id of the folder to look for.
597 *
598 * @return A UserFolderInfo if the folder exists or null otherwise.
599 */
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800600 FolderInfo findFolderById(long id) {
601 return mFolders.get(id);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700602 }
603
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800604 void addFolder(FolderInfo info) {
605 mFolders.put(info.id, info);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700606 }
607
608 /**
609 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
610 * new one.
611 */
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800612 private UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700613 // See if a placeholder was created for us already
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800614 FolderInfo folderInfo = folders.get(id);
615 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700616 // No placeholder -- create a new instance
617 folderInfo = new UserFolderInfo();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800618 folders.put(id, folderInfo);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700619 }
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800620 return (UserFolderInfo) folderInfo;
621 }
622
623 /**
624 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
625 * new one.
626 */
627 private LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
628 // See if a placeholder was created for us already
629 FolderInfo folderInfo = folders.get(id);
630 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
631 // No placeholder -- create a new instance
632 folderInfo = new LiveFolderInfo();
633 folders.put(id, folderInfo);
634 }
635 return (LiveFolderInfo) folderInfo;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700636 }
637
638 /**
639 * Remove the callback for the cached drawables or we leak the previous
640 * Home screen on orientation change.
641 */
642 void unbind() {
643 mApplicationsAdapter = null;
644 unbindAppDrawables(mApplications);
645 unbindDrawables(mDesktopItems);
646 }
647
648 /**
649 * Remove the callback for the cached drawables or we leak the previous
650 * Home screen on orientation change.
651 */
652 private void unbindDrawables(ArrayList<ItemInfo> desktopItems) {
653 if (desktopItems != null) {
654 final int count = desktopItems.size();
655 for (int i = 0; i < count; i++) {
656 ItemInfo item = desktopItems.get(i);
657 switch (item.itemType) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800658 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
659 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700660 ((ApplicationInfo)item).icon.setCallback(null);
661 }
662 }
663 }
664 }
665
666 /**
667 * Remove the callback for the cached drawables or we leak the previous
668 * Home screen on orientation change.
669 */
670 private void unbindAppDrawables(ArrayList<ApplicationInfo> applications) {
671 if (applications != null) {
672 final int count = applications.size();
673 for (int i = 0; i < count; i++) {
674 applications.get(i).icon.setCallback(null);
675 }
676 }
677 }
678
679 /**
680 * @return The current list of applications
681 */
682 public ArrayList<ApplicationInfo> getApplications() {
683 return mApplications;
684 }
685
686 /**
687 * @return The current list of applications
688 */
689 public ApplicationsAdapter getApplicationsAdapter() {
690 return mApplicationsAdapter;
691 }
692
693 /**
694 * @return The current list of desktop items
695 */
696 public ArrayList<ItemInfo> getDesktopItems() {
697 return mDesktopItems;
698 }
699
700 /**
701 * Add an item to the desktop
702 * @param info
703 */
704 public void addDesktopItem(ItemInfo info) {
705 // TODO: write to DB; also check that folder has been added to folders list
706 mDesktopItems.add(info);
707 }
708
709 /**
710 * Remove an item from the desktop
711 * @param info
712 */
713 public void removeDesktopItem(ItemInfo info) {
714 // TODO: write to DB; figure out if we should remove folder from folders list
715 mDesktopItems.remove(info);
716 }
717
718 /**
719 * Make an ApplicationInfo object for an application
720 */
721 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) {
722 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
723
724 if (resolveInfo == null) {
725 return null;
726 }
727
728 final ApplicationInfo info = new ApplicationInfo();
729 final ActivityInfo activityInfo = resolveInfo.activityInfo;
730 info.icon = activityInfo.loadIcon(manager);
731 if (info.title == null || info.title.length() == 0) {
732 info.title = activityInfo.loadLabel(manager);
733 }
734 if (info.title == null) {
735 info.title = "";
736 }
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800737 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700738 return info;
739 }
740
741 /**
742 * Make an ApplicationInfo object for a sortcut
743 */
744 private ApplicationInfo getApplicationInfoShortcut(Cursor c, Launcher launcher,
745 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
746
747 final ApplicationInfo info = new ApplicationInfo();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800748 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700749
750 int iconType = c.getInt(iconTypeIndex);
751 switch (iconType) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800752 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700753 String packageName = c.getString(iconPackageIndex);
754 String resourceName = c.getString(iconResourceIndex);
755 PackageManager packageManager = launcher.getPackageManager();
756 try {
757 Resources resources = packageManager.getResourcesForApplication(packageName);
758 final int id = resources.getIdentifier(resourceName, null, null);
759 info.icon = resources.getDrawable(id);
760 } catch (Exception e) {
761 info.icon = packageManager.getDefaultActivityIcon();
762 }
763 info.iconResource = new Intent.ShortcutIconResource();
764 info.iconResource.packageName = packageName;
765 info.iconResource.resourceName = resourceName;
766 info.customIcon = false;
767 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800768 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700769 byte[] data = c.getBlob(iconIndex);
770 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800771 info.icon = new FastBitmapDrawable(
772 Utilities.createBitmapThumbnail(bitmap, launcher));
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700773 info.filtered = true;
774 info.customIcon = true;
775 break;
776 default:
777 info.icon = launcher.getPackageManager().getDefaultActivityIcon();
778 info.customIcon = false;
779 break;
780 }
781 return info;
782 }
783
784 /**
785 * Remove an item from the in-memory represention of a user folder. Does not change the DB.
786 */
787 void removeUserFolderItem(UserFolderInfo folder, ItemInfo info) {
788 //noinspection SuspiciousMethodCalls
789 folder.contents.remove(info);
790 }
791
792 /**
793 * Removes a UserFolder from the in-memory list of folders. Does not change the DB.
794 * @param userFolderInfo
795 */
796 void removeUserFolder(UserFolderInfo userFolderInfo) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800797 mFolders.remove(userFolderInfo.id);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700798 }
799
800 /**
801 * Adds an item to the DB if it was not created previously, or move it to a new
802 * <container, screen, cellX, cellY>
803 */
804 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
805 int screen, int cellX, int cellY) {
806 if (item.container == ItemInfo.NO_ID) {
807 // From all apps
808 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
809 } else {
810 // From somewhere else
811 moveItemInDatabase(context, item, container, screen, cellX, cellY);
812 }
813 }
814
815 /**
816 * Move an item in the DB to a new <container, screen, cellX, cellY>
817 */
818 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
819 int cellX, int cellY) {
820 item.container = container;
821 item.screen = screen;
822 item.cellX = cellX;
823 item.cellY = cellY;
824
825 final ContentValues values = new ContentValues();
826 final ContentResolver cr = context.getContentResolver();
827
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800828 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
829 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
830 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
831 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700832
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800833 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700834 }
835
836 /**
837 * Returns true if the shortcuts already exists in the database.
838 * we identify a shortcut by its title and intent.
839 */
840 static boolean shortcutExists(Context context, String title, Intent intent) {
841 final ContentResolver cr = context.getContentResolver();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800842 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700843 new String[] { "title", "intent" }, "title=? and intent=?",
844 new String[] { title, intent.toURI() }, null);
845 boolean result = false;
846 try {
847 result = c.moveToFirst();
848 } finally {
849 c.close();
850 }
851 return result;
852 }
853
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800854 FolderInfo getFolderById(Context context, long id) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700855 final ContentResolver cr = context.getContentResolver();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800856 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
857 "_id=? and itemType=? or itemType=?",
858 new String[] { String.valueOf(id),
859 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
860 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700861
862 try {
863 if (c.moveToFirst()) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800864 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
865 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
866 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
867 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
868 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
869 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700870
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800871 FolderInfo folderInfo = null;
872 switch (c.getInt(itemTypeIndex)) {
873 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
874 folderInfo = findOrMakeUserFolder(mFolders, id);
875 break;
876 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
877 folderInfo = findOrMakeLiveFolder(mFolders, id);
878 break;
879 }
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700880
881 folderInfo.title = c.getString(titleIndex);
882 folderInfo.id = id;
883 folderInfo.container = c.getInt(containerIndex);
884 folderInfo.screen = c.getInt(screenIndex);
885 folderInfo.cellX = c.getInt(cellXIndex);
886 folderInfo.cellY = c.getInt(cellYIndex);
887
888 return folderInfo;
889 }
890 } finally {
891 c.close();
892 }
893
894 return null;
895 }
896
897 static Widget getPhotoFrameInfo(Context context, int screen, int cellX, int cellY) {
898 final ContentResolver cr = context.getContentResolver();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800899 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700900 null, "screen=? and cellX=? and cellY=? and itemType=?",
901 new String[] { String.valueOf(screen), String.valueOf(cellX), String.valueOf(cellY),
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800902 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) }, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700903
904 try {
905 if (c.moveToFirst()) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800906 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
907 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
908 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
909 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
910 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700911
912 Widget widgetInfo = Widget.makePhotoFrame();
913 widgetInfo.id = c.getLong(idIndex);
914 widgetInfo.screen = c.getInt(screenIndex);
915 widgetInfo.container = c.getInt(containerIndex);
916 widgetInfo.cellX = c.getInt(cellXIndex);
917 widgetInfo.cellY = c.getInt(cellYIndex);
918
919 return widgetInfo;
920 }
921 } finally {
922 c.close();
923 }
924
925 return null;
926 }
927
928 /**
929 * Add an item to the database in a specified container. Sets the container, screen, cellX and
930 * cellY fields of the item. Also assigns an ID to the item.
931 */
932 static void addItemToDatabase(Context context, ItemInfo item, long container,
933 int screen, int cellX, int cellY, boolean notify) {
934 item.container = container;
935 item.screen = screen;
936 item.cellX = cellX;
937 item.cellY = cellY;
938
939 final ContentValues values = new ContentValues();
940 final ContentResolver cr = context.getContentResolver();
941
942 item.onAddToDatabase(values);
943
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800944 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
945 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700946
947 if (result != null) {
948 item.id = Integer.parseInt(result.getPathSegments().get(1));
949 }
950 }
951
952 /**
953 * Update an item to the database in a specified container.
954 */
955 static void updateItemInDatabase(Context context, ItemInfo item) {
956 final ContentValues values = new ContentValues();
957 final ContentResolver cr = context.getContentResolver();
958
959 item.onAddToDatabase(values);
960
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800961 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700962 }
963
964 /**
965 * Removes the specified item from the database
966 * @param context
967 * @param item
968 */
969 static void deleteItemFromDatabase(Context context, ItemInfo item) {
970 final ContentResolver cr = context.getContentResolver();
971
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800972 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700973 }
974
975
976 /**
977 * Remove the contents of the specified folder from the database
978 */
979 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
980 final ContentResolver cr = context.getContentResolver();
981
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800982 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
The Android Open Source Project15a88802009-02-10 15:44:05 -0800983 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
984 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700985 }
986}