Launcher.java revision e77c08d15f23c403293dbb40c6a36967de822c89
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.ImageView; 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 static final int DIALOG_ALL_APPS = 1; 127 static final int DIALOG_CREATE_SHORTCUT = 2; 128 static final int DIALOG_RENAME_FOLDER = 3; 129 130 private static final String PREFERENCES = "launcher.preferences"; 131 132 // Type: int 133 private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; 134 // Type: boolean 135 private static final String RUNTIME_STATE_ALL_APPS_FOLDER = "launcher.all_apps_folder"; 136 // Type: long 137 private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder"; 138 // Type: int 139 private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen"; 140 // Type: int 141 private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX"; 142 // Type: int 143 private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY"; 144 // Type: int 145 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX"; 146 // Type: int 147 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY"; 148 // Type: int 149 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX"; 150 // Type: int 151 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY"; 152 // Type: int[] 153 private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells"; 154 // Type: boolean 155 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder"; 156 // Type: long 157 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id"; 158 159 private static final LauncherModel sModel = new LauncherModel(); 160 161 private static Bitmap sWallpaper; 162 163 private static final Object sLock = new Object(); 164 private static int sScreen = DEFAULT_SCREN; 165 166 private static WallpaperIntentReceiver sWallpaperReceiver; 167 168 private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver(); 169 private final ContentObserver mObserver = new FavoritesChangeObserver(); 170 171 private LayoutInflater mInflater; 172 173 private DragLayer mDragLayer; 174 private WallpaperView mWallpaperView; 175 private Workspace mWorkspace; 176 177 private AppWidgetManager mAppWidgetManager; 178 private LauncherAppWidgetHost mAppWidgetHost; 179 180 static final int APPWIDGET_HOST_ID = 1024; 181 182 private CellLayout.CellInfo mAddItemCellInfo; 183 private CellLayout.CellInfo mMenuAddInfo; 184 private final int[] mCellCoordinates = new int[2]; 185 private FolderInfo mFolderInfo; 186 187 private AllAppsDialog mAllAppsDialog; 188 private TransitionDrawable mHandleIcon; 189 private HandleView mHandleView; 190 private AllAppsGridView mAllAppsGrid; // TODO: put this into AllAppsDialog 191 192 private boolean mDesktopLocked = true; 193 private Bundle mSavedState; 194 195 private SpannableStringBuilder mDefaultKeySsb = null; 196 197 private boolean mDestroyed; 198 199 private boolean mIsNewIntent; 200 201 private boolean mRestoring; 202 private boolean mWaitingForResult; 203 private boolean mLocaleChanged; 204 205 private boolean mHomeDown; 206 private boolean mBackDown; 207 208 private Bundle mSavedInstanceState; 209 210 private DesktopBinder mBinder; 211 212 @Override 213 protected void onCreate(Bundle savedInstanceState) { 214 super.onCreate(savedInstanceState); 215 mInflater = getLayoutInflater(); 216 217 mAppWidgetManager = AppWidgetManager.getInstance(this); 218 219 mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); 220 mAppWidgetHost.startListening(); 221 222 if (PROFILE_STARTUP) { 223 android.os.Debug.startMethodTracing("/sdcard/launcher"); 224 } 225 226 checkForLocaleChange(); 227 setWallpaperDimension(); 228 229 setContentView(R.layout.launcher); 230 setupViews(); 231 232 mAllAppsDialog = new AllAppsDialog(this); 233 mAllAppsDialog.lock(); 234 235 registerIntentReceivers(); 236 registerContentObservers(); 237 238 mSavedState = savedInstanceState; 239 restoreState(mSavedState); 240 241 if (PROFILE_STARTUP) { 242 android.os.Debug.stopMethodTracing(); 243 } 244 245 if (!mRestoring) { 246 startLoaders(); 247 } 248 249 // For handling default keys 250 mDefaultKeySsb = new SpannableStringBuilder(); 251 Selection.setSelection(mDefaultKeySsb, 0); 252 } 253 254 private void checkForLocaleChange() { 255 final LocaleConfiguration localeConfiguration = new LocaleConfiguration(); 256 readConfiguration(this, localeConfiguration); 257 258 final Configuration configuration = getResources().getConfiguration(); 259 260 final String previousLocale = localeConfiguration.locale; 261 final String locale = configuration.locale.toString(); 262 263 final int previousMcc = localeConfiguration.mcc; 264 final int mcc = configuration.mcc; 265 266 final int previousMnc = localeConfiguration.mnc; 267 final int mnc = configuration.mnc; 268 269 mLocaleChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc; 270 271 if (mLocaleChanged) { 272 localeConfiguration.locale = locale; 273 localeConfiguration.mcc = mcc; 274 localeConfiguration.mnc = mnc; 275 276 writeConfiguration(this, localeConfiguration); 277 } 278 } 279 280 private static class LocaleConfiguration { 281 public String locale; 282 public int mcc = -1; 283 public int mnc = -1; 284 } 285 286 private static void readConfiguration(Context context, LocaleConfiguration configuration) { 287 DataInputStream in = null; 288 try { 289 in = new DataInputStream(context.openFileInput(PREFERENCES)); 290 configuration.locale = in.readUTF(); 291 configuration.mcc = in.readInt(); 292 configuration.mnc = in.readInt(); 293 } catch (FileNotFoundException e) { 294 // Ignore 295 } catch (IOException e) { 296 // Ignore 297 } finally { 298 if (in != null) { 299 try { 300 in.close(); 301 } catch (IOException e) { 302 // Ignore 303 } 304 } 305 } 306 } 307 308 private static void writeConfiguration(Context context, LocaleConfiguration configuration) { 309 DataOutputStream out = null; 310 try { 311 out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE)); 312 out.writeUTF(configuration.locale); 313 out.writeInt(configuration.mcc); 314 out.writeInt(configuration.mnc); 315 out.flush(); 316 } catch (FileNotFoundException e) { 317 // Ignore 318 } catch (IOException e) { 319 //noinspection ResultOfMethodCallIgnored 320 context.getFileStreamPath(PREFERENCES).delete(); 321 } finally { 322 if (out != null) { 323 try { 324 out.close(); 325 } catch (IOException e) { 326 // Ignore 327 } 328 } 329 } 330 } 331 332 static int getScreen() { 333 synchronized (sLock) { 334 return sScreen; 335 } 336 } 337 338 static void setScreen(int screen) { 339 synchronized (sLock) { 340 sScreen = screen; 341 } 342 } 343 344 private void startLoaders() { 345 boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged); 346 sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications); 347 348 mRestoring = false; 349 } 350 351 private void setWallpaperDimension() { 352 IBinder binder = ServiceManager.getService(WALLPAPER_SERVICE); 353 IWallpaperService wallpaperService = IWallpaperService.Stub.asInterface(binder); 354 355 Display display = getWindowManager().getDefaultDisplay(); 356 boolean isPortrait = display.getWidth() < display.getHeight(); 357 358 final int width = isPortrait ? display.getWidth() : display.getHeight(); 359 final int height = isPortrait ? display.getHeight() : display.getWidth(); 360 try { 361 wallpaperService.setDimensionHints(width * WALLPAPER_SCREENS_SPAN, height); 362 } catch (RemoteException e) { 363 // System is dead! 364 } 365 } 366 367 @Override 368 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 369 mWaitingForResult = false; 370 371 // The pattern used here is that a user PICKs a specific application, 372 // which, depending on the target, might need to CREATE the actual target. 373 374 // For example, the user would PICK_SHORTCUT for "Music playlist", and we 375 // launch over to the Music app to actually CREATE_SHORTCUT. 376 377 if (resultCode == RESULT_OK && mAddItemCellInfo != null) { 378 switch (requestCode) { 379 case REQUEST_PICK_APPLICATION: 380 completeAddApplication(this, data, mAddItemCellInfo, !mDesktopLocked); 381 break; 382 case REQUEST_PICK_SHORTCUT: 383 processShortcut(data, REQUEST_PICK_APPLICATION, REQUEST_CREATE_SHORTCUT); 384 break; 385 case REQUEST_CREATE_SHORTCUT: 386 completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked); 387 break; 388 case REQUEST_PICK_LIVE_FOLDER: 389 addLiveFolder(data); 390 break; 391 case REQUEST_CREATE_LIVE_FOLDER: 392 completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked); 393 break; 394 case REQUEST_PICK_APPWIDGET: 395 addAppWidget(data); 396 break; 397 case REQUEST_CREATE_APPWIDGET: 398 completeAddAppWidget(data, mAddItemCellInfo, !mDesktopLocked); 399 break; 400 } 401 } else if (requestCode == REQUEST_PICK_APPWIDGET && 402 resultCode == RESULT_CANCELED && data != null) { 403 // Clean up the appWidgetId if we canceled 404 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 405 if (appWidgetId != -1) { 406 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 407 } 408 } 409 } 410 411 @Override 412 protected void onResume() { 413 super.onResume(); 414 415 if (mRestoring) { 416 startLoaders(); 417 } 418 419 // If this was a new intent (i.e., the mIsNewIntent flag got set to true by 420 // onNewIntent), then close the search dialog if needed, because it probably 421 // came from the user pressing 'home' (rather than, for example, pressing 'back'). 422 if (mIsNewIntent) { 423 // Post to a handler so that this happens after the search dialog tries to open 424 // itself again. 425 mWorkspace.post(new Runnable() { 426 public void run() { 427 ISearchManager searchManagerService = ISearchManager.Stub.asInterface( 428 ServiceManager.getService(Context.SEARCH_SERVICE)); 429 try { 430 searchManagerService.stopSearch(); 431 } catch (RemoteException e) { 432 e(LOG_TAG, "error stopping search", e); 433 } 434 } 435 }); 436 } 437 438 mIsNewIntent = false; 439 } 440 441 @Override 442 protected void onPause() { 443 super.onPause(); 444 closeAllAppsDialog(false); 445 } 446 447 @Override 448 public Object onRetainNonConfigurationInstance() { 449 // Flag any binder to stop early before switching 450 if (mBinder != null) { 451 mBinder.mTerminate = true; 452 } 453 454 if (PROFILE_ROTATE) { 455 android.os.Debug.startMethodTracing("/sdcard/launcher-rotate"); 456 } 457 return null; 458 } 459 460 private boolean acceptFilter() { 461 final InputMethodManager inputManager = (InputMethodManager) 462 getSystemService(Context.INPUT_METHOD_SERVICE); 463 return !inputManager.isFullscreenMode(); 464 } 465 466 @Override 467 public boolean onKeyDown(int keyCode, KeyEvent event) { 468 boolean handled = super.onKeyDown(keyCode, event); 469 if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) { 470 boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb, 471 keyCode, event); 472 if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) { 473 // something usable has been typed - start a search 474 // the typed text will be retrieved and cleared by 475 // showSearchDialog() 476 // If there are multiple keystrokes before the search dialog takes focus, 477 // onSearchRequested() will be called for every keystroke, 478 // but it is idempotent, so it's fine. 479 return onSearchRequested(); 480 } 481 } 482 483 return handled; 484 } 485 486 private String getTypedText() { 487 return mDefaultKeySsb.toString(); 488 } 489 490 private void clearTypedText() { 491 mDefaultKeySsb.clear(); 492 mDefaultKeySsb.clearSpans(); 493 Selection.setSelection(mDefaultKeySsb, 0); 494 } 495 496 /** 497 * Restores the previous state, if it exists. 498 * 499 * @param savedState The previous state. 500 */ 501 private void restoreState(Bundle savedState) { 502 if (savedState == null) { 503 return; 504 } 505 506 final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1); 507 if (currentScreen > -1) { 508 mWorkspace.setCurrentScreen(currentScreen); 509 } 510 511 final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1); 512 if (addScreen > -1) { 513 mAddItemCellInfo = new CellLayout.CellInfo(); 514 final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo; 515 addItemCellInfo.valid = true; 516 addItemCellInfo.screen = addScreen; 517 addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X); 518 addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y); 519 addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X); 520 addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y); 521 addItemCellInfo.findVacantCellsFromOccupied( 522 savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS), 523 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X), 524 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y)); 525 mRestoring = true; 526 } 527 528 boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false); 529 if (renameFolder) { 530 long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID); 531 mFolderInfo = sModel.getFolderById(this, id); 532 mRestoring = true; 533 } 534 } 535 536 /** 537 * Finds all the views we need and configure them properly. 538 */ 539 private void setupViews() { 540 mDragLayer = (DragLayer) findViewById(R.id.drag_layer); 541 final DragLayer dragLayer = mDragLayer; 542 543 mWallpaperView = (WallpaperView) dragLayer.findViewById(R.id.wallpaper); 544 final WallpaperView wallpaper = mWallpaperView; 545 546 mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace); 547 final Workspace workspace = mWorkspace; 548 549 final DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone); 550 551 mHandleView = (HandleView) findViewById(R.id.all_apps); 552 mHandleView.setLauncher(this); 553 mHandleView.setOnClickListener(this); 554 mHandleIcon = (TransitionDrawable) mHandleView.getDrawable(); 555 mHandleIcon.setCrossFadeEnabled(true); 556 557 workspace.setOnLongClickListener(this); 558 workspace.setDragger(dragLayer); 559 workspace.setLauncher(this); 560 workspace.setWallpaper(wallpaper); 561 562 loadWallpaper(); 563 wallpaper.setScreenCount(workspace.getScreenCount()); 564 565 deleteZone.setLauncher(this); 566 deleteZone.setDragController(dragLayer); 567 deleteZone.setHandle(mHandleView); 568 569 // TODO dragLayer.setIgnoredDropTarget(grid); 570 dragLayer.setDragScoller(workspace); 571 dragLayer.setDragListener(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 mAllAppsGrid.clearTextFilter(); 907 mAllAppsGrid.setAdapter(null); 908 sModel.unbind(); 909 sModel.abortLoaders(); 910 911 getContentResolver().unregisterContentObserver(mObserver); 912 unregisterReceiver(mApplicationsReceiver); 913 } 914 915 @Override 916 public void startActivityForResult(Intent intent, int requestCode) { 917 if (requestCode >= 0) mWaitingForResult = true; 918 super.startActivityForResult(intent, requestCode); 919 } 920 921 @Override 922 public void startSearch(String initialQuery, boolean selectInitialQuery, 923 Bundle appSearchData, boolean globalSearch) { 924 925 closeAllAppsDialog(false); 926 927 // Slide the search widget to the top, if it's on the current screen, 928 // otherwise show the search dialog immediately. 929 Search searchWidget = mWorkspace.findSearchWidgetOnCurrentScreen(); 930 if (searchWidget == null) { 931 showSearchDialog(initialQuery, selectInitialQuery, appSearchData, globalSearch); 932 } else { 933 searchWidget.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); 934 // show the currently typed text in the search widget while sliding 935 searchWidget.setQuery(getTypedText()); 936 } 937 } 938 939 /** 940 * Show the search dialog immediately, without changing the search widget. 941 * 942 * @see Activity#startSearch(String, boolean, android.os.Bundle, boolean) 943 */ 944 void showSearchDialog(String initialQuery, boolean selectInitialQuery, 945 Bundle appSearchData, boolean globalSearch) { 946 947 if (initialQuery == null) { 948 // Use any text typed in the launcher as the initial query 949 initialQuery = getTypedText(); 950 clearTypedText(); 951 } 952 if (appSearchData == null) { 953 appSearchData = new Bundle(); 954 appSearchData.putString(SearchManager.SOURCE, "launcher-search"); 955 } 956 957 final SearchManager searchManager = 958 (SearchManager) getSystemService(Context.SEARCH_SERVICE); 959 960 final Search searchWidget = mWorkspace.findSearchWidgetOnCurrentScreen(); 961 if (searchWidget != null) { 962 // This gets called when the user leaves the search dialog to go back to 963 // the Launcher. 964 searchManager.setOnCancelListener(new SearchManager.OnCancelListener() { 965 public void onCancel() { 966 searchManager.setOnCancelListener(null); 967 stopSearch(); 968 } 969 }); 970 } 971 972 searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), 973 appSearchData, globalSearch); 974 } 975 976 /** 977 * Cancel search dialog if it is open. 978 */ 979 void stopSearch() { 980 // Close search dialog 981 SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); 982 searchManager.stopSearch(); 983 // Restore search widget to its normal position 984 Search searchWidget = mWorkspace.findSearchWidgetOnCurrentScreen(); 985 if (searchWidget != null) { 986 searchWidget.stopSearch(false); 987 } 988 } 989 990 @Override 991 public boolean onCreateOptionsMenu(Menu menu) { 992 if (mDesktopLocked) return false; 993 994 super.onCreateOptionsMenu(menu); 995 menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add) 996 .setIcon(android.R.drawable.ic_menu_add) 997 .setAlphabeticShortcut('A'); 998 menu.add(0, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper) 999 .setIcon(android.R.drawable.ic_menu_gallery) 1000 .setAlphabeticShortcut('W'); 1001 menu.add(0, MENU_SEARCH, 0, R.string.menu_search) 1002 .setIcon(android.R.drawable.ic_search_category_default) 1003 .setAlphabeticShortcut(SearchManager.MENU_KEY); 1004 menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications) 1005 .setIcon(com.android.internal.R.drawable.ic_menu_notifications) 1006 .setAlphabeticShortcut('N'); 1007 1008 final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS); 1009 settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 1010 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 1011 1012 menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings) 1013 .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P') 1014 .setIntent(settings); 1015 1016 return true; 1017 } 1018 1019 @Override 1020 public boolean onPrepareOptionsMenu(Menu menu) { 1021 super.onPrepareOptionsMenu(menu); 1022 1023 mMenuAddInfo = mWorkspace.findAllVacantCells(null); 1024 menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid); 1025 1026 return true; 1027 } 1028 1029 @Override 1030 public boolean onOptionsItemSelected(MenuItem item) { 1031 switch (item.getItemId()) { 1032 case MENU_ADD: 1033 addItems(); 1034 return true; 1035 case MENU_WALLPAPER_SETTINGS: 1036 startWallpaper(); 1037 return true; 1038 case MENU_SEARCH: 1039 onSearchRequested(); 1040 return true; 1041 case MENU_NOTIFICATIONS: 1042 showNotifications(); 1043 return true; 1044 } 1045 1046 return super.onOptionsItemSelected(item); 1047 } 1048 1049 /** 1050 * Indicates that we want global search for this activity by setting the globalSearch 1051 * argument for {@link #startSearch} to true. 1052 */ 1053 1054 @Override 1055 public boolean onSearchRequested() { 1056 startSearch(null, false, null, true); 1057 return true; 1058 } 1059 1060 private void addItems() { 1061 showAddDialog(mMenuAddInfo); 1062 } 1063 1064 private void removeShortcutsForPackage(String packageName) { 1065 if (packageName != null && packageName.length() > 0) { 1066 mWorkspace.removeShortcutsForPackage(packageName); 1067 } 1068 } 1069 1070 private void updateShortcutsForPackage(String packageName) { 1071 if (packageName != null && packageName.length() > 0) { 1072 mWorkspace.updateShortcutsForPackage(packageName); 1073 } 1074 } 1075 1076 void addAppWidget(Intent data) { 1077 // TODO: catch bad widget exception when sent 1078 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 1079 1080 String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET); 1081 if (SEARCH_WIDGET.equals(customWidget)) { 1082 // We don't need this any more, since this isn't a real app widget. 1083 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 1084 // add the search widget 1085 addSearch(); 1086 } else { 1087 AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 1088 1089 if (appWidget.configure != null) { 1090 // Launch over to configure widget, if needed 1091 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); 1092 intent.setComponent(appWidget.configure); 1093 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 1094 1095 startActivityForResult(intent, REQUEST_CREATE_APPWIDGET); 1096 } else { 1097 // Otherwise just add it 1098 onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data); 1099 } 1100 } 1101 } 1102 1103 void addSearch() { 1104 final Widget info = Widget.makeSearch(); 1105 final CellLayout.CellInfo cellInfo = mAddItemCellInfo; 1106 1107 final int[] xy = mCellCoordinates; 1108 final int spanX = info.spanX; 1109 final int spanY = info.spanY; 1110 1111 if (!findSlot(cellInfo, xy, spanX, spanY)) return; 1112 1113 sModel.addDesktopItem(info); 1114 LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, 1115 mWorkspace.getCurrentScreen(), xy[0], xy[1], false); 1116 1117 final View view = mInflater.inflate(info.layoutResource, null); 1118 view.setTag(info); 1119 Search search = (Search) view.findViewById(R.id.widget_search); 1120 search.setLauncher(this); 1121 1122 mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY); 1123 } 1124 1125 void processShortcut(Intent intent, int requestCodeApplication, int requestCodeShortcut) { 1126 // Handle case where user selected "Applications" 1127 String applicationName = getResources().getString(R.string.group_applications); 1128 String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 1129 1130 if (applicationName != null && applicationName.equals(shortcutName)) { 1131 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 1132 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1133 1134 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 1135 pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent); 1136 startActivityForResult(pickIntent, requestCodeApplication); 1137 } else { 1138 startActivityForResult(intent, requestCodeShortcut); 1139 } 1140 } 1141 1142 void addLiveFolder(Intent intent) { 1143 // Handle case where user selected "Folder" 1144 String folderName = getResources().getString(R.string.group_folder); 1145 String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 1146 1147 if (folderName != null && folderName.equals(shortcutName)) { 1148 addFolder(!mDesktopLocked); 1149 } else { 1150 startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER); 1151 } 1152 } 1153 1154 void addFolder(boolean insertAtFirst) { 1155 UserFolderInfo folderInfo = new UserFolderInfo(); 1156 folderInfo.title = getText(R.string.folder_name); 1157 1158 CellLayout.CellInfo cellInfo = mAddItemCellInfo; 1159 cellInfo.screen = mWorkspace.getCurrentScreen(); 1160 if (!findSingleSlot(cellInfo)) return; 1161 1162 // Update the model 1163 LauncherModel.addItemToDatabase(this, folderInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, 1164 mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false); 1165 sModel.addDesktopItem(folderInfo); 1166 sModel.addFolder(folderInfo); 1167 1168 // Create the view 1169 FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, 1170 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo); 1171 mWorkspace.addInCurrentScreen(newFolder, 1172 cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst); 1173 } 1174 1175 private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo, 1176 boolean insertAtFirst) { 1177 cellInfo.screen = mWorkspace.getCurrentScreen(); 1178 if (!findSingleSlot(cellInfo)) return; 1179 1180 final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false); 1181 1182 if (!mRestoring) { 1183 sModel.addDesktopItem(info); 1184 1185 final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this, 1186 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info); 1187 mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst); 1188 } else if (sModel.isDesktopLoaded()) { 1189 sModel.addDesktopItem(info); 1190 } 1191 } 1192 1193 static LiveFolderInfo addLiveFolder(Context context, Intent data, 1194 CellLayout.CellInfo cellInfo, boolean notify) { 1195 1196 Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT); 1197 String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME); 1198 1199 Drawable icon = null; 1200 boolean filtered = false; 1201 Intent.ShortcutIconResource iconResource = null; 1202 1203 Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON); 1204 if (extra != null && extra instanceof Intent.ShortcutIconResource) { 1205 try { 1206 iconResource = (Intent.ShortcutIconResource) extra; 1207 final PackageManager packageManager = context.getPackageManager(); 1208 Resources resources = packageManager.getResourcesForApplication( 1209 iconResource.packageName); 1210 final int id = resources.getIdentifier(iconResource.resourceName, null, null); 1211 icon = resources.getDrawable(id); 1212 } catch (Exception e) { 1213 w(LOG_TAG, "Could not load live folder icon: " + extra); 1214 } 1215 } 1216 1217 if (icon == null) { 1218 icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder); 1219 } 1220 1221 final LiveFolderInfo info = new LiveFolderInfo(); 1222 info.icon = icon; 1223 info.filtered = filtered; 1224 info.title = name; 1225 info.iconResource = iconResource; 1226 info.uri = data.getData(); 1227 info.baseIntent = baseIntent; 1228 info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, 1229 LiveFolders.DISPLAY_MODE_GRID); 1230 1231 LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, 1232 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify); 1233 sModel.addFolder(info); 1234 1235 return info; 1236 } 1237 1238 private boolean findSingleSlot(CellLayout.CellInfo cellInfo) { 1239 final int[] xy = new int[2]; 1240 if (findSlot(cellInfo, xy, 1, 1)) { 1241 cellInfo.cellX = xy[0]; 1242 cellInfo.cellY = xy[1]; 1243 return true; 1244 } 1245 return false; 1246 } 1247 1248 private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) { 1249 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) { 1250 boolean[] occupied = mSavedState != null ? 1251 mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null; 1252 cellInfo = mWorkspace.findAllVacantCells(occupied); 1253 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) { 1254 Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); 1255 return false; 1256 } 1257 } 1258 return true; 1259 } 1260 1261 private void showNotifications() { 1262 final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE); 1263 if (statusBar != null) { 1264 statusBar.expand(); 1265 } 1266 } 1267 1268 private void startWallpaper() { 1269 final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); 1270 startActivity(Intent.createChooser(pickWallpaper, getString(R.string.chooser_wallpaper))); 1271 } 1272 1273 /** 1274 * Registers various intent receivers. The current implementation registers 1275 * only a wallpaper intent receiver to let other applications change the 1276 * wallpaper. 1277 */ 1278 private void registerIntentReceivers() { 1279 if (sWallpaperReceiver == null) { 1280 final Application application = getApplication(); 1281 1282 sWallpaperReceiver = new WallpaperIntentReceiver(application, this); 1283 1284 IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); 1285 application.registerReceiver(sWallpaperReceiver, filter); 1286 } else { 1287 sWallpaperReceiver.setLauncher(this); 1288 } 1289 1290 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 1291 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1292 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1293 filter.addDataScheme("package"); 1294 registerReceiver(mApplicationsReceiver, filter); 1295 } 1296 1297 /** 1298 * Registers various content observers. The current implementation registers 1299 * only a favorites observer to keep track of the favorites applications. 1300 */ 1301 private void registerContentObservers() { 1302 ContentResolver resolver = getContentResolver(); 1303 resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mObserver); 1304 } 1305 1306 @Override 1307 public void onWindowFocusChanged(boolean hasFocus) { 1308 super.onWindowFocusChanged(hasFocus); 1309 if (!hasFocus) { 1310 mBackDown = mHomeDown = false; 1311 } 1312 } 1313 1314 @Override 1315 public boolean dispatchKeyEvent(KeyEvent event) { 1316 if (event.getAction() == KeyEvent.ACTION_DOWN) { 1317 switch (event.getKeyCode()) { 1318 case KeyEvent.KEYCODE_BACK: 1319 mBackDown = true; 1320 return true; 1321 case KeyEvent.KEYCODE_HOME: 1322 mHomeDown = true; 1323 return true; 1324 } 1325 } else if (event.getAction() == KeyEvent.ACTION_UP) { 1326 switch (event.getKeyCode()) { 1327 case KeyEvent.KEYCODE_BACK: 1328 if (!event.isCanceled()) { 1329 mWorkspace.dispatchKeyEvent(event); 1330 if (mAllAppsDialog.isOpen) { 1331 closeAllAppsDialog(true); 1332 } else { 1333 closeFolder(); 1334 } 1335 } 1336 mBackDown = false; 1337 return true; 1338 case KeyEvent.KEYCODE_HOME: 1339 mHomeDown = false; 1340 return true; 1341 } 1342 } 1343 1344 return super.dispatchKeyEvent(event); 1345 } 1346 1347 private void closeFolder() { 1348 Folder folder = mWorkspace.getOpenFolder(); 1349 if (folder != null) { 1350 closeFolder(folder); 1351 } 1352 } 1353 1354 void closeFolder(Folder folder) { 1355 folder.getInfo().opened = false; 1356 ViewGroup parent = (ViewGroup) folder.getParent(); 1357 if (parent != null) { 1358 parent.removeView(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 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 DragController getDragController() { 1558 return mDragLayer; 1559 } 1560 1561 /** 1562 * Launches the intent referred by the clicked shortcut. 1563 * 1564 * @param v The view representing the clicked shortcut. 1565 */ 1566 public void onClick(View v) { 1567 Object tag = v.getTag(); 1568 if (tag instanceof ApplicationInfo) { 1569 // Open shortcut 1570 final Intent intent = ((ApplicationInfo) tag).intent; 1571 startActivitySafely(intent); 1572 } else if (tag instanceof FolderInfo) { 1573 handleFolderClick((FolderInfo) tag); 1574 } else if (v == mHandleView) { 1575 if (mAllAppsDialog.isOpen) { 1576 // TODO how can we be here? 1577 } else { 1578 showAllAppsDialog(); 1579 } 1580 } 1581 } 1582 1583 void startActivitySafely(Intent intent) { 1584 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1585 try { 1586 startActivity(intent); 1587 } catch (ActivityNotFoundException e) { 1588 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 1589 } catch (SecurityException e) { 1590 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 1591 e(LOG_TAG, "Launcher does not have the permission to launch " + intent + 1592 ". Make sure to create a MAIN intent-filter for the corresponding activity " + 1593 "or use the exported attribute for this activity.", e); 1594 } 1595 } 1596 1597 private void handleFolderClick(FolderInfo folderInfo) { 1598 if (!folderInfo.opened) { 1599 // Close any open folder 1600 closeFolder(); 1601 // Open the requested folder 1602 openFolder(folderInfo); 1603 } else { 1604 // Find the open folder... 1605 Folder openFolder = mWorkspace.getFolderForTag(folderInfo); 1606 int folderScreen; 1607 if (openFolder != null) { 1608 folderScreen = mWorkspace.getScreenForView(openFolder); 1609 // .. and close it 1610 closeFolder(openFolder); 1611 if (folderScreen != mWorkspace.getCurrentScreen()) { 1612 // Close any folder open on the current screen 1613 closeFolder(); 1614 // Pull the folder onto this screen 1615 openFolder(folderInfo); 1616 } 1617 } 1618 } 1619 } 1620 1621 private void loadWallpaper() { 1622 // The first time the application is started, we load the wallpaper from 1623 // the ApplicationContext 1624 if (sWallpaper == null) { 1625 final Drawable drawable; 1626 if (false) { 1627 drawable = getWallpaper(); 1628 } else { 1629 drawable = getResources().getDrawable(R.drawable.wallpaper_path); 1630 } 1631 if (drawable instanceof BitmapDrawable) { 1632 sWallpaper = ((BitmapDrawable) drawable).getBitmap(); 1633 } else { 1634 throw new IllegalStateException("The wallpaper must be a BitmapDrawable."); 1635 } 1636 } 1637 mWallpaperView.loadWallpaper(sWallpaper); 1638 } 1639 1640 /** 1641 * Opens the user fodler described by the specified tag. The opening of the folder 1642 * is animated relative to the specified View. If the View is null, no animation 1643 * is played. 1644 * 1645 * @param folderInfo The FolderInfo describing the folder to open. 1646 */ 1647 private void openFolder(FolderInfo folderInfo) { 1648 Folder openFolder; 1649 1650 if (folderInfo instanceof UserFolderInfo) { 1651 openFolder = UserFolder.fromXml(this); 1652 } else if (folderInfo instanceof LiveFolderInfo) { 1653 openFolder = com.android.launcher2.LiveFolder.fromXml(this, folderInfo); 1654 } else { 1655 return; 1656 } 1657 1658 openFolder.setDragger(mDragLayer); 1659 openFolder.setLauncher(this); 1660 1661 openFolder.bind(folderInfo); 1662 folderInfo.opened = true; 1663 1664 mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4); 1665 openFolder.onOpen(); 1666 } 1667 1668 /** 1669 * Returns true if the workspace is being loaded. When the workspace is loading, 1670 * no user interaction should be allowed to avoid any conflict. 1671 * 1672 * @return True if the workspace is locked, false otherwise. 1673 */ 1674 boolean isWorkspaceLocked() { 1675 return mDesktopLocked; 1676 } 1677 1678 public boolean onLongClick(View v) { 1679 if (mDesktopLocked) { 1680 return false; 1681 } 1682 1683 if (!(v instanceof CellLayout)) { 1684 v = (View) v.getParent(); 1685 } 1686 1687 CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag(); 1688 1689 // This happens when long clicking an item with the dpad/trackball 1690 if (cellInfo == null) { 1691 return true; 1692 } 1693 1694 if (mWorkspace.allowLongPress()) { 1695 if (cellInfo.cell == null) { 1696 if (cellInfo.valid) { 1697 // User long pressed on empty space 1698 mWorkspace.setAllowLongPress(false); 1699 showAddDialog(cellInfo); 1700 } 1701 } else { 1702 if (!(cellInfo.cell instanceof Folder)) { 1703 // User long pressed on an item 1704 mWorkspace.startDrag(cellInfo); 1705 } 1706 } 1707 } 1708 return true; 1709 } 1710 1711 static LauncherModel getModel() { 1712 return sModel; 1713 } 1714 1715 void closeAllApplications() { 1716 // TODO mDrawer.close(); 1717 mAllAppsDialog.dismiss(); 1718 } 1719 1720 View getDrawerHandle() { 1721 return mHandleView; 1722 } 1723 1724 boolean isDrawerDown() { 1725 return /* TODO !mDrawer.isMoving() && */ !mAllAppsDialog.isOpen; 1726 } 1727 1728 Workspace getWorkspace() { 1729 return mWorkspace; 1730 } 1731 1732 /* TODO 1733 GridView getApplicationsGrid() { 1734 return mAllAppsGrid; 1735 } 1736 */ 1737 1738 @Override 1739 protected Dialog onCreateDialog(int id) { 1740 switch (id) { 1741 case DIALOG_ALL_APPS: 1742 return mAllAppsDialog; 1743 case DIALOG_CREATE_SHORTCUT: 1744 return new CreateShortcut().createDialog(); 1745 case DIALOG_RENAME_FOLDER: 1746 return new RenameFolder().createDialog(); 1747 } 1748 1749 return super.onCreateDialog(id); 1750 } 1751 1752 @Override 1753 protected void onPrepareDialog(int id, Dialog dialog) { 1754 switch (id) { 1755 case DIALOG_ALL_APPS: 1756 break; 1757 case DIALOG_CREATE_SHORTCUT: 1758 break; 1759 case DIALOG_RENAME_FOLDER: 1760 if (mFolderInfo != null) { 1761 EditText input = (EditText) dialog.findViewById(R.id.folder_name); 1762 final CharSequence text = mFolderInfo.title; 1763 input.setText(text); 1764 input.setSelection(0, text.length()); 1765 } 1766 break; 1767 } 1768 } 1769 1770 void showRenameDialog(FolderInfo info) { 1771 mFolderInfo = info; 1772 mWaitingForResult = true; 1773 showDialog(DIALOG_RENAME_FOLDER); 1774 } 1775 1776 private void showAddDialog(CellLayout.CellInfo cellInfo) { 1777 mAddItemCellInfo = cellInfo; 1778 mWaitingForResult = true; 1779 showDialog(DIALOG_CREATE_SHORTCUT); 1780 } 1781 1782 private void pickShortcut(int requestCode, int title) { 1783 Bundle bundle = new Bundle(); 1784 1785 ArrayList<String> shortcutNames = new ArrayList<String>(); 1786 shortcutNames.add(getString(R.string.group_applications)); 1787 bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames); 1788 1789 ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>(); 1790 shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this, 1791 R.drawable.ic_launcher_application)); 1792 bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons); 1793 1794 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 1795 pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT)); 1796 pickIntent.putExtra(Intent.EXTRA_TITLE, getText(title)); 1797 pickIntent.putExtras(bundle); 1798 1799 startActivityForResult(pickIntent, requestCode); 1800 } 1801 1802 private class RenameFolder { 1803 private EditText mInput; 1804 1805 Dialog createDialog() { 1806 mWaitingForResult = true; 1807 final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null); 1808 mInput = (EditText) layout.findViewById(R.id.folder_name); 1809 1810 AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this); 1811 builder.setIcon(0); 1812 builder.setTitle(getString(R.string.rename_folder_title)); 1813 builder.setCancelable(true); 1814 builder.setOnCancelListener(new Dialog.OnCancelListener() { 1815 public void onCancel(DialogInterface dialog) { 1816 cleanup(); 1817 } 1818 }); 1819 builder.setNegativeButton(getString(R.string.cancel_action), 1820 new Dialog.OnClickListener() { 1821 public void onClick(DialogInterface dialog, int which) { 1822 cleanup(); 1823 } 1824 } 1825 ); 1826 builder.setPositiveButton(getString(R.string.rename_action), 1827 new Dialog.OnClickListener() { 1828 public void onClick(DialogInterface dialog, int which) { 1829 changeFolderName(); 1830 } 1831 } 1832 ); 1833 builder.setView(layout); 1834 1835 final AlertDialog dialog = builder.create(); 1836 dialog.setOnShowListener(new DialogInterface.OnShowListener() { 1837 public void onShow(DialogInterface dialog) { 1838 mWorkspace.lock(); 1839 } 1840 }); 1841 1842 return dialog; 1843 } 1844 1845 private void changeFolderName() { 1846 final String name = mInput.getText().toString(); 1847 if (!TextUtils.isEmpty(name)) { 1848 // Make sure we have the right folder info 1849 mFolderInfo = sModel.findFolderById(mFolderInfo.id); 1850 mFolderInfo.title = name; 1851 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo); 1852 1853 if (mDesktopLocked) { 1854 mAllAppsDialog.lock(); 1855 sModel.loadUserItems(false, Launcher.this, false, false); 1856 } else { 1857 final FolderIcon folderIcon = (FolderIcon) 1858 mWorkspace.getViewForTag(mFolderInfo); 1859 if (folderIcon != null) { 1860 folderIcon.setText(name); 1861 getWorkspace().requestLayout(); 1862 } else { 1863 mDesktopLocked = true; 1864 mAllAppsDialog.lock(); 1865 sModel.loadUserItems(false, Launcher.this, false, false); 1866 } 1867 } 1868 } 1869 cleanup(); 1870 } 1871 1872 private void cleanup() { 1873 mWorkspace.unlock(); 1874 dismissDialog(DIALOG_RENAME_FOLDER); 1875 mWaitingForResult = false; 1876 mFolderInfo = null; 1877 } 1878 } 1879 1880 /** 1881 * Holds the 3d all apps view. 1882 */ 1883 private class AllAppsDialog extends Dialog implements DialogInterface.OnCancelListener, 1884 DialogInterface.OnDismissListener, DialogInterface.OnShowListener { 1885 1886 boolean isOpen; 1887 1888 AllAppsDialog(Context context) { 1889 super(context, android.R.style.Theme_Translucent_NoTitleBar); 1890 1891 setOnCancelListener(this); 1892 setOnDismissListener(this); 1893 setOnShowListener(this); 1894 1895 setContentView(R.layout.all_apps); 1896 AllAppsGridView grid = mAllAppsGrid = (AllAppsGridView)findViewById(R.id.all_apps); 1897 1898 grid.setTextFilterEnabled(false); 1899 // TODO grid.setDragger(dragLayer); 1900 grid.setLauncher(Launcher.this); 1901 } 1902 1903 public void onCancel(DialogInterface dialog) { 1904 onDestroy(); 1905 } 1906 1907 public void onDismiss(DialogInterface dialog) { 1908 onDestroy(); 1909 } 1910 1911 public void onShow(DialogInterface dialog) { 1912 } 1913 1914 private void onDestroy() { 1915 } 1916 1917 void lock() { 1918 // TODO 1919 } 1920 1921 void unlock() { 1922 // TODO 1923 } 1924 1925 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { 1926 switch (keyCode) { 1927 case KeyEvent.KEYCODE_BACK: 1928 closeAllAppsDialog(true); 1929 return true; 1930 default: 1931 return super.onKeyDown(keyCode, event); 1932 } 1933 } 1934 } 1935 1936 void showAllAppsDialog() { 1937 mAllAppsDialog.isOpen = true; 1938 showDialog(DIALOG_ALL_APPS); 1939 mWorkspace.hide(); 1940 } 1941 1942 private void closeAllAppsDialog(boolean animated) { 1943 if (mAllAppsDialog.isOpen) { 1944 if (animated) { 1945 // TODO mDrawer.animateClose(); 1946 mAllAppsDialog.dismiss(); 1947 } else { 1948 mAllAppsDialog.dismiss(); 1949 } 1950 mAllAppsDialog.isOpen = false; 1951 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); 1952 mWorkspace.show(); 1953 } 1954 } 1955 1956 /** 1957 * Displays the shortcut creation dialog and launches, if necessary, the 1958 * appropriate activity. 1959 */ 1960 private class CreateShortcut implements DialogInterface.OnClickListener, 1961 DialogInterface.OnCancelListener, DialogInterface.OnDismissListener, 1962 DialogInterface.OnShowListener { 1963 1964 private AddAdapter mAdapter; 1965 1966 Dialog createDialog() { 1967 mWaitingForResult = true; 1968 1969 mAdapter = new AddAdapter(Launcher.this); 1970 1971 final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this); 1972 builder.setTitle(getString(R.string.menu_item_add_item)); 1973 builder.setAdapter(mAdapter, this); 1974 1975 builder.setInverseBackgroundForced(true); 1976 1977 AlertDialog dialog = builder.create(); 1978 dialog.setOnCancelListener(this); 1979 dialog.setOnDismissListener(this); 1980 dialog.setOnShowListener(this); 1981 1982 return dialog; 1983 } 1984 1985 public void onCancel(DialogInterface dialog) { 1986 mWaitingForResult = false; 1987 cleanup(); 1988 } 1989 1990 public void onDismiss(DialogInterface dialog) { 1991 mWorkspace.unlock(); 1992 } 1993 1994 private void cleanup() { 1995 mWorkspace.unlock(); 1996 dismissDialog(DIALOG_CREATE_SHORTCUT); 1997 } 1998 1999 /** 2000 * Handle the action clicked in the "Add to home" dialog. 2001 */ 2002 public void onClick(DialogInterface dialog, int which) { 2003 Resources res = getResources(); 2004 cleanup(); 2005 2006 switch (which) { 2007 case AddAdapter.ITEM_SHORTCUT: { 2008 // Insert extra item to handle picking application 2009 pickShortcut(REQUEST_PICK_SHORTCUT, R.string.title_select_shortcut); 2010 break; 2011 } 2012 2013 case AddAdapter.ITEM_APPWIDGET: { 2014 int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId(); 2015 2016 Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK); 2017 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 2018 // add the search widget 2019 ArrayList<AppWidgetProviderInfo> customInfo = 2020 new ArrayList<AppWidgetProviderInfo>(); 2021 AppWidgetProviderInfo info = new AppWidgetProviderInfo(); 2022 info.provider = new ComponentName(getPackageName(), "XXX.YYY"); 2023 info.label = getString(R.string.group_search); 2024 info.icon = R.drawable.ic_search_widget; 2025 customInfo.add(info); 2026 pickIntent.putParcelableArrayListExtra( 2027 AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo); 2028 ArrayList<Bundle> customExtras = new ArrayList<Bundle>(); 2029 Bundle b = new Bundle(); 2030 b.putString(EXTRA_CUSTOM_WIDGET, SEARCH_WIDGET); 2031 customExtras.add(b); 2032 pickIntent.putParcelableArrayListExtra( 2033 AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras); 2034 // start the pick activity 2035 startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET); 2036 break; 2037 } 2038 2039 case AddAdapter.ITEM_LIVE_FOLDER: { 2040 // Insert extra item to handle inserting folder 2041 Bundle bundle = new Bundle(); 2042 2043 ArrayList<String> shortcutNames = new ArrayList<String>(); 2044 shortcutNames.add(res.getString(R.string.group_folder)); 2045 bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames); 2046 2047 ArrayList<ShortcutIconResource> shortcutIcons = 2048 new ArrayList<ShortcutIconResource>(); 2049 shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this, 2050 R.drawable.ic_launcher_folder)); 2051 bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons); 2052 2053 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 2054 pickIntent.putExtra(Intent.EXTRA_INTENT, 2055 new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER)); 2056 pickIntent.putExtra(Intent.EXTRA_TITLE, 2057 getText(R.string.title_select_live_folder)); 2058 pickIntent.putExtras(bundle); 2059 2060 startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER); 2061 break; 2062 } 2063 2064 case AddAdapter.ITEM_WALLPAPER: { 2065 startWallpaper(); 2066 break; 2067 } 2068 } 2069 } 2070 2071 public void onShow(DialogInterface dialog) { 2072 mWorkspace.lock(); 2073 } 2074 } 2075 2076 /** 2077 * Receives notifications when applications are added/removed. 2078 */ 2079 private class ApplicationsIntentReceiver extends BroadcastReceiver { 2080 @Override 2081 public void onReceive(Context context, Intent intent) { 2082 final String action = intent.getAction(); 2083 final String packageName = intent.getData().getSchemeSpecificPart(); 2084 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 2085 2086 if (LauncherModel.DEBUG_LOADERS) { 2087 d(LauncherModel.LOG_TAG, "application intent received: " + action + 2088 ", replacing=" + replacing); 2089 d(LauncherModel.LOG_TAG, " --> " + intent.getData()); 2090 } 2091 2092 if (!Intent.ACTION_PACKAGE_CHANGED.equals(action)) { 2093 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 2094 if (!replacing) { 2095 removeShortcutsForPackage(packageName); 2096 if (LauncherModel.DEBUG_LOADERS) { 2097 d(LauncherModel.LOG_TAG, " --> remove package"); 2098 } 2099 sModel.removePackage(Launcher.this, packageName); 2100 } 2101 // else, we are replacing the package, so a PACKAGE_ADDED will be sent 2102 // later, we will update the package at this time 2103 } else { 2104 if (!replacing) { 2105 if (LauncherModel.DEBUG_LOADERS) { 2106 d(LauncherModel.LOG_TAG, " --> add package"); 2107 } 2108 sModel.addPackage(Launcher.this, packageName); 2109 } else { 2110 if (LauncherModel.DEBUG_LOADERS) { 2111 d(LauncherModel.LOG_TAG, " --> update package " + packageName); 2112 } 2113 sModel.updatePackage(Launcher.this, packageName); 2114 updateShortcutsForPackage(packageName); 2115 } 2116 } 2117 removeDialog(DIALOG_CREATE_SHORTCUT); 2118 } else { 2119 if (LauncherModel.DEBUG_LOADERS) { 2120 d(LauncherModel.LOG_TAG, " --> sync package " + packageName); 2121 } 2122 sModel.syncPackage(Launcher.this, packageName); 2123 } 2124 } 2125 } 2126 2127 /** 2128 * Receives notifications whenever the user favorites have changed. 2129 */ 2130 private class FavoritesChangeObserver extends ContentObserver { 2131 public FavoritesChangeObserver() { 2132 super(new Handler()); 2133 } 2134 2135 @Override 2136 public void onChange(boolean selfChange) { 2137 onFavoritesChanged(); 2138 } 2139 } 2140 2141 /** 2142 * Receives intents from other applications to change the wallpaper. 2143 */ 2144 private static class WallpaperIntentReceiver extends BroadcastReceiver { 2145 private final Application mApplication; 2146 private WeakReference<Launcher> mLauncher; 2147 2148 WallpaperIntentReceiver(Application application, Launcher launcher) { 2149 mApplication = application; 2150 setLauncher(launcher); 2151 } 2152 2153 void setLauncher(Launcher launcher) { 2154 mLauncher = new WeakReference<Launcher>(launcher); 2155 } 2156 2157 @Override 2158 public void onReceive(Context context, Intent intent) { 2159 // Load the wallpaper from the ApplicationContext and store it locally 2160 // until the Launcher Activity is ready to use it 2161 final Drawable drawable = mApplication.getWallpaper(); 2162 if (drawable instanceof BitmapDrawable) { 2163 sWallpaper = ((BitmapDrawable) drawable).getBitmap(); 2164 } else { 2165 throw new IllegalStateException("The wallpaper must be a BitmapDrawable."); 2166 } 2167 2168 // If Launcher is alive, notify we have a new wallpaper 2169 if (mLauncher != null) { 2170 final Launcher launcher = mLauncher.get(); 2171 if (launcher != null) { 2172 launcher.loadWallpaper(); 2173 } 2174 } 2175 } 2176 } 2177 2178 private static class DesktopBinder extends Handler implements MessageQueue.IdleHandler { 2179 static final int MESSAGE_BIND_ITEMS = 0x1; 2180 static final int MESSAGE_BIND_APPWIDGETS = 0x2; 2181 static final int MESSAGE_BIND_DRAWER = 0x3; 2182 2183 // Number of items to bind in every pass 2184 static final int ITEMS_COUNT = 6; 2185 2186 private final ArrayList<ItemInfo> mShortcuts; 2187 private final LinkedList<LauncherAppWidgetInfo> mAppWidgets; 2188 private final ApplicationsAdapter mDrawerAdapter; 2189 private final WeakReference<Launcher> mLauncher; 2190 2191 public boolean mTerminate = false; 2192 2193 DesktopBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts, 2194 ArrayList<LauncherAppWidgetInfo> appWidgets, 2195 ApplicationsAdapter drawerAdapter) { 2196 2197 mLauncher = new WeakReference<Launcher>(launcher); 2198 mShortcuts = shortcuts; 2199 mDrawerAdapter = drawerAdapter; 2200 2201 // Sort widgets so active workspace is bound first 2202 final int currentScreen = launcher.mWorkspace.getCurrentScreen(); 2203 final int size = appWidgets.size(); 2204 mAppWidgets = new LinkedList<LauncherAppWidgetInfo>(); 2205 2206 for (int i = 0; i < size; i++) { 2207 LauncherAppWidgetInfo appWidgetInfo = appWidgets.get(i); 2208 if (appWidgetInfo.screen == currentScreen) { 2209 mAppWidgets.addFirst(appWidgetInfo); 2210 } else { 2211 mAppWidgets.addLast(appWidgetInfo); 2212 } 2213 } 2214 } 2215 2216 public void startBindingItems() { 2217 obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget(); 2218 } 2219 2220 public void startBindingDrawer() { 2221 obtainMessage(MESSAGE_BIND_DRAWER).sendToTarget(); 2222 } 2223 2224 public void startBindingAppWidgetsWhenIdle() { 2225 // Ask for notification when message queue becomes idle 2226 final MessageQueue messageQueue = Looper.myQueue(); 2227 messageQueue.addIdleHandler(this); 2228 } 2229 2230 public boolean queueIdle() { 2231 // Queue is idle, so start binding items 2232 startBindingAppWidgets(); 2233 return false; 2234 } 2235 2236 public void startBindingAppWidgets() { 2237 obtainMessage(MESSAGE_BIND_APPWIDGETS).sendToTarget(); 2238 } 2239 2240 @Override 2241 public void handleMessage(Message msg) { 2242 Launcher launcher = mLauncher.get(); 2243 if (launcher == null || mTerminate) { 2244 return; 2245 } 2246 2247 switch (msg.what) { 2248 case MESSAGE_BIND_ITEMS: { 2249 launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2); 2250 break; 2251 } 2252 case MESSAGE_BIND_DRAWER: { 2253 launcher.bindDrawer(this, mDrawerAdapter); 2254 break; 2255 } 2256 case MESSAGE_BIND_APPWIDGETS: { 2257 launcher.bindAppWidgets(this, mAppWidgets); 2258 break; 2259 } 2260 } 2261 } 2262 } 2263} 2264