blob: de84560be84bdad6b203fc3ca7e7f5dd149b83d5 [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.launcher;
18
19import android.content.ComponentName;
20import android.content.ContentResolver;
21import android.content.ContentValues;
22import android.content.Intent;
23import android.content.Context;
24import android.content.pm.ActivityInfo;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.content.res.Resources;
28import android.database.Cursor;
29import android.graphics.Bitmap;
30import android.graphics.BitmapFactory;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070031import android.graphics.drawable.Drawable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.net.Uri;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070033import static android.util.Log.*;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.os.Process;
35
36import java.util.ArrayList;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import 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
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070047 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080048 */
49public class LauncherModel {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070050 static final boolean DEBUG_LOADERS = false;
51 static final String LOG_TAG = "HomeLoaders";
52
The Android Open Source Project31dd5032009-03-03 19:32:27 -080053 private static final int UI_NOTIFICATION_RATE = 4;
54 private static final int DEFAULT_APPLICATIONS_NUMBER = 42;
55 private static final long APPLICATION_NOT_RESPONDING_TIMEOUT = 5000;
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070056 private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057
The Android Open Source Project7376fae2009-03-11 12:11:58 -070058 private static final Collator sCollator = Collator.getInstance();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059
60 private boolean mApplicationsLoaded;
61 private boolean mDesktopItemsLoaded;
62
63 private ArrayList<ItemInfo> mDesktopItems;
The Android Open Source Project7376fae2009-03-11 12:11:58 -070064 private ArrayList<LauncherAppWidgetInfo> mDesktopAppWidgets;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065 private HashMap<Long, FolderInfo> mFolders;
66
67 private ArrayList<ApplicationInfo> mApplications;
68 private ApplicationsAdapter mApplicationsAdapter;
69 private ApplicationsLoader mApplicationsLoader;
70 private DesktopItemsLoader mDesktopItemsLoader;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -070071 private Thread mApplicationsLoaderThread;
72 private Thread mDesktopLoaderThread;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080073
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070074 private final HashMap<ComponentName, ApplicationInfo> mAppInfoCache =
75 new HashMap<ComponentName, ApplicationInfo>(INITIAL_ICON_CACHE_CAPACITY);
76
The Android Open Source Projectca9475f2009-03-13 13:04:24 -070077 synchronized void abortLoaders() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -080078 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
79 mApplicationsLoader.stop();
80 mApplicationsLoaded = false;
81 }
The Android Open Source Projectca9475f2009-03-13 13:04:24 -070082
The Android Open Source Project31dd5032009-03-03 19:32:27 -080083 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
84 mDesktopItemsLoader.stop();
85 mDesktopItemsLoaded = false;
86 }
87 }
88
89 /**
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070090 * Drop our cache of components to their lables & icons. We do
91 * this from Launcher when applications are added/removed. It's a
92 * bit overkill, but it's a rare operation anyway.
93 */
94 synchronized void dropApplicationCache() {
95 mAppInfoCache.clear();
96 }
97
98 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -080099 * Loads the list of installed applications in mApplications.
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700100 *
101 * @return true if the applications loader must be started
102 * (see startApplicationsLoader()), false otherwise.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800103 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700104 synchronized boolean loadApplications(boolean isLaunching, Launcher launcher,
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700105 boolean localeChanged) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700106
107 if (DEBUG_LOADERS) d(LOG_TAG, "load applications");
108
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800109 if (isLaunching && mApplicationsLoaded && !localeChanged) {
110 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700111 if (DEBUG_LOADERS) d(LOG_TAG, " --> applications loaded, return");
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700112 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113 }
114
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700115 stopAndWaitForApplicationsLoader();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700116
117 if (localeChanged) {
118 dropApplicationCache();
119 }
120
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800121 if (mApplicationsAdapter == null || isLaunching || localeChanged) {
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700122 mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
123 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800124 }
125
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800126 mApplicationsLoaded = false;
127
128 if (!isLaunching) {
129 startApplicationsLoader(launcher);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700130 return false;
131 }
132
133 return true;
134 }
135
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700136 private synchronized void stopAndWaitForApplicationsLoader() {
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700137 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700138 if (DEBUG_LOADERS) d(LOG_TAG, " --> wait for applications loader");
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700139
140 mApplicationsLoader.stop();
141 // Wait for the currently running thread to finish, this can take a little
142 // time but it should be well below the timeout limit
143 try {
144 mApplicationsLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
145 } catch (InterruptedException e) {
146 // EMpty
147 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800148 }
149 }
150
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700151 private synchronized void startApplicationsLoader(Launcher launcher) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700152 if (DEBUG_LOADERS) d(LOG_TAG, " --> starting applications loader");
153
154 stopAndWaitForApplicationsLoader();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700155
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800156 mApplicationsLoader = new ApplicationsLoader(launcher);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700157 mApplicationsLoaderThread = new Thread(mApplicationsLoader, "Applications Loader");
158 mApplicationsLoaderThread.start();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800159 }
160
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700161 synchronized void addPackage(Launcher launcher, String packageName) {
162 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
163 startApplicationsLoader(launcher);
164 return;
165 }
166
167 if (packageName != null && packageName.length() > 0) {
168 final PackageManager packageManager = launcher.getPackageManager();
169 final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName);
170
171 if (matches.size() > 0) {
172 final ApplicationsAdapter adapter = mApplicationsAdapter;
173 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
174
175 for (ResolveInfo info : matches) {
176 adapter.setNotifyOnChange(false);
177 adapter.add(makeAndCacheApplicationInfo(packageManager, cache, info));
178 }
179
180 adapter.sort(new ApplicationInfoComparator());
181 adapter.notifyDataSetChanged();
182 }
183 }
184 }
185
186 synchronized void removePackage(Launcher launcher, String packageName) {
187 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
188 dropApplicationCache(); // TODO: this could be optimized
189 startApplicationsLoader(launcher);
190 return;
191 }
192
193 if (packageName != null && packageName.length() > 0) {
194 final ApplicationsAdapter adapter = mApplicationsAdapter;
195
196 final List<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>();
197 final int count = adapter.getCount();
198
199 for (int i = 0; i < count; i++) {
200 final ApplicationInfo applicationInfo = adapter.getItem(i);
201 final Intent intent = applicationInfo.intent;
202 final ComponentName component = intent.getComponent();
203 if (packageName.equals(component.getPackageName())) {
204 toRemove.add(applicationInfo);
205 }
206 }
207
208 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
209 for (ApplicationInfo info : toRemove) {
210 adapter.setNotifyOnChange(false);
211 adapter.remove(info);
212 cache.remove(info.intent.getComponent());
213 }
214
215 if (toRemove.size() > 0) {
216 adapter.sort(new ApplicationInfoComparator());
217 adapter.notifyDataSetChanged();
218 }
219 }
220 }
221
222 synchronized void updatePackage(Launcher launcher, String packageName) {
223 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
224 startApplicationsLoader(launcher);
225 return;
226 }
227
228 if (packageName != null && packageName.length() > 0) {
229 final PackageManager packageManager = launcher.getPackageManager();
230 final ApplicationsAdapter adapter = mApplicationsAdapter;
231
232 final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName);
233 final int count = matches.size();
234
235 boolean changed = false;
236
237 for (int i = 0; i < count; i++) {
238 final ResolveInfo info = matches.get(i);
239 final ApplicationInfo applicationInfo = findIntent(adapter,
240 info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
241 if (applicationInfo != null) {
242 updateAndCacheApplicationInfo(packageManager, info, applicationInfo);
243 changed = true;
244 }
245 }
246
247 if (changed) {
248 adapter.sort(new ApplicationInfoComparator());
249 adapter.notifyDataSetChanged();
250 }
251 }
252 }
253
254 private void updateAndCacheApplicationInfo(PackageManager packageManager, ResolveInfo info,
255 ApplicationInfo applicationInfo) {
256
257 updateApplicationInfoTitleAndIcon(packageManager, info, applicationInfo);
258
259 ComponentName componentName = new ComponentName(
260 info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
261 mAppInfoCache.put(componentName, applicationInfo);
262 }
263
264 synchronized void syncPackage(Launcher launcher, String packageName) {
265 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
266 startApplicationsLoader(launcher);
267 return;
268 }
269
270 if (packageName != null && packageName.length() > 0) {
271 final PackageManager packageManager = launcher.getPackageManager();
272 final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName);
273
274 if (matches.size() > 0) {
275 final ApplicationsAdapter adapter = mApplicationsAdapter;
276
277 // Find disabled activities and remove them from the adapter
278 boolean removed = removeDisabledActivities(packageName, matches, adapter);
279 // Find enable activities and add them to the adapter
280 // Also updates existing activities with new labels/icons
281 boolean added = addEnabledAndUpdateActivities(matches, adapter, launcher);
282
283 if (added || removed) {
284 adapter.sort(new ApplicationInfoComparator());
285 adapter.notifyDataSetChanged();
286 }
287 }
288 }
289 }
290
291 private static List<ResolveInfo> findActivitiesForPackage(PackageManager packageManager,
292 String packageName) {
293
294 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
295 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
296
297 final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
298 final List<ResolveInfo> matches = new ArrayList<ResolveInfo>();
299
300 if (apps != null) {
301 // Find all activities that match the packageName
302 int count = apps.size();
303 for (int i = 0; i < count; i++) {
304 final ResolveInfo info = apps.get(i);
305 final ActivityInfo activityInfo = info.activityInfo;
306 if (packageName.equals(activityInfo.packageName)) {
307 matches.add(info);
308 }
309 }
310 }
311
312 return matches;
313 }
314
315 private boolean addEnabledAndUpdateActivities(List<ResolveInfo> matches,
316 ApplicationsAdapter adapter, Launcher launcher) {
317
318 final List<ApplicationInfo> toAdd = new ArrayList<ApplicationInfo>();
319 final int count = matches.size();
320
321 boolean changed = false;
322
323 for (int i = 0; i < count; i++) {
324 final ResolveInfo info = matches.get(i);
325 final ApplicationInfo applicationInfo = findIntent(adapter,
326 info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
327 if (applicationInfo == null) {
328 toAdd.add(makeAndCacheApplicationInfo(launcher.getPackageManager(),
329 mAppInfoCache, info));
330 changed = true;
331 } else {
332 updateAndCacheApplicationInfo(launcher.getPackageManager(), info, applicationInfo);
333 changed = true;
334 }
335 }
336
337 for (ApplicationInfo info : toAdd) {
338 adapter.setNotifyOnChange(false);
339 adapter.add(info);
340 }
341
342 return changed;
343 }
344
345 private boolean removeDisabledActivities(String packageName, List<ResolveInfo> matches,
346 ApplicationsAdapter adapter) {
347
348 final List<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>();
349 final int count = adapter.getCount();
350
351 boolean changed = false;
352
353 for (int i = 0; i < count; i++) {
354 final ApplicationInfo applicationInfo = adapter.getItem(i);
355 final Intent intent = applicationInfo.intent;
356 final ComponentName component = intent.getComponent();
357 if (packageName.equals(component.getPackageName())) {
358 if (!findIntent(matches, component)) {
359 toRemove.add(applicationInfo);
360 changed = true;
361 }
362 }
363 }
364
365 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
366 for (ApplicationInfo info : toRemove) {
367 adapter.setNotifyOnChange(false);
368 adapter.remove(info);
369 cache.remove(info.intent.getComponent());
370 }
371
372 return changed;
373 }
374
375 private static ApplicationInfo findIntent(ApplicationsAdapter adapter, String packageName,
376 String name) {
377
378 final int count = adapter.getCount();
379 for (int i = 0; i < count; i++) {
380 final ApplicationInfo applicationInfo = adapter.getItem(i);
381 final Intent intent = applicationInfo.intent;
382 final ComponentName component = intent.getComponent();
383 if (packageName.equals(component.getPackageName()) &&
384 name.equals(component.getClassName())) {
385 return applicationInfo;
386 }
387 }
388
389 return null;
390 }
391
392 private static boolean findIntent(List<ResolveInfo> apps, ComponentName component) {
393 final String className = component.getClassName();
394 for (ResolveInfo info : apps) {
395 final ActivityInfo activityInfo = info.activityInfo;
396 if (activityInfo.name.equals(className)) {
397 return true;
398 }
399 }
400 return false;
401 }
402
403 Drawable getApplicationInfoIcon(PackageManager manager, ApplicationInfo info) {
404 final ResolveInfo resolveInfo = manager.resolveActivity(info.intent, 0);
405 if (resolveInfo == null) {
406 return null;
407 }
408
409 ComponentName componentName = new ComponentName(
410 resolveInfo.activityInfo.applicationInfo.packageName,
411 resolveInfo.activityInfo.name);
412 ApplicationInfo application = mAppInfoCache.get(componentName);
413
414 if (application == null) {
415 return resolveInfo.activityInfo.loadIcon(manager);
416 }
417
418 return application.icon;
419 }
420
421 private static ApplicationInfo makeAndCacheApplicationInfo(PackageManager manager,
422 HashMap<ComponentName, ApplicationInfo> appInfoCache, ResolveInfo info) {
423
424 ComponentName componentName = new ComponentName(
425 info.activityInfo.applicationInfo.packageName,
426 info.activityInfo.name);
427 ApplicationInfo application = appInfoCache.get(componentName);
428
429 if (application == null) {
430 application = new ApplicationInfo();
431 application.container = ItemInfo.NO_ID;
432
433 updateApplicationInfoTitleAndIcon(manager, info, application);
434
435 application.setActivity(componentName,
436 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
437
438 appInfoCache.put(componentName, application);
439 }
440
441 return application;
442 }
443
444 private static void updateApplicationInfoTitleAndIcon(PackageManager manager, ResolveInfo info,
445 ApplicationInfo application) {
446
447 application.title = info.loadLabel(manager);
448 if (application.title == null) {
449 application.title = info.activityInfo.name;
450 }
451
452 application.icon = info.activityInfo.loadIcon(manager);
453 application.filtered = false;
454 }
455
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800456 private class ApplicationsLoader implements Runnable {
457 private final WeakReference<Launcher> mLauncher;
458
459 private volatile boolean mStopped;
460 private volatile boolean mRunning;
461
462 ApplicationsLoader(Launcher launcher) {
463 mLauncher = new WeakReference<Launcher>(launcher);
464 }
465
466 void stop() {
467 mStopped = true;
468 }
469
470 boolean isRunning() {
471 return mRunning;
472 }
473
474 public void run() {
475 mRunning = true;
476
477 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
478
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700479 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800480 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
481
482 final Launcher launcher = mLauncher.get();
483 final PackageManager manager = launcher.getPackageManager();
484 final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
485
486 if (apps != null && !mStopped) {
487 final int count = apps.size();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700488 // Can be set to null on the UI thread by the unbind() method
489 // Do not access without checking for null first
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800490 final ApplicationsAdapter applicationList = mApplicationsAdapter;
491
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700492 ChangeNotifier action = new ChangeNotifier(applicationList, true);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700493 final HashMap<ComponentName, ApplicationInfo> appInfoCache = mAppInfoCache;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800494
495 for (int i = 0; i < count && !mStopped; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800496 ResolveInfo info = apps.get(i);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700497 ApplicationInfo application =
498 makeAndCacheApplicationInfo(manager, appInfoCache, info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800499
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700500 if (action.add(application) && !mStopped) {
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700501 launcher.runOnUiThread(action);
502 action = new ChangeNotifier(applicationList, false);
503 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800504 }
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700505
506 launcher.runOnUiThread(action);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800507 }
508
509 if (!mStopped) {
510 mApplicationsLoaded = true;
511 }
512 mRunning = false;
513 }
514 }
515
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700516 private static class ChangeNotifier implements Runnable {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800517 private final ApplicationsAdapter mApplicationList;
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700518 private final ArrayList<ApplicationInfo> mBuffer;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800519
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700520 private boolean mFirst = true;
521
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700522 ChangeNotifier(ApplicationsAdapter applicationList, boolean first) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800523 mApplicationList = applicationList;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700524 mFirst = first;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800525 mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
526 }
527
528 public void run() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800529 final ApplicationsAdapter applicationList = mApplicationList;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700530 // Can be set to null on the UI thread by the unbind() method
531 if (applicationList == null) return;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800532
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700533 if (mFirst) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800534 applicationList.setNotifyOnChange(false);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700535 applicationList.clear();
536 mFirst = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800537 }
538
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700539 final ArrayList<ApplicationInfo> buffer = mBuffer;
540 final int count = buffer.size();
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700541
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700542 for (int i = 0; i < count; i++) {
543 applicationList.setNotifyOnChange(false);
544 applicationList.add(buffer.get(i));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700545 }
546
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700547 buffer.clear();
548
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700549 applicationList.sort(new ApplicationInfoComparator());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800550 applicationList.notifyDataSetChanged();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800551 }
552
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700553 boolean add(ApplicationInfo application) {
554 final ArrayList<ApplicationInfo> buffer = mBuffer;
555 buffer.add(application);
556 return buffer.size() >= UI_NOTIFICATION_RATE;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800557 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700558 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800559
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700560 private static class ApplicationInfoComparator implements Comparator<ApplicationInfo> {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700561 public final int compare(ApplicationInfo a, ApplicationInfo b) {
562 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800563 }
564 }
565
566 boolean isDesktopLoaded() {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700567 return mDesktopItems != null && mDesktopAppWidgets != null && mDesktopItemsLoaded;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800568 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700569
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800570 /**
571 * Loads all of the items on the desktop, in folders, or in the dock.
572 * These can be apps, shortcuts or widgets
573 */
574 void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
575 boolean loadApplications) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700576 if (DEBUG_LOADERS) d(LOG_TAG, "loading user items");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800577
578 if (isLaunching && isDesktopLoaded()) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700579 if (DEBUG_LOADERS) d(LOG_TAG, " --> items loaded, return");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800580 if (loadApplications) startApplicationsLoader(launcher);
581 // We have already loaded our data from the DB
582 launcher.onDesktopItemsLoaded();
583 return;
584 }
585
586 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
587 mDesktopItemsLoader.stop();
588 // Wait for the currently running thread to finish, this can take a little
589 // time but it should be well below the timeout limit
590 try {
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700591 mDesktopLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800592 } catch (InterruptedException e) {
593 // Empty
594 }
595 }
596
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700597 if (DEBUG_LOADERS) d(LOG_TAG, " --> starting workspace loader");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800598 mDesktopItemsLoaded = false;
599 mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700600 mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
601 mDesktopLoaderThread.start();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800602 }
603
604 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
605 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
606 new String[] { LauncherSettings.Favorites.ID, LauncherSettings.Favorites.TITLE,
607 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
608 null, null, null);
609
610 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
611 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
612 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
613 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
614
615 // boolean changed = false;
616
617 try {
618 while (c.moveToNext()) {
619 try {
620 if (c.getInt(itemTypeIndex) !=
621 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
622 continue;
623 }
624
625 final String intentUri = c.getString(intentIndex);
626 if (intentUri != null) {
627 final Intent shortcut = Intent.getIntent(intentUri);
628 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
629 final ComponentName name = shortcut.getComponent();
630 if (name != null) {
631 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
632 final String title = c.getString(titleIndex);
633 String label = getLabel(manager, activityInfo);
634
635 if (title == null || !title.equals(label)) {
636 final ContentValues values = new ContentValues();
637 values.put(LauncherSettings.Favorites.TITLE, label);
638
639 resolver.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
640 values, "_id=?",
641 new String[] { String.valueOf(c.getLong(idIndex)) });
642
643 // changed = true;
644 }
645 }
646 }
647 }
648 } catch (URISyntaxException e) {
649 // Ignore
650 } catch (PackageManager.NameNotFoundException e) {
651 // Ignore
652 }
653 }
654 } finally {
655 c.close();
656 }
657
658 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
659 }
660
661 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
662 String label = activityInfo.loadLabel(manager).toString();
663 if (label == null) {
664 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
665 if (label == null) {
666 label = activityInfo.name;
667 }
668 }
669 return label;
670 }
671
672 private class DesktopItemsLoader implements Runnable {
673 private volatile boolean mStopped;
674 private volatile boolean mRunning;
675
676 private final WeakReference<Launcher> mLauncher;
677 private final boolean mLocaleChanged;
678 private final boolean mLoadApplications;
679
680 DesktopItemsLoader(Launcher launcher, boolean localeChanged, boolean loadApplications) {
681 mLoadApplications = loadApplications;
682 mLauncher = new WeakReference<Launcher>(launcher);
683 mLocaleChanged = localeChanged;
684 }
685
686 void stop() {
687 mStopped = true;
688 }
689
690 boolean isRunning() {
691 return mRunning;
692 }
693
694 public void run() {
695 mRunning = true;
696
697 final Launcher launcher = mLauncher.get();
698 final ContentResolver contentResolver = launcher.getContentResolver();
699 final PackageManager manager = launcher.getPackageManager();
700
701 if (mLocaleChanged) {
702 updateShortcutLabels(contentResolver, manager);
703 }
704
705 mDesktopItems = new ArrayList<ItemInfo>();
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700706 mDesktopAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800707 mFolders = new HashMap<Long, FolderInfo>();
708
709 final ArrayList<ItemInfo> desktopItems = mDesktopItems;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700710 final ArrayList<LauncherAppWidgetInfo> desktopAppWidgets = mDesktopAppWidgets;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800711
712 final Cursor c = contentResolver.query(
713 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
714
715 try {
716 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
717 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
718 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
719 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
720 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
721 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
722 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
723 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
724 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700725 final int appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWIDGET_ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800726 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
727 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
728 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
729 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
730 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
731 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
732 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
733
734 ApplicationInfo info;
735 String intentDescription;
736 Widget widgetInfo;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700737 LauncherAppWidgetInfo appWidgetInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800738 int container;
739 long id;
740 Intent intent;
741
742 final HashMap<Long, FolderInfo> folders = mFolders;
743
744 while (!mStopped && c.moveToNext()) {
745 try {
746 int itemType = c.getInt(itemTypeIndex);
747
748 switch (itemType) {
749 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
750 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
751 intentDescription = c.getString(intentIndex);
752 try {
753 intent = Intent.getIntent(intentDescription);
754 } catch (java.net.URISyntaxException e) {
755 continue;
756 }
757
758 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
759 info = getApplicationInfo(manager, intent);
760 } else {
761 info = getApplicationInfoShortcut(c, launcher, iconTypeIndex,
762 iconPackageIndex, iconResourceIndex, iconIndex);
763 }
764
765 if (info == null) {
766 info = new ApplicationInfo();
767 info.icon = manager.getDefaultActivityIcon();
768 }
769
770 if (info != null) {
771 info.title = c.getString(titleIndex);
772 info.intent = intent;
773
774 info.id = c.getLong(idIndex);
775 container = c.getInt(containerIndex);
776 info.container = container;
777 info.screen = c.getInt(screenIndex);
778 info.cellX = c.getInt(cellXIndex);
779 info.cellY = c.getInt(cellYIndex);
780
781 switch (container) {
782 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
783 desktopItems.add(info);
784 break;
785 default:
786 // Item is in a user folder
787 UserFolderInfo folderInfo =
788 findOrMakeUserFolder(folders, container);
789 folderInfo.add(info);
790 break;
791 }
792 }
793 break;
794 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
795
796 id = c.getLong(idIndex);
797 UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id);
798
799 folderInfo.title = c.getString(titleIndex);
800
801 folderInfo.id = id;
802 container = c.getInt(containerIndex);
803 folderInfo.container = container;
804 folderInfo.screen = c.getInt(screenIndex);
805 folderInfo.cellX = c.getInt(cellXIndex);
806 folderInfo.cellY = c.getInt(cellYIndex);
807
808 switch (container) {
809 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
810 desktopItems.add(folderInfo);
811 break;
812 }
813 break;
814 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
815
816 id = c.getLong(idIndex);
817 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id);
818
819 intentDescription = c.getString(intentIndex);
820 intent = null;
821 if (intentDescription != null) {
822 try {
823 intent = Intent.getIntent(intentDescription);
824 } catch (java.net.URISyntaxException e) {
825 // Ignore, a live folder might not have a base intent
826 }
827 }
828
829 liveFolderInfo.title = c.getString(titleIndex);
830 liveFolderInfo.id = id;
831 container = c.getInt(containerIndex);
832 liveFolderInfo.container = container;
833 liveFolderInfo.screen = c.getInt(screenIndex);
834 liveFolderInfo.cellX = c.getInt(cellXIndex);
835 liveFolderInfo.cellY = c.getInt(cellYIndex);
836 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
837 liveFolderInfo.baseIntent = intent;
838 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
839
840 loadLiveFolderIcon(launcher, c, iconTypeIndex, iconPackageIndex,
841 iconResourceIndex, liveFolderInfo);
842
843 switch (container) {
844 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
845 desktopItems.add(liveFolderInfo);
846 break;
847 }
848 break;
849 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
850 widgetInfo = Widget.makeSearch();
851
852 container = c.getInt(containerIndex);
853 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700854 e(Launcher.LOG_TAG, "Widget found where container "
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800855 + "!= CONTAINER_DESKTOP ignoring!");
856 continue;
857 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700858
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800859 widgetInfo.id = c.getLong(idIndex);
860 widgetInfo.screen = c.getInt(screenIndex);
861 widgetInfo.container = container;
862 widgetInfo.cellX = c.getInt(cellXIndex);
863 widgetInfo.cellY = c.getInt(cellYIndex);
864
865 desktopItems.add(widgetInfo);
866 break;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700867 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
868 // Read all Launcher-specific widget details
869 int appWidgetId = c.getInt(appWidgetIdIndex);
870 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
871 appWidgetInfo.id = c.getLong(idIndex);
872 appWidgetInfo.screen = c.getInt(screenIndex);
873 appWidgetInfo.cellX = c.getInt(cellXIndex);
874 appWidgetInfo.cellY = c.getInt(cellYIndex);
875 appWidgetInfo.spanX = c.getInt(spanXIndex);
876 appWidgetInfo.spanY = c.getInt(spanYIndex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800877
878 container = c.getInt(containerIndex);
879 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700880 e(Launcher.LOG_TAG, "Widget found where container "
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800881 + "!= CONTAINER_DESKTOP -- ignoring!");
882 continue;
883 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700884 appWidgetInfo.container = c.getInt(containerIndex);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700885
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700886 desktopAppWidgets.add(appWidgetInfo);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800887 break;
888 }
889 } catch (Exception e) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700890 w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800891 }
892 }
893 } finally {
894 c.close();
895 }
896
897 if (!mStopped) {
898 launcher.runOnUiThread(new Runnable() {
899 public void run() {
900 launcher.onDesktopItemsLoaded();
901 }
902 });
903 if (mLoadApplications) startApplicationsLoader(launcher);
904 }
905
906 if (!mStopped) {
907 mDesktopItemsLoaded = true;
908 }
909 mRunning = false;
910 }
911 }
912
913 private static void loadLiveFolderIcon(Launcher launcher, Cursor c, int iconTypeIndex,
914 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
915
916 int iconType = c.getInt(iconTypeIndex);
917 switch (iconType) {
918 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
919 String packageName = c.getString(iconPackageIndex);
920 String resourceName = c.getString(iconResourceIndex);
921 PackageManager packageManager = launcher.getPackageManager();
922 try {
923 Resources resources = packageManager.getResourcesForApplication(packageName);
924 final int id = resources.getIdentifier(resourceName, null, null);
925 liveFolderInfo.icon = resources.getDrawable(id);
926 } catch (Exception e) {
927 liveFolderInfo.icon =
928 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
929 }
930 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
931 liveFolderInfo.iconResource.packageName = packageName;
932 liveFolderInfo.iconResource.resourceName = resourceName;
933 break;
934 default:
935 liveFolderInfo.icon =
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700936 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800937 }
938 }
939
940 /**
941 * Finds the user folder defined by the specified id.
942 *
943 * @param id The id of the folder to look for.
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700944 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800945 * @return A UserFolderInfo if the folder exists or null otherwise.
946 */
947 FolderInfo findFolderById(long id) {
948 return mFolders.get(id);
949 }
950
951 void addFolder(FolderInfo info) {
952 mFolders.put(info.id, info);
953 }
954
955 /**
956 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
957 * new one.
958 */
959 private UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
960 // See if a placeholder was created for us already
961 FolderInfo folderInfo = folders.get(id);
962 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
963 // No placeholder -- create a new instance
964 folderInfo = new UserFolderInfo();
965 folders.put(id, folderInfo);
966 }
967 return (UserFolderInfo) folderInfo;
968 }
969
970 /**
971 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
972 * new one.
973 */
974 private LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
975 // See if a placeholder was created for us already
976 FolderInfo folderInfo = folders.get(id);
977 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
978 // No placeholder -- create a new instance
979 folderInfo = new LiveFolderInfo();
980 folders.put(id, folderInfo);
981 }
982 return (LiveFolderInfo) folderInfo;
983 }
984
985 /**
986 * Remove the callback for the cached drawables or we leak the previous
987 * Home screen on orientation change.
988 */
989 void unbind() {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700990 // Interrupt the applications loader before setting the adapter to null
991 stopAndWaitForApplicationsLoader();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800992 mApplicationsAdapter = null;
993 unbindAppDrawables(mApplications);
994 unbindDrawables(mDesktopItems);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700995 unbindAppWidgetHostViews(mDesktopAppWidgets);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700996 unbindCachedIconDrawables();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800997 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700998
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800999 /**
1000 * Remove the callback for the cached drawables or we leak the previous
1001 * Home screen on orientation change.
1002 */
1003 private void unbindDrawables(ArrayList<ItemInfo> desktopItems) {
1004 if (desktopItems != null) {
1005 final int count = desktopItems.size();
1006 for (int i = 0; i < count; i++) {
1007 ItemInfo item = desktopItems.get(i);
1008 switch (item.itemType) {
1009 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1010 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1011 ((ApplicationInfo)item).icon.setCallback(null);
1012 break;
1013 }
1014 }
1015 }
1016 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001017
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001018 /**
1019 * Remove the callback for the cached drawables or we leak the previous
1020 * Home screen on orientation change.
1021 */
1022 private void unbindAppDrawables(ArrayList<ApplicationInfo> applications) {
1023 if (applications != null) {
1024 final int count = applications.size();
1025 for (int i = 0; i < count; i++) {
1026 applications.get(i).icon.setCallback(null);
1027 }
1028 }
1029 }
1030
1031 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001032 * Remove any {@link LauncherAppWidgetHostView} references in our widgets.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001033 */
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001034 private void unbindAppWidgetHostViews(ArrayList<LauncherAppWidgetInfo> appWidgets) {
1035 if (appWidgets != null) {
1036 final int count = appWidgets.size();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001037 for (int i = 0; i < count; i++) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001038 LauncherAppWidgetInfo launcherInfo = appWidgets.get(i);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001039 launcherInfo.hostView = null;
1040 }
1041 }
1042 }
1043
1044 /**
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001045 * Remove the callback for the cached drawables or we leak the previous
1046 * Home screen on orientation change.
1047 */
1048 private void unbindCachedIconDrawables() {
1049 for (ApplicationInfo appInfo : mAppInfoCache.values()) {
1050 appInfo.icon.setCallback(null);
1051 }
1052 }
1053
1054 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001055 * @return The current list of applications
1056 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001057 ApplicationsAdapter getApplicationsAdapter() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001058 return mApplicationsAdapter;
1059 }
1060
1061 /**
1062 * @return The current list of desktop items
1063 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001064 ArrayList<ItemInfo> getDesktopItems() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001065 return mDesktopItems;
1066 }
1067
1068 /**
1069 * @return The current list of desktop items
1070 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001071 ArrayList<LauncherAppWidgetInfo> getDesktopAppWidgets() {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001072 return mDesktopAppWidgets;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001073 }
1074
1075 /**
1076 * Add an item to the desktop
1077 * @param info
1078 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001079 void addDesktopItem(ItemInfo info) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001080 // TODO: write to DB; also check that folder has been added to folders list
1081 mDesktopItems.add(info);
1082 }
1083
1084 /**
1085 * Remove an item from the desktop
1086 * @param info
1087 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001088 void removeDesktopItem(ItemInfo info) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001089 // TODO: write to DB; figure out if we should remove folder from folders list
1090 mDesktopItems.remove(info);
1091 }
1092
1093 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001094 * Add a widget to the desktop
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001095 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001096 void addDesktopAppWidget(LauncherAppWidgetInfo info) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001097 mDesktopAppWidgets.add(info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001098 }
1099
1100 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001101 * Remove a widget from the desktop
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001102 */
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001103 void removeDesktopAppWidget(LauncherAppWidgetInfo info) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001104 mDesktopAppWidgets.remove(info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001105 }
1106
1107 /**
1108 * Make an ApplicationInfo object for an application
1109 */
1110 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) {
1111 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1112
1113 if (resolveInfo == null) {
1114 return null;
1115 }
1116
1117 final ApplicationInfo info = new ApplicationInfo();
1118 final ActivityInfo activityInfo = resolveInfo.activityInfo;
1119 info.icon = activityInfo.loadIcon(manager);
1120 if (info.title == null || info.title.length() == 0) {
1121 info.title = activityInfo.loadLabel(manager);
1122 }
1123 if (info.title == null) {
1124 info.title = "";
1125 }
1126 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1127 return info;
1128 }
1129
1130 /**
1131 * Make an ApplicationInfo object for a sortcut
1132 */
1133 private ApplicationInfo getApplicationInfoShortcut(Cursor c, Launcher launcher,
1134 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
1135
1136 final ApplicationInfo info = new ApplicationInfo();
1137 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
1138
1139 int iconType = c.getInt(iconTypeIndex);
1140 switch (iconType) {
1141 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1142 String packageName = c.getString(iconPackageIndex);
1143 String resourceName = c.getString(iconResourceIndex);
1144 PackageManager packageManager = launcher.getPackageManager();
1145 try {
1146 Resources resources = packageManager.getResourcesForApplication(packageName);
1147 final int id = resources.getIdentifier(resourceName, null, null);
1148 info.icon = resources.getDrawable(id);
1149 } catch (Exception e) {
1150 info.icon = packageManager.getDefaultActivityIcon();
1151 }
1152 info.iconResource = new Intent.ShortcutIconResource();
1153 info.iconResource.packageName = packageName;
1154 info.iconResource.resourceName = resourceName;
1155 info.customIcon = false;
1156 break;
1157 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
1158 byte[] data = c.getBlob(iconIndex);
1159 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
1160 info.icon = new FastBitmapDrawable(
1161 Utilities.createBitmapThumbnail(bitmap, launcher));
1162 info.filtered = true;
1163 info.customIcon = true;
1164 break;
1165 default:
1166 info.icon = launcher.getPackageManager().getDefaultActivityIcon();
1167 info.customIcon = false;
1168 break;
1169 }
1170 return info;
1171 }
1172
1173 /**
1174 * Remove an item from the in-memory represention of a user folder. Does not change the DB.
1175 */
1176 void removeUserFolderItem(UserFolderInfo folder, ItemInfo info) {
1177 //noinspection SuspiciousMethodCalls
1178 folder.contents.remove(info);
1179 }
1180
1181 /**
1182 * Removes a UserFolder from the in-memory list of folders. Does not change the DB.
1183 * @param userFolderInfo
1184 */
1185 void removeUserFolder(UserFolderInfo userFolderInfo) {
1186 mFolders.remove(userFolderInfo.id);
1187 }
1188
1189 /**
1190 * Adds an item to the DB if it was not created previously, or move it to a new
1191 * <container, screen, cellX, cellY>
1192 */
1193 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
1194 int screen, int cellX, int cellY) {
1195 if (item.container == ItemInfo.NO_ID) {
1196 // From all apps
1197 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
1198 } else {
1199 // From somewhere else
1200 moveItemInDatabase(context, item, container, screen, cellX, cellY);
1201 }
1202 }
1203
1204 /**
1205 * Move an item in the DB to a new <container, screen, cellX, cellY>
1206 */
1207 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
1208 int cellX, int cellY) {
1209 item.container = container;
1210 item.screen = screen;
1211 item.cellX = cellX;
1212 item.cellY = cellY;
1213
1214 final ContentValues values = new ContentValues();
1215 final ContentResolver cr = context.getContentResolver();
1216
1217 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
1218 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
1219 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
1220 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
1221
1222 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
1223 }
1224
1225 /**
1226 * Returns true if the shortcuts already exists in the database.
1227 * we identify a shortcut by its title and intent.
1228 */
1229 static boolean shortcutExists(Context context, String title, Intent intent) {
1230 final ContentResolver cr = context.getContentResolver();
1231 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
1232 new String[] { "title", "intent" }, "title=? and intent=?",
1233 new String[] { title, intent.toURI() }, null);
1234 boolean result = false;
1235 try {
1236 result = c.moveToFirst();
1237 } finally {
1238 c.close();
1239 }
1240 return result;
1241 }
1242
1243 FolderInfo getFolderById(Context context, long id) {
1244 final ContentResolver cr = context.getContentResolver();
1245 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
1246 "_id=? and itemType=? or itemType=?",
1247 new String[] { String.valueOf(id),
1248 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
1249 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
1250
1251 try {
1252 if (c.moveToFirst()) {
1253 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1254 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1255 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1256 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1257 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1258 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1259
1260 FolderInfo folderInfo = null;
1261 switch (c.getInt(itemTypeIndex)) {
1262 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1263 folderInfo = findOrMakeUserFolder(mFolders, id);
1264 break;
1265 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
1266 folderInfo = findOrMakeLiveFolder(mFolders, id);
1267 break;
1268 }
1269
1270 folderInfo.title = c.getString(titleIndex);
1271 folderInfo.id = id;
1272 folderInfo.container = c.getInt(containerIndex);
1273 folderInfo.screen = c.getInt(screenIndex);
1274 folderInfo.cellX = c.getInt(cellXIndex);
1275 folderInfo.cellY = c.getInt(cellYIndex);
1276
1277 return folderInfo;
1278 }
1279 } finally {
1280 c.close();
1281 }
1282
1283 return null;
1284 }
1285
1286 /**
1287 * Add an item to the database in a specified container. Sets the container, screen, cellX and
1288 * cellY fields of the item. Also assigns an ID to the item.
1289 */
1290 static void addItemToDatabase(Context context, ItemInfo item, long container,
1291 int screen, int cellX, int cellY, boolean notify) {
1292 item.container = container;
1293 item.screen = screen;
1294 item.cellX = cellX;
1295 item.cellY = cellY;
1296
1297 final ContentValues values = new ContentValues();
1298 final ContentResolver cr = context.getContentResolver();
1299
1300 item.onAddToDatabase(values);
1301
1302 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
1303 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
1304
1305 if (result != null) {
1306 item.id = Integer.parseInt(result.getPathSegments().get(1));
1307 }
1308 }
1309
1310 /**
1311 * Update an item to the database in a specified container.
1312 */
1313 static void updateItemInDatabase(Context context, ItemInfo item) {
1314 final ContentValues values = new ContentValues();
1315 final ContentResolver cr = context.getContentResolver();
1316
1317 item.onAddToDatabase(values);
1318
1319 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
1320 }
1321
1322 /**
1323 * Removes the specified item from the database
1324 * @param context
1325 * @param item
1326 */
1327 static void deleteItemFromDatabase(Context context, ItemInfo item) {
1328 final ContentResolver cr = context.getContentResolver();
1329
1330 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
1331 }
1332
1333
1334 /**
1335 * Remove the contents of the specified folder from the database
1336 */
1337 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
1338 final ContentResolver cr = context.getContentResolver();
1339
1340 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
1341 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
1342 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
1343 }
1344}