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