Launcher.java revision 49cdb1b18c12a81cac9817c4800e43d5b507e0c4
1/* 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.launcher2; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.Dialog; 22import android.app.ISearchManager; 23import android.app.SearchManager; 24import android.app.StatusBarManager; 25import android.app.WallpaperManager; 26import android.content.ActivityNotFoundException; 27import android.content.BroadcastReceiver; 28import android.content.ComponentName; 29import android.content.ContentResolver; 30import android.content.Context; 31import android.content.DialogInterface; 32import android.content.Intent; 33import android.content.IntentFilter; 34import android.content.Intent.ShortcutIconResource; 35import android.content.pm.ActivityInfo; 36import android.content.pm.PackageManager; 37import android.content.pm.PackageManager.NameNotFoundException; 38import android.content.res.Configuration; 39import android.content.res.Resources; 40import android.database.ContentObserver; 41import android.graphics.Bitmap; 42import android.graphics.drawable.Drawable; 43import android.graphics.drawable.TransitionDrawable; 44import android.os.Bundle; 45import android.os.Handler; 46import android.os.Looper; 47import android.os.Message; 48import android.os.MessageQueue; 49import android.os.Parcelable; 50import android.os.RemoteException; 51import android.os.ServiceManager; 52import android.provider.LiveFolders; 53import android.text.Selection; 54import android.text.SpannableStringBuilder; 55import android.text.TextUtils; 56import android.text.method.TextKeyListener; 57import static android.util.Log.*; 58import android.view.Display; 59import android.view.KeyEvent; 60import android.view.LayoutInflater; 61import android.view.Menu; 62import android.view.MenuItem; 63import android.view.View; 64import android.view.ViewGroup; 65import android.view.WindowManager; 66import android.view.View.OnLongClickListener; 67import android.view.inputmethod.InputMethodManager; 68import android.widget.EditText; 69import android.widget.TextView; 70import android.widget.Toast; 71import android.appwidget.AppWidgetManager; 72import android.appwidget.AppWidgetProviderInfo; 73 74import java.lang.ref.WeakReference; 75import java.util.ArrayList; 76import java.util.LinkedList; 77import java.io.DataOutputStream; 78import java.io.FileNotFoundException; 79import java.io.IOException; 80import java.io.DataInputStream; 81 82/** 83 * Default launcher application. 84 */ 85public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener { 86 static final String LOG_TAG = "Launcher"; 87 static final boolean LOGD = false; 88 89 private static final boolean PROFILE_STARTUP = false; 90 private static final boolean PROFILE_ROTATE = false; 91 private static final boolean DEBUG_USER_INTERFACE = false; 92 93 private static final int WALLPAPER_SCREENS_SPAN = 2; 94 95 private static final int MENU_GROUP_ADD = 1; 96 private static final int MENU_ADD = Menu.FIRST + 1; 97 private static final int MENU_WALLPAPER_SETTINGS = MENU_ADD + 1; 98 private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1; 99 private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1; 100 private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1; 101 102 private static final int REQUEST_CREATE_SHORTCUT = 1; 103 private static final int REQUEST_CREATE_LIVE_FOLDER = 4; 104 private static final int REQUEST_CREATE_APPWIDGET = 5; 105 private static final int REQUEST_PICK_APPLICATION = 6; 106 private static final int REQUEST_PICK_SHORTCUT = 7; 107 private static final int REQUEST_PICK_LIVE_FOLDER = 8; 108 private static final int REQUEST_PICK_APPWIDGET = 9; 109 110 static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate"; 111 112 static final String EXTRA_CUSTOM_WIDGET = "custom_widget"; 113 static final String SEARCH_WIDGET = "search_widget"; 114 115 static final int SCREEN_COUNT = 3; 116 static final int DEFAULT_SCREN = 1; 117 static final int NUMBER_CELLS_X = 4; 118 static final int NUMBER_CELLS_Y = 4; 119 120 static final int DIALOG_ALL_APPS = 1; 121 static final int DIALOG_CREATE_SHORTCUT = 2; 122 static final int DIALOG_RENAME_FOLDER = 3; 123 124 private static final String PREFERENCES = "launcher.preferences"; 125 126 // Type: int 127 private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; 128 // Type: boolean 129 private static final String RUNTIME_STATE_ALL_APPS_FOLDER = "launcher.all_apps_folder"; 130 // Type: long 131 private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder"; 132 // Type: int 133 private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen"; 134 // Type: int 135 private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX"; 136 // Type: int 137 private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY"; 138 // Type: int 139 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX"; 140 // Type: int 141 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY"; 142 // Type: int 143 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX"; 144 // Type: int 145 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY"; 146 // Type: int[] 147 private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells"; 148 // Type: boolean 149 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder"; 150 // Type: long 151 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id"; 152 153 private static final LauncherModel sModel = new LauncherModel(); 154 155 private static final Object sLock = new Object(); 156 private static int sScreen = DEFAULT_SCREN; 157 158 private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver(); 159 private final ContentObserver mObserver = new FavoritesChangeObserver(); 160 161 private LayoutInflater mInflater; 162 163 private DragController mDragController; 164 private Workspace mWorkspace; 165 166 private AppWidgetManager mAppWidgetManager; 167 private LauncherAppWidgetHost mAppWidgetHost; 168 169 static final int APPWIDGET_HOST_ID = 1024; 170 171 private CellLayout.CellInfo mAddItemCellInfo; 172 private CellLayout.CellInfo mMenuAddInfo; 173 private final int[] mCellCoordinates = new int[2]; 174 private FolderInfo mFolderInfo; 175 176 private AllAppsDialog mAllAppsDialog; 177 private HandleView mHandleView; 178 private AllAppsView mAllAppsGrid; // TODO: put this into AllAppsDialog 179 180 private boolean mDesktopLocked = true; 181 private Bundle mSavedState; 182 183 private SpannableStringBuilder mDefaultKeySsb = null; 184 185 private boolean mDestroyed; 186 187 private boolean mIsNewIntent; 188 189 private boolean mRestoring; 190 private boolean mWaitingForResult; 191 private boolean mLocaleChanged; 192 193 private boolean mHomeDown; 194 private boolean mBackDown; 195 196 private Bundle mSavedInstanceState; 197 198 private DesktopBinder mBinder; 199 200 @Override 201 protected void onCreate(Bundle savedInstanceState) { 202 super.onCreate(savedInstanceState); 203 204 mInflater = getLayoutInflater(); 205 206 mAppWidgetManager = AppWidgetManager.getInstance(this); 207 208 mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); 209 mAppWidgetHost.startListening(); 210 211 if (PROFILE_STARTUP) { 212 android.os.Debug.startMethodTracing("/sdcard/launcher"); 213 } 214 215 checkForLocaleChange(); 216 setWallpaperDimension(); 217 218 setContentView(R.layout.launcher); 219 setupViews(); 220 221 mAllAppsDialog = new AllAppsDialog(this); 222 mAllAppsDialog.lock(); 223 224 registerIntentReceivers(); 225 registerContentObservers(); 226 227 mSavedState = savedInstanceState; 228 restoreState(mSavedState); 229 230 if (PROFILE_STARTUP) { 231 android.os.Debug.stopMethodTracing(); 232 } 233 234 if (!mRestoring) { 235 startLoaders(); 236 } 237 238 // For handling default keys 239 mDefaultKeySsb = new SpannableStringBuilder(); 240 Selection.setSelection(mDefaultKeySsb, 0); 241 } 242 243 private void checkForLocaleChange() { 244 final LocaleConfiguration localeConfiguration = new LocaleConfiguration(); 245 readConfiguration(this, localeConfiguration); 246 247 final Configuration configuration = getResources().getConfiguration(); 248 249 final String previousLocale = localeConfiguration.locale; 250 final String locale = configuration.locale.toString(); 251 252 final int previousMcc = localeConfiguration.mcc; 253 final int mcc = configuration.mcc; 254 255 final int previousMnc = localeConfiguration.mnc; 256 final int mnc = configuration.mnc; 257 258 mLocaleChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc; 259 260 if (mLocaleChanged) { 261 localeConfiguration.locale = locale; 262 localeConfiguration.mcc = mcc; 263 localeConfiguration.mnc = mnc; 264 265 writeConfiguration(this, localeConfiguration); 266 } 267 } 268 269 private static class LocaleConfiguration { 270 public String locale; 271 public int mcc = -1; 272 public int mnc = -1; 273 } 274 275 private static void readConfiguration(Context context, LocaleConfiguration configuration) { 276 DataInputStream in = null; 277 try { 278 in = new DataInputStream(context.openFileInput(PREFERENCES)); 279 configuration.locale = in.readUTF(); 280 configuration.mcc = in.readInt(); 281 configuration.mnc = in.readInt(); 282 } catch (FileNotFoundException e) { 283 // Ignore 284 } catch (IOException e) { 285 // Ignore 286 } finally { 287 if (in != null) { 288 try { 289 in.close(); 290 } catch (IOException e) { 291 // Ignore 292 } 293 } 294 } 295 } 296 297 private static void writeConfiguration(Context context, LocaleConfiguration configuration) { 298 DataOutputStream out = null; 299 try { 300 out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE)); 301 out.writeUTF(configuration.locale); 302 out.writeInt(configuration.mcc); 303 out.writeInt(configuration.mnc); 304 out.flush(); 305 } catch (FileNotFoundException e) { 306 // Ignore 307 } catch (IOException e) { 308 //noinspection ResultOfMethodCallIgnored 309 context.getFileStreamPath(PREFERENCES).delete(); 310 } finally { 311 if (out != null) { 312 try { 313 out.close(); 314 } catch (IOException e) { 315 // Ignore 316 } 317 } 318 } 319 } 320 321 static int getScreen() { 322 synchronized (sLock) { 323 return sScreen; 324 } 325 } 326 327 static void setScreen(int screen) { 328 synchronized (sLock) { 329 sScreen = screen; 330 } 331 } 332 333 private void startLoaders() { 334 boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged); 335 sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications); 336 337 mRestoring = false; 338 } 339 340 private void setWallpaperDimension() { 341 WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE); 342 343 Display display = getWindowManager().getDefaultDisplay(); 344 boolean isPortrait = display.getWidth() < display.getHeight(); 345 346 final int width = isPortrait ? display.getWidth() : display.getHeight(); 347 final int height = isPortrait ? display.getHeight() : display.getWidth(); 348 wpm.suggestDesiredDimensions(width * WALLPAPER_SCREENS_SPAN, height); 349 } 350 351 @Override 352 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 353 mWaitingForResult = false; 354 355 // The pattern used here is that a user PICKs a specific application, 356 // which, depending on the target, might need to CREATE the actual target. 357 358 // For example, the user would PICK_SHORTCUT for "Music playlist", and we 359 // launch over to the Music app to actually CREATE_SHORTCUT. 360 361 if (resultCode == RESULT_OK && mAddItemCellInfo != null) { 362 switch (requestCode) { 363 case REQUEST_PICK_APPLICATION: 364 completeAddApplication(this, data, mAddItemCellInfo, !mDesktopLocked); 365 break; 366 case REQUEST_PICK_SHORTCUT: 367 processShortcut(data, REQUEST_PICK_APPLICATION, REQUEST_CREATE_SHORTCUT); 368 break; 369 case REQUEST_CREATE_SHORTCUT: 370 completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked); 371 break; 372 case REQUEST_PICK_LIVE_FOLDER: 373 addLiveFolder(data); 374 break; 375 case REQUEST_CREATE_LIVE_FOLDER: 376 completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked); 377 break; 378 case REQUEST_PICK_APPWIDGET: 379 addAppWidget(data); 380 break; 381 case REQUEST_CREATE_APPWIDGET: 382 completeAddAppWidget(data, mAddItemCellInfo, !mDesktopLocked); 383 break; 384 } 385 } else if (requestCode == REQUEST_PICK_APPWIDGET && 386 resultCode == RESULT_CANCELED && data != null) { 387 // Clean up the appWidgetId if we canceled 388 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 389 if (appWidgetId != -1) { 390 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 391 } 392 } 393 } 394 395 @Override 396 protected void onResume() { 397 super.onResume(); 398 399 if (mRestoring) { 400 startLoaders(); 401 } 402 403 // If this was a new intent (i.e., the mIsNewIntent flag got set to true by 404 // onNewIntent), then close the search dialog if needed, because it probably 405 // came from the user pressing 'home' (rather than, for example, pressing 'back'). 406 if (mIsNewIntent) { 407 // Post to a handler so that this happens after the search dialog tries to open 408 // itself again. 409 mWorkspace.post(new Runnable() { 410 public void run() { 411 ISearchManager searchManagerService = ISearchManager.Stub.asInterface( 412 ServiceManager.getService(Context.SEARCH_SERVICE)); 413 try { 414 searchManagerService.stopSearch(); 415 } catch (RemoteException e) { 416 e(LOG_TAG, "error stopping search", e); 417 } 418 } 419 }); 420 } 421 422 mIsNewIntent = false; 423 } 424 425 @Override 426 protected void onPause() { 427 super.onPause(); 428 closeAllAppsDialog(false); 429 } 430 431 @Override 432 public Object onRetainNonConfigurationInstance() { 433 // Flag any binder to stop early before switching 434 if (mBinder != null) { 435 mBinder.mTerminate = true; 436 } 437 438 if (PROFILE_ROTATE) { 439 android.os.Debug.startMethodTracing("/sdcard/launcher-rotate"); 440 } 441 return null; 442 } 443 444 private boolean acceptFilter() { 445 final InputMethodManager inputManager = (InputMethodManager) 446 getSystemService(Context.INPUT_METHOD_SERVICE); 447 return !inputManager.isFullscreenMode(); 448 } 449 450 @Override 451 public boolean onKeyDown(int keyCode, KeyEvent event) { 452 boolean handled = super.onKeyDown(keyCode, event); 453 if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) { 454 boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb, 455 keyCode, event); 456 if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) { 457 // something usable has been typed - start a search 458 // the typed text will be retrieved and cleared by 459 // showSearchDialog() 460 // If there are multiple keystrokes before the search dialog takes focus, 461 // onSearchRequested() will be called for every keystroke, 462 // but it is idempotent, so it's fine. 463 return onSearchRequested(); 464 } 465 } 466 467 return handled; 468 } 469 470 private String getTypedText() { 471 return mDefaultKeySsb.toString(); 472 } 473 474 private void clearTypedText() { 475 mDefaultKeySsb.clear(); 476 mDefaultKeySsb.clearSpans(); 477 Selection.setSelection(mDefaultKeySsb, 0); 478 } 479 480 /** 481 * Restores the previous state, if it exists. 482 * 483 * @param savedState The previous state. 484 */ 485 private void restoreState(Bundle savedState) { 486 if (savedState == null) { 487 return; 488 } 489 490 final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1); 491 if (currentScreen > -1) { 492 mWorkspace.setCurrentScreen(currentScreen); 493 } 494 495 final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1); 496 if (addScreen > -1) { 497 mAddItemCellInfo = new CellLayout.CellInfo(); 498 final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo; 499 addItemCellInfo.valid = true; 500 addItemCellInfo.screen = addScreen; 501 addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X); 502 addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y); 503 addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X); 504 addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y); 505 addItemCellInfo.findVacantCellsFromOccupied( 506 savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS), 507 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X), 508 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y)); 509 mRestoring = true; 510 } 511 512 boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false); 513 if (renameFolder) { 514 long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID); 515 mFolderInfo = sModel.getFolderById(this, id); 516 mRestoring = true; 517 } 518 } 519 520 /** 521 * Finds all the views we need and configure them properly. 522 */ 523 private void setupViews() { 524 mDragController = new DragController(this); 525 DragController dragController = mDragController; 526 527 DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer); 528 dragLayer.setDragController(dragController); 529 530 mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace); 531 final Workspace workspace = mWorkspace; 532 533 final DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone); 534 535 mHandleView = (HandleView) findViewById(R.id.all_apps); 536 mHandleView.setLauncher(this); 537 mHandleView.setOnClickListener(this); 538 TransitionDrawable handleIcon = (TransitionDrawable) mHandleView.getDrawable(); 539 handleIcon.setCrossFadeEnabled(true); 540 541 workspace.setOnLongClickListener(this); 542 workspace.setDragController(dragController); 543 workspace.setLauncher(this); 544 545 deleteZone.setLauncher(this); 546 deleteZone.setDragController(dragController); 547 deleteZone.setHandle(mHandleView); 548 549 dragController.setDragScoller(workspace); 550 dragController.setDragListener(deleteZone); 551 dragController.setScrollView(dragLayer); 552 553 // The order here is bottom to top. 554 dragController.addDropTarget(workspace); 555 dragController.addDropTarget(deleteZone); 556 } 557 558 /** 559 * Creates a view representing a shortcut. 560 * 561 * @param info The data structure describing the shortcut. 562 * 563 * @return A View inflated from R.layout.application. 564 */ 565 View createShortcut(ApplicationInfo info) { 566 return createShortcut(R.layout.application, 567 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info); 568 } 569 570 /** 571 * Creates a view representing a shortcut inflated from the specified resource. 572 * 573 * @param layoutResId The id of the XML layout used to create the shortcut. 574 * @param parent The group the shortcut belongs to. 575 * @param info The data structure describing the shortcut. 576 * 577 * @return A View inflated from layoutResId. 578 */ 579 View createShortcut(int layoutResId, ViewGroup parent, ApplicationInfo info) { 580 TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false); 581 582 if (!info.filtered) { 583 info.icon = Utilities.createIconThumbnail(info.icon, this); 584 info.filtered = true; 585 } 586 587 favorite.setCompoundDrawablesWithIntrinsicBounds(null, info.icon, null, null); 588 favorite.setText(info.title); 589 favorite.setTag(info); 590 favorite.setOnClickListener(this); 591 592 return favorite; 593 } 594 595 /** 596 * Add an application shortcut to the workspace. 597 * 598 * @param data The intent describing the application. 599 * @param cellInfo The position on screen where to create the shortcut. 600 */ 601 void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo, 602 boolean insertAtFirst) { 603 cellInfo.screen = mWorkspace.getCurrentScreen(); 604 if (!findSingleSlot(cellInfo)) return; 605 606 final ApplicationInfo info = infoFromApplicationIntent(context, data); 607 if (info != null) { 608 mWorkspace.addApplicationShortcut(info, cellInfo, insertAtFirst); 609 } 610 } 611 612 private static ApplicationInfo infoFromApplicationIntent(Context context, Intent data) { 613 ComponentName component = data.getComponent(); 614 PackageManager packageManager = context.getPackageManager(); 615 ActivityInfo activityInfo = null; 616 try { 617 activityInfo = packageManager.getActivityInfo(component, 0 /* no flags */); 618 } catch (NameNotFoundException e) { 619 e(LOG_TAG, "Couldn't find ActivityInfo for selected application", e); 620 } 621 622 if (activityInfo != null) { 623 ApplicationInfo itemInfo = new ApplicationInfo(); 624 625 itemInfo.title = activityInfo.loadLabel(packageManager); 626 if (itemInfo.title == null) { 627 itemInfo.title = activityInfo.name; 628 } 629 630 itemInfo.setActivity(component, Intent.FLAG_ACTIVITY_NEW_TASK | 631 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 632 itemInfo.icon = activityInfo.loadIcon(packageManager); 633 itemInfo.container = ItemInfo.NO_ID; 634 635 return itemInfo; 636 } 637 638 return null; 639 } 640 641 /** 642 * Add a shortcut to the workspace. 643 * 644 * @param data The intent describing the shortcut. 645 * @param cellInfo The position on screen where to create the shortcut. 646 * @param insertAtFirst 647 */ 648 private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo, 649 boolean insertAtFirst) { 650 cellInfo.screen = mWorkspace.getCurrentScreen(); 651 if (!findSingleSlot(cellInfo)) return; 652 653 final ApplicationInfo info = addShortcut(this, data, cellInfo, false); 654 655 if (!mRestoring) { 656 sModel.addDesktopItem(info); 657 658 final View view = createShortcut(info); 659 mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst); 660 } else if (sModel.isDesktopLoaded()) { 661 sModel.addDesktopItem(info); 662 } 663 } 664 665 666 /** 667 * Add a widget to the workspace. 668 * 669 * @param data The intent describing the appWidgetId. 670 * @param cellInfo The position on screen where to create the widget. 671 */ 672 private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo, 673 boolean insertAtFirst) { 674 675 Bundle extras = data.getExtras(); 676 int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 677 678 d(LOG_TAG, "dumping extras content="+extras.toString()); 679 680 AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 681 682 // Calculate the grid spans needed to fit this widget 683 CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen); 684 int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight); 685 686 // Try finding open space on Launcher screen 687 final int[] xy = mCellCoordinates; 688 if (!findSlot(cellInfo, xy, spans[0], spans[1])) return; 689 690 // Build Launcher-specific widget info and save to database 691 LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId); 692 launcherInfo.spanX = spans[0]; 693 launcherInfo.spanY = spans[1]; 694 695 LauncherModel.addItemToDatabase(this, launcherInfo, 696 LauncherSettings.Favorites.CONTAINER_DESKTOP, 697 mWorkspace.getCurrentScreen(), xy[0], xy[1], false); 698 699 if (!mRestoring) { 700 sModel.addDesktopAppWidget(launcherInfo); 701 702 // Perform actual inflation because we're live 703 launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 704 705 launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo); 706 launcherInfo.hostView.setTag(launcherInfo); 707 708 mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1], 709 launcherInfo.spanX, launcherInfo.spanY, insertAtFirst); 710 } else if (sModel.isDesktopLoaded()) { 711 sModel.addDesktopAppWidget(launcherInfo); 712 } 713 } 714 715 public LauncherAppWidgetHost getAppWidgetHost() { 716 return mAppWidgetHost; 717 } 718 719 static ApplicationInfo addShortcut(Context context, Intent data, 720 CellLayout.CellInfo cellInfo, boolean notify) { 721 722 final ApplicationInfo info = infoFromShortcutIntent(context, data); 723 LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, 724 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify); 725 726 return info; 727 } 728 729 private static ApplicationInfo infoFromShortcutIntent(Context context, Intent data) { 730 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); 731 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 732 Bitmap bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); 733 734 Drawable icon = null; 735 boolean filtered = false; 736 boolean customIcon = false; 737 ShortcutIconResource iconResource = null; 738 739 if (bitmap != null) { 740 icon = new FastBitmapDrawable(Utilities.createBitmapThumbnail(bitmap, context)); 741 filtered = true; 742 customIcon = true; 743 } else { 744 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); 745 if (extra != null && extra instanceof ShortcutIconResource) { 746 try { 747 iconResource = (ShortcutIconResource) extra; 748 final PackageManager packageManager = context.getPackageManager(); 749 Resources resources = packageManager.getResourcesForApplication( 750 iconResource.packageName); 751 final int id = resources.getIdentifier(iconResource.resourceName, null, null); 752 icon = resources.getDrawable(id); 753 } catch (Exception e) { 754 w(LOG_TAG, "Could not load shortcut icon: " + extra); 755 } 756 } 757 } 758 759 if (icon == null) { 760 icon = context.getPackageManager().getDefaultActivityIcon(); 761 } 762 763 final ApplicationInfo info = new ApplicationInfo(); 764 info.icon = icon; 765 info.filtered = filtered; 766 info.title = name; 767 info.intent = intent; 768 info.customIcon = customIcon; 769 info.iconResource = iconResource; 770 771 return info; 772 } 773 774 @Override 775 protected void onNewIntent(Intent intent) { 776 super.onNewIntent(intent); 777 778 // Close the menu 779 if (Intent.ACTION_MAIN.equals(intent.getAction())) { 780 getWindow().closeAllPanels(); 781 782 // Set this flag so that onResume knows to close the search dialog if it's open, 783 // because this was a new intent (thus a press of 'home' or some such) rather than 784 // for example onResume being called when the user pressed the 'back' button. 785 mIsNewIntent = true; 786 787 try { 788 dismissDialog(DIALOG_CREATE_SHORTCUT); 789 // Unlock the workspace if the dialog was showing 790 mWorkspace.unlock(); 791 } catch (Exception e) { 792 // An exception is thrown if the dialog is not visible, which is fine 793 } 794 795 try { 796 dismissDialog(DIALOG_RENAME_FOLDER); 797 // Unlock the workspace if the dialog was showing 798 mWorkspace.unlock(); 799 } catch (Exception e) { 800 // An exception is thrown if the dialog is not visible, which is fine 801 } 802 803 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 804 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) { 805 806 if (!mWorkspace.isDefaultScreenShowing()) { 807 mWorkspace.moveToDefaultScreen(); 808 } 809 810 closeAllAppsDialog(true); 811 812 final View v = getWindow().peekDecorView(); 813 if (v != null && v.getWindowToken() != null) { 814 InputMethodManager imm = (InputMethodManager)getSystemService( 815 INPUT_METHOD_SERVICE); 816 imm.hideSoftInputFromWindow(v.getWindowToken(), 0); 817 } 818 } else { 819 closeAllAppsDialog(false); 820 } 821 } 822 } 823 824 @Override 825 protected void onRestoreInstanceState(Bundle savedInstanceState) { 826 // Do not call super here 827 mSavedInstanceState = savedInstanceState; 828 } 829 830 @Override 831 protected void onSaveInstanceState(Bundle outState) { 832 outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen()); 833 834 final ArrayList<Folder> folders = mWorkspace.getOpenFolders(); 835 if (folders.size() > 0) { 836 final int count = folders.size(); 837 long[] ids = new long[count]; 838 for (int i = 0; i < count; i++) { 839 final FolderInfo info = folders.get(i).getInfo(); 840 ids[i] = info.id; 841 } 842 outState.putLongArray(RUNTIME_STATE_USER_FOLDERS, ids); 843 } else { 844 super.onSaveInstanceState(outState); 845 } 846 847 final boolean isConfigurationChange = getChangingConfigurations() != 0; 848 849 // When the drawer is opened and we are saving the state because of a 850 // configuration change 851 if (mAllAppsDialog.isOpen && isConfigurationChange) { 852 outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true); 853 } 854 855 if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) { 856 final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo; 857 final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen); 858 859 outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen); 860 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX); 861 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY); 862 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX); 863 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY); 864 outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX()); 865 outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY()); 866 outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS, 867 layout.getOccupiedCells()); 868 } 869 870 if (mFolderInfo != null && mWaitingForResult) { 871 outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true); 872 outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id); 873 } 874 } 875 876 @Override 877 public void onDestroy() { 878 mDestroyed = true; 879 880 super.onDestroy(); 881 882 try { 883 mAppWidgetHost.stopListening(); 884 } catch (NullPointerException ex) { 885 w(LOG_TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex); 886 } 887 888 TextKeyListener.getInstance().release(); 889 890 // TODO mAllAppsGrid.setAdapter(null); 891 sModel.unbind(); 892 sModel.abortLoaders(); 893 894 getContentResolver().unregisterContentObserver(mObserver); 895 unregisterReceiver(mApplicationsReceiver); 896 } 897 898 @Override 899 public void startActivityForResult(Intent intent, int requestCode) { 900 if (requestCode >= 0) mWaitingForResult = true; 901 super.startActivityForResult(intent, requestCode); 902 } 903 904 @Override 905 public void startSearch(String initialQuery, boolean selectInitialQuery, 906 Bundle appSearchData, boolean globalSearch) { 907 908 closeAllAppsDialog(false); 909 910 // Slide the search widget to the top, if it's on the current screen, 911 // otherwise show the search dialog immediately. 912 Search searchWidget = mWorkspace.findSearchWidgetOnCurrentScreen(); 913 if (searchWidget == null) { 914 showSearchDialog(initialQuery, selectInitialQuery, appSearchData, globalSearch); 915 } else { 916 searchWidget.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); 917 // show the currently typed text in the search widget while sliding 918 searchWidget.setQuery(getTypedText()); 919 } 920 } 921 922 /** 923 * Show the search dialog immediately, without changing the search widget. 924 * 925 * @see Activity#startSearch(String, boolean, android.os.Bundle, boolean) 926 */ 927 void showSearchDialog(String initialQuery, boolean selectInitialQuery, 928 Bundle appSearchData, boolean globalSearch) { 929 930 if (initialQuery == null) { 931 // Use any text typed in the launcher as the initial query 932 initialQuery = getTypedText(); 933 clearTypedText(); 934 } 935 if (appSearchData == null) { 936 appSearchData = new Bundle(); 937 appSearchData.putString(SearchManager.SOURCE, "launcher-search"); 938 } 939 940 final SearchManager searchManager = 941 (SearchManager) getSystemService(Context.SEARCH_SERVICE); 942 943 final Search searchWidget = mWorkspace.findSearchWidgetOnCurrentScreen(); 944 if (searchWidget != null) { 945 // This gets called when the user leaves the search dialog to go back to 946 // the Launcher. 947 searchManager.setOnCancelListener(new SearchManager.OnCancelListener() { 948 public void onCancel() { 949 searchManager.setOnCancelListener(null); 950 stopSearch(); 951 } 952 }); 953 } 954 955 searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), 956 appSearchData, globalSearch); 957 } 958 959 /** 960 * Cancel search dialog if it is open. 961 */ 962 void stopSearch() { 963 // Close search dialog 964 SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); 965 searchManager.stopSearch(); 966 // Restore search widget to its normal position 967 Search searchWidget = mWorkspace.findSearchWidgetOnCurrentScreen(); 968 if (searchWidget != null) { 969 searchWidget.stopSearch(false); 970 } 971 } 972 973 @Override 974 public boolean onCreateOptionsMenu(Menu menu) { 975 if (mDesktopLocked) return false; 976 977 super.onCreateOptionsMenu(menu); 978 menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add) 979 .setIcon(android.R.drawable.ic_menu_add) 980 .setAlphabeticShortcut('A'); 981 menu.add(0, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper) 982 .setIcon(android.R.drawable.ic_menu_gallery) 983 .setAlphabeticShortcut('W'); 984 menu.add(0, MENU_SEARCH, 0, R.string.menu_search) 985 .setIcon(android.R.drawable.ic_search_category_default) 986 .setAlphabeticShortcut(SearchManager.MENU_KEY); 987 menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications) 988 .setIcon(com.android.internal.R.drawable.ic_menu_notifications) 989 .setAlphabeticShortcut('N'); 990 991 final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS); 992 settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 993 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 994 995 menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings) 996 .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P') 997 .setIntent(settings); 998 999 return true; 1000 } 1001 1002 @Override 1003 public boolean onPrepareOptionsMenu(Menu menu) { 1004 super.onPrepareOptionsMenu(menu); 1005 1006 mMenuAddInfo = mWorkspace.findAllVacantCells(null); 1007 menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid); 1008 1009 return true; 1010 } 1011 1012 @Override 1013 public boolean onOptionsItemSelected(MenuItem item) { 1014 switch (item.getItemId()) { 1015 case MENU_ADD: 1016 addItems(); 1017 return true; 1018 case MENU_WALLPAPER_SETTINGS: 1019 startWallpaper(); 1020 return true; 1021 case MENU_SEARCH: 1022 onSearchRequested(); 1023 return true; 1024 case MENU_NOTIFICATIONS: 1025 showNotifications(); 1026 return true; 1027 } 1028 1029 return super.onOptionsItemSelected(item); 1030 } 1031 1032 /** 1033 * Indicates that we want global search for this activity by setting the globalSearch 1034 * argument for {@link #startSearch} to true. 1035 */ 1036 1037 @Override 1038 public boolean onSearchRequested() { 1039 startSearch(null, false, null, true); 1040 return true; 1041 } 1042 1043 private void addItems() { 1044 showAddDialog(mMenuAddInfo); 1045 } 1046 1047 private void removeShortcutsForPackage(String packageName) { 1048 if (packageName != null && packageName.length() > 0) { 1049 mWorkspace.removeShortcutsForPackage(packageName); 1050 } 1051 } 1052 1053 private void updateShortcutsForPackage(String packageName) { 1054 if (packageName != null && packageName.length() > 0) { 1055 mWorkspace.updateShortcutsForPackage(packageName); 1056 } 1057 } 1058 1059 void addAppWidget(Intent data) { 1060 // TODO: catch bad widget exception when sent 1061 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 1062 1063 String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET); 1064 if (SEARCH_WIDGET.equals(customWidget)) { 1065 // We don't need this any more, since this isn't a real app widget. 1066 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 1067 // add the search widget 1068 addSearch(); 1069 } else { 1070 AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 1071 1072 if (appWidget.configure != null) { 1073 // Launch over to configure widget, if needed 1074 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); 1075 intent.setComponent(appWidget.configure); 1076 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 1077 1078 startActivityForResult(intent, REQUEST_CREATE_APPWIDGET); 1079 } else { 1080 // Otherwise just add it 1081 onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data); 1082 } 1083 } 1084 } 1085 1086 void addSearch() { 1087 final Widget info = Widget.makeSearch(); 1088 final CellLayout.CellInfo cellInfo = mAddItemCellInfo; 1089 1090 final int[] xy = mCellCoordinates; 1091 final int spanX = info.spanX; 1092 final int spanY = info.spanY; 1093 1094 if (!findSlot(cellInfo, xy, spanX, spanY)) return; 1095 1096 sModel.addDesktopItem(info); 1097 LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, 1098 mWorkspace.getCurrentScreen(), xy[0], xy[1], false); 1099 1100 final View view = mInflater.inflate(info.layoutResource, null); 1101 view.setTag(info); 1102 Search search = (Search) view.findViewById(R.id.widget_search); 1103 search.setLauncher(this); 1104 1105 mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY); 1106 } 1107 1108 void processShortcut(Intent intent, int requestCodeApplication, int requestCodeShortcut) { 1109 // Handle case where user selected "Applications" 1110 String applicationName = getResources().getString(R.string.group_applications); 1111 String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 1112 1113 if (applicationName != null && applicationName.equals(shortcutName)) { 1114 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 1115 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1116 1117 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 1118 pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent); 1119 startActivityForResult(pickIntent, requestCodeApplication); 1120 } else { 1121 startActivityForResult(intent, requestCodeShortcut); 1122 } 1123 } 1124 1125 void addLiveFolder(Intent intent) { 1126 // Handle case where user selected "Folder" 1127 String folderName = getResources().getString(R.string.group_folder); 1128 String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 1129 1130 if (folderName != null && folderName.equals(shortcutName)) { 1131 addFolder(!mDesktopLocked); 1132 } else { 1133 startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER); 1134 } 1135 } 1136 1137 void addFolder(boolean insertAtFirst) { 1138 UserFolderInfo folderInfo = new UserFolderInfo(); 1139 folderInfo.title = getText(R.string.folder_name); 1140 1141 CellLayout.CellInfo cellInfo = mAddItemCellInfo; 1142 cellInfo.screen = mWorkspace.getCurrentScreen(); 1143 if (!findSingleSlot(cellInfo)) return; 1144 1145 // Update the model 1146 LauncherModel.addItemToDatabase(this, folderInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, 1147 mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false); 1148 sModel.addDesktopItem(folderInfo); 1149 sModel.addFolder(folderInfo); 1150 1151 // Create the view 1152 FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, 1153 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo); 1154 mWorkspace.addInCurrentScreen(newFolder, 1155 cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst); 1156 } 1157 1158 private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo, 1159 boolean insertAtFirst) { 1160 cellInfo.screen = mWorkspace.getCurrentScreen(); 1161 if (!findSingleSlot(cellInfo)) return; 1162 1163 final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false); 1164 1165 if (!mRestoring) { 1166 sModel.addDesktopItem(info); 1167 1168 final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this, 1169 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info); 1170 mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst); 1171 } else if (sModel.isDesktopLoaded()) { 1172 sModel.addDesktopItem(info); 1173 } 1174 } 1175 1176 static LiveFolderInfo addLiveFolder(Context context, Intent data, 1177 CellLayout.CellInfo cellInfo, boolean notify) { 1178 1179 Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT); 1180 String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME); 1181 1182 Drawable icon = null; 1183 boolean filtered = false; 1184 Intent.ShortcutIconResource iconResource = null; 1185 1186 Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON); 1187 if (extra != null && extra instanceof Intent.ShortcutIconResource) { 1188 try { 1189 iconResource = (Intent.ShortcutIconResource) extra; 1190 final PackageManager packageManager = context.getPackageManager(); 1191 Resources resources = packageManager.getResourcesForApplication( 1192 iconResource.packageName); 1193 final int id = resources.getIdentifier(iconResource.resourceName, null, null); 1194 icon = resources.getDrawable(id); 1195 } catch (Exception e) { 1196 w(LOG_TAG, "Could not load live folder icon: " + extra); 1197 } 1198 } 1199 1200 if (icon == null) { 1201 icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder); 1202 } 1203 1204 final LiveFolderInfo info = new LiveFolderInfo(); 1205 info.icon = icon; 1206 info.filtered = filtered; 1207 info.title = name; 1208 info.iconResource = iconResource; 1209 info.uri = data.getData(); 1210 info.baseIntent = baseIntent; 1211 info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, 1212 LiveFolders.DISPLAY_MODE_GRID); 1213 1214 LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, 1215 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify); 1216 sModel.addFolder(info); 1217 1218 return info; 1219 } 1220 1221 private boolean findSingleSlot(CellLayout.CellInfo cellInfo) { 1222 final int[] xy = new int[2]; 1223 if (findSlot(cellInfo, xy, 1, 1)) { 1224 cellInfo.cellX = xy[0]; 1225 cellInfo.cellY = xy[1]; 1226 return true; 1227 } 1228 return false; 1229 } 1230 1231 private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) { 1232 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) { 1233 boolean[] occupied = mSavedState != null ? 1234 mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null; 1235 cellInfo = mWorkspace.findAllVacantCells(occupied); 1236 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) { 1237 Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); 1238 return false; 1239 } 1240 } 1241 return true; 1242 } 1243 1244 private void showNotifications() { 1245 final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE); 1246 if (statusBar != null) { 1247 statusBar.expand(); 1248 } 1249 } 1250 1251 private void startWallpaper() { 1252 final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); 1253 startActivity(Intent.createChooser(pickWallpaper, getString(R.string.chooser_wallpaper))); 1254 } 1255 1256 /** 1257 * Registers various intent receivers. The current implementation registers 1258 * only a wallpaper intent receiver to let other applications change the 1259 * wallpaper. 1260 */ 1261 private void registerIntentReceivers() { 1262 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 1263 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1264 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1265 filter.addDataScheme("package"); 1266 registerReceiver(mApplicationsReceiver, filter); 1267 } 1268 1269 /** 1270 * Registers various content observers. The current implementation registers 1271 * only a favorites observer to keep track of the favorites applications. 1272 */ 1273 private void registerContentObservers() { 1274 ContentResolver resolver = getContentResolver(); 1275 resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mObserver); 1276 } 1277 1278 @Override 1279 public void onWindowFocusChanged(boolean hasFocus) { 1280 super.onWindowFocusChanged(hasFocus); 1281 if (!hasFocus) { 1282 mBackDown = mHomeDown = false; 1283 } 1284 } 1285 1286 @Override 1287 public boolean dispatchKeyEvent(KeyEvent event) { 1288 if (event.getAction() == KeyEvent.ACTION_DOWN) { 1289 switch (event.getKeyCode()) { 1290 case KeyEvent.KEYCODE_BACK: 1291 mBackDown = true; 1292 return true; 1293 case KeyEvent.KEYCODE_HOME: 1294 mHomeDown = true; 1295 return true; 1296 } 1297 } else if (event.getAction() == KeyEvent.ACTION_UP) { 1298 switch (event.getKeyCode()) { 1299 case KeyEvent.KEYCODE_BACK: 1300 if (!event.isCanceled()) { 1301 mWorkspace.dispatchKeyEvent(event); 1302 if (mAllAppsDialog.isOpen) { 1303 closeAllAppsDialog(true); 1304 } else { 1305 closeFolder(); 1306 } 1307 } 1308 mBackDown = false; 1309 return true; 1310 case KeyEvent.KEYCODE_HOME: 1311 mHomeDown = false; 1312 return true; 1313 } 1314 } 1315 1316 return super.dispatchKeyEvent(event); 1317 } 1318 1319 private void closeFolder() { 1320 Folder folder = mWorkspace.getOpenFolder(); 1321 if (folder != null) { 1322 closeFolder(folder); 1323 } 1324 } 1325 1326 void closeFolder(Folder folder) { 1327 folder.getInfo().opened = false; 1328 ViewGroup parent = (ViewGroup) folder.getParent(); 1329 if (parent != null) { 1330 parent.removeView(folder); 1331 mDragController.removeDropTarget((DropTarget)folder); 1332 } 1333 folder.onClose(); 1334 } 1335 1336 /** 1337 * When the notification that favorites have changed is received, requests 1338 * a favorites list refresh. 1339 */ 1340 private void onFavoritesChanged() { 1341 mDesktopLocked = true; 1342 mAllAppsDialog.lock(); 1343 sModel.loadUserItems(false, this, false, false); 1344 } 1345 1346 void onDesktopItemsLoaded() { 1347 if (mDestroyed) return; 1348 bindDesktopItems(); 1349 } 1350 1351 /** 1352 * Refreshes the shortcuts shown on the workspace. 1353 */ 1354 private void bindDesktopItems() { 1355 final ArrayList<ItemInfo> shortcuts = sModel.getDesktopItems(); 1356 final ArrayList<LauncherAppWidgetInfo> appWidgets = sModel.getDesktopAppWidgets(); 1357 final ApplicationsAdapter drawerAdapter = sModel.getApplicationsAdapter(); 1358 if (shortcuts == null || appWidgets == null || drawerAdapter == null) { 1359 return; 1360 } 1361 1362 final Workspace workspace = mWorkspace; 1363 int count = workspace.getChildCount(); 1364 for (int i = 0; i < count; i++) { 1365 ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout(); 1366 } 1367 1368 if (DEBUG_USER_INTERFACE) { 1369 android.widget.Button finishButton = new android.widget.Button(this); 1370 finishButton.setText("Finish"); 1371 workspace.addInScreen(finishButton, 1, 0, 0, 1, 1); 1372 1373 finishButton.setOnClickListener(new android.widget.Button.OnClickListener() { 1374 public void onClick(View v) { 1375 finish(); 1376 } 1377 }); 1378 } 1379 1380 // Flag any old binder to terminate early 1381 if (mBinder != null) { 1382 mBinder.mTerminate = true; 1383 } 1384 1385 mBinder = new DesktopBinder(this, shortcuts, appWidgets, drawerAdapter); 1386 mBinder.startBindingItems(); 1387 } 1388 1389 private void bindItems(Launcher.DesktopBinder binder, 1390 ArrayList<ItemInfo> shortcuts, int start, int count) { 1391 1392 final Workspace workspace = mWorkspace; 1393 final boolean desktopLocked = mDesktopLocked; 1394 1395 final int end = Math.min(start + DesktopBinder.ITEMS_COUNT, count); 1396 int i = start; 1397 1398 for ( ; i < end; i++) { 1399 final ItemInfo item = shortcuts.get(i); 1400 switch (item.itemType) { 1401 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1402 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1403 final View shortcut = createShortcut((ApplicationInfo) item); 1404 workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1, 1405 !desktopLocked); 1406 break; 1407 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1408 final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, 1409 (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()), 1410 (UserFolderInfo) item); 1411 workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1, 1412 !desktopLocked); 1413 break; 1414 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 1415 final FolderIcon newLiveFolder = LiveFolderIcon.fromXml( 1416 R.layout.live_folder_icon, this, 1417 (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()), 1418 (LiveFolderInfo) item); 1419 workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1, 1420 !desktopLocked); 1421 break; 1422 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH: 1423 final int screen = workspace.getCurrentScreen(); 1424 final View view = mInflater.inflate(R.layout.widget_search, 1425 (ViewGroup) workspace.getChildAt(screen), false); 1426 1427 Search search = (Search) view.findViewById(R.id.widget_search); 1428 search.setLauncher(this); 1429 1430 final Widget widget = (Widget) item; 1431 view.setTag(widget); 1432 1433 workspace.addWidget(view, widget, !desktopLocked); 1434 break; 1435 } 1436 } 1437 1438 workspace.requestLayout(); 1439 1440 if (end >= count) { 1441 finishBindDesktopItems(); 1442 binder.startBindingDrawer(); 1443 } else { 1444 binder.obtainMessage(DesktopBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget(); 1445 } 1446 } 1447 1448 private void finishBindDesktopItems() { 1449 if (mSavedState != null) { 1450 if (!mWorkspace.hasFocus()) { 1451 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); 1452 } 1453 1454 final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS); 1455 if (userFolders != null) { 1456 for (long folderId : userFolders) { 1457 final FolderInfo info = sModel.findFolderById(folderId); 1458 if (info != null) { 1459 openFolder(info); 1460 } 1461 } 1462 final Folder openFolder = mWorkspace.getOpenFolder(); 1463 if (openFolder != null) { 1464 openFolder.requestFocus(); 1465 } 1466 } 1467 1468 final boolean allApps = mSavedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false); 1469 if (allApps) { 1470 showAllAppsDialog(); 1471 } 1472 1473 mSavedState = null; 1474 } 1475 1476 if (mSavedInstanceState != null) { 1477 super.onRestoreInstanceState(mSavedInstanceState); 1478 mSavedInstanceState = null; 1479 } 1480 1481 /* TODO 1482 if (mAllAppsDialog.isOpen && !mDrawer.hasFocus()) { 1483 mDrawer.requestFocus(); 1484 } 1485 */ 1486 1487 mDesktopLocked = false; 1488 mAllAppsDialog.unlock(); 1489 } 1490 1491 private void bindDrawer(Launcher.DesktopBinder binder, 1492 ApplicationsAdapter drawerAdapter) { 1493 // TODO mAllAppsGrid.setAdapter(drawerAdapter); 1494 binder.startBindingAppWidgetsWhenIdle(); 1495 } 1496 1497 private void bindAppWidgets(Launcher.DesktopBinder binder, 1498 LinkedList<LauncherAppWidgetInfo> appWidgets) { 1499 1500 final Workspace workspace = mWorkspace; 1501 final boolean desktopLocked = mDesktopLocked; 1502 1503 if (!appWidgets.isEmpty()) { 1504 final LauncherAppWidgetInfo item = appWidgets.removeFirst(); 1505 1506 final int appWidgetId = item.appWidgetId; 1507 final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 1508 item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 1509 1510 if (LOGD) d(LOG_TAG, String.format("about to setAppWidget for id=%d, info=%s", appWidgetId, appWidgetInfo)); 1511 1512 item.hostView.setAppWidget(appWidgetId, appWidgetInfo); 1513 item.hostView.setTag(item); 1514 1515 workspace.addInScreen(item.hostView, item.screen, item.cellX, 1516 item.cellY, item.spanX, item.spanY, !desktopLocked); 1517 1518 workspace.requestLayout(); 1519 } 1520 1521 if (appWidgets.isEmpty()) { 1522 if (PROFILE_ROTATE) { 1523 android.os.Debug.stopMethodTracing(); 1524 } 1525 } else { 1526 binder.obtainMessage(DesktopBinder.MESSAGE_BIND_APPWIDGETS).sendToTarget(); 1527 } 1528 } 1529 1530 /** 1531 * Launches the intent referred by the clicked shortcut. 1532 * 1533 * @param v The view representing the clicked shortcut. 1534 */ 1535 public void onClick(View v) { 1536 Object tag = v.getTag(); 1537 if (tag instanceof ApplicationInfo) { 1538 // Open shortcut 1539 final Intent intent = ((ApplicationInfo) tag).intent; 1540 startActivitySafely(intent); 1541 } else if (tag instanceof FolderInfo) { 1542 handleFolderClick((FolderInfo) tag); 1543 } else if (v == mHandleView) { 1544 if (mAllAppsDialog.isOpen) { 1545 // TODO how can we be here? 1546 } else { 1547 showAllAppsDialog(); 1548 } 1549 } 1550 } 1551 1552 void startActivitySafely(Intent intent) { 1553 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1554 try { 1555 startActivity(intent); 1556 } catch (ActivityNotFoundException e) { 1557 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 1558 } catch (SecurityException e) { 1559 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 1560 e(LOG_TAG, "Launcher does not have the permission to launch " + intent + 1561 ". Make sure to create a MAIN intent-filter for the corresponding activity " + 1562 "or use the exported attribute for this activity.", e); 1563 } 1564 } 1565 1566 private void handleFolderClick(FolderInfo folderInfo) { 1567 if (!folderInfo.opened) { 1568 // Close any open folder 1569 closeFolder(); 1570 // Open the requested folder 1571 openFolder(folderInfo); 1572 } else { 1573 // Find the open folder... 1574 Folder openFolder = mWorkspace.getFolderForTag(folderInfo); 1575 int folderScreen; 1576 if (openFolder != null) { 1577 folderScreen = mWorkspace.getScreenForView(openFolder); 1578 // .. and close it 1579 closeFolder(openFolder); 1580 if (folderScreen != mWorkspace.getCurrentScreen()) { 1581 // Close any folder open on the current screen 1582 closeFolder(); 1583 // Pull the folder onto this screen 1584 openFolder(folderInfo); 1585 } 1586 } 1587 } 1588 } 1589 1590 /** 1591 * Opens the user fodler described by the specified tag. The opening of the folder 1592 * is animated relative to the specified View. If the View is null, no animation 1593 * is played. 1594 * 1595 * @param folderInfo The FolderInfo describing the folder to open. 1596 */ 1597 private void openFolder(FolderInfo folderInfo) { 1598 Folder openFolder; 1599 1600 if (folderInfo instanceof UserFolderInfo) { 1601 openFolder = UserFolder.fromXml(this); 1602 } else if (folderInfo instanceof LiveFolderInfo) { 1603 openFolder = com.android.launcher2.LiveFolder.fromXml(this, folderInfo); 1604 } else { 1605 return; 1606 } 1607 1608 openFolder.setDragController(mDragController); 1609 openFolder.setLauncher(this); 1610 1611 openFolder.bind(folderInfo); 1612 folderInfo.opened = true; 1613 1614 mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4); 1615 openFolder.onOpen(); 1616 } 1617 1618 /** 1619 * Returns true if the workspace is being loaded. When the workspace is loading, 1620 * no user interaction should be allowed to avoid any conflict. 1621 * 1622 * @return True if the workspace is locked, false otherwise. 1623 */ 1624 boolean isWorkspaceLocked() { 1625 return mDesktopLocked; 1626 } 1627 1628 public boolean onLongClick(View v) { 1629 if (mDesktopLocked) { 1630 return false; 1631 } 1632 1633 if (!(v instanceof CellLayout)) { 1634 v = (View) v.getParent(); 1635 } 1636 1637 CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag(); 1638 1639 // This happens when long clicking an item with the dpad/trackball 1640 if (cellInfo == null) { 1641 return true; 1642 } 1643 1644 if (mWorkspace.allowLongPress()) { 1645 if (cellInfo.cell == null) { 1646 if (cellInfo.valid) { 1647 // User long pressed on empty space 1648 mWorkspace.setAllowLongPress(false); 1649 showAddDialog(cellInfo); 1650 } 1651 } else { 1652 if (!(cellInfo.cell instanceof Folder)) { 1653 // User long pressed on an item 1654 mWorkspace.startDrag(cellInfo); 1655 } 1656 } 1657 } 1658 return true; 1659 } 1660 1661 static LauncherModel getModel() { 1662 return sModel; 1663 } 1664 1665 View getDrawerHandle() { 1666 return mHandleView; 1667 } 1668 1669 boolean isDrawerDown() { 1670 return /* TODO !mDrawer.isMoving() && */ !mAllAppsDialog.isOpen; 1671 } 1672 1673 Workspace getWorkspace() { 1674 return mWorkspace; 1675 } 1676 1677 /* TODO 1678 GridView getApplicationsGrid() { 1679 return mAllAppsGrid; 1680 } 1681 */ 1682 1683 @Override 1684 protected Dialog onCreateDialog(int id) { 1685 switch (id) { 1686 case DIALOG_ALL_APPS: 1687 return mAllAppsDialog; 1688 case DIALOG_CREATE_SHORTCUT: 1689 return new CreateShortcut().createDialog(); 1690 case DIALOG_RENAME_FOLDER: 1691 return new RenameFolder().createDialog(); 1692 } 1693 1694 return super.onCreateDialog(id); 1695 } 1696 1697 @Override 1698 protected void onPrepareDialog(int id, Dialog dialog) { 1699 switch (id) { 1700 case DIALOG_ALL_APPS: 1701 // TODO mAllAppsGrid.onPrepareDialog(); 1702 break; 1703 case DIALOG_CREATE_SHORTCUT: 1704 break; 1705 case DIALOG_RENAME_FOLDER: 1706 if (mFolderInfo != null) { 1707 EditText input = (EditText) dialog.findViewById(R.id.folder_name); 1708 final CharSequence text = mFolderInfo.title; 1709 input.setText(text); 1710 input.setSelection(0, text.length()); 1711 } 1712 break; 1713 } 1714 } 1715 1716 void showRenameDialog(FolderInfo info) { 1717 mFolderInfo = info; 1718 mWaitingForResult = true; 1719 showDialog(DIALOG_RENAME_FOLDER); 1720 } 1721 1722 private void showAddDialog(CellLayout.CellInfo cellInfo) { 1723 mAddItemCellInfo = cellInfo; 1724 mWaitingForResult = true; 1725 showDialog(DIALOG_CREATE_SHORTCUT); 1726 } 1727 1728 private void pickShortcut(int requestCode, int title) { 1729 Bundle bundle = new Bundle(); 1730 1731 ArrayList<String> shortcutNames = new ArrayList<String>(); 1732 shortcutNames.add(getString(R.string.group_applications)); 1733 bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames); 1734 1735 ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>(); 1736 shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this, 1737 R.drawable.ic_launcher_application)); 1738 bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons); 1739 1740 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 1741 pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT)); 1742 pickIntent.putExtra(Intent.EXTRA_TITLE, getText(title)); 1743 pickIntent.putExtras(bundle); 1744 1745 startActivityForResult(pickIntent, requestCode); 1746 } 1747 1748 private class RenameFolder { 1749 private EditText mInput; 1750 1751 Dialog createDialog() { 1752 mWaitingForResult = true; 1753 final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null); 1754 mInput = (EditText) layout.findViewById(R.id.folder_name); 1755 1756 AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this); 1757 builder.setIcon(0); 1758 builder.setTitle(getString(R.string.rename_folder_title)); 1759 builder.setCancelable(true); 1760 builder.setOnCancelListener(new Dialog.OnCancelListener() { 1761 public void onCancel(DialogInterface dialog) { 1762 cleanup(); 1763 } 1764 }); 1765 builder.setNegativeButton(getString(R.string.cancel_action), 1766 new Dialog.OnClickListener() { 1767 public void onClick(DialogInterface dialog, int which) { 1768 cleanup(); 1769 } 1770 } 1771 ); 1772 builder.setPositiveButton(getString(R.string.rename_action), 1773 new Dialog.OnClickListener() { 1774 public void onClick(DialogInterface dialog, int which) { 1775 changeFolderName(); 1776 } 1777 } 1778 ); 1779 builder.setView(layout); 1780 1781 final AlertDialog dialog = builder.create(); 1782 dialog.setOnShowListener(new DialogInterface.OnShowListener() { 1783 public void onShow(DialogInterface dialog) { 1784 mWorkspace.lock(); 1785 } 1786 }); 1787 1788 return dialog; 1789 } 1790 1791 private void changeFolderName() { 1792 final String name = mInput.getText().toString(); 1793 if (!TextUtils.isEmpty(name)) { 1794 // Make sure we have the right folder info 1795 mFolderInfo = sModel.findFolderById(mFolderInfo.id); 1796 mFolderInfo.title = name; 1797 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo); 1798 1799 if (mDesktopLocked) { 1800 mAllAppsDialog.lock(); 1801 sModel.loadUserItems(false, Launcher.this, false, false); 1802 } else { 1803 final FolderIcon folderIcon = (FolderIcon) 1804 mWorkspace.getViewForTag(mFolderInfo); 1805 if (folderIcon != null) { 1806 folderIcon.setText(name); 1807 getWorkspace().requestLayout(); 1808 } else { 1809 mDesktopLocked = true; 1810 mAllAppsDialog.lock(); 1811 sModel.loadUserItems(false, Launcher.this, false, false); 1812 } 1813 } 1814 } 1815 cleanup(); 1816 } 1817 1818 private void cleanup() { 1819 mWorkspace.unlock(); 1820 dismissDialog(DIALOG_RENAME_FOLDER); 1821 mWaitingForResult = false; 1822 mFolderInfo = null; 1823 } 1824 } 1825 1826 /** 1827 * Holds the 3d all apps view. 1828 */ 1829 private class AllAppsDialog extends Dialog implements DialogInterface.OnCancelListener, 1830 DialogInterface.OnDismissListener, DialogInterface.OnShowListener { 1831 1832 boolean isOpen; 1833 1834 AllAppsDialog(Context context) { 1835 super(context, android.R.style.Theme_Translucent_NoTitleBar); 1836 1837 setOnCancelListener(this); 1838 setOnDismissListener(this); 1839 setOnShowListener(this); 1840 1841 setContentView(R.layout.all_apps); 1842 mAllAppsGrid = (AllAppsView) findViewById(R.id.all_apps); 1843 1844 DragLayer dragLayer = (DragLayer)findViewById(R.id.drag_layer); 1845 dragLayer.setDragController(mDragController); 1846 1847 // TODO grid.setDragController(mDragController); 1848 // TODO grid.setLauncher(Launcher.this); 1849 } 1850 1851 public void onCancel(DialogInterface dialog) { 1852 onDestroy(); 1853 } 1854 1855 public void onDismiss(DialogInterface dialog) { 1856 onDestroy(); 1857 } 1858 1859 public void onShow(DialogInterface dialog) { 1860 } 1861 1862 private void onDestroy() { 1863 } 1864 1865 void lock() { 1866 // TODO 1867 } 1868 1869 void unlock() { 1870 // TODO 1871 } 1872 1873 @Override 1874 public boolean onKeyDown(int keyCode, KeyEvent event) { 1875 switch (keyCode) { 1876 case KeyEvent.KEYCODE_BACK: 1877 closeAllAppsDialog(true); 1878 return true; 1879 default: 1880 return super.onKeyDown(keyCode, event); 1881 } 1882 } 1883 } 1884 1885 void showAllAppsDialog() { 1886 mAllAppsDialog.isOpen = true; 1887 showDialog(DIALOG_ALL_APPS); 1888 mWorkspace.hide(); 1889 } 1890 1891 void showWorkspace() { 1892 mWorkspace.show(); 1893 } 1894 1895 void closeAllAppsDialog(boolean animated) { 1896 if (mAllAppsDialog.isOpen) { 1897 if (animated) { 1898 // TODO mDrawer.animateClose(); 1899 mAllAppsDialog.dismiss(); 1900 } else { 1901 mAllAppsDialog.dismiss(); 1902 } 1903 mAllAppsDialog.isOpen = false; 1904 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); 1905 mWorkspace.show(); 1906 } 1907 } 1908 1909 /** 1910 * Displays the shortcut creation dialog and launches, if necessary, the 1911 * appropriate activity. 1912 */ 1913 private class CreateShortcut implements DialogInterface.OnClickListener, 1914 DialogInterface.OnCancelListener, DialogInterface.OnDismissListener, 1915 DialogInterface.OnShowListener { 1916 1917 private AddAdapter mAdapter; 1918 1919 Dialog createDialog() { 1920 mWaitingForResult = true; 1921 1922 mAdapter = new AddAdapter(Launcher.this); 1923 1924 final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this); 1925 builder.setTitle(getString(R.string.menu_item_add_item)); 1926 builder.setAdapter(mAdapter, this); 1927 1928 builder.setInverseBackgroundForced(true); 1929 1930 AlertDialog dialog = builder.create(); 1931 dialog.setOnCancelListener(this); 1932 dialog.setOnDismissListener(this); 1933 dialog.setOnShowListener(this); 1934 1935 return dialog; 1936 } 1937 1938 public void onCancel(DialogInterface dialog) { 1939 mWaitingForResult = false; 1940 cleanup(); 1941 } 1942 1943 public void onDismiss(DialogInterface dialog) { 1944 mWorkspace.unlock(); 1945 } 1946 1947 private void cleanup() { 1948 mWorkspace.unlock(); 1949 dismissDialog(DIALOG_CREATE_SHORTCUT); 1950 } 1951 1952 /** 1953 * Handle the action clicked in the "Add to home" dialog. 1954 */ 1955 public void onClick(DialogInterface dialog, int which) { 1956 Resources res = getResources(); 1957 cleanup(); 1958 1959 switch (which) { 1960 case AddAdapter.ITEM_SHORTCUT: { 1961 // Insert extra item to handle picking application 1962 pickShortcut(REQUEST_PICK_SHORTCUT, R.string.title_select_shortcut); 1963 break; 1964 } 1965 1966 case AddAdapter.ITEM_APPWIDGET: { 1967 int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId(); 1968 1969 Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK); 1970 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 1971 // add the search widget 1972 ArrayList<AppWidgetProviderInfo> customInfo = 1973 new ArrayList<AppWidgetProviderInfo>(); 1974 AppWidgetProviderInfo info = new AppWidgetProviderInfo(); 1975 info.provider = new ComponentName(getPackageName(), "XXX.YYY"); 1976 info.label = getString(R.string.group_search); 1977 info.icon = R.drawable.ic_search_widget; 1978 customInfo.add(info); 1979 pickIntent.putParcelableArrayListExtra( 1980 AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo); 1981 ArrayList<Bundle> customExtras = new ArrayList<Bundle>(); 1982 Bundle b = new Bundle(); 1983 b.putString(EXTRA_CUSTOM_WIDGET, SEARCH_WIDGET); 1984 customExtras.add(b); 1985 pickIntent.putParcelableArrayListExtra( 1986 AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras); 1987 // start the pick activity 1988 startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET); 1989 break; 1990 } 1991 1992 case AddAdapter.ITEM_LIVE_FOLDER: { 1993 // Insert extra item to handle inserting folder 1994 Bundle bundle = new Bundle(); 1995 1996 ArrayList<String> shortcutNames = new ArrayList<String>(); 1997 shortcutNames.add(res.getString(R.string.group_folder)); 1998 bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames); 1999 2000 ArrayList<ShortcutIconResource> shortcutIcons = 2001 new ArrayList<ShortcutIconResource>(); 2002 shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this, 2003 R.drawable.ic_launcher_folder)); 2004 bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons); 2005 2006 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 2007 pickIntent.putExtra(Intent.EXTRA_INTENT, 2008 new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER)); 2009 pickIntent.putExtra(Intent.EXTRA_TITLE, 2010 getText(R.string.title_select_live_folder)); 2011 pickIntent.putExtras(bundle); 2012 2013 startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER); 2014 break; 2015 } 2016 2017 case AddAdapter.ITEM_WALLPAPER: { 2018 startWallpaper(); 2019 break; 2020 } 2021 } 2022 } 2023 2024 public void onShow(DialogInterface dialog) { 2025 mWorkspace.lock(); 2026 } 2027 } 2028 2029 /** 2030 * Receives notifications when applications are added/removed. 2031 */ 2032 private class ApplicationsIntentReceiver extends BroadcastReceiver { 2033 @Override 2034 public void onReceive(Context context, Intent intent) { 2035 final String action = intent.getAction(); 2036 final String packageName = intent.getData().getSchemeSpecificPart(); 2037 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 2038 2039 if (LauncherModel.DEBUG_LOADERS) { 2040 d(LauncherModel.LOG_TAG, "application intent received: " + action + 2041 ", replacing=" + replacing); 2042 d(LauncherModel.LOG_TAG, " --> " + intent.getData()); 2043 } 2044 2045 if (!Intent.ACTION_PACKAGE_CHANGED.equals(action)) { 2046 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 2047 if (!replacing) { 2048 removeShortcutsForPackage(packageName); 2049 if (LauncherModel.DEBUG_LOADERS) { 2050 d(LauncherModel.LOG_TAG, " --> remove package"); 2051 } 2052 sModel.removePackage(Launcher.this, packageName); 2053 } 2054 // else, we are replacing the package, so a PACKAGE_ADDED will be sent 2055 // later, we will update the package at this time 2056 } else { 2057 if (!replacing) { 2058 if (LauncherModel.DEBUG_LOADERS) { 2059 d(LauncherModel.LOG_TAG, " --> add package"); 2060 } 2061 sModel.addPackage(Launcher.this, packageName); 2062 } else { 2063 if (LauncherModel.DEBUG_LOADERS) { 2064 d(LauncherModel.LOG_TAG, " --> update package " + packageName); 2065 } 2066 sModel.updatePackage(Launcher.this, packageName); 2067 updateShortcutsForPackage(packageName); 2068 } 2069 } 2070 removeDialog(DIALOG_CREATE_SHORTCUT); 2071 } else { 2072 if (LauncherModel.DEBUG_LOADERS) { 2073 d(LauncherModel.LOG_TAG, " --> sync package " + packageName); 2074 } 2075 sModel.syncPackage(Launcher.this, packageName); 2076 } 2077 } 2078 } 2079 2080 /** 2081 * Receives notifications whenever the user favorites have changed. 2082 */ 2083 private class FavoritesChangeObserver extends ContentObserver { 2084 public FavoritesChangeObserver() { 2085 super(new Handler()); 2086 } 2087 2088 @Override 2089 public void onChange(boolean selfChange) { 2090 onFavoritesChanged(); 2091 } 2092 } 2093 2094 private static class DesktopBinder extends Handler implements MessageQueue.IdleHandler { 2095 static final int MESSAGE_BIND_ITEMS = 0x1; 2096 static final int MESSAGE_BIND_APPWIDGETS = 0x2; 2097 static final int MESSAGE_BIND_DRAWER = 0x3; 2098 2099 // Number of items to bind in every pass 2100 static final int ITEMS_COUNT = 6; 2101 2102 private final ArrayList<ItemInfo> mShortcuts; 2103 private final LinkedList<LauncherAppWidgetInfo> mAppWidgets; 2104 private final ApplicationsAdapter mDrawerAdapter; 2105 private final WeakReference<Launcher> mLauncher; 2106 2107 public boolean mTerminate = false; 2108 2109 DesktopBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts, 2110 ArrayList<LauncherAppWidgetInfo> appWidgets, 2111 ApplicationsAdapter drawerAdapter) { 2112 2113 mLauncher = new WeakReference<Launcher>(launcher); 2114 mShortcuts = shortcuts; 2115 mDrawerAdapter = drawerAdapter; 2116 2117 // Sort widgets so active workspace is bound first 2118 final int currentScreen = launcher.mWorkspace.getCurrentScreen(); 2119 final int size = appWidgets.size(); 2120 mAppWidgets = new LinkedList<LauncherAppWidgetInfo>(); 2121 2122 for (int i = 0; i < size; i++) { 2123 LauncherAppWidgetInfo appWidgetInfo = appWidgets.get(i); 2124 if (appWidgetInfo.screen == currentScreen) { 2125 mAppWidgets.addFirst(appWidgetInfo); 2126 } else { 2127 mAppWidgets.addLast(appWidgetInfo); 2128 } 2129 } 2130 } 2131 2132 public void startBindingItems() { 2133 obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget(); 2134 } 2135 2136 public void startBindingDrawer() { 2137 obtainMessage(MESSAGE_BIND_DRAWER).sendToTarget(); 2138 } 2139 2140 public void startBindingAppWidgetsWhenIdle() { 2141 // Ask for notification when message queue becomes idle 2142 final MessageQueue messageQueue = Looper.myQueue(); 2143 messageQueue.addIdleHandler(this); 2144 } 2145 2146 public boolean queueIdle() { 2147 // Queue is idle, so start binding items 2148 startBindingAppWidgets(); 2149 return false; 2150 } 2151 2152 public void startBindingAppWidgets() { 2153 obtainMessage(MESSAGE_BIND_APPWIDGETS).sendToTarget(); 2154 } 2155 2156 @Override 2157 public void handleMessage(Message msg) { 2158 Launcher launcher = mLauncher.get(); 2159 if (launcher == null || mTerminate) { 2160 return; 2161 } 2162 2163 switch (msg.what) { 2164 case MESSAGE_BIND_ITEMS: { 2165 launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2); 2166 break; 2167 } 2168 case MESSAGE_BIND_DRAWER: { 2169 launcher.bindDrawer(this, mDrawerAdapter); 2170 break; 2171 } 2172 case MESSAGE_BIND_APPWIDGETS: { 2173 launcher.bindAppWidgets(this, mAppWidgets); 2174 break; 2175 } 2176 } 2177 } 2178 } 2179} 2180