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