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