blob: 0346b228efb0cb7769c022a7afc45f6616cd6ec8 [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.app.Activity;
20import android.app.AlertDialog;
21import android.app.Application;
22import android.app.Dialog;
23import android.app.SearchManager;
24import android.app.StatusBarManager;
25import android.content.ActivityNotFoundException;
26import android.content.BroadcastReceiver;
27import android.content.ComponentName;
28import android.content.ContentResolver;
29import android.content.Context;
30import android.content.DialogInterface;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.content.SharedPreferences;
34import android.content.pm.ActivityInfo;
35import android.content.pm.PackageManager;
36import android.content.pm.PackageManager.NameNotFoundException;
37import android.content.res.Resources;
38import android.content.res.Configuration;
39import android.database.ContentObserver;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080040import android.graphics.Bitmap;
41import android.graphics.Rect;
42import android.graphics.drawable.BitmapDrawable;
43import android.graphics.drawable.Drawable;
44import android.graphics.drawable.TransitionDrawable;
45import android.net.Uri;
46import android.os.Bundle;
47import android.os.Handler;
48import android.os.IBinder;
Jeffrey Sharkey99c87582009-03-24 17:59:43 -070049import android.os.Looper;
50import android.os.MessageQueue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051import android.os.Parcelable;
52import android.os.RemoteException;
53import android.os.ServiceManager;
54import android.os.Message;
55import android.provider.*;
56import android.telephony.PhoneNumberUtils;
57import android.text.Selection;
58import android.text.SpannableStringBuilder;
59import android.text.TextUtils;
60import android.text.method.TextKeyListener;
61import android.util.Log;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070062import static android.util.Log.*;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080063import android.view.Display;
64import android.view.Gravity;
65import android.view.KeyEvent;
66import android.view.LayoutInflater;
67import android.view.Menu;
68import android.view.MenuItem;
69import android.view.View;
70import android.view.ViewGroup;
71import android.view.WindowManager;
72import android.view.View.OnLongClickListener;
73import android.view.inputmethod.InputMethodManager;
74import android.widget.AdapterView;
75import android.widget.EditText;
76import android.widget.ListView;
77import android.widget.TextView;
78import android.widget.Toast;
79import android.widget.GridView;
80import android.widget.SlidingDrawer;
81import android.app.IWallpaperService;
The Android Open Source Project7376fae2009-03-11 12:11:58 -070082import android.appwidget.AppWidgetManager;
83import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080084
85import java.lang.ref.WeakReference;
86import java.util.ArrayList;
Jeffrey Sharkey99c87582009-03-24 17:59:43 -070087import java.util.LinkedList;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080088
89/**
90 * Default launcher application.
91 */
92public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener {
93 static final String LOG_TAG = "Launcher";
94 static final boolean LOGD = false;
95
96 private static final boolean PROFILE_STARTUP = false;
The Android Open Source Project7376fae2009-03-11 12:11:58 -070097 private static final boolean PROFILE_DRAWER = false;
Jeffrey Sharkey99c87582009-03-24 17:59:43 -070098 private static final boolean PROFILE_ROTATE = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080099 private static final boolean DEBUG_USER_INTERFACE = false;
100
101 private static final int WALLPAPER_SCREENS_SPAN = 2;
102
103 private static final int MENU_GROUP_ADD = 1;
104 private static final int MENU_ADD = Menu.FIRST + 1;
105 private static final int MENU_WALLPAPER_SETTINGS = MENU_ADD + 1;
106 private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
107 private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1;
108 private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
109
110 private static final int REQUEST_CREATE_SHORTCUT = 1;
111 private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700112 private static final int REQUEST_CREATE_APPWIDGET = 5;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113 private static final int REQUEST_PICK_APPLICATION = 6;
114 private static final int REQUEST_PICK_SHORTCUT = 7;
115 private static final int REQUEST_PICK_LIVE_FOLDER = 8;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700116 private static final int REQUEST_PICK_APPWIDGET = 9;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800117
118 static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
119
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700120 static final String EXTRA_CUSTOM_WIDGET = "custom_widget";
121 static final String SEARCH_WIDGET = "search_widget";
122
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800123 static final int SCREEN_COUNT = 3;
124 static final int DEFAULT_SCREN = 1;
125 static final int NUMBER_CELLS_X = 4;
126 static final int NUMBER_CELLS_Y = 4;
127
128 private static final int DIALOG_CREATE_SHORTCUT = 1;
129 static final int DIALOG_RENAME_FOLDER = 2;
130
131 private static final String PREFERENCES = "launcher";
132 private static final String KEY_LOCALE = "locale";
133 private static final String KEY_MCC = "mcc";
134 private static final String KEY_MNC = "mnc";
135
136 // Type: int
137 private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
138 // Type: boolean
139 private static final String RUNTIME_STATE_ALL_APPS_FOLDER = "launcher.all_apps_folder";
140 // Type: long
141 private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder";
142 // Type: int
143 private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
144 // Type: int
145 private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX";
146 // Type: int
147 private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY";
148 // Type: int
149 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX";
150 // Type: int
151 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY";
152 // Type: int
153 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX";
154 // Type: int
155 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY";
156 // Type: int[]
157 private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells";
158 // Type: boolean
159 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
160 // Type: long
161 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
162
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700163 private static final LauncherModel sModel = new LauncherModel();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800164
165 private static Bitmap sWallpaper;
166
167 private static final Object sLock = new Object();
168 private static int sScreen = DEFAULT_SCREN;
169
170 private static WallpaperIntentReceiver sWallpaperReceiver;
171
172 private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver();
173 private final ContentObserver mObserver = new FavoritesChangeObserver();
174
175 private LayoutInflater mInflater;
176
177 private DragLayer mDragLayer;
178 private Workspace mWorkspace;
179
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700180 private AppWidgetManager mAppWidgetManager;
181 private LauncherAppWidgetHost mAppWidgetHost;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800182
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700183 static final int APPWIDGET_HOST_ID = 1024;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800184
185 private CellLayout.CellInfo mAddItemCellInfo;
186 private CellLayout.CellInfo mMenuAddInfo;
187 private final int[] mCellCoordinates = new int[2];
188 private FolderInfo mFolderInfo;
189
190 private SlidingDrawer mDrawer;
191 private TransitionDrawable mHandleIcon;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700192 private HandleView mHandleView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800193 private AllAppsGridView mAllAppsGrid;
194
195 private boolean mDesktopLocked = true;
196 private Bundle mSavedState;
197
198 private SpannableStringBuilder mDefaultKeySsb = null;
199
200 private boolean mDestroyed;
201
202 private boolean mRestoring;
203 private boolean mWaitingForResult;
204 private boolean mLocaleChanged;
205
206 private Bundle mSavedInstanceState;
207
Jeffrey Sharkey99c87582009-03-24 17:59:43 -0700208 private DesktopBinder mBinder;
209
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800210 @Override
211 protected void onCreate(Bundle savedInstanceState) {
212 super.onCreate(savedInstanceState);
213 mInflater = getLayoutInflater();
214
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700215 mAppWidgetManager = AppWidgetManager.getInstance(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800216
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700217 mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
218 mAppWidgetHost.startListening();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800219
220 if (PROFILE_STARTUP) {
221 android.os.Debug.startMethodTracing("/sdcard/launcher");
222 }
223
224 checkForLocaleChange();
225 setWallpaperDimension();
226
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800227 setContentView(R.layout.launcher);
228 setupViews();
229
230 registerIntentReceivers();
231 registerContentObservers();
232
233 mSavedState = savedInstanceState;
234 restoreState(mSavedState);
235
236 if (PROFILE_STARTUP) {
237 android.os.Debug.stopMethodTracing();
238 }
239
240 if (!mRestoring) {
241 startLoaders();
242 }
243
244 // For handling default keys
245 mDefaultKeySsb = new SpannableStringBuilder();
246 Selection.setSelection(mDefaultKeySsb, 0);
247 }
248
249 private void checkForLocaleChange() {
250 final SharedPreferences preferences = getSharedPreferences(PREFERENCES, MODE_PRIVATE);
251 final Configuration configuration = getResources().getConfiguration();
252
253 final String previousLocale = preferences.getString(KEY_LOCALE, null);
254 final String locale = configuration.locale.toString();
255
256 final int previousMcc = preferences.getInt(KEY_MCC, -1);
257 final int mcc = configuration.mcc;
258
259 final int previousMnc = preferences.getInt(KEY_MNC, -1);
260 final int mnc = configuration.mnc;
261
262 mLocaleChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
263
264 if (mLocaleChanged) {
265 final SharedPreferences.Editor editor = preferences.edit();
266 editor.putString(KEY_LOCALE, locale);
267 editor.putInt(KEY_MCC, mcc);
268 editor.putInt(KEY_MNC, mnc);
269 editor.commit();
270 }
271 }
272
273 static int getScreen() {
274 synchronized (sLock) {
275 return sScreen;
276 }
277 }
278
279 static void setScreen(int screen) {
280 synchronized (sLock) {
281 sScreen = screen;
282 }
283 }
284
285 private void startLoaders() {
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700286 boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged);
287 sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications);
288
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800289 mRestoring = false;
290 }
291
292 private void setWallpaperDimension() {
293 IBinder binder = ServiceManager.getService(WALLPAPER_SERVICE);
294 IWallpaperService wallpaperService = IWallpaperService.Stub.asInterface(binder);
295
296 Display display = getWindowManager().getDefaultDisplay();
297 boolean isPortrait = display.getWidth() < display.getHeight();
298
299 final int width = isPortrait ? display.getWidth() : display.getHeight();
300 final int height = isPortrait ? display.getHeight() : display.getWidth();
301 try {
302 wallpaperService.setDimensionHints(width * WALLPAPER_SCREENS_SPAN, height);
303 } catch (RemoteException e) {
304 // System is dead!
305 }
306 }
307
308 @Override
309 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
310 // The pattern used here is that a user PICKs a specific application,
311 // which, depending on the target, might need to CREATE the actual target.
312
313 // For example, the user would PICK_SHORTCUT for "Music playlist", and we
314 // launch over to the Music app to actually CREATE_SHORTCUT.
315
316 if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
317 switch (requestCode) {
318 case REQUEST_PICK_APPLICATION:
319 completeAddApplication(this, data, mAddItemCellInfo, !mDesktopLocked);
320 break;
321 case REQUEST_PICK_SHORTCUT:
322 addShortcut(data);
323 break;
324 case REQUEST_CREATE_SHORTCUT:
325 completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked);
326 break;
327 case REQUEST_PICK_LIVE_FOLDER:
328 addLiveFolder(data);
329 break;
330 case REQUEST_CREATE_LIVE_FOLDER:
331 completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked);
332 break;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700333 case REQUEST_PICK_APPWIDGET:
334 addAppWidget(data);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800335 break;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700336 case REQUEST_CREATE_APPWIDGET:
337 completeAddAppWidget(data, mAddItemCellInfo, !mDesktopLocked);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800338 break;
339 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700340 } else if (requestCode == REQUEST_PICK_APPWIDGET &&
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800341 resultCode == RESULT_CANCELED && data != null) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700342 // Clean up the appWidgetId if we canceled
343 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
344 if (appWidgetId != -1) {
345 mAppWidgetHost.deleteAppWidgetId(appWidgetId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800346 }
347 }
348 mWaitingForResult = false;
349 }
350
351 @Override
352 protected void onResume() {
353 super.onResume();
354
355 if (mRestoring) {
356 startLoaders();
357 }
358 }
359
360 @Override
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700361 protected void onPause() {
362 super.onPause();
363 closeDrawer(false);
364 }
Jeffrey Sharkey99c87582009-03-24 17:59:43 -0700365
366 @Override
367 public Object onRetainNonConfigurationInstance() {
368 // Flag any binder to stop early before switching
369 if (mBinder != null) {
370 mBinder.mTerminate = true;
371 }
372
373 if (PROFILE_ROTATE) {
374 android.os.Debug.startMethodTracing("/sdcard/launcher-rotate");
375 }
376 return null;
377 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700378
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800379 private boolean acceptFilter() {
380 final InputMethodManager inputManager = (InputMethodManager)
381 getSystemService(Context.INPUT_METHOD_SERVICE);
382 return !inputManager.isFullscreenMode();
383 }
384
385 @Override
386 public boolean onKeyDown(int keyCode, KeyEvent event) {
387 boolean handled = super.onKeyDown(keyCode, event);
388 if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) {
389 boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
390 keyCode, event);
391 if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
392 // something usable has been typed - dispatch it now.
393 final String str = mDefaultKeySsb.toString();
394
395 boolean isDialable = true;
396 final int count = str.length();
397 for (int i = 0; i < count; i++) {
398 if (!PhoneNumberUtils.isReallyDialable(str.charAt(i))) {
399 isDialable = false;
400 break;
401 }
402 }
403 Intent intent;
404 if (isDialable) {
405 intent = new Intent(Intent.ACTION_DIAL, Uri.fromParts("tel", str, null));
406 } else {
407 intent = new Intent(Contacts.Intents.UI.FILTER_CONTACTS_ACTION);
408 intent.putExtra(Contacts.Intents.UI.FILTER_TEXT_EXTRA_KEY, str);
409 }
410
411 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
412
413 try {
414 startActivity(intent);
415 } catch (android.content.ActivityNotFoundException ex) {
416 // Oh well... no one knows how to filter/dial. Life goes on.
417 }
418
419 mDefaultKeySsb.clear();
420 mDefaultKeySsb.clearSpans();
421 Selection.setSelection(mDefaultKeySsb, 0);
422
423 return true;
424 }
425 }
426
427 return handled;
428 }
429
430 /**
431 * Restores the previous state, if it exists.
432 *
433 * @param savedState The previous state.
434 */
435 private void restoreState(Bundle savedState) {
436 if (savedState == null) {
437 return;
438 }
439
440 final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
441 if (currentScreen > -1) {
442 mWorkspace.setCurrentScreen(currentScreen);
443 }
444
445 final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
446 if (addScreen > -1) {
447 mAddItemCellInfo = new CellLayout.CellInfo();
448 final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
449 addItemCellInfo.valid = true;
450 addItemCellInfo.screen = addScreen;
451 addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
452 addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
453 addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
454 addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
455 addItemCellInfo.findVacantCellsFromOccupied(
456 savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS),
457 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X),
458 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y));
459 mRestoring = true;
460 }
461
462 boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
463 if (renameFolder) {
464 long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
465 mFolderInfo = sModel.getFolderById(this, id);
466 mRestoring = true;
467 }
468 }
469
470 /**
471 * Finds all the views we need and configure them properly.
472 */
473 private void setupViews() {
474 mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
475 final DragLayer dragLayer = mDragLayer;
476
477 mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
478 final Workspace workspace = mWorkspace;
479
480 mDrawer = (SlidingDrawer) dragLayer.findViewById(R.id.drawer);
481 final SlidingDrawer drawer = mDrawer;
482
483 mAllAppsGrid = (AllAppsGridView) drawer.getContent();
484 final AllAppsGridView grid = mAllAppsGrid;
485
486 final DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
487
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700488 mHandleView = (HandleView) drawer.findViewById(R.id.all_apps);
489 mHandleView.setLauncher(this);
490 mHandleIcon = (TransitionDrawable) mHandleView.getDrawable();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800491 mHandleIcon.setCrossFadeEnabled(true);
492
493 drawer.lock();
494 final DrawerManager drawerManager = new DrawerManager();
495 drawer.setOnDrawerOpenListener(drawerManager);
496 drawer.setOnDrawerCloseListener(drawerManager);
497 drawer.setOnDrawerScrollListener(drawerManager);
498
499 grid.setTextFilterEnabled(true);
500 grid.setDragger(dragLayer);
501 grid.setLauncher(this);
502
503 workspace.setOnLongClickListener(this);
504 workspace.setDragger(dragLayer);
505 workspace.setLauncher(this);
506 loadWallpaper();
507
508 deleteZone.setLauncher(this);
509 deleteZone.setDragController(dragLayer);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700510 deleteZone.setHandle(mHandleView);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800511
512 dragLayer.setIgnoredDropTarget(grid);
513 dragLayer.setDragScoller(workspace);
514 dragLayer.setDragListener(deleteZone);
515 }
516
517 /**
518 * Creates a view representing a shortcut.
519 *
520 * @param info The data structure describing the shortcut.
521 *
522 * @return A View inflated from R.layout.application.
523 */
524 View createShortcut(ApplicationInfo info) {
525 return createShortcut(R.layout.application,
526 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
527 }
528
529 /**
530 * Creates a view representing a shortcut inflated from the specified resource.
531 *
532 * @param layoutResId The id of the XML layout used to create the shortcut.
533 * @param parent The group the shortcut belongs to.
534 * @param info The data structure describing the shortcut.
535 *
536 * @return A View inflated from layoutResId.
537 */
538 View createShortcut(int layoutResId, ViewGroup parent, ApplicationInfo info) {
539 TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);
540
541 if (!info.filtered) {
542 info.icon = Utilities.createIconThumbnail(info.icon, this);
543 info.filtered = true;
544 }
545
546 favorite.setCompoundDrawablesWithIntrinsicBounds(null, info.icon, null, null);
547 favorite.setText(info.title);
548 favorite.setTag(info);
549 favorite.setOnClickListener(this);
550
551 return favorite;
552 }
553
554 /**
555 * Add an application shortcut to the workspace.
556 *
557 * @param data The intent describing the application.
558 * @param cellInfo The position on screen where to create the shortcut.
559 */
560 void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo,
561 boolean insertAtFirst) {
562 cellInfo.screen = mWorkspace.getCurrentScreen();
563 if (!findSingleSlot(cellInfo)) return;
564
565 // Find details for this application
566 ComponentName component = data.getComponent();
567 PackageManager packageManager = context.getPackageManager();
568 ActivityInfo activityInfo = null;
569 try {
570 activityInfo = packageManager.getActivityInfo(component, 0 /* no flags */);
571 } catch (NameNotFoundException e) {
572 Log.e(LOG_TAG, "Couldn't find ActivityInfo for selected application", e);
573 }
574
575 if (activityInfo != null) {
576 ApplicationInfo itemInfo = new ApplicationInfo();
577
578 itemInfo.title = activityInfo.loadLabel(packageManager);
579 if (itemInfo.title == null) {
580 itemInfo.title = activityInfo.name;
581 }
582
583 itemInfo.setActivity(component, Intent.FLAG_ACTIVITY_NEW_TASK |
584 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
585 itemInfo.icon = activityInfo.loadIcon(packageManager);
586 itemInfo.container = ItemInfo.NO_ID;
587
588 mWorkspace.addApplicationShortcut(itemInfo, cellInfo, insertAtFirst);
589 }
590 }
591
592 /**
593 * Add a shortcut to the workspace.
594 *
595 * @param data The intent describing the shortcut.
596 * @param cellInfo The position on screen where to create the shortcut.
597 * @param insertAtFirst
598 */
599 private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo,
600 boolean insertAtFirst) {
601 cellInfo.screen = mWorkspace.getCurrentScreen();
602 if (!findSingleSlot(cellInfo)) return;
603
604 final ApplicationInfo info = addShortcut(this, data, cellInfo, false);
605
606 if (!mRestoring) {
607 sModel.addDesktopItem(info);
608
609 final View view = createShortcut(info);
610 mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
611 } else if (sModel.isDesktopLoaded()) {
612 sModel.addDesktopItem(info);
613 }
614 }
615
616
617 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700618 * Add a widget to the workspace.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800619 *
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700620 * @param data The intent describing the appWidgetId.
621 * @param cellInfo The position on screen where to create the widget.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800622 */
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700623 private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800624 boolean insertAtFirst) {
625
626 Bundle extras = data.getExtras();
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700627 int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800628
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700629 d(LOG_TAG, "dumping extras content="+extras.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800630
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700631 AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800632
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700633 // Calculate the grid spans needed to fit this widget
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800634 CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700635 int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800636
637 // Try finding open space on Launcher screen
638 final int[] xy = mCellCoordinates;
639 if (!findSlot(cellInfo, xy, spans[0], spans[1])) return;
640
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700641 // Build Launcher-specific widget info and save to database
642 LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800643 launcherInfo.spanX = spans[0];
644 launcherInfo.spanY = spans[1];
645
646 LauncherModel.addItemToDatabase(this, launcherInfo,
647 LauncherSettings.Favorites.CONTAINER_DESKTOP,
648 mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
649
650 if (!mRestoring) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700651 sModel.addDesktopAppWidget(launcherInfo);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800652
653 // Perform actual inflation because we're live
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700654 launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800655
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700656 launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800657 launcherInfo.hostView.setTag(launcherInfo);
658
659 mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
660 launcherInfo.spanX, launcherInfo.spanY, insertAtFirst);
661 } else if (sModel.isDesktopLoaded()) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700662 sModel.addDesktopAppWidget(launcherInfo);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800663 }
664 }
665
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700666 public LauncherAppWidgetHost getAppWidgetHost() {
667 return mAppWidgetHost;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800668 }
669
670 static ApplicationInfo addShortcut(Context context, Intent data,
671 CellLayout.CellInfo cellInfo, boolean notify) {
672
673 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
674 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
675 Bitmap bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
676
677 Drawable icon = null;
678 boolean filtered = false;
679 boolean customIcon = false;
680 Intent.ShortcutIconResource iconResource = null;
681
682 if (bitmap != null) {
683 icon = new FastBitmapDrawable(Utilities.createBitmapThumbnail(bitmap, context));
684 filtered = true;
685 customIcon = true;
686 } else {
687 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
688 if (extra != null && extra instanceof Intent.ShortcutIconResource) {
689 try {
690 iconResource = (Intent.ShortcutIconResource) extra;
691 final PackageManager packageManager = context.getPackageManager();
692 Resources resources = packageManager.getResourcesForApplication(
693 iconResource.packageName);
694 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
695 icon = resources.getDrawable(id);
696 } catch (Exception e) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700697 w(LOG_TAG, "Could not load shortcut icon: " + extra);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800698 }
699 }
700 }
701
702 if (icon == null) {
703 icon = context.getPackageManager().getDefaultActivityIcon();
704 }
705
706 final ApplicationInfo info = new ApplicationInfo();
707 info.icon = icon;
708 info.filtered = filtered;
709 info.title = name;
710 info.intent = intent;
711 info.customIcon = customIcon;
712 info.iconResource = iconResource;
713
714 LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
715 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
716 return info;
717 }
718
719 @Override
720 protected void onNewIntent(Intent intent) {
721 super.onNewIntent(intent);
722
723 // Close the menu
724 if (Intent.ACTION_MAIN.equals(intent.getAction())) {
725 getWindow().closeAllPanels();
726
727 try {
728 dismissDialog(DIALOG_CREATE_SHORTCUT);
729 // Unlock the workspace if the dialog was showing
730 mWorkspace.unlock();
731 } catch (Exception e) {
732 // An exception is thrown if the dialog is not visible, which is fine
733 }
734
735 try {
736 dismissDialog(DIALOG_RENAME_FOLDER);
737 // Unlock the workspace if the dialog was showing
738 mWorkspace.unlock();
739 } catch (Exception e) {
740 // An exception is thrown if the dialog is not visible, which is fine
741 }
742
743 // If we are already in front we go back to the default screen,
744 // otherwise we don't
745 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) !=
746 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) {
747 if (!mWorkspace.isDefaultScreenShowing()) {
748 mWorkspace.moveToDefaultScreen();
749 }
750 closeDrawer();
751 View v = getWindow().peekDecorView();
752 if (v != null && v.getWindowToken() != null) {
753 InputMethodManager imm = (InputMethodManager)getSystemService(
754 INPUT_METHOD_SERVICE);
755 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
756 }
757 } else {
758 closeDrawer(false);
759 }
760 }
761 }
762
763 @Override
764 protected void onRestoreInstanceState(Bundle savedInstanceState) {
765 // Do not call super here
766 mSavedInstanceState = savedInstanceState;
767 }
768
769 @Override
770 protected void onSaveInstanceState(Bundle outState) {
771 outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
772
773 final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
774 if (folders.size() > 0) {
775 final int count = folders.size();
776 long[] ids = new long[count];
777 for (int i = 0; i < count; i++) {
778 final FolderInfo info = folders.get(i).getInfo();
779 ids[i] = info.id;
780 }
781 outState.putLongArray(RUNTIME_STATE_USER_FOLDERS, ids);
782 } else {
783 super.onSaveInstanceState(outState);
784 }
785
786 if (mDrawer.isOpened()) {
787 outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true);
788 }
789
790 if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) {
791 final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
792 final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen);
793
794 outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen);
795 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX);
796 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY);
797 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX);
798 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY);
799 outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX());
800 outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY());
801 outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS,
802 layout.getOccupiedCells());
803 }
804
805 if (mFolderInfo != null && mWaitingForResult) {
806 outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
807 outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
808 }
809 }
810
811 @Override
812 public void onDestroy() {
813 mDestroyed = true;
814
815 super.onDestroy();
816
817 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700818 mAppWidgetHost.stopListening();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800819 } catch (NullPointerException ex) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700820 w(LOG_TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800821 }
822
823 TextKeyListener.getInstance().release();
824
825 mAllAppsGrid.clearTextFilter();
826 mAllAppsGrid.setAdapter(null);
827 sModel.unbind();
828 sModel.abortLoaders();
829
830 getContentResolver().unregisterContentObserver(mObserver);
831 unregisterReceiver(mApplicationsReceiver);
832 }
833
834 @Override
835 public void startActivityForResult(Intent intent, int requestCode) {
836 mWaitingForResult = true;
837 super.startActivityForResult(intent, requestCode);
838 }
839
840 @Override
841 public void startSearch(String initialQuery, boolean selectInitialQuery,
842 Bundle appSearchData, boolean globalSearch) {
843 if (appSearchData == null) {
844 appSearchData = new Bundle();
845 appSearchData.putString(SearchManager.SOURCE, "launcher-search");
846 }
847 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
848 }
849
850 @Override
851 public boolean onCreateOptionsMenu(Menu menu) {
852 if (mDesktopLocked) return false;
853
854 super.onCreateOptionsMenu(menu);
855 menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
856 .setIcon(android.R.drawable.ic_menu_add)
857 .setAlphabeticShortcut('A');
858 menu.add(0, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
859 .setIcon(android.R.drawable.ic_menu_gallery)
860 .setAlphabeticShortcut('W');
861 menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
862 .setIcon(android.R.drawable.ic_search_category_default)
863 .setAlphabeticShortcut(SearchManager.MENU_KEY);
864 menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications)
865 .setIcon(com.android.internal.R.drawable.ic_menu_notifications)
866 .setAlphabeticShortcut('N');
867
868 final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
869 settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
870 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
871
872 menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings)
873 .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P')
874 .setIntent(settings);
875
876 return true;
877 }
878
879 @Override
880 public boolean onPrepareOptionsMenu(Menu menu) {
881 super.onPrepareOptionsMenu(menu);
882
883 mMenuAddInfo = mWorkspace.findAllVacantCells(null);
884 menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid);
885
886 return true;
887 }
888
889 @Override
890 public boolean onOptionsItemSelected(MenuItem item) {
891 switch (item.getItemId()) {
892 case MENU_ADD:
893 addItems();
894 return true;
895 case MENU_WALLPAPER_SETTINGS:
896 startWallpaper();
897 return true;
898 case MENU_SEARCH:
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700899 onSearchRequested();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800900 return true;
901 case MENU_NOTIFICATIONS:
902 showNotifications();
903 return true;
904 }
905
906 return super.onOptionsItemSelected(item);
907 }
908
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700909 @Override
910 public boolean onSearchRequested() {
911 if (mWorkspace.snapToSearch()) {
912 closeDrawer(true); // search widget: get drawer out of the way
913 return true;
914 } else {
915 return super.onSearchRequested(); // no search widget: use system search UI
916 }
917 }
918
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800919 private void addItems() {
920 showAddDialog(mMenuAddInfo);
921 }
922
923 private void removeShortcutsForPackage(String packageName) {
924 if (packageName != null && packageName.length() > 0) {
925 mWorkspace.removeShortcutsForPackage(packageName);
926 }
927 }
928
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700929 private void updateShortcutsForPackage(String packageName) {
930 if (packageName != null && packageName.length() > 0) {
931 mWorkspace.updateShortcutsForPackage(packageName);
932 }
933 }
934
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700935 void addAppWidget(Intent data) {
936 // TODO: catch bad widget exception when sent
937 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800938
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700939 String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET);
940 if (SEARCH_WIDGET.equals(customWidget)) {
941 // We don't need this any more, since this isn't a real app widget.
942 mAppWidgetHost.deleteAppWidgetId(appWidgetId);
943 // add the search widget
944 addSearch();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800945 } else {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700946 AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
947
948 if (appWidget.configure != null) {
949 // Launch over to configure widget, if needed
950 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
951 intent.setComponent(appWidget.configure);
952 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
953
954 startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
955 } else {
956 // Otherwise just add it
957 onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
958 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800959 }
960 }
961
962 void addSearch() {
963 final Widget info = Widget.makeSearch();
964 final CellLayout.CellInfo cellInfo = mAddItemCellInfo;
965
966 final int[] xy = mCellCoordinates;
967 final int spanX = info.spanX;
968 final int spanY = info.spanY;
969
970 if (!findSlot(cellInfo, xy, spanX, spanY)) return;
971
972 sModel.addDesktopItem(info);
973 LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
974 mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
975
976 final View view = mInflater.inflate(info.layoutResource, null);
977 view.setTag(info);
978
979 mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
980 }
981
982 void addShortcut(Intent intent) {
983 startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
984 }
985
986 void addLiveFolder(Intent intent) {
987 startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER);
988 }
989
990 void addFolder(boolean insertAtFirst) {
991 UserFolderInfo folderInfo = new UserFolderInfo();
992 folderInfo.title = getText(R.string.folder_name);
993
994 CellLayout.CellInfo cellInfo = mAddItemCellInfo;
995 cellInfo.screen = mWorkspace.getCurrentScreen();
996 if (!findSingleSlot(cellInfo)) return;
997
998 // Update the model
999 LauncherModel.addItemToDatabase(this, folderInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1000 mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
1001 sModel.addDesktopItem(folderInfo);
1002 sModel.addFolder(folderInfo);
1003
1004 // Create the view
1005 FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
1006 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
1007 mWorkspace.addInCurrentScreen(newFolder,
1008 cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
1009 }
1010
1011 private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo,
1012 boolean insertAtFirst) {
1013 cellInfo.screen = mWorkspace.getCurrentScreen();
1014 if (!findSingleSlot(cellInfo)) return;
1015
1016 final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
1017
1018 if (!mRestoring) {
1019 sModel.addDesktopItem(info);
1020
1021 final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
1022 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
1023 mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
1024 } else if (sModel.isDesktopLoaded()) {
1025 sModel.addDesktopItem(info);
1026 }
1027 }
1028
1029 static LiveFolderInfo addLiveFolder(Context context, Intent data,
1030 CellLayout.CellInfo cellInfo, boolean notify) {
1031
1032 Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT);
1033 String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME);
1034
1035 Drawable icon = null;
1036 boolean filtered = false;
1037 Intent.ShortcutIconResource iconResource = null;
1038
1039 Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON);
1040 if (extra != null && extra instanceof Intent.ShortcutIconResource) {
1041 try {
1042 iconResource = (Intent.ShortcutIconResource) extra;
1043 final PackageManager packageManager = context.getPackageManager();
1044 Resources resources = packageManager.getResourcesForApplication(
1045 iconResource.packageName);
1046 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1047 icon = resources.getDrawable(id);
1048 } catch (Exception e) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001049 w(LOG_TAG, "Could not load live folder icon: " + extra);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001050 }
1051 }
1052
1053 if (icon == null) {
1054 icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder);
1055 }
1056
1057 final LiveFolderInfo info = new LiveFolderInfo();
1058 info.icon = icon;
1059 info.filtered = filtered;
1060 info.title = name;
1061 info.iconResource = iconResource;
1062 info.uri = data.getData();
1063 info.baseIntent = baseIntent;
1064 info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
1065 LiveFolders.DISPLAY_MODE_GRID);
1066
1067 LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1068 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1069 sModel.addFolder(info);
1070
1071 return info;
1072 }
1073
1074 private boolean findSingleSlot(CellLayout.CellInfo cellInfo) {
1075 final int[] xy = new int[2];
1076 if (findSlot(cellInfo, xy, 1, 1)) {
1077 cellInfo.cellX = xy[0];
1078 cellInfo.cellY = xy[1];
1079 return true;
1080 }
1081 return false;
1082 }
1083
1084 private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
1085 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
1086 boolean[] occupied = mSavedState != null ?
1087 mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null;
1088 cellInfo = mWorkspace.findAllVacantCells(occupied);
1089 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
1090 Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
1091 return false;
1092 }
1093 }
1094 return true;
1095 }
1096
1097 private void showNotifications() {
1098 final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
1099 if (statusBar != null) {
1100 statusBar.expand();
1101 }
1102 }
1103
1104 private void startWallpaper() {
1105 final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
1106 startActivity(Intent.createChooser(pickWallpaper, getString(R.string.chooser_wallpaper)));
1107 }
1108
1109 /**
1110 * Registers various intent receivers. The current implementation registers
1111 * only a wallpaper intent receiver to let other applications change the
1112 * wallpaper.
1113 */
1114 private void registerIntentReceivers() {
1115 if (sWallpaperReceiver == null) {
1116 final Application application = getApplication();
1117
1118 sWallpaperReceiver = new WallpaperIntentReceiver(application, this);
1119
1120 IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
1121 application.registerReceiver(sWallpaperReceiver, filter);
1122 } else {
1123 sWallpaperReceiver.setLauncher(this);
1124 }
1125
1126 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1127 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1128 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1129 filter.addDataScheme("package");
1130 registerReceiver(mApplicationsReceiver, filter);
1131 }
1132
1133 /**
1134 * Registers various content observers. The current implementation registers
1135 * only a favorites observer to keep track of the favorites applications.
1136 */
1137 private void registerContentObservers() {
1138 ContentResolver resolver = getContentResolver();
1139 resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mObserver);
1140 }
1141
1142 @Override
1143 public boolean dispatchKeyEvent(KeyEvent event) {
1144 if (event.getAction() == KeyEvent.ACTION_DOWN) {
1145 switch (event.getKeyCode()) {
1146 case KeyEvent.KEYCODE_BACK:
1147 mWorkspace.dispatchKeyEvent(event);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001148 if (mDrawer.isOpened()) {
1149 closeDrawer();
1150 } else {
1151 closeFolder();
1152 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001153 return true;
1154 case KeyEvent.KEYCODE_HOME:
1155 return true;
1156 }
1157 }
1158
1159 return super.dispatchKeyEvent(event);
1160 }
1161
1162 private void closeDrawer() {
1163 closeDrawer(true);
1164 }
1165
1166 private void closeDrawer(boolean animated) {
1167 if (mDrawer.isOpened()) {
1168 if (animated) {
1169 mDrawer.animateClose();
1170 } else {
1171 mDrawer.close();
1172 }
1173 if (mDrawer.hasFocus()) {
1174 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
1175 }
1176 }
1177 }
1178
1179 private void closeFolder() {
1180 Folder folder = mWorkspace.getOpenFolder();
1181 if (folder != null) {
1182 closeFolder(folder);
1183 }
1184 }
1185
1186 void closeFolder(Folder folder) {
1187 folder.getInfo().opened = false;
1188 ViewGroup parent = (ViewGroup) folder.getParent();
1189 if (parent != null) {
1190 parent.removeView(folder);
1191 }
1192 folder.onClose();
1193 }
1194
1195 /**
1196 * When the notification that favorites have changed is received, requests
1197 * a favorites list refresh.
1198 */
1199 private void onFavoritesChanged() {
1200 mDesktopLocked = true;
1201 mDrawer.lock();
1202 sModel.loadUserItems(false, this, false, false);
1203 }
1204
1205 void onDesktopItemsLoaded() {
1206 if (mDestroyed) return;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001207 bindDesktopItems();
1208 }
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001209
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001210 /**
1211 * Refreshes the shortcuts shown on the workspace.
1212 */
1213 private void bindDesktopItems() {
1214 final ArrayList<ItemInfo> shortcuts = sModel.getDesktopItems();
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001215 final ArrayList<LauncherAppWidgetInfo> appWidgets = sModel.getDesktopAppWidgets();
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001216 final ApplicationsAdapter drawerAdapter = sModel.getApplicationsAdapter();
1217 if (shortcuts == null || appWidgets == null || drawerAdapter == null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001218 return;
1219 }
1220
1221 final Workspace workspace = mWorkspace;
1222 int count = workspace.getChildCount();
1223 for (int i = 0; i < count; i++) {
1224 ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
1225 }
1226
1227 if (DEBUG_USER_INTERFACE) {
1228 android.widget.Button finishButton = new android.widget.Button(this);
1229 finishButton.setText("Finish");
1230 workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
1231
1232 finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
1233 public void onClick(View v) {
1234 finish();
1235 }
1236 });
1237 }
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001238
1239 // Flag any old binder to terminate early
1240 if (mBinder != null) {
1241 mBinder.mTerminate = true;
1242 }
1243
1244 mBinder = new DesktopBinder(this, shortcuts, appWidgets, drawerAdapter);
1245 mBinder.startWhenIdle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001246 }
1247
1248 private void bindItems(Launcher.DesktopBinder binder,
1249 ArrayList<ItemInfo> shortcuts, int start, int count) {
1250
1251 final Workspace workspace = mWorkspace;
1252 final boolean desktopLocked = mDesktopLocked;
1253
1254 final int end = Math.min(start + DesktopBinder.ITEMS_COUNT, count);
1255 int i = start;
1256
1257 for ( ; i < end; i++) {
1258 final ItemInfo item = shortcuts.get(i);
1259 switch (item.itemType) {
1260 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1261 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1262 final View shortcut = createShortcut((ApplicationInfo) item);
1263 workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
1264 !desktopLocked);
1265 break;
1266 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1267 final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
1268 (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
1269 (UserFolderInfo) item);
1270 workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
1271 !desktopLocked);
1272 break;
1273 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
1274 final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
1275 R.layout.live_folder_icon, this,
1276 (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
1277 (LiveFolderInfo) item);
1278 workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
1279 !desktopLocked);
1280 break;
1281 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
1282 final int screen = workspace.getCurrentScreen();
1283 final View view = mInflater.inflate(R.layout.widget_search,
1284 (ViewGroup) workspace.getChildAt(screen), false);
1285
1286 final Widget widget = (Widget) item;
1287 view.setTag(widget);
1288
1289 workspace.addWidget(view, widget, !desktopLocked);
1290 break;
1291 }
1292 }
1293
1294 workspace.requestLayout();
1295
1296 if (end >= count) {
1297 finishBindDesktopItems();
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001298 binder.startBindingDrawer();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001299 } else {
1300 binder.obtainMessage(DesktopBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget();
1301 }
1302 }
1303
1304 private void finishBindDesktopItems() {
1305 if (mSavedState != null) {
1306 if (!mWorkspace.hasFocus()) {
1307 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
1308 }
1309
1310 final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
1311 if (userFolders != null) {
1312 for (long folderId : userFolders) {
1313 final FolderInfo info = sModel.findFolderById(folderId);
1314 if (info != null) {
1315 openFolder(info);
1316 }
1317 }
1318 final Folder openFolder = mWorkspace.getOpenFolder();
1319 if (openFolder != null) {
1320 openFolder.requestFocus();
1321 }
1322 }
1323
1324 final boolean allApps = mSavedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
1325 if (allApps) {
1326 mDrawer.open();
1327 }
1328
1329 mSavedState = null;
1330 }
1331
1332 if (mSavedInstanceState != null) {
1333 super.onRestoreInstanceState(mSavedInstanceState);
1334 mSavedInstanceState = null;
1335 }
1336
1337 if (mDrawer.isOpened() && !mDrawer.hasFocus()) {
1338 mDrawer.requestFocus();
1339 }
1340
1341 mDesktopLocked = false;
1342 mDrawer.unlock();
1343 }
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001344
1345 private void bindDrawer(Launcher.DesktopBinder binder,
1346 ApplicationsAdapter drawerAdapter) {
1347 mAllAppsGrid.setAdapter(drawerAdapter);
1348 binder.startBindingAppWidgets();
1349 }
1350
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001351 private void bindAppWidgets(Launcher.DesktopBinder binder,
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001352 LinkedList<LauncherAppWidgetInfo> appWidgets) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001353
1354 final Workspace workspace = mWorkspace;
1355 final boolean desktopLocked = mDesktopLocked;
1356
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001357 final LauncherAppWidgetInfo item = appWidgets.removeFirst();
1358
1359 final int appWidgetId = item.appWidgetId;
1360 final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
1361 item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
1362
1363 if (LOGD) d(LOG_TAG, String.format("about to setAppWidget for id=%d, info=%s", appWidgetId, appWidgetInfo));
1364
1365 item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
1366 item.hostView.setTag(item);
1367
1368 workspace.addInScreen(item.hostView, item.screen, item.cellX,
1369 item.cellY, item.spanX, item.spanY, !desktopLocked);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001370
1371 workspace.requestLayout();
1372
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001373 if (appWidgets.isEmpty()) {
1374 if (PROFILE_ROTATE) {
1375 android.os.Debug.stopMethodTracing();
1376 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001377 } else {
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001378 binder.obtainMessage(DesktopBinder.MESSAGE_BIND_APPWIDGETS).sendToTarget();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001379 }
1380 }
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001381
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001382 DragController getDragController() {
1383 return mDragLayer;
1384 }
1385
1386 /**
1387 * Launches the intent referred by the clicked shortcut.
1388 *
1389 * @param v The view representing the clicked shortcut.
1390 */
1391 public void onClick(View v) {
1392 Object tag = v.getTag();
1393 if (tag instanceof ApplicationInfo) {
1394 // Open shortcut
1395 final Intent intent = ((ApplicationInfo) tag).intent;
1396 startActivitySafely(intent);
1397 } else if (tag instanceof FolderInfo) {
1398 handleFolderClick((FolderInfo) tag);
1399 }
1400 }
1401
1402 void startActivitySafely(Intent intent) {
1403 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1404 try {
1405 startActivity(intent);
1406 } catch (ActivityNotFoundException e) {
1407 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1408 } catch (SecurityException e) {
1409 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1410 Log.e(LOG_TAG, "Launcher does not have the permission to launch " + intent +
1411 ". Make sure to create a MAIN intent-filter for the corresponding activity " +
1412 "or use the exported attribute for this activity.", e);
1413 }
1414 }
1415
1416 private void handleFolderClick(FolderInfo folderInfo) {
1417 if (!folderInfo.opened) {
1418 // Close any open folder
1419 closeFolder();
1420 // Open the requested folder
1421 openFolder(folderInfo);
1422 } else {
1423 // Find the open folder...
1424 Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
1425 int folderScreen;
1426 if (openFolder != null) {
1427 folderScreen = mWorkspace.getScreenForView(openFolder);
1428 // .. and close it
1429 closeFolder(openFolder);
1430 if (folderScreen != mWorkspace.getCurrentScreen()) {
1431 // Close any folder open on the current screen
1432 closeFolder();
1433 // Pull the folder onto this screen
1434 openFolder(folderInfo);
1435 }
1436 }
1437 }
1438 }
1439
1440 private void loadWallpaper() {
1441 // The first time the application is started, we load the wallpaper from
1442 // the ApplicationContext
1443 if (sWallpaper == null) {
1444 final Drawable drawable = getWallpaper();
1445 if (drawable instanceof BitmapDrawable) {
1446 sWallpaper = ((BitmapDrawable) drawable).getBitmap();
1447 } else {
1448 throw new IllegalStateException("The wallpaper must be a BitmapDrawable.");
1449 }
1450 }
1451 mWorkspace.loadWallpaper(sWallpaper);
1452 }
1453
1454 /**
1455 * Opens the user fodler described by the specified tag. The opening of the folder
1456 * is animated relative to the specified View. If the View is null, no animation
1457 * is played.
1458 *
1459 * @param folderInfo The FolderInfo describing the folder to open.
1460 */
1461 private void openFolder(FolderInfo folderInfo) {
1462 Folder openFolder;
1463
1464 if (folderInfo instanceof UserFolderInfo) {
1465 openFolder = UserFolder.fromXml(this);
1466 } else if (folderInfo instanceof LiveFolderInfo) {
1467 openFolder = com.android.launcher.LiveFolder.fromXml(this, folderInfo);
1468 } else {
1469 return;
1470 }
1471
1472 openFolder.setDragger(mDragLayer);
1473 openFolder.setLauncher(this);
1474
1475 openFolder.bind(folderInfo);
1476 folderInfo.opened = true;
1477
1478 mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4);
1479 openFolder.onOpen();
1480 }
1481
1482 /**
1483 * Returns true if the workspace is being loaded. When the workspace is loading,
1484 * no user interaction should be allowed to avoid any conflict.
1485 *
1486 * @return True if the workspace is locked, false otherwise.
1487 */
1488 boolean isWorkspaceLocked() {
1489 return mDesktopLocked;
1490 }
1491
1492 public boolean onLongClick(View v) {
1493 if (mDesktopLocked) {
1494 return false;
1495 }
1496
1497 if (!(v instanceof CellLayout)) {
1498 v = (View) v.getParent();
1499 }
1500
1501 CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
1502
1503 // This happens when long clicking an item with the dpad/trackball
1504 if (cellInfo == null) {
1505 return true;
1506 }
1507
1508 if (mWorkspace.allowLongPress()) {
1509 if (cellInfo.cell == null) {
1510 if (cellInfo.valid) {
1511 // User long pressed on empty space
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001512 mWorkspace.setAllowLongPress(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001513 showAddDialog(cellInfo);
1514 }
1515 } else {
1516 if (!(cellInfo.cell instanceof Folder)) {
1517 // User long pressed on an item
1518 mWorkspace.startDrag(cellInfo);
1519 }
1520 }
1521 }
1522 return true;
1523 }
1524
1525 static LauncherModel getModel() {
1526 return sModel;
1527 }
1528
1529 void closeAllApplications() {
1530 mDrawer.close();
1531 }
1532
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001533 View getDrawerHandle() {
1534 return mHandleView;
1535 }
1536
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001537 boolean isDrawerDown() {
1538 return !mDrawer.isMoving() && !mDrawer.isOpened();
1539 }
1540
1541 boolean isDrawerUp() {
1542 return mDrawer.isOpened() && !mDrawer.isMoving();
1543 }
1544
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001545 boolean isDrawerMoving() {
1546 return mDrawer.isMoving();
1547 }
1548
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001549 Workspace getWorkspace() {
1550 return mWorkspace;
1551 }
1552
1553 GridView getApplicationsGrid() {
1554 return mAllAppsGrid;
1555 }
1556
1557 @Override
1558 protected Dialog onCreateDialog(int id) {
1559 switch (id) {
1560 case DIALOG_CREATE_SHORTCUT:
1561 return new CreateShortcut().createDialog();
1562 case DIALOG_RENAME_FOLDER:
1563 return new RenameFolder().createDialog();
1564 }
1565
1566 return super.onCreateDialog(id);
1567 }
1568
1569 @Override
1570 protected void onPrepareDialog(int id, Dialog dialog) {
1571 switch (id) {
1572 case DIALOG_CREATE_SHORTCUT:
1573 mWorkspace.lock();
1574 break;
1575 case DIALOG_RENAME_FOLDER:
1576 mWorkspace.lock();
1577 EditText input = (EditText) dialog.findViewById(R.id.folder_name);
1578 final CharSequence text = mFolderInfo.title;
1579 input.setText(text);
1580 input.setSelection(0, text.length());
1581 break;
1582 }
1583 }
1584
1585 void showRenameDialog(FolderInfo info) {
1586 mFolderInfo = info;
1587 mWaitingForResult = true;
1588 showDialog(DIALOG_RENAME_FOLDER);
1589 }
1590
1591 private void showAddDialog(CellLayout.CellInfo cellInfo) {
1592 mAddItemCellInfo = cellInfo;
1593 mWaitingForResult = true;
1594 showDialog(DIALOG_CREATE_SHORTCUT);
1595 }
1596
1597 private class RenameFolder {
1598 private EditText mInput;
1599
1600 Dialog createDialog() {
1601 mWaitingForResult = true;
1602 final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null);
1603 mInput = (EditText) layout.findViewById(R.id.folder_name);
1604
1605 AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
1606 builder.setIcon(0);
1607 builder.setTitle(getString(R.string.rename_folder_title));
1608 builder.setCancelable(true);
1609 builder.setOnCancelListener(new Dialog.OnCancelListener() {
1610 public void onCancel(DialogInterface dialog) {
1611 cleanup();
1612 }
1613 });
1614 builder.setNegativeButton(getString(R.string.cancel_action),
1615 new Dialog.OnClickListener() {
1616 public void onClick(DialogInterface dialog, int which) {
1617 cleanup();
1618 }
1619 }
1620 );
1621 builder.setPositiveButton(getString(R.string.rename_action),
1622 new Dialog.OnClickListener() {
1623 public void onClick(DialogInterface dialog, int which) {
1624 changeFolderName();
1625 }
1626 }
1627 );
1628 builder.setView(layout);
1629 return builder.create();
1630 }
1631
1632 private void changeFolderName() {
1633 final String name = mInput.getText().toString();
1634 if (!TextUtils.isEmpty(name)) {
1635 // Make sure we have the right folder info
1636 mFolderInfo = sModel.findFolderById(mFolderInfo.id);
1637 mFolderInfo.title = name;
1638 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
1639
1640 if (mDesktopLocked) {
1641 mDrawer.lock();
1642 sModel.loadUserItems(false, Launcher.this, false, false);
1643 } else {
1644 final FolderIcon folderIcon = (FolderIcon)
1645 mWorkspace.getViewForTag(mFolderInfo);
1646 if (folderIcon != null) {
1647 folderIcon.setText(name);
1648 getWorkspace().requestLayout();
1649 } else {
1650 mDesktopLocked = true;
1651 mDrawer.lock();
1652 sModel.loadUserItems(false, Launcher.this, false, false);
1653 }
1654 }
1655 }
1656 cleanup();
1657 }
1658
1659 private void cleanup() {
1660 mWorkspace.unlock();
1661 dismissDialog(DIALOG_RENAME_FOLDER);
1662 mWaitingForResult = false;
1663 mFolderInfo = null;
1664 }
1665 }
1666
1667 /**
1668 * Displays the shortcut creation dialog and launches, if necessary, the
1669 * appropriate activity.
1670 */
1671 private class CreateShortcut implements AdapterView.OnItemClickListener,
1672 DialogInterface.OnCancelListener {
1673 private AddAdapter mAdapter;
1674 private ListView mList;
1675
1676 Dialog createDialog() {
1677 mWaitingForResult = true;
1678
1679 mAdapter = new AddAdapter(Launcher.this);
1680
1681 final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
1682 builder.setTitle(getString(R.string.menu_item_add_item));
1683 builder.setIcon(0);
1684
1685 mList = (ListView)
1686 View.inflate(Launcher.this, R.layout.create_shortcut_list, null);
1687 mList.setAdapter(mAdapter);
1688 mList.setOnItemClickListener(this);
1689 builder.setView(mList);
1690 builder.setInverseBackgroundForced(true);
1691
1692 AlertDialog dialog = builder.create();
1693 dialog.setOnCancelListener(this);
1694
1695 WindowManager.LayoutParams attributes = dialog.getWindow().getAttributes();
1696 attributes.gravity = Gravity.TOP;
1697 dialog.onWindowAttributesChanged(attributes);
1698
1699 return dialog;
1700 }
1701
1702 public void onCancel(DialogInterface dialog) {
1703 mWaitingForResult = false;
1704 cleanup();
1705 }
1706
1707 private void cleanup() {
1708 mWorkspace.unlock();
1709 dismissDialog(DIALOG_CREATE_SHORTCUT);
1710 }
1711
1712 public void onItemClick(AdapterView parent, View view, int position, long id) {
1713 // handle which item was clicked based on position
1714 // this will launch off pick intent
1715
1716 Object tag = view.getTag();
1717 if (tag instanceof AddAdapter.ListItem) {
1718 AddAdapter.ListItem item = (AddAdapter.ListItem) tag;
1719 cleanup();
1720 switch (item.actionTag) {
1721 case AddAdapter.ITEM_APPLICATION: {
1722 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1723 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1724
1725 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
1726 pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
1727 startActivityForResult(pickIntent, REQUEST_PICK_APPLICATION);
1728 break;
1729 }
1730
1731 case AddAdapter.ITEM_SHORTCUT: {
1732 Intent shortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
1733
1734 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
1735 pickIntent.putExtra(Intent.EXTRA_INTENT, shortcutIntent);
1736 pickIntent.putExtra(Intent.EXTRA_TITLE,
1737 getText(R.string.title_select_shortcut));
1738 startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
1739 break;
1740 }
1741
1742 case AddAdapter.ITEM_SEARCH: {
1743 addSearch();
1744 break;
1745 }
1746
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001747 case AddAdapter.ITEM_APPWIDGET: {
1748 int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001749
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001750 Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
1751 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001752 // add the search widget
1753 ArrayList<AppWidgetProviderInfo> customInfo =
1754 new ArrayList<AppWidgetProviderInfo>();
1755 AppWidgetProviderInfo info = new AppWidgetProviderInfo();
1756 info.provider = new ComponentName(getPackageName(), "XXX.YYY");
1757 info.label = getString(R.string.group_search);
1758 info.icon = R.drawable.ic_search_widget;
1759 customInfo.add(info);
1760 pickIntent.putParcelableArrayListExtra(
1761 AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
1762 ArrayList<Bundle> customExtras = new ArrayList<Bundle>();
1763 Bundle b = new Bundle();
1764 b.putString(EXTRA_CUSTOM_WIDGET, SEARCH_WIDGET);
1765 customExtras.add(b);
1766 pickIntent.putParcelableArrayListExtra(
1767 AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
1768 // start the pick activity
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001769 startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001770 break;
1771 }
1772
1773 case AddAdapter.ITEM_LIVE_FOLDER: {
1774 Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
1775
1776 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
1777 pickIntent.putExtra(Intent.EXTRA_INTENT, liveFolderIntent);
1778 pickIntent.putExtra(Intent.EXTRA_TITLE,
1779 getText(R.string.title_select_live_folder));
1780 startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
1781 break;
1782 }
1783
1784 case AddAdapter.ITEM_FOLDER: {
1785 addFolder(!mDesktopLocked);
1786 dismissDialog(DIALOG_CREATE_SHORTCUT);
1787 break;
1788 }
1789
1790 case AddAdapter.ITEM_WALLPAPER: {
1791 startWallpaper();
1792 break;
1793 }
1794
1795 }
1796
1797 }
1798 }
1799 }
1800
1801 /**
1802 * Receives notifications when applications are added/removed.
1803 */
1804 private class ApplicationsIntentReceiver extends BroadcastReceiver {
1805 @Override
1806 public void onReceive(Context context, Intent intent) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001807 final String action = intent.getAction();
1808 final String packageName = intent.getData().getSchemeSpecificPart();
1809 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1810
1811 if (LauncherModel.DEBUG_LOADERS) {
1812 d(LauncherModel.LOG_TAG, "application intent received: " + action +
1813 ", replacing=" + replacing);
1814 d(LauncherModel.LOG_TAG, " --> " + intent.getData());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001815 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001816
1817 if (!Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
1818 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
1819 if (!replacing) {
1820 removeShortcutsForPackage(packageName);
1821 if (LauncherModel.DEBUG_LOADERS) {
1822 d(LauncherModel.LOG_TAG, " --> remove package");
1823 }
1824 sModel.removePackage(Launcher.this, packageName);
1825 }
1826 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
1827 // later, we will update the package at this time
1828 } else {
1829 if (!replacing) {
1830 if (LauncherModel.DEBUG_LOADERS) {
1831 d(LauncherModel.LOG_TAG, " --> add package");
1832 }
1833 sModel.addPackage(Launcher.this, packageName);
1834 } else {
1835 if (LauncherModel.DEBUG_LOADERS) {
1836 d(LauncherModel.LOG_TAG, " --> update package " + packageName);
1837 }
1838 sModel.updatePackage(Launcher.this, packageName);
1839 updateShortcutsForPackage(packageName);
1840 }
1841 }
1842 removeDialog(DIALOG_CREATE_SHORTCUT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001843 } else {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001844 if (LauncherModel.DEBUG_LOADERS) {
1845 d(LauncherModel.LOG_TAG, " --> sync package " + packageName);
1846 }
1847 sModel.syncPackage(Launcher.this, packageName);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001848 }
1849 }
1850 }
1851
1852 /**
1853 * Receives notifications whenever the user favorites have changed.
1854 */
1855 private class FavoritesChangeObserver extends ContentObserver {
1856 public FavoritesChangeObserver() {
1857 super(new Handler());
1858 }
1859
1860 @Override
1861 public void onChange(boolean selfChange) {
1862 onFavoritesChanged();
1863 }
1864 }
1865
1866 /**
1867 * Receives intents from other applications to change the wallpaper.
1868 */
1869 private static class WallpaperIntentReceiver extends BroadcastReceiver {
1870 private final Application mApplication;
1871 private WeakReference<Launcher> mLauncher;
1872
1873 WallpaperIntentReceiver(Application application, Launcher launcher) {
1874 mApplication = application;
1875 setLauncher(launcher);
1876 }
1877
1878 void setLauncher(Launcher launcher) {
1879 mLauncher = new WeakReference<Launcher>(launcher);
1880 }
1881
1882 @Override
1883 public void onReceive(Context context, Intent intent) {
1884 // Load the wallpaper from the ApplicationContext and store it locally
1885 // until the Launcher Activity is ready to use it
1886 final Drawable drawable = mApplication.getWallpaper();
1887 if (drawable instanceof BitmapDrawable) {
1888 sWallpaper = ((BitmapDrawable) drawable).getBitmap();
1889 } else {
1890 throw new IllegalStateException("The wallpaper must be a BitmapDrawable.");
1891 }
1892
1893 // If Launcher is alive, notify we have a new wallpaper
1894 if (mLauncher != null) {
1895 final Launcher launcher = mLauncher.get();
1896 if (launcher != null) {
1897 launcher.loadWallpaper();
1898 }
1899 }
1900 }
1901 }
1902
1903 private class DrawerManager implements SlidingDrawer.OnDrawerOpenListener,
1904 SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerScrollListener {
1905 private boolean mOpen;
1906
1907 public void onDrawerOpened() {
1908 if (!mOpen) {
1909 mHandleIcon.reverseTransition(150);
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001910
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001911 final Rect bounds = mWorkspace.mDrawerBounds;
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001912 offsetBoundsToDragLayer(bounds, mAllAppsGrid);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001913
1914 mOpen = true;
1915 }
1916 }
1917
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001918 private void offsetBoundsToDragLayer(Rect bounds, View view) {
1919 view.getDrawingRect(bounds);
1920
1921 while (view != mDragLayer) {
1922 bounds.offset(view.getLeft(), view.getTop());
1923 view = (View) view.getParent();
1924 }
1925 }
1926
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001927 public void onDrawerClosed() {
1928 if (mOpen) {
1929 mHandleIcon.reverseTransition(150);
1930 mWorkspace.mDrawerBounds.setEmpty();
1931 mOpen = false;
1932 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001933
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001934 mAllAppsGrid.setSelection(0);
1935 mAllAppsGrid.clearTextFilter();
1936 }
1937
1938 public void onScrollStarted() {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001939 if (PROFILE_DRAWER) {
1940 android.os.Debug.startMethodTracing("/sdcard/launcher-drawer");
1941 }
1942
1943 mWorkspace.mDrawerContentWidth = mAllAppsGrid.getWidth();
1944 mWorkspace.mDrawerContentHeight = mAllAppsGrid.getHeight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001945 }
1946
1947 public void onScrollEnded() {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001948 if (PROFILE_DRAWER) {
1949 android.os.Debug.stopMethodTracing();
1950 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001951 }
1952 }
1953
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001954 private static class DesktopBinder extends Handler implements MessageQueue.IdleHandler {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001955 static final int MESSAGE_BIND_ITEMS = 0x1;
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001956 static final int MESSAGE_BIND_APPWIDGETS = 0x2;
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001957 static final int MESSAGE_BIND_DRAWER = 0x3;
1958
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001959 // Number of items to bind in every pass
1960 static final int ITEMS_COUNT = 6;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001961
1962 private final ArrayList<ItemInfo> mShortcuts;
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001963 private final LinkedList<LauncherAppWidgetInfo> mAppWidgets;
1964 private final ApplicationsAdapter mDrawerAdapter;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001965 private final WeakReference<Launcher> mLauncher;
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001966
1967 public boolean mTerminate = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001968
1969 DesktopBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts,
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001970 ArrayList<LauncherAppWidgetInfo> appWidgets,
1971 ApplicationsAdapter drawerAdapter) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001972
1973 mLauncher = new WeakReference<Launcher>(launcher);
1974 mShortcuts = shortcuts;
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07001975 mDrawerAdapter = drawerAdapter;
1976
1977 // Sort widgets so active workspace is bound first
1978 final int currentScreen = launcher.mWorkspace.getCurrentScreen();
1979 final int size = appWidgets.size();
1980 mAppWidgets = new LinkedList<LauncherAppWidgetInfo>();
1981
1982 for (int i = 0; i < size; i++) {
1983 LauncherAppWidgetInfo appWidgetInfo = appWidgets.get(i);
1984 if (appWidgetInfo.screen == currentScreen) {
1985 mAppWidgets.addFirst(appWidgetInfo);
1986 } else {
1987 mAppWidgets.addLast(appWidgetInfo);
1988 }
1989 }
1990 }
1991
1992 public void startWhenIdle() {
1993 // Ask for notification when message queue becomes idle
1994 final MessageQueue messageQueue = Looper.myQueue();
1995 messageQueue.addIdleHandler(this);
1996 }
1997
1998 public boolean queueIdle() {
1999 // Queue is idle, so start binding items
2000 startBindingItems();
2001 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002002 }
2003
2004 public void startBindingItems() {
2005 obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget();
2006 }
2007
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07002008 public void startBindingDrawer() {
2009 obtainMessage(MESSAGE_BIND_DRAWER).sendToTarget();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002010 }
2011
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07002012 public void startBindingAppWidgets() {
2013 obtainMessage(MESSAGE_BIND_APPWIDGETS).sendToTarget();
2014 }
2015
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002016 @Override
2017 public void handleMessage(Message msg) {
2018 Launcher launcher = mLauncher.get();
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07002019 if (launcher == null || mTerminate) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002020 return;
2021 }
2022
2023 switch (msg.what) {
2024 case MESSAGE_BIND_ITEMS: {
2025 launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2);
2026 break;
2027 }
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07002028 case MESSAGE_BIND_DRAWER: {
2029 launcher.bindDrawer(this, mDrawerAdapter);
2030 break;
2031 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -07002032 case MESSAGE_BIND_APPWIDGETS: {
Jeffrey Sharkey99c87582009-03-24 17:59:43 -07002033 launcher.bindAppWidgets(this, mAppWidgets);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002034 break;
2035 }
2036 }
2037 }
2038 }
2039}