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