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