Launcher.java revision 4bb4f735c80ba6baca6678f6508b3862da61313a
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 com.android.common.Search; 20import com.android.launcher.R; 21 22import android.animation.Animatable; 23import android.animation.AnimatableListenerAdapter; 24import android.animation.Animator; 25import android.animation.PropertyAnimator; 26import android.animation.Sequencer; 27import android.app.Activity; 28import android.app.AlertDialog; 29import android.app.Dialog; 30import android.app.SearchManager; 31import android.app.StatusBarManager; 32import android.app.WallpaperManager; 33import android.appwidget.AppWidgetManager; 34import android.appwidget.AppWidgetProviderInfo; 35import android.content.ActivityNotFoundException; 36import android.content.BroadcastReceiver; 37import android.content.ComponentName; 38import android.content.ContentResolver; 39import android.content.Context; 40import android.content.DialogInterface; 41import android.content.Intent; 42import android.content.Intent.ShortcutIconResource; 43import android.content.IntentFilter; 44import android.content.pm.ActivityInfo; 45import android.content.pm.PackageManager; 46import android.content.pm.ResolveInfo; 47import android.content.res.Configuration; 48import android.content.res.Resources; 49import android.content.res.TypedArray; 50import android.database.ContentObserver; 51import android.graphics.Bitmap; 52import android.graphics.Canvas; 53import android.graphics.Rect; 54import android.graphics.drawable.ColorDrawable; 55import android.graphics.drawable.Drawable; 56import android.net.Uri; 57import android.os.Bundle; 58import android.os.Handler; 59import android.os.Parcelable; 60import android.os.SystemClock; 61import android.os.SystemProperties; 62import android.provider.LiveFolders; 63import android.text.Selection; 64import android.text.SpannableStringBuilder; 65import android.text.TextUtils; 66import android.text.method.TextKeyListener; 67import android.util.Log; 68import android.view.Display; 69import android.view.HapticFeedbackConstants; 70import android.view.KeyEvent; 71import android.view.LayoutInflater; 72import android.view.Menu; 73import android.view.MenuItem; 74import android.view.MotionEvent; 75import android.view.View; 76import android.view.View.OnLongClickListener; 77import android.view.ViewGroup; 78import android.view.WindowManager; 79import android.view.animation.Animation; 80import android.view.animation.AnimationUtils; 81import android.view.animation.DecelerateInterpolator; 82import android.view.animation.Interpolator; 83import android.view.inputmethod.InputMethodManager; 84import android.widget.EditText; 85import android.widget.ImageView; 86import android.widget.LinearLayout; 87import android.widget.PopupWindow; 88import android.widget.TabHost; 89import android.widget.TextView; 90import android.widget.Toast; 91 92import java.io.DataInputStream; 93import java.io.DataOutputStream; 94import java.io.FileNotFoundException; 95import java.io.IOException; 96import java.util.ArrayList; 97import java.util.HashMap; 98import java.util.List; 99 100/** 101 * Default launcher application. 102 */ 103public final class Launcher extends Activity 104 implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, 105 AllAppsView.Watcher, View.OnTouchListener { 106 static final String TAG = "Launcher"; 107 static final boolean LOGD = false; 108 109 static final boolean PROFILE_STARTUP = false; 110 static final boolean DEBUG_WIDGETS = false; 111 static final boolean DEBUG_USER_INTERFACE = false; 112 113 private static final int WALLPAPER_SCREENS_SPAN = 2; 114 115 private static final int MENU_GROUP_ADD = 1; 116 private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1; 117 118 private static final int MENU_ADD = Menu.FIRST + 1; 119 private static final int MENU_WALLPAPER_SETTINGS = MENU_ADD + 1; 120 private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1; 121 private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1; 122 private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1; 123 124 private static final int REQUEST_CREATE_SHORTCUT = 1; 125 private static final int REQUEST_CREATE_LIVE_FOLDER = 4; 126 private static final int REQUEST_CREATE_APPWIDGET = 5; 127 private static final int REQUEST_PICK_APPLICATION = 6; 128 private static final int REQUEST_PICK_SHORTCUT = 7; 129 private static final int REQUEST_PICK_LIVE_FOLDER = 8; 130 private static final int REQUEST_PICK_APPWIDGET = 9; 131 private static final int REQUEST_PICK_WALLPAPER = 10; 132 133 static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate"; 134 135 static final int SCREEN_COUNT = 5; 136 static final int DEFAULT_SCREEN = 2; 137 138 static final int DIALOG_CREATE_SHORTCUT = 1; 139 static final int DIALOG_RENAME_FOLDER = 2; 140 141 private static final String PREFERENCES = "launcher.preferences"; 142 143 // Type: int 144 private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; 145 // Type: boolean 146 private static final String RUNTIME_STATE_ALL_APPS_FOLDER = "launcher.all_apps_folder"; 147 // Type: long 148 private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder"; 149 // Type: int 150 private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen"; 151 // Type: int 152 private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX"; 153 // Type: int 154 private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY"; 155 // Type: int 156 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX"; 157 // Type: int 158 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY"; 159 // Type: int 160 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX"; 161 // Type: int 162 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY"; 163 // Type: int[] 164 private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells"; 165 // Type: boolean 166 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder"; 167 // Type: long 168 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id"; 169 170 static final int APPWIDGET_HOST_ID = 1024; 171 172 private static final Object sLock = new Object(); 173 private static int sScreen = DEFAULT_SCREEN; 174 175 private final BroadcastReceiver mCloseSystemDialogsReceiver 176 = new CloseSystemDialogsIntentReceiver(); 177 private final ContentObserver mWidgetObserver = new AppWidgetResetObserver(); 178 179 private LayoutInflater mInflater; 180 181 private DragController mDragController; 182 private Workspace mWorkspace; 183 184 private AppWidgetManager mAppWidgetManager; 185 private LauncherAppWidgetHost mAppWidgetHost; 186 187 private CellLayout.CellInfo mAddItemCellInfo; 188 private CellLayout.CellInfo mMenuAddInfo; 189 private final int[] mCellCoordinates = new int[2]; 190 private FolderInfo mFolderInfo; 191 192 private DeleteZone mDeleteZone; 193 private HandleView mHandleView; 194 private AllAppsView mAllAppsGrid; 195 private TabHost mHomeCustomizationDrawer; 196 197 private Bundle mSavedState; 198 199 private SpannableStringBuilder mDefaultKeySsb = null; 200 201 private boolean mWorkspaceLoading = true; 202 203 private boolean mPaused = true; 204 private boolean mRestoring; 205 private boolean mWaitingForResult; 206 207 private Bundle mSavedInstanceState; 208 209 private LauncherModel mModel; 210 private IconCache mIconCache; 211 212 private ArrayList<ItemInfo> mDesktopItems = new ArrayList<ItemInfo>(); 213 private static HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>(); 214 215 private ImageView mPreviousView; 216 private ImageView mNextView; 217 218 // Hotseats (quick-launch icons next to AllApps) 219 private static final int NUM_HOTSEATS = 2; 220 private String[] mHotseatConfig = null; 221 private Intent[] mHotseats = null; 222 private Drawable[] mHotseatIcons = null; 223 private CharSequence[] mHotseatLabels = null; 224 225 @Override 226 protected void onCreate(Bundle savedInstanceState) { 227 super.onCreate(savedInstanceState); 228 229 if (LauncherApplication.isInPlaceRotationEnabled()) { 230 // hide the status bar (temporary until we get the status bar design figured out) 231 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 232 this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); 233 } 234 235 LauncherApplication app = ((LauncherApplication)getApplication()); 236 mModel = app.setLauncher(this); 237 mIconCache = app.getIconCache(); 238 mDragController = new DragController(this); 239 mInflater = getLayoutInflater(); 240 241 mAppWidgetManager = AppWidgetManager.getInstance(this); 242 mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); 243 mAppWidgetHost.startListening(); 244 245 if (PROFILE_STARTUP) { 246 android.os.Debug.startMethodTracing("/sdcard/launcher"); 247 } 248 249 loadHotseats(); 250 checkForLocaleChange(); 251 setWallpaperDimension(); 252 setContentView(R.layout.launcher); 253 mHomeCustomizationDrawer = (TabHost) findViewById(com.android.internal.R.id.tabhost); 254 if (mHomeCustomizationDrawer != null) { 255 mHomeCustomizationDrawer.setup(); 256 257 String widgetsLabel = getString(R.string.widgets_tab_label); 258 mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("widgets") 259 .setIndicator(widgetsLabel).setContent(R.id.widget_chooser)); 260 String foldersLabel = getString(R.string.folders_tab_label); 261 mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("folders") 262 .setIndicator(foldersLabel).setContent(R.id.folder_chooser)); 263 String shortcutsLabel = getString(R.string.shortcuts_tab_label); 264 mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("shortcuts") 265 .setIndicator(shortcutsLabel).setContent(R.id.shortcut_chooser)); 266 String wallpapersLabel = getString(R.string.wallpapers_tab_label); 267 mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("wallpapers") 268 .setIndicator(wallpapersLabel).setContent(R.id.wallpaperstab)); 269 270 mHomeCustomizationDrawer.setCurrentTab(0); 271 } 272 setupViews(); 273 274 registerContentObservers(); 275 276 lockAllApps(); 277 278 mSavedState = savedInstanceState; 279 restoreState(mSavedState); 280 281 if (PROFILE_STARTUP) { 282 android.os.Debug.stopMethodTracing(); 283 } 284 285 if (!mRestoring) { 286 mModel.startLoader(this, true); 287 } 288 289 // For handling default keys 290 mDefaultKeySsb = new SpannableStringBuilder(); 291 Selection.setSelection(mDefaultKeySsb, 0); 292 293 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 294 registerReceiver(mCloseSystemDialogsReceiver, filter); 295 } 296 297 @Override 298 public void onConfigurationChanged(Configuration newConfig) { 299 // TODO Auto-generated method stub 300 super.onConfigurationChanged(newConfig); 301 } 302 303 304 private void checkForLocaleChange() { 305 final LocaleConfiguration localeConfiguration = new LocaleConfiguration(); 306 readConfiguration(this, localeConfiguration); 307 308 final Configuration configuration = getResources().getConfiguration(); 309 310 final String previousLocale = localeConfiguration.locale; 311 final String locale = configuration.locale.toString(); 312 313 final int previousMcc = localeConfiguration.mcc; 314 final int mcc = configuration.mcc; 315 316 final int previousMnc = localeConfiguration.mnc; 317 final int mnc = configuration.mnc; 318 319 boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc; 320 321 if (localeChanged) { 322 localeConfiguration.locale = locale; 323 localeConfiguration.mcc = mcc; 324 localeConfiguration.mnc = mnc; 325 326 writeConfiguration(this, localeConfiguration); 327 mIconCache.flush(); 328 329 loadHotseats(); 330 } 331 } 332 333 private static class LocaleConfiguration { 334 public String locale; 335 public int mcc = -1; 336 public int mnc = -1; 337 } 338 339 private static void readConfiguration(Context context, LocaleConfiguration configuration) { 340 DataInputStream in = null; 341 try { 342 in = new DataInputStream(context.openFileInput(PREFERENCES)); 343 configuration.locale = in.readUTF(); 344 configuration.mcc = in.readInt(); 345 configuration.mnc = in.readInt(); 346 } catch (FileNotFoundException e) { 347 // Ignore 348 } catch (IOException e) { 349 // Ignore 350 } finally { 351 if (in != null) { 352 try { 353 in.close(); 354 } catch (IOException e) { 355 // Ignore 356 } 357 } 358 } 359 } 360 361 private static void writeConfiguration(Context context, LocaleConfiguration configuration) { 362 DataOutputStream out = null; 363 try { 364 out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE)); 365 out.writeUTF(configuration.locale); 366 out.writeInt(configuration.mcc); 367 out.writeInt(configuration.mnc); 368 out.flush(); 369 } catch (FileNotFoundException e) { 370 // Ignore 371 } catch (IOException e) { 372 //noinspection ResultOfMethodCallIgnored 373 context.getFileStreamPath(PREFERENCES).delete(); 374 } finally { 375 if (out != null) { 376 try { 377 out.close(); 378 } catch (IOException e) { 379 // Ignore 380 } 381 } 382 } 383 } 384 385 static int getScreen() { 386 synchronized (sLock) { 387 return sScreen; 388 } 389 } 390 391 static void setScreen(int screen) { 392 synchronized (sLock) { 393 sScreen = screen; 394 } 395 } 396 397 private void setWallpaperDimension() { 398 WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE); 399 400 Display display = getWindowManager().getDefaultDisplay(); 401 boolean isPortrait = display.getWidth() < display.getHeight(); 402 403 final int width = isPortrait ? display.getWidth() : display.getHeight(); 404 final int height = isPortrait ? display.getHeight() : display.getWidth(); 405 wpm.suggestDesiredDimensions(width * WALLPAPER_SCREENS_SPAN, height); 406 } 407 408 // Note: This doesn't do all the client-id magic that BrowserProvider does 409 // in Browser. (http://b/2425179) 410 private Uri getDefaultBrowserUri() { 411 String url = getString(R.string.default_browser_url); 412 if (url.indexOf("{CID}") != -1) { 413 url = url.replace("{CID}", "android-google"); 414 } 415 return Uri.parse(url); 416 } 417 418 // Load the Intent templates from arrays.xml to populate the hotseats. For 419 // each Intent, if it resolves to a single app, use that as the launch 420 // intent & use that app's label as the contentDescription. Otherwise, 421 // retain the ResolveActivity so the user can pick an app. 422 private void loadHotseats() { 423 if (mHotseatConfig == null) { 424 mHotseatConfig = getResources().getStringArray(R.array.hotseats); 425 if (mHotseatConfig.length > 0) { 426 mHotseats = new Intent[mHotseatConfig.length]; 427 mHotseatLabels = new CharSequence[mHotseatConfig.length]; 428 mHotseatIcons = new Drawable[mHotseatConfig.length]; 429 } else { 430 mHotseats = null; 431 mHotseatIcons = null; 432 mHotseatLabels = null; 433 } 434 435 TypedArray hotseatIconDrawables = getResources().obtainTypedArray(R.array.hotseat_icons); 436 for (int i=0; i<mHotseatConfig.length; i++) { 437 // load icon for this slot; currently unrelated to the actual activity 438 try { 439 mHotseatIcons[i] = hotseatIconDrawables.getDrawable(i); 440 } catch (ArrayIndexOutOfBoundsException ex) { 441 Log.w(TAG, "Missing hotseat_icons array item #" + i); 442 mHotseatIcons[i] = null; 443 } 444 } 445 hotseatIconDrawables.recycle(); 446 } 447 448 PackageManager pm = getPackageManager(); 449 for (int i=0; i<mHotseatConfig.length; i++) { 450 Intent intent = null; 451 if (mHotseatConfig[i].equals("*BROWSER*")) { 452 // magic value meaning "launch user's default web browser" 453 // replace it with a generic web request so we can see if there is indeed a default 454 String defaultUri = getString(R.string.default_browser_url); 455 intent = new Intent( 456 Intent.ACTION_VIEW, 457 ((defaultUri != null) 458 ? Uri.parse(defaultUri) 459 : getDefaultBrowserUri()) 460 ).addCategory(Intent.CATEGORY_BROWSABLE); 461 // note: if the user launches this without a default set, she 462 // will always be taken to the default URL above; this is 463 // unavoidable as we must specify a valid URL in order for the 464 // chooser to appear, and once the user selects something, that 465 // URL is unavoidably sent to the chosen app. 466 } else { 467 try { 468 intent = Intent.parseUri(mHotseatConfig[i], 0); 469 } catch (java.net.URISyntaxException ex) { 470 Log.w(TAG, "Invalid hotseat intent: " + mHotseatConfig[i]); 471 // bogus; leave intent=null 472 } 473 } 474 475 if (intent == null) { 476 mHotseats[i] = null; 477 mHotseatLabels[i] = getText(R.string.activity_not_found); 478 continue; 479 } 480 481 if (LOGD) { 482 Log.d(TAG, "loadHotseats: hotseat " + i 483 + " initial intent=[" 484 + intent.toUri(Intent.URI_INTENT_SCHEME) 485 + "]"); 486 } 487 488 ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); 489 List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); 490 if (LOGD) { 491 Log.d(TAG, "Best match for intent: " + bestMatch); 492 Log.d(TAG, "All matches: "); 493 for (ResolveInfo ri : allMatches) { 494 Log.d(TAG, " --> " + ri); 495 } 496 } 497 // did this resolve to a single app, or the resolver? 498 if (allMatches.size() == 0 || bestMatch == null) { 499 // can't find any activity to handle this. let's leave the 500 // intent as-is and let Launcher show a toast when it fails 501 // to launch. 502 mHotseats[i] = intent; 503 504 // set accessibility text to "Not installed" 505 mHotseatLabels[i] = getText(R.string.activity_not_found); 506 } else { 507 boolean found = false; 508 for (ResolveInfo ri : allMatches) { 509 if (bestMatch.activityInfo.name.equals(ri.activityInfo.name) 510 && bestMatch.activityInfo.applicationInfo.packageName 511 .equals(ri.activityInfo.applicationInfo.packageName)) { 512 found = true; 513 break; 514 } 515 } 516 517 if (!found) { 518 if (LOGD) Log.d(TAG, "Multiple options, no default yet"); 519 // the bestMatch is probably the ResolveActivity, meaning the 520 // user has not yet selected a default 521 // so: we'll keep the original intent for now 522 mHotseats[i] = intent; 523 524 // set the accessibility text to "Select shortcut" 525 mHotseatLabels[i] = getText(R.string.title_select_shortcut); 526 } else { 527 // we have an app! 528 // now reconstruct the intent to launch it through the front 529 // door 530 ComponentName com = new ComponentName( 531 bestMatch.activityInfo.applicationInfo.packageName, 532 bestMatch.activityInfo.name); 533 mHotseats[i] = new Intent(Intent.ACTION_MAIN).setComponent(com); 534 535 // load the app label for accessibility 536 mHotseatLabels[i] = bestMatch.activityInfo.loadLabel(pm); 537 } 538 } 539 540 if (LOGD) { 541 Log.d(TAG, "loadHotseats: hotseat " + i 542 + " final intent=[" 543 + ((mHotseats[i] == null) 544 ? "null" 545 : mHotseats[i].toUri(Intent.URI_INTENT_SCHEME)) 546 + "] label=[" + mHotseatLabels[i] 547 + "]" 548 ); 549 } 550 } 551 } 552 553 @Override 554 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 555 mWaitingForResult = false; 556 557 // The pattern used here is that a user PICKs a specific application, 558 // which, depending on the target, might need to CREATE the actual target. 559 560 // For example, the user would PICK_SHORTCUT for "Music playlist", and we 561 // launch over to the Music app to actually CREATE_SHORTCUT. 562 563 if (resultCode == RESULT_OK && mAddItemCellInfo != null) { 564 switch (requestCode) { 565 case REQUEST_PICK_APPLICATION: 566 completeAddApplication(this, data, mAddItemCellInfo); 567 break; 568 case REQUEST_PICK_SHORTCUT: 569 processShortcut(data); 570 break; 571 case REQUEST_CREATE_SHORTCUT: 572 completeAddShortcut(data, mAddItemCellInfo); 573 break; 574 case REQUEST_PICK_LIVE_FOLDER: 575 addLiveFolder(data); 576 break; 577 case REQUEST_CREATE_LIVE_FOLDER: 578 completeAddLiveFolder(data, mAddItemCellInfo); 579 break; 580 case REQUEST_PICK_APPWIDGET: 581 addAppWidgetFromPick(data); 582 break; 583 case REQUEST_CREATE_APPWIDGET: 584 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 585 completeAddAppWidget(appWidgetId, mAddItemCellInfo); 586 break; 587 case REQUEST_PICK_WALLPAPER: 588 // We just wanted the activity result here so we can clear mWaitingForResult 589 break; 590 } 591 } else if ((requestCode == REQUEST_PICK_APPWIDGET || 592 requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED && 593 data != null) { 594 // Clean up the appWidgetId if we canceled 595 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 596 if (appWidgetId != -1) { 597 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 598 } 599 } 600 } 601 602 @Override 603 protected void onResume() { 604 super.onResume(); 605 606 mPaused = false; 607 608 if (mRestoring) { 609 mWorkspaceLoading = true; 610 mModel.startLoader(this, true); 611 mRestoring = false; 612 } 613 } 614 615 @Override 616 protected void onPause() { 617 super.onPause(); 618 // Some launcher layouts don't have a previous and next view 619 if (mPreviousView != null) { 620 dismissPreview(mPreviousView); 621 } 622 if (mNextView != null) { 623 dismissPreview(mNextView); 624 } 625 mDragController.cancelDrag(); 626 } 627 628 @Override 629 public Object onRetainNonConfigurationInstance() { 630 // Flag the loader to stop early before switching 631 mModel.stopLoader(); 632 mAllAppsGrid.surrender(); 633 return Boolean.TRUE; 634 } 635 636 // We can't hide the IME if it was forced open. So don't bother 637 /* 638 @Override 639 public void onWindowFocusChanged(boolean hasFocus) { 640 super.onWindowFocusChanged(hasFocus); 641 642 if (hasFocus) { 643 final InputMethodManager inputManager = (InputMethodManager) 644 getSystemService(Context.INPUT_METHOD_SERVICE); 645 WindowManager.LayoutParams lp = getWindow().getAttributes(); 646 inputManager.hideSoftInputFromWindow(lp.token, 0, new android.os.ResultReceiver(new 647 android.os.Handler()) { 648 protected void onReceiveResult(int resultCode, Bundle resultData) { 649 Log.d(TAG, "ResultReceiver got resultCode=" + resultCode); 650 } 651 }); 652 Log.d(TAG, "called hideSoftInputFromWindow from onWindowFocusChanged"); 653 } 654 } 655 */ 656 657 private boolean acceptFilter() { 658 final InputMethodManager inputManager = (InputMethodManager) 659 getSystemService(Context.INPUT_METHOD_SERVICE); 660 return !inputManager.isFullscreenMode(); 661 } 662 663 @Override 664 public boolean onKeyDown(int keyCode, KeyEvent event) { 665 boolean handled = super.onKeyDown(keyCode, event); 666 if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) { 667 boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb, 668 keyCode, event); 669 if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) { 670 // something usable has been typed - start a search 671 // the typed text will be retrieved and cleared by 672 // showSearchDialog() 673 // If there are multiple keystrokes before the search dialog takes focus, 674 // onSearchRequested() will be called for every keystroke, 675 // but it is idempotent, so it's fine. 676 return onSearchRequested(); 677 } 678 } 679 680 // Eat the long press event so the keyboard doesn't come up. 681 if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) { 682 return true; 683 } 684 685 return handled; 686 } 687 688 private String getTypedText() { 689 return mDefaultKeySsb.toString(); 690 } 691 692 private void clearTypedText() { 693 mDefaultKeySsb.clear(); 694 mDefaultKeySsb.clearSpans(); 695 Selection.setSelection(mDefaultKeySsb, 0); 696 } 697 698 /** 699 * Restores the previous state, if it exists. 700 * 701 * @param savedState The previous state. 702 */ 703 private void restoreState(Bundle savedState) { 704 if (savedState == null) { 705 return; 706 } 707 708 final boolean allApps = savedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false); 709 if (allApps) { 710 showAllApps(false); 711 } 712 713 final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1); 714 if (currentScreen > -1) { 715 mWorkspace.setCurrentScreen(currentScreen); 716 } 717 718 final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1); 719 if (addScreen > -1) { 720 mAddItemCellInfo = new CellLayout.CellInfo(); 721 final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo; 722 addItemCellInfo.valid = true; 723 addItemCellInfo.screen = addScreen; 724 addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X); 725 addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y); 726 addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X); 727 addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y); 728 addItemCellInfo.findVacantCellsFromOccupied( 729 savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS), 730 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X), 731 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y)); 732 mRestoring = true; 733 } 734 735 boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false); 736 if (renameFolder) { 737 long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID); 738 mFolderInfo = mModel.getFolderById(this, mFolders, id); 739 mRestoring = true; 740 } 741 } 742 743 /** 744 * Finds all the views we need and configure them properly. 745 */ 746 private void setupViews() { 747 DragController dragController = mDragController; 748 749 DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer); 750 dragLayer.setDragController(dragController); 751 752 mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view); 753 mAllAppsGrid.setLauncher(this); 754 mAllAppsGrid.setDragController(dragController); 755 ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window. 756 // Manage focusability manually since this thing is always visible 757 ((View) mAllAppsGrid).setFocusable(false); 758 759 mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace); 760 final Workspace workspace = mWorkspace; 761 workspace.setHapticFeedbackEnabled(false); 762 763 DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone); 764 mDeleteZone = deleteZone; 765 766 View handleView = findViewById(R.id.all_apps_button); 767 if (handleView != null && handleView instanceof HandleView) { 768 // we don't use handle view in xlarge mode 769 mHandleView = (HandleView)handleView; 770 mHandleView.setLauncher(this); 771 mHandleView.setOnClickListener(this); 772 mHandleView.setOnLongClickListener(this); 773 } 774 775 WidgetChooser widgetChooser = (WidgetChooser) findViewById(R.id.widget_chooser); 776 if (widgetChooser != null) { 777 WidgetListAdapter widgetGalleryAdapter = new WidgetListAdapter(this); 778 widgetChooser.setAdapter(widgetGalleryAdapter); 779 widgetChooser.setDragController(dragController); 780 widgetChooser.setLauncher(this); 781 782 FolderChooser folderChooser = (FolderChooser) findViewById(R.id.folder_chooser); 783 IntentListAdapter folderTypes = new FolderListAdapter( 784 this, LiveFolders.ACTION_CREATE_LIVE_FOLDER); 785 folderChooser.setAdapter(folderTypes); 786 folderChooser.setLauncher(this); 787 788 ShortcutChooser shortcutChooser = (ShortcutChooser) findViewById(R.id.shortcut_chooser); 789 IntentListAdapter shortcutTypes = new IntentListAdapter( 790 this, Intent.ACTION_CREATE_SHORTCUT); 791 shortcutChooser.setAdapter(shortcutTypes); 792 shortcutChooser.setLauncher(this); 793 } else { 794 ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left); 795 hotseatLeft.setContentDescription(mHotseatLabels[0]); 796 hotseatLeft.setImageDrawable(mHotseatIcons[0]); 797 ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right); 798 hotseatRight.setContentDescription(mHotseatLabels[1]); 799 hotseatRight.setImageDrawable(mHotseatIcons[1]); 800 801 mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen); 802 mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen); 803 804 Drawable previous = mPreviousView.getDrawable(); 805 Drawable next = mNextView.getDrawable(); 806 mWorkspace.setIndicators(previous, next); 807 808 mPreviousView.setHapticFeedbackEnabled(false); 809 mPreviousView.setOnLongClickListener(this); 810 mNextView.setHapticFeedbackEnabled(false); 811 mNextView.setOnLongClickListener(this); 812 } 813 814 workspace.setOnLongClickListener(this); 815 workspace.setDragController(dragController); 816 workspace.setLauncher(this); 817 818 deleteZone.setLauncher(this); 819 deleteZone.setDragController(dragController); 820 int deleteZoneHandleId; 821 if (LauncherApplication.isScreenXLarge()) { 822 deleteZoneHandleId = R.id.configure_button; 823 } else { 824 deleteZoneHandleId = R.id.all_apps_button_cluster; 825 } 826 deleteZone.setHandle(findViewById(deleteZoneHandleId)); 827 828 dragController.setDragScoller(workspace); 829 dragController.setDragListener(deleteZone); 830 dragController.setScrollView(dragLayer); 831 dragController.setMoveTarget(workspace); 832 833 // The order here is bottom to top. 834 dragController.addDropTarget(workspace); 835 dragController.addDropTarget(deleteZone); 836 } 837 838 @SuppressWarnings({"UnusedDeclaration"}) 839 public void previousScreen(View v) { 840 if (!isAllAppsVisible()) { 841 mWorkspace.scrollLeft(); 842 } 843 } 844 845 @SuppressWarnings({"UnusedDeclaration"}) 846 public void nextScreen(View v) { 847 if (!isAllAppsVisible()) { 848 mWorkspace.scrollRight(); 849 } 850 } 851 852 @SuppressWarnings({"UnusedDeclaration"}) 853 public void launchHotSeat(View v) { 854 if (isAllAppsVisible()) return; 855 856 int index = -1; 857 if (v.getId() == R.id.hotseat_left) { 858 index = 0; 859 } else if (v.getId() == R.id.hotseat_right) { 860 index = 1; 861 } 862 863 // reload these every tap; you never know when they might change 864 loadHotseats(); 865 if (index >= 0 && index < mHotseats.length && mHotseats[index] != null) { 866 Intent intent = mHotseats[index]; 867 startActivitySafely( 868 mHotseats[index], 869 "hotseat" 870 ); 871 } 872 } 873 874 /** 875 * Creates a view representing a shortcut. 876 * 877 * @param info The data structure describing the shortcut. 878 * 879 * @return A View inflated from R.layout.application. 880 */ 881 View createShortcut(ShortcutInfo info) { 882 return createShortcut(R.layout.application, 883 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info); 884 } 885 886 /** 887 * Creates a view representing a shortcut inflated from the specified resource. 888 * 889 * @param layoutResId The id of the XML layout used to create the shortcut. 890 * @param parent The group the shortcut belongs to. 891 * @param info The data structure describing the shortcut. 892 * 893 * @return A View inflated from layoutResId. 894 */ 895 View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) { 896 TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false); 897 898 favorite.setCompoundDrawablesWithIntrinsicBounds(null, 899 new FastBitmapDrawable(info.getIcon(mIconCache)), 900 null, null); 901 favorite.setText(info.title); 902 favorite.setTag(info); 903 favorite.setOnClickListener(this); 904 905 return favorite; 906 } 907 908 /** 909 * Add an application shortcut to the workspace. 910 * 911 * @param data The intent describing the application. 912 * @param cellInfo The position on screen where to create the shortcut. 913 */ 914 void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) { 915 cellInfo.screen = mWorkspace.getCurrentScreen(); 916 if (!findSingleSlot(cellInfo)) return; 917 918 final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(), 919 data, context); 920 921 if (info != null) { 922 info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK | 923 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 924 info.container = ItemInfo.NO_ID; 925 mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked()); 926 } else { 927 Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data); 928 } 929 } 930 931 /** 932 * Add a shortcut to the workspace. 933 * 934 * @param data The intent describing the shortcut. 935 * @param cellInfo The position on screen where to create the shortcut. 936 */ 937 private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) { 938 cellInfo.screen = mWorkspace.getCurrentScreen(); 939 if (!findSingleSlot(cellInfo)) return; 940 941 final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false); 942 943 if (!mRestoring) { 944 final View view = createShortcut(info); 945 mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, 946 isWorkspaceLocked()); 947 } 948 } 949 950 951 /** 952 * Add a widget to the workspace. 953 * 954 * @param data The intent describing the appWidgetId. 955 * @param cellInfo The position on screen where to create the widget. 956 */ 957 private void completeAddAppWidget(int appWidgetId, CellLayout.CellInfo cellInfo) { 958 AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 959 960 // Calculate the grid spans needed to fit this widget 961 CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen); 962 int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight, null); 963 964 // Try finding open space on Launcher screen 965 final int[] xy = mCellCoordinates; 966 if (!findSlot(cellInfo, xy, spans[0], spans[1])) { 967 if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId); 968 return; 969 } 970 971 // Build Launcher-specific widget info and save to database 972 LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId); 973 launcherInfo.spanX = spans[0]; 974 launcherInfo.spanY = spans[1]; 975 976 LauncherModel.addItemToDatabase(this, launcherInfo, 977 LauncherSettings.Favorites.CONTAINER_DESKTOP, 978 mWorkspace.getCurrentScreen(), xy[0], xy[1], false); 979 980 if (!mRestoring) { 981 mDesktopItems.add(launcherInfo); 982 983 // Perform actual inflation because we're live 984 launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 985 986 launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo); 987 launcherInfo.hostView.setTag(launcherInfo); 988 989 mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1], 990 launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked()); 991 } 992 } 993 994 public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) { 995 mDesktopItems.remove(launcherInfo); 996 launcherInfo.hostView = null; 997 } 998 999 public LauncherAppWidgetHost getAppWidgetHost() { 1000 return mAppWidgetHost; 1001 } 1002 1003 void closeSystemDialogs() { 1004 getWindow().closeAllPanels(); 1005 1006 try { 1007 dismissDialog(DIALOG_CREATE_SHORTCUT); 1008 // Unlock the workspace if the dialog was showing 1009 } catch (Exception e) { 1010 // An exception is thrown if the dialog is not visible, which is fine 1011 } 1012 1013 try { 1014 dismissDialog(DIALOG_RENAME_FOLDER); 1015 // Unlock the workspace if the dialog was showing 1016 } catch (Exception e) { 1017 // An exception is thrown if the dialog is not visible, which is fine 1018 } 1019 1020 // Whatever we were doing is hereby canceled. 1021 mWaitingForResult = false; 1022 } 1023 1024 @Override 1025 protected void onNewIntent(Intent intent) { 1026 super.onNewIntent(intent); 1027 1028 // Close the menu 1029 if (Intent.ACTION_MAIN.equals(intent.getAction())) { 1030 // also will cancel mWaitingForResult. 1031 closeSystemDialogs(); 1032 1033 boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) 1034 != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); 1035 boolean allAppsVisible = isAllAppsVisible(); 1036 1037 // TODO: Figure out the right thing to do in XLarge mode here 1038 if (!mWorkspace.isDefaultScreenShowing()) { 1039 mWorkspace.moveToDefaultScreen(alreadyOnHome && !allAppsVisible); 1040 } 1041 closeAllApps(alreadyOnHome && allAppsVisible); 1042 hideCustomizationDrawer(); 1043 1044 final View v = getWindow().peekDecorView(); 1045 if (v != null && v.getWindowToken() != null) { 1046 InputMethodManager imm = (InputMethodManager)getSystemService( 1047 INPUT_METHOD_SERVICE); 1048 imm.hideSoftInputFromWindow(v.getWindowToken(), 0); 1049 } 1050 } 1051 } 1052 1053 @Override 1054 protected void onRestoreInstanceState(Bundle savedInstanceState) { 1055 // Do not call super here 1056 mSavedInstanceState = savedInstanceState; 1057 1058 if (mHomeCustomizationDrawer != null) { 1059 String cur = savedInstanceState.getString("currentTab"); 1060 if (cur != null) { 1061 mHomeCustomizationDrawer.setCurrentTabByTag(cur); 1062 } 1063 } 1064 } 1065 1066 @Override 1067 protected void onSaveInstanceState(Bundle outState) { 1068 outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen()); 1069 1070 final ArrayList<Folder> folders = mWorkspace.getOpenFolders(); 1071 if (folders.size() > 0) { 1072 final int count = folders.size(); 1073 long[] ids = new long[count]; 1074 for (int i = 0; i < count; i++) { 1075 final FolderInfo info = folders.get(i).getInfo(); 1076 ids[i] = info.id; 1077 } 1078 outState.putLongArray(RUNTIME_STATE_USER_FOLDERS, ids); 1079 } else { 1080 super.onSaveInstanceState(outState); 1081 } 1082 1083 // TODO should not do this if the drawer is currently closing. 1084 if (isAllAppsVisible()) { 1085 outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true); 1086 } 1087 1088 if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) { 1089 final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo; 1090 final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen); 1091 1092 outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen); 1093 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX); 1094 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY); 1095 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX); 1096 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY); 1097 outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX()); 1098 outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY()); 1099 outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS, 1100 layout.getOccupiedCellsFlattened()); 1101 } 1102 1103 if (mFolderInfo != null && mWaitingForResult) { 1104 outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true); 1105 outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id); 1106 } 1107 1108 if (mHomeCustomizationDrawer != null) { 1109 String currentTabTag = mHomeCustomizationDrawer.getCurrentTabTag(); 1110 if (currentTabTag != null) { 1111 outState.putString("currentTab", currentTabTag); 1112 } 1113 } 1114 } 1115 1116 @Override 1117 public void onDestroy() { 1118 super.onDestroy(); 1119 1120 try { 1121 mAppWidgetHost.stopListening(); 1122 } catch (NullPointerException ex) { 1123 Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex); 1124 } 1125 1126 TextKeyListener.getInstance().release(); 1127 1128 mModel.stopLoader(); 1129 1130 unbindDesktopItems(); 1131 1132 getContentResolver().unregisterContentObserver(mWidgetObserver); 1133 1134 // Some launcher layouts don't have a previous and next view 1135 if (mPreviousView != null) { 1136 dismissPreview(mPreviousView); 1137 } 1138 if (mNextView != null) { 1139 dismissPreview(mNextView); 1140 } 1141 1142 unregisterReceiver(mCloseSystemDialogsReceiver); 1143 } 1144 1145 @Override 1146 public void startActivityForResult(Intent intent, int requestCode) { 1147 if (requestCode >= 0) mWaitingForResult = true; 1148 super.startActivityForResult(intent, requestCode); 1149 } 1150 1151 @Override 1152 public void startSearch(String initialQuery, boolean selectInitialQuery, 1153 Bundle appSearchData, boolean globalSearch) { 1154 1155 closeAllApps(true); 1156 1157 if (initialQuery == null) { 1158 // Use any text typed in the launcher as the initial query 1159 initialQuery = getTypedText(); 1160 clearTypedText(); 1161 } 1162 if (appSearchData == null) { 1163 appSearchData = new Bundle(); 1164 appSearchData.putString(Search.SOURCE, "launcher-search"); 1165 } 1166 1167 final SearchManager searchManager = 1168 (SearchManager) getSystemService(Context.SEARCH_SERVICE); 1169 searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), 1170 appSearchData, globalSearch); 1171 } 1172 1173 @Override 1174 public boolean onCreateOptionsMenu(Menu menu) { 1175 if (isWorkspaceLocked()) { 1176 return false; 1177 } 1178 1179 super.onCreateOptionsMenu(menu); 1180 1181 menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add) 1182 .setIcon(android.R.drawable.ic_menu_add) 1183 .setAlphabeticShortcut('A'); 1184 menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper) 1185 .setIcon(android.R.drawable.ic_menu_gallery) 1186 .setAlphabeticShortcut('W'); 1187 menu.add(0, MENU_SEARCH, 0, R.string.menu_search) 1188 .setIcon(android.R.drawable.ic_search_category_default) 1189 .setAlphabeticShortcut(SearchManager.MENU_KEY); 1190 menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications) 1191 .setIcon(com.android.internal.R.drawable.ic_menu_notifications) 1192 .setAlphabeticShortcut('N'); 1193 1194 final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS); 1195 settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 1196 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 1197 1198 menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings) 1199 .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P') 1200 .setIntent(settings); 1201 1202 return true; 1203 } 1204 1205 @Override 1206 public boolean onPrepareOptionsMenu(Menu menu) { 1207 super.onPrepareOptionsMenu(menu); 1208 1209 // If all apps is animating, don't show the menu, because we don't know 1210 // which one to show. 1211 if (mAllAppsGrid.isAnimating()) { 1212 return false; 1213 } 1214 1215 // Only show the add and wallpaper options when we're not in all apps. 1216 boolean visible = !mAllAppsGrid.isVisible(); 1217 menu.setGroupVisible(MENU_GROUP_ADD, visible); 1218 menu.setGroupVisible(MENU_GROUP_WALLPAPER, visible); 1219 1220 // Disable add if the workspace is full. 1221 if (visible) { 1222 mMenuAddInfo = mWorkspace.findAllVacantCells(null); 1223 menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid); 1224 } 1225 1226 return true; 1227 } 1228 1229 // we need to initialize mAddItemCellInfo before adding something to the homescreen -- when 1230 // using the settings menu to add an item, something similar happens in showAddDialog 1231 public void prepareAddItemFromHomeCustomizationDrawer() { 1232 mMenuAddInfo = mWorkspace.findAllVacantCells(null); 1233 mAddItemCellInfo = mMenuAddInfo; 1234 } 1235 1236 @Override 1237 public boolean onOptionsItemSelected(MenuItem item) { 1238 switch (item.getItemId()) { 1239 case MENU_ADD: 1240 addItems(); 1241 return true; 1242 case MENU_WALLPAPER_SETTINGS: 1243 startWallpaper(); 1244 return true; 1245 case MENU_SEARCH: 1246 onSearchRequested(); 1247 return true; 1248 case MENU_NOTIFICATIONS: 1249 showNotifications(); 1250 return true; 1251 } 1252 1253 return super.onOptionsItemSelected(item); 1254 } 1255 1256 /** 1257 * Indicates that we want global search for this activity by setting the globalSearch 1258 * argument for {@link #startSearch} to true. 1259 */ 1260 1261 @Override 1262 public boolean onSearchRequested() { 1263 startSearch(null, false, null, true); 1264 return true; 1265 } 1266 1267 public boolean isWorkspaceLocked() { 1268 return mWorkspaceLoading || mWaitingForResult; 1269 } 1270 1271 private void addItems() { 1272 closeAllApps(true); 1273 if (LauncherApplication.isScreenXLarge()) { 1274 // Animate the widget chooser up from the bottom of the screen 1275 if (!isCustomizationDrawerVisible()) { 1276 showCustomizationDrawer(); 1277 mWorkspace.shrinkToTop(); 1278 } 1279 } else { 1280 showAddDialog(mMenuAddInfo); 1281 } 1282 } 1283 1284 void addAppWidgetFromDrop(ComponentName appWidgetProvider, CellLayout.CellInfo cellInfo) { 1285 mAddItemCellInfo = cellInfo; 1286 int appWidgetId = getAppWidgetHost().allocateAppWidgetId(); 1287 AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, appWidgetProvider); 1288 addAppWidgetImpl(appWidgetId); 1289 } 1290 1291 void addAppWidgetFromPick(Intent data) { 1292 // TODO: catch bad widget exception when sent 1293 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 1294 // TODO: Is this log message meaningful? 1295 if (LOGD) Log.d(TAG, "dumping extras content=" + data.getExtras()); 1296 addAppWidgetImpl(appWidgetId); 1297 } 1298 1299 void addAppWidgetImpl(int appWidgetId) { 1300 AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 1301 1302 if (appWidget.configure != null) { 1303 // Launch over to configure widget, if needed 1304 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); 1305 intent.setComponent(appWidget.configure); 1306 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 1307 1308 startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET); 1309 } else { 1310 // Otherwise just add it 1311 completeAddAppWidget(appWidgetId, mAddItemCellInfo); 1312 } 1313 } 1314 1315 void processShortcut(Intent intent) { 1316 // Handle case where user selected "Applications" 1317 String applicationName = getResources().getString(R.string.group_applications); 1318 String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 1319 1320 if (applicationName != null && applicationName.equals(shortcutName)) { 1321 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 1322 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1323 1324 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 1325 pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent); 1326 startActivityForResult(pickIntent, REQUEST_PICK_APPLICATION); 1327 } else { 1328 startActivityForResult(intent, REQUEST_CREATE_SHORTCUT); 1329 } 1330 } 1331 1332 void addLiveFolder(Intent intent) { 1333 // Handle case where user selected "Folder" 1334 String folderName = getResources().getString(R.string.group_folder); 1335 String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 1336 1337 if (folderName != null && folderName.equals(shortcutName)) { 1338 addFolder(); 1339 } else { 1340 startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER); 1341 } 1342 } 1343 1344 void addFolder() { 1345 UserFolderInfo folderInfo = new UserFolderInfo(); 1346 folderInfo.title = getText(R.string.folder_name); 1347 1348 CellLayout.CellInfo cellInfo = mAddItemCellInfo; 1349 cellInfo.screen = mWorkspace.getCurrentScreen(); 1350 if (!findSingleSlot(cellInfo)) return; 1351 1352 // Update the model 1353 LauncherModel.addItemToDatabase(this, folderInfo, 1354 LauncherSettings.Favorites.CONTAINER_DESKTOP, 1355 mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false); 1356 mFolders.put(folderInfo.id, folderInfo); 1357 1358 // Create the view 1359 FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, 1360 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo); 1361 mWorkspace.addInCurrentScreen(newFolder, 1362 cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked()); 1363 } 1364 1365 void removeFolder(FolderInfo folder) { 1366 mFolders.remove(folder.id); 1367 } 1368 1369 private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) { 1370 cellInfo.screen = mWorkspace.getCurrentScreen(); 1371 if (!findSingleSlot(cellInfo)) return; 1372 1373 final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false); 1374 1375 if (!mRestoring) { 1376 final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this, 1377 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info); 1378 mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, 1379 isWorkspaceLocked()); 1380 } 1381 } 1382 1383 static LiveFolderInfo addLiveFolder(Context context, Intent data, 1384 CellLayout.CellInfo cellInfo, boolean notify) { 1385 1386 Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT); 1387 String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME); 1388 1389 Drawable icon = null; 1390 Intent.ShortcutIconResource iconResource = null; 1391 1392 Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON); 1393 if (extra != null && extra instanceof Intent.ShortcutIconResource) { 1394 try { 1395 iconResource = (Intent.ShortcutIconResource) extra; 1396 final PackageManager packageManager = context.getPackageManager(); 1397 Resources resources = packageManager.getResourcesForApplication( 1398 iconResource.packageName); 1399 final int id = resources.getIdentifier(iconResource.resourceName, null, null); 1400 icon = resources.getDrawable(id); 1401 } catch (Exception e) { 1402 Log.w(TAG, "Could not load live folder icon: " + extra); 1403 } 1404 } 1405 1406 if (icon == null) { 1407 icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder); 1408 } 1409 1410 final LiveFolderInfo info = new LiveFolderInfo(); 1411 info.icon = Utilities.createIconBitmap(icon, context); 1412 info.title = name; 1413 info.iconResource = iconResource; 1414 info.uri = data.getData(); 1415 info.baseIntent = baseIntent; 1416 info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, 1417 LiveFolders.DISPLAY_MODE_GRID); 1418 1419 LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, 1420 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify); 1421 mFolders.put(info.id, info); 1422 1423 return info; 1424 } 1425 1426 private boolean findSingleSlot(CellLayout.CellInfo cellInfo) { 1427 final int[] xy = new int[2]; 1428 if (findSlot(cellInfo, xy, 1, 1)) { 1429 cellInfo.cellX = xy[0]; 1430 cellInfo.cellY = xy[1]; 1431 return true; 1432 } 1433 return false; 1434 } 1435 1436 private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) { 1437 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) { 1438 boolean[] occupied = mSavedState != null ? 1439 mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null; 1440 cellInfo = mWorkspace.findAllVacantCells(occupied); 1441 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) { 1442 Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); 1443 return false; 1444 } 1445 } 1446 return true; 1447 } 1448 1449 private void showNotifications() { 1450 final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE); 1451 if (statusBar != null) { 1452 statusBar.expand(); 1453 } 1454 } 1455 1456 private void startWallpaper() { 1457 closeAllApps(true); 1458 final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); 1459 Intent chooser = Intent.createChooser(pickWallpaper, 1460 getText(R.string.chooser_wallpaper)); 1461 // NOTE: Adds a configure option to the chooser if the wallpaper supports it 1462 // Removed in Eclair MR1 1463// WallpaperManager wm = (WallpaperManager) 1464// getSystemService(Context.WALLPAPER_SERVICE); 1465// WallpaperInfo wi = wm.getWallpaperInfo(); 1466// if (wi != null && wi.getSettingsActivity() != null) { 1467// LabeledIntent li = new LabeledIntent(getPackageName(), 1468// R.string.configure_wallpaper, 0); 1469// li.setClassName(wi.getPackageName(), wi.getSettingsActivity()); 1470// chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li }); 1471// } 1472 startActivityForResult(chooser, REQUEST_PICK_WALLPAPER); 1473 } 1474 1475 /** 1476 * Registers various content observers. The current implementation registers 1477 * only a favorites observer to keep track of the favorites applications. 1478 */ 1479 private void registerContentObservers() { 1480 ContentResolver resolver = getContentResolver(); 1481 resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI, 1482 true, mWidgetObserver); 1483 } 1484 1485 @Override 1486 public boolean dispatchKeyEvent(KeyEvent event) { 1487 if (event.getAction() == KeyEvent.ACTION_DOWN) { 1488 switch (event.getKeyCode()) { 1489 case KeyEvent.KEYCODE_HOME: 1490 return true; 1491 case KeyEvent.KEYCODE_VOLUME_DOWN: 1492 if (SystemProperties.getInt("debug.launcher2.dumpstate", 0) != 0) { 1493 dumpState(); 1494 return true; 1495 } 1496 break; 1497 } 1498 } else if (event.getAction() == KeyEvent.ACTION_UP) { 1499 switch (event.getKeyCode()) { 1500 case KeyEvent.KEYCODE_HOME: 1501 return true; 1502 } 1503 } 1504 1505 return super.dispatchKeyEvent(event); 1506 } 1507 1508 @Override 1509 public void onBackPressed() { 1510 if (isAllAppsVisible()) { 1511 closeAllApps(true); 1512 } else if (isCustomizationDrawerVisible()) { 1513 hideCustomizationDrawer(); 1514 } else { 1515 closeFolder(); 1516 } 1517 // Some launcher layouts don't have a previous and next view 1518 if (mPreviousView != null) { 1519 dismissPreview(mPreviousView); 1520 dismissPreview(mNextView); 1521 } 1522 } 1523 1524 private void closeFolder() { 1525 Folder folder = mWorkspace.getOpenFolder(); 1526 if (folder != null) { 1527 closeFolder(folder); 1528 } 1529 } 1530 1531 void closeFolder(Folder folder) { 1532 folder.getInfo().opened = false; 1533 ViewGroup parent = (ViewGroup) folder.getParent(); 1534 if (parent != null) { 1535 parent.removeView(folder); 1536 if (folder instanceof DropTarget) { 1537 // Live folders aren't DropTargets. 1538 mDragController.removeDropTarget((DropTarget)folder); 1539 } 1540 } 1541 folder.onClose(); 1542 } 1543 1544 /** 1545 * Re-listen when widgets are reset. 1546 */ 1547 private void onAppWidgetReset() { 1548 mAppWidgetHost.startListening(); 1549 } 1550 1551 /** 1552 * Go through the and disconnect any of the callbacks in the drawables and the views or we 1553 * leak the previous Home screen on orientation change. 1554 */ 1555 private void unbindDesktopItems() { 1556 for (ItemInfo item: mDesktopItems) { 1557 item.unbind(); 1558 } 1559 } 1560 1561 /** 1562 * Launches the intent referred by the clicked shortcut. 1563 * 1564 * @param v The view representing the clicked shortcut. 1565 */ 1566 public void onClick(View v) { 1567 Object tag = v.getTag(); 1568 if (tag instanceof ShortcutInfo) { 1569 // Open shortcut 1570 final Intent intent = ((ShortcutInfo) tag).intent; 1571 int[] pos = new int[2]; 1572 v.getLocationOnScreen(pos); 1573 intent.setSourceBounds(new Rect(pos[0], pos[1], 1574 pos[0] + v.getWidth(), pos[1] + v.getHeight())); 1575 startActivitySafely(intent, tag); 1576 } else if (tag instanceof FolderInfo) { 1577 handleFolderClick((FolderInfo) tag); 1578 } else if (v == mHandleView) { 1579 if (isAllAppsVisible()) { 1580 closeAllApps(true); 1581 } else { 1582 showAllApps(true); 1583 } 1584 } 1585 } 1586 1587 public boolean onTouch(View v, MotionEvent event) { 1588 // this is an intercepted event being forwarded from mWorkspace; 1589 // clicking anywhere on the workspace causes the drawer to slide down 1590 hideCustomizationDrawer(); 1591 return false; 1592 } 1593 1594 /** 1595 * Event handler for the search button 1596 * 1597 * @param v The view that was clicked. 1598 */ 1599 public void onClickSearchButton(View v) { 1600 Intent i = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); 1601 View button = findViewById(R.id.search_button); 1602 i.setSourceBounds( 1603 new Rect(button.getLeft(), button.getTop(), button.getRight(), button.getBottom())); 1604 startActivity(i); 1605 } 1606 1607 /** 1608 * Event handler for the "gear" button that appears on the home screen, which 1609 * enters home screen customization mode. 1610 * 1611 * @param v The view that was clicked. 1612 */ 1613 public void onClickConfigureButton(View v) { 1614 addItems(); 1615 } 1616 1617 /** 1618 * Event handler for the "grid" button that appears on the home screen, which 1619 * enters all apps mode. 1620 * 1621 * @param v The view that was clicked. 1622 */ 1623 public void onClickAllAppsButton(View v) { 1624 showAllApps(true); 1625 } 1626 1627 void startActivitySafely(Intent intent, Object tag) { 1628 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1629 try { 1630 startActivity(intent); 1631 } catch (ActivityNotFoundException e) { 1632 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 1633 Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e); 1634 } catch (SecurityException e) { 1635 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 1636 Log.e(TAG, "Launcher does not have the permission to launch " + intent + 1637 ". Make sure to create a MAIN intent-filter for the corresponding activity " + 1638 "or use the exported attribute for this activity. " 1639 + "tag="+ tag + " intent=" + intent, e); 1640 } 1641 } 1642 1643 void startActivityForResultSafely(Intent intent, int requestCode) { 1644 try { 1645 startActivityForResult(intent, requestCode); 1646 } catch (ActivityNotFoundException e) { 1647 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 1648 } catch (SecurityException e) { 1649 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 1650 Log.e(TAG, "Launcher does not have the permission to launch " + intent + 1651 ". Make sure to create a MAIN intent-filter for the corresponding activity " + 1652 "or use the exported attribute for this activity.", e); 1653 } 1654 } 1655 1656 private void handleFolderClick(FolderInfo folderInfo) { 1657 if (!folderInfo.opened) { 1658 // Close any open folder 1659 closeFolder(); 1660 // Open the requested folder 1661 openFolder(folderInfo); 1662 } else { 1663 // Find the open folder... 1664 Folder openFolder = mWorkspace.getFolderForTag(folderInfo); 1665 int folderScreen; 1666 if (openFolder != null) { 1667 folderScreen = mWorkspace.getScreenForView(openFolder); 1668 // .. and close it 1669 closeFolder(openFolder); 1670 if (folderScreen != mWorkspace.getCurrentScreen()) { 1671 // Close any folder open on the current screen 1672 closeFolder(); 1673 // Pull the folder onto this screen 1674 openFolder(folderInfo); 1675 } 1676 } 1677 } 1678 } 1679 1680 /** 1681 * Opens the user fodler described by the specified tag. The opening of the folder 1682 * is animated relative to the specified View. If the View is null, no animation 1683 * is played. 1684 * 1685 * @param folderInfo The FolderInfo describing the folder to open. 1686 */ 1687 public void openFolder(FolderInfo folderInfo) { 1688 Folder openFolder; 1689 1690 if (folderInfo instanceof UserFolderInfo) { 1691 openFolder = UserFolder.fromXml(this); 1692 } else if (folderInfo instanceof LiveFolderInfo) { 1693 openFolder = com.android.launcher2.LiveFolder.fromXml(this, folderInfo); 1694 } else { 1695 return; 1696 } 1697 1698 openFolder.setDragController(mDragController); 1699 openFolder.setLauncher(this); 1700 1701 openFolder.bind(folderInfo); 1702 folderInfo.opened = true; 1703 1704 mWorkspace.addInFullScreen(openFolder, folderInfo.screen); 1705 1706 openFolder.onOpen(); 1707 } 1708 1709 public boolean onLongClick(View v) { 1710 switch (v.getId()) { 1711 case R.id.previous_screen: 1712 if (!isAllAppsVisible()) { 1713 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, 1714 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 1715 showPreviews(v); 1716 } 1717 return true; 1718 case R.id.next_screen: 1719 if (!isAllAppsVisible()) { 1720 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, 1721 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 1722 showPreviews(v); 1723 } 1724 return true; 1725 case R.id.all_apps_button: 1726 if (!isAllAppsVisible()) { 1727 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, 1728 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 1729 showPreviews(v); 1730 } 1731 return true; 1732 } 1733 1734 if (isWorkspaceLocked()) { 1735 return false; 1736 } 1737 1738 if (!(v instanceof CellLayout)) { 1739 v = (View) v.getParent(); 1740 } 1741 1742 CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag(); 1743 1744 // This happens when long clicking an item with the dpad/trackball 1745 if (cellInfo == null) { 1746 return true; 1747 } 1748 1749 if (mWorkspace.allowLongPress()) { 1750 if (cellInfo.cell == null) { 1751 if (cellInfo.valid) { 1752 // User long pressed on empty space 1753 mWorkspace.setAllowLongPress(false); 1754 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, 1755 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 1756 showAddDialog(cellInfo); 1757 } 1758 } else { 1759 if (!(cellInfo.cell instanceof Folder)) { 1760 // User long pressed on an item 1761 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, 1762 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 1763 mWorkspace.startDrag(cellInfo); 1764 } 1765 } 1766 } 1767 return true; 1768 } 1769 1770 @SuppressWarnings({"unchecked"}) 1771 private void dismissPreview(final View v) { 1772 final PopupWindow window = (PopupWindow) v.getTag(); 1773 if (window != null) { 1774 window.setOnDismissListener(new PopupWindow.OnDismissListener() { 1775 public void onDismiss() { 1776 ViewGroup group = (ViewGroup) v.getTag(R.id.workspace); 1777 int count = group.getChildCount(); 1778 for (int i = 0; i < count; i++) { 1779 ((ImageView) group.getChildAt(i)).setImageDrawable(null); 1780 } 1781 ArrayList<Bitmap> bitmaps = (ArrayList<Bitmap>) v.getTag(R.id.icon); 1782 for (Bitmap bitmap : bitmaps) bitmap.recycle(); 1783 1784 v.setTag(R.id.workspace, null); 1785 v.setTag(R.id.icon, null); 1786 window.setOnDismissListener(null); 1787 } 1788 }); 1789 window.dismiss(); 1790 } 1791 v.setTag(null); 1792 } 1793 1794 private void showPreviews(View anchor) { 1795 showPreviews(anchor, 0, mWorkspace.getChildCount()); 1796 } 1797 1798 private void showPreviews(final View anchor, int start, int end) { 1799 final Resources resources = getResources(); 1800 final Workspace workspace = mWorkspace; 1801 1802 CellLayout cell = ((CellLayout) workspace.getChildAt(start)); 1803 1804 float max = workspace.getChildCount(); 1805 1806 final Rect r = new Rect(); 1807 resources.getDrawable(R.drawable.preview_background).getPadding(r); 1808 int extraW = (int) ((r.left + r.right) * max); 1809 int extraH = r.top + r.bottom; 1810 1811 int aW = cell.getWidth() - extraW; 1812 float w = aW / max; 1813 1814 int width = cell.getWidth(); 1815 int height = cell.getHeight(); 1816 int x = cell.getLeftPadding(); 1817 int y = cell.getTopPadding(); 1818 width -= (x + cell.getRightPadding()); 1819 height -= (y + cell.getBottomPadding()); 1820 1821 float scale = w / width; 1822 1823 int count = end - start; 1824 1825 final float sWidth = width * scale; 1826 float sHeight = height * scale; 1827 1828 LinearLayout preview = new LinearLayout(this); 1829 1830 PreviewTouchHandler handler = new PreviewTouchHandler(anchor); 1831 ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>(count); 1832 1833 for (int i = start; i < end; i++) { 1834 ImageView image = new ImageView(this); 1835 cell = (CellLayout) workspace.getChildAt(i); 1836 1837 final Bitmap bitmap = Bitmap.createBitmap((int) sWidth, (int) sHeight, 1838 Bitmap.Config.ARGB_8888); 1839 1840 final Canvas c = new Canvas(bitmap); 1841 c.scale(scale, scale); 1842 c.translate(-cell.getLeftPadding(), -cell.getTopPadding()); 1843 cell.dispatchDraw(c); 1844 1845 image.setBackgroundDrawable(resources.getDrawable(R.drawable.preview_background)); 1846 image.setImageBitmap(bitmap); 1847 image.setTag(i); 1848 image.setOnClickListener(handler); 1849 image.setOnFocusChangeListener(handler); 1850 image.setFocusable(true); 1851 if (i == mWorkspace.getCurrentScreen()) image.requestFocus(); 1852 1853 preview.addView(image, 1854 LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); 1855 1856 bitmaps.add(bitmap); 1857 } 1858 1859 final PopupWindow p = new PopupWindow(this); 1860 p.setContentView(preview); 1861 p.setWidth((int) (sWidth * count + extraW)); 1862 p.setHeight((int) (sHeight + extraH)); 1863 p.setAnimationStyle(R.style.AnimationPreview); 1864 p.setOutsideTouchable(true); 1865 p.setFocusable(true); 1866 p.setBackgroundDrawable(new ColorDrawable(0)); 1867 p.showAsDropDown(anchor, 0, 0); 1868 1869 p.setOnDismissListener(new PopupWindow.OnDismissListener() { 1870 public void onDismiss() { 1871 dismissPreview(anchor); 1872 } 1873 }); 1874 1875 anchor.setTag(p); 1876 anchor.setTag(R.id.workspace, preview); 1877 anchor.setTag(R.id.icon, bitmaps); 1878 } 1879 1880 class PreviewTouchHandler implements View.OnClickListener, Runnable, View.OnFocusChangeListener { 1881 private final View mAnchor; 1882 1883 public PreviewTouchHandler(View anchor) { 1884 mAnchor = anchor; 1885 } 1886 1887 public void onClick(View v) { 1888 mWorkspace.snapToScreen((Integer) v.getTag()); 1889 v.post(this); 1890 } 1891 1892 public void run() { 1893 dismissPreview(mAnchor); 1894 } 1895 1896 public void onFocusChange(View v, boolean hasFocus) { 1897 if (hasFocus) { 1898 mWorkspace.snapToScreen((Integer) v.getTag()); 1899 } 1900 } 1901 } 1902 1903 Workspace getWorkspace() { 1904 return mWorkspace; 1905 } 1906 1907 @Override 1908 protected Dialog onCreateDialog(int id) { 1909 switch (id) { 1910 case DIALOG_CREATE_SHORTCUT: 1911 return new CreateShortcut().createDialog(); 1912 case DIALOG_RENAME_FOLDER: 1913 return new RenameFolder().createDialog(); 1914 } 1915 1916 return super.onCreateDialog(id); 1917 } 1918 1919 @Override 1920 protected void onPrepareDialog(int id, Dialog dialog) { 1921 switch (id) { 1922 case DIALOG_CREATE_SHORTCUT: 1923 break; 1924 case DIALOG_RENAME_FOLDER: 1925 if (mFolderInfo != null) { 1926 EditText input = (EditText) dialog.findViewById(R.id.folder_name); 1927 final CharSequence text = mFolderInfo.title; 1928 input.setText(text); 1929 input.setSelection(0, text.length()); 1930 } 1931 break; 1932 } 1933 } 1934 1935 void showRenameDialog(FolderInfo info) { 1936 mFolderInfo = info; 1937 mWaitingForResult = true; 1938 showDialog(DIALOG_RENAME_FOLDER); 1939 } 1940 1941 private void showAddDialog(CellLayout.CellInfo cellInfo) { 1942 mAddItemCellInfo = cellInfo; 1943 mWaitingForResult = true; 1944 showDialog(DIALOG_CREATE_SHORTCUT); 1945 } 1946 1947 private void pickShortcut() { 1948 // Insert extra item to handle picking application 1949 Bundle bundle = new Bundle(); 1950 1951 ArrayList<String> shortcutNames = new ArrayList<String>(); 1952 shortcutNames.add(getString(R.string.group_applications)); 1953 bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames); 1954 1955 ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>(); 1956 shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this, 1957 R.drawable.ic_launcher_application)); 1958 bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons); 1959 1960 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 1961 pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT)); 1962 pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_shortcut)); 1963 pickIntent.putExtras(bundle); 1964 1965 startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT); 1966 } 1967 1968 private class RenameFolder { 1969 private EditText mInput; 1970 1971 Dialog createDialog() { 1972 final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null); 1973 mInput = (EditText) layout.findViewById(R.id.folder_name); 1974 1975 AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this); 1976 builder.setIcon(0); 1977 builder.setTitle(getString(R.string.rename_folder_title)); 1978 builder.setCancelable(true); 1979 builder.setOnCancelListener(new Dialog.OnCancelListener() { 1980 public void onCancel(DialogInterface dialog) { 1981 cleanup(); 1982 } 1983 }); 1984 builder.setNegativeButton(getString(R.string.cancel_action), 1985 new Dialog.OnClickListener() { 1986 public void onClick(DialogInterface dialog, int which) { 1987 cleanup(); 1988 } 1989 } 1990 ); 1991 builder.setPositiveButton(getString(R.string.rename_action), 1992 new Dialog.OnClickListener() { 1993 public void onClick(DialogInterface dialog, int which) { 1994 changeFolderName(); 1995 } 1996 } 1997 ); 1998 builder.setView(layout); 1999 2000 final AlertDialog dialog = builder.create(); 2001 dialog.setOnShowListener(new DialogInterface.OnShowListener() { 2002 public void onShow(DialogInterface dialog) { 2003 mWaitingForResult = true; 2004 mInput.requestFocus(); 2005 InputMethodManager inputManager = (InputMethodManager) 2006 getSystemService(Context.INPUT_METHOD_SERVICE); 2007 inputManager.showSoftInput(mInput, 0); 2008 } 2009 }); 2010 2011 return dialog; 2012 } 2013 2014 private void changeFolderName() { 2015 final String name = mInput.getText().toString(); 2016 if (!TextUtils.isEmpty(name)) { 2017 // Make sure we have the right folder info 2018 mFolderInfo = mFolders.get(mFolderInfo.id); 2019 mFolderInfo.title = name; 2020 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo); 2021 2022 if (mWorkspaceLoading) { 2023 lockAllApps(); 2024 mModel.startLoader(Launcher.this, false); 2025 } else { 2026 final FolderIcon folderIcon = (FolderIcon) 2027 mWorkspace.getViewForTag(mFolderInfo); 2028 if (folderIcon != null) { 2029 folderIcon.setText(name); 2030 getWorkspace().requestLayout(); 2031 } else { 2032 lockAllApps(); 2033 mWorkspaceLoading = true; 2034 mModel.startLoader(Launcher.this, false); 2035 } 2036 } 2037 } 2038 cleanup(); 2039 } 2040 2041 private void cleanup() { 2042 dismissDialog(DIALOG_RENAME_FOLDER); 2043 mWaitingForResult = false; 2044 mFolderInfo = null; 2045 } 2046 } 2047 2048 // Now a part of LauncherModel.Callbacks. Used to reorder loading steps. 2049 public boolean isAllAppsVisible() { 2050 return (mAllAppsGrid != null) ? mAllAppsGrid.isVisible() : false; 2051 } 2052 2053 // AllAppsView.Watcher 2054 public void zoomed(float zoom) { 2055 // In XLarge view, we zoom down the workspace below all apps so it's still visible 2056 if (zoom == 1.0f && !LauncherApplication.isScreenXLarge()) { 2057 mWorkspace.setVisibility(View.GONE); 2058 } 2059 } 2060 2061 void showAllApps(boolean animated) { 2062 boolean unshrinkWorkspace = false; 2063 hideCustomizationDrawer(unshrinkWorkspace); 2064 2065 if (LauncherApplication.isScreenXLarge()) { 2066 mWorkspace.shrinkToBottom(animated); 2067 } 2068 if (LauncherApplication.isScreenXLarge() && animated) { 2069 final View allApps = (View)mAllAppsGrid; 2070 2071 final Resources res = getResources(); 2072 final int duration = res.getInteger(R.integer.config_allAppsZoomInTime); 2073 final float scale = (float) res.getInteger(R.integer.config_allAppsZoomScaleFactor); 2074 final int height = allApps.getHeight(); 2075 2076 // All apps should appear right at the end of the workspace shrink animation 2077 final int startDelay = res.getInteger(R.integer.config_workspaceShrinkTime) - duration; 2078 2079 Interpolator interp = new DecelerateInterpolator(2.0f); 2080 2081 allApps.setPivotX(allApps.getWidth() / 2.0f); 2082 allApps.setPivotY(height); 2083 2084 Animator scaleXAnim = new PropertyAnimator(duration, allApps, "scaleX", scale, 1.0f); 2085 scaleXAnim.setInterpolator(interp); 2086 scaleXAnim.addListener(new AnimatableListenerAdapter() { 2087 public void onAnimationStart(Animatable animation) { 2088 // Not really a zoom -- this just makes the view visible 2089 mAllAppsGrid.zoom(1.0f, false); 2090 } 2091 }); 2092 2093 Animator scaleYAnim = new PropertyAnimator(duration, allApps, "scaleY", scale, 1.0f); 2094 scaleYAnim.setInterpolator(interp); 2095 2096 // Translate down by 20% of the total height 2097 float oldY = (-allApps.getHeight() * 0.2f); 2098 Animator yAnim = new PropertyAnimator(duration, allApps, "y", oldY, 0.0f); 2099 yAnim.setInterpolator(interp); 2100 2101 Sequencer s = new Sequencer(); 2102 s.playTogether(scaleXAnim, scaleYAnim, yAnim); 2103 s.play(scaleXAnim).after(startDelay); 2104 s.start(); 2105 } else { 2106 mAllAppsGrid.zoom(1.0f, animated); 2107 } 2108 2109 ((View) mAllAppsGrid).setFocusable(true); 2110 ((View) mAllAppsGrid).requestFocus(); 2111 2112 // TODO: fade these two too 2113 mDeleteZone.setVisibility(View.GONE); 2114 } 2115 2116 /** 2117 * Things to test when changing this code. 2118 * - Home from workspace 2119 * - from center screen 2120 * - from other screens 2121 * - Home from all apps 2122 * - from center screen 2123 * - from other screens 2124 * - Back from all apps 2125 * - from center screen 2126 * - from other screens 2127 * - Launch app from workspace and quit 2128 * - with back 2129 * - with home 2130 * - Launch app from all apps and quit 2131 * - with back 2132 * - with home 2133 * - Go to a screen that's not the default, then all 2134 * apps, and launch and app, and go back 2135 * - with back 2136 * -with home 2137 * - On workspace, long press power and go back 2138 * - with back 2139 * - with home 2140 * - On all apps, long press power and go back 2141 * - with back 2142 * - with home 2143 * - On workspace, power off 2144 * - On all apps, power off 2145 * - Launch an app and turn off the screen while in that app 2146 * - Go back with home key 2147 * - Go back with back key TODO: make this not go to workspace 2148 * - From all apps 2149 * - From workspace 2150 * - Enter and exit car mode (becuase it causes an extra configuration changed) 2151 * - From all apps 2152 * - From the center workspace 2153 * - From another workspace 2154 */ 2155 void closeAllApps(boolean animated) { 2156 if (mAllAppsGrid.isVisible()) { 2157 mWorkspace.setVisibility(View.VISIBLE); 2158 if (LauncherApplication.isScreenXLarge() && animated) { 2159 Resources res = getResources(); 2160 int duration = res.getInteger(R.integer.config_allAppsZoomOutTime); 2161 float scaleFactor = (float) res.getInteger(R.integer.config_allAppsZoomScaleFactor); 2162 View allApps = (View) mAllAppsGrid; 2163 2164 allApps.setPivotX(allApps.getWidth() / 2.0f); 2165 allApps.setPivotY(allApps.getHeight()); 2166 2167 // Translate up by 20% of the total height 2168 float newY = allApps.getY() - allApps.getHeight() * 0.2f; 2169 2170 Sequencer seq = new Sequencer(); 2171 seq.playTogether( 2172 new PropertyAnimator(duration, allApps, "scaleX", scaleFactor), 2173 new PropertyAnimator(duration, allApps, "scaleY", scaleFactor), 2174 new PropertyAnimator(duration, allApps, "y", newY)); 2175 seq.addListener(new AnimatableListenerAdapter() { 2176 public void onAnimationEnd(Animatable animation) { 2177 mAllAppsGrid.zoom(0.0f, false); 2178 } 2179 }); 2180 // Start the AllApps animation at the same time as the workspace unshrink 2181 seq.start(); 2182 mWorkspace.unshrink(); 2183 } else { 2184 mAllAppsGrid.zoom(0.0f, animated); 2185 } 2186 ((View)mAllAppsGrid).setFocusable(false); 2187 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); 2188 } 2189 } 2190 2191 void lockAllApps() { 2192 // TODO 2193 } 2194 2195 void unlockAllApps() { 2196 // TODO 2197 } 2198 2199 private boolean isCustomizationDrawerVisible() { 2200 return mHomeCustomizationDrawer != null && 2201 mHomeCustomizationDrawer.getVisibility() == View.VISIBLE; 2202 } 2203 2204 private void showCustomizationDrawer() { 2205 if (isAllAppsVisible()) { 2206 // TODO: Make a smoother transition here 2207 closeAllApps(false); 2208 } 2209 mHomeCustomizationDrawer.setVisibility(View.VISIBLE); 2210 mHomeCustomizationDrawer.startAnimation( 2211 AnimationUtils.loadAnimation(this, R.anim.home_customization_drawer_slide_up)); 2212 } 2213 2214 void hideCustomizationDrawer() { 2215 hideCustomizationDrawer(true); 2216 } 2217 2218 void hideCustomizationDrawer(boolean unshrinkWorkspace) { 2219 if (isCustomizationDrawerVisible()) { 2220 Animation slideDownAnimation = AnimationUtils.loadAnimation( 2221 this, R.anim.home_customization_drawer_slide_down); 2222 slideDownAnimation.setAnimationListener(new Animation.AnimationListener() { 2223 public void onAnimationEnd(Animation animation) { 2224 mHomeCustomizationDrawer.setVisibility(View.GONE); 2225 } 2226 public void onAnimationRepeat(Animation animation) {} 2227 public void onAnimationStart(Animation animation) {} 2228 }); 2229 mHomeCustomizationDrawer.startAnimation(slideDownAnimation); 2230 if (unshrinkWorkspace) { 2231 mWorkspace.unshrink(); 2232 } 2233 } 2234 } 2235 2236 void onWorkspaceUnshrink() { 2237 if (isAllAppsVisible()) { 2238 closeAllApps(true); 2239 } 2240 if (isCustomizationDrawerVisible()) { 2241 hideCustomizationDrawer(); 2242 } 2243 } 2244 2245 /** 2246 * Displays the shortcut creation dialog and launches, if necessary, the 2247 * appropriate activity. 2248 */ 2249 private class CreateShortcut implements DialogInterface.OnClickListener, 2250 DialogInterface.OnCancelListener, DialogInterface.OnDismissListener, 2251 DialogInterface.OnShowListener { 2252 2253 private AddAdapter mAdapter; 2254 2255 Dialog createDialog() { 2256 mAdapter = new AddAdapter(Launcher.this); 2257 2258 final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this); 2259 builder.setTitle(getString(R.string.menu_item_add_item)); 2260 builder.setAdapter(mAdapter, this); 2261 2262 builder.setInverseBackgroundForced(true); 2263 2264 AlertDialog dialog = builder.create(); 2265 dialog.setOnCancelListener(this); 2266 dialog.setOnDismissListener(this); 2267 dialog.setOnShowListener(this); 2268 2269 return dialog; 2270 } 2271 2272 public void onCancel(DialogInterface dialog) { 2273 mWaitingForResult = false; 2274 cleanup(); 2275 } 2276 2277 public void onDismiss(DialogInterface dialog) { 2278 } 2279 2280 private void cleanup() { 2281 try { 2282 dismissDialog(DIALOG_CREATE_SHORTCUT); 2283 } catch (Exception e) { 2284 // An exception is thrown if the dialog is not visible, which is fine 2285 } 2286 } 2287 2288 /** 2289 * Handle the action clicked in the "Add to home" dialog. 2290 */ 2291 public void onClick(DialogInterface dialog, int which) { 2292 Resources res = getResources(); 2293 cleanup(); 2294 2295 switch (which) { 2296 case AddAdapter.ITEM_SHORTCUT: { 2297 pickShortcut(); 2298 break; 2299 } 2300 2301 case AddAdapter.ITEM_APPWIDGET: { 2302 int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId(); 2303 2304 Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK); 2305 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 2306 // start the pick activity 2307 startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET); 2308 break; 2309 } 2310 2311 case AddAdapter.ITEM_LIVE_FOLDER: { 2312 // Insert extra item to handle inserting folder 2313 Bundle bundle = new Bundle(); 2314 2315 ArrayList<String> shortcutNames = new ArrayList<String>(); 2316 shortcutNames.add(res.getString(R.string.group_folder)); 2317 bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames); 2318 2319 ArrayList<ShortcutIconResource> shortcutIcons = 2320 new ArrayList<ShortcutIconResource>(); 2321 shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this, 2322 R.drawable.ic_launcher_folder)); 2323 bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons); 2324 2325 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 2326 pickIntent.putExtra(Intent.EXTRA_INTENT, 2327 new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER)); 2328 pickIntent.putExtra(Intent.EXTRA_TITLE, 2329 getText(R.string.title_select_live_folder)); 2330 pickIntent.putExtras(bundle); 2331 2332 startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER); 2333 break; 2334 } 2335 2336 case AddAdapter.ITEM_WALLPAPER: { 2337 startWallpaper(); 2338 break; 2339 } 2340 } 2341 } 2342 2343 public void onShow(DialogInterface dialog) { 2344 mWaitingForResult = true; 2345 } 2346 } 2347 2348 /** 2349 * Receives notifications when applications are added/removed. 2350 */ 2351 private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver { 2352 @Override 2353 public void onReceive(Context context, Intent intent) { 2354 closeSystemDialogs(); 2355 String reason = intent.getStringExtra("reason"); 2356 if (!"homekey".equals(reason)) { 2357 boolean animate = true; 2358 if (mPaused || "lock".equals(reason)) { 2359 animate = false; 2360 } 2361 closeAllApps(animate); 2362 } 2363 } 2364 } 2365 2366 /** 2367 * Receives notifications whenever the appwidgets are reset. 2368 */ 2369 private class AppWidgetResetObserver extends ContentObserver { 2370 public AppWidgetResetObserver() { 2371 super(new Handler()); 2372 } 2373 2374 @Override 2375 public void onChange(boolean selfChange) { 2376 onAppWidgetReset(); 2377 } 2378 } 2379 2380 /** 2381 * Implementation of the method from LauncherModel.Callbacks. 2382 */ 2383 public int getCurrentWorkspaceScreen() { 2384 if (mWorkspace != null) { 2385 return mWorkspace.getCurrentScreen(); 2386 } else { 2387 return SCREEN_COUNT / 2; 2388 } 2389 } 2390 2391 /** 2392 * Refreshes the shortcuts shown on the workspace. 2393 * 2394 * Implementation of the method from LauncherModel.Callbacks. 2395 */ 2396 public void startBinding() { 2397 final Workspace workspace = mWorkspace; 2398 int count = workspace.getChildCount(); 2399 for (int i = 0; i < count; i++) { 2400 // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate(). 2401 ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout(); 2402 } 2403 2404 if (DEBUG_USER_INTERFACE) { 2405 android.widget.Button finishButton = new android.widget.Button(this); 2406 finishButton.setText("Finish"); 2407 workspace.addInScreen(finishButton, 1, 0, 0, 1, 1); 2408 2409 finishButton.setOnClickListener(new android.widget.Button.OnClickListener() { 2410 public void onClick(View v) { 2411 finish(); 2412 } 2413 }); 2414 } 2415 } 2416 2417 /** 2418 * Bind the items start-end from the list. 2419 * 2420 * Implementation of the method from LauncherModel.Callbacks. 2421 */ 2422 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) { 2423 2424 final Workspace workspace = mWorkspace; 2425 2426 for (int i=start; i<end; i++) { 2427 final ItemInfo item = shortcuts.get(i); 2428 mDesktopItems.add(item); 2429 switch (item.itemType) { 2430 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 2431 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 2432 final View shortcut = createShortcut((ShortcutInfo)item); 2433 workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1, 2434 false); 2435 break; 2436 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 2437 final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, 2438 (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()), 2439 (UserFolderInfo) item); 2440 workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1, 2441 false); 2442 break; 2443 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 2444 final FolderIcon newLiveFolder = LiveFolderIcon.fromXml( 2445 R.layout.live_folder_icon, this, 2446 (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()), 2447 (LiveFolderInfo) item); 2448 workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1, 2449 false); 2450 break; 2451 } 2452 } 2453 2454 workspace.requestLayout(); 2455 } 2456 2457 /** 2458 * Implementation of the method from LauncherModel.Callbacks. 2459 */ 2460 public void bindFolders(HashMap<Long, FolderInfo> folders) { 2461 mFolders.clear(); 2462 mFolders.putAll(folders); 2463 } 2464 2465 /** 2466 * Add the views for a widget to the workspace. 2467 * 2468 * Implementation of the method from LauncherModel.Callbacks. 2469 */ 2470 public void bindAppWidget(LauncherAppWidgetInfo item) { 2471 final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0; 2472 if (DEBUG_WIDGETS) { 2473 Log.d(TAG, "bindAppWidget: " + item); 2474 } 2475 final Workspace workspace = mWorkspace; 2476 2477 final int appWidgetId = item.appWidgetId; 2478 final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 2479 if (DEBUG_WIDGETS) { 2480 Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider); 2481 } 2482 2483 item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 2484 2485 item.hostView.setAppWidget(appWidgetId, appWidgetInfo); 2486 item.hostView.setTag(item); 2487 2488 workspace.addInScreen(item.hostView, item.screen, item.cellX, 2489 item.cellY, item.spanX, item.spanY, false); 2490 2491 workspace.requestLayout(); 2492 2493 mDesktopItems.add(item); 2494 2495 if (DEBUG_WIDGETS) { 2496 Log.d(TAG, "bound widget id="+item.appWidgetId+" in " 2497 + (SystemClock.uptimeMillis()-start) + "ms"); 2498 } 2499 } 2500 2501 /** 2502 * Callback saying that there aren't any more items to bind. 2503 * 2504 * Implementation of the method from LauncherModel.Callbacks. 2505 */ 2506 public void finishBindingItems() { 2507 if (mSavedState != null) { 2508 if (!mWorkspace.hasFocus()) { 2509 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); 2510 } 2511 2512 final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS); 2513 if (userFolders != null) { 2514 for (long folderId : userFolders) { 2515 final FolderInfo info = mFolders.get(folderId); 2516 if (info != null) { 2517 openFolder(info); 2518 } 2519 } 2520 final Folder openFolder = mWorkspace.getOpenFolder(); 2521 if (openFolder != null) { 2522 openFolder.requestFocus(); 2523 } 2524 } 2525 2526 mSavedState = null; 2527 } 2528 2529 if (mSavedInstanceState != null) { 2530 super.onRestoreInstanceState(mSavedInstanceState); 2531 mSavedInstanceState = null; 2532 } 2533 2534 mWorkspaceLoading = false; 2535 } 2536 2537 /** 2538 * Add the icons for all apps. 2539 * 2540 * Implementation of the method from LauncherModel.Callbacks. 2541 */ 2542 public void bindAllApplications(ArrayList<ApplicationInfo> apps) { 2543 mAllAppsGrid.setApps(apps); 2544 } 2545 2546 /** 2547 * A package was installed. 2548 * 2549 * Implementation of the method from LauncherModel.Callbacks. 2550 */ 2551 public void bindAppsAdded(ArrayList<ApplicationInfo> apps) { 2552 removeDialog(DIALOG_CREATE_SHORTCUT); 2553 mAllAppsGrid.addApps(apps); 2554 } 2555 2556 /** 2557 * A package was updated. 2558 * 2559 * Implementation of the method from LauncherModel.Callbacks. 2560 */ 2561 public void bindAppsUpdated(ArrayList<ApplicationInfo> apps) { 2562 removeDialog(DIALOG_CREATE_SHORTCUT); 2563 mWorkspace.updateShortcuts(apps); 2564 mAllAppsGrid.updateApps(apps); 2565 } 2566 2567 /** 2568 * A package was uninstalled. 2569 * 2570 * Implementation of the method from LauncherModel.Callbacks. 2571 */ 2572 public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent) { 2573 removeDialog(DIALOG_CREATE_SHORTCUT); 2574 if (permanent) { 2575 mWorkspace.removeItems(apps); 2576 } 2577 mAllAppsGrid.removeApps(apps); 2578 } 2579 2580 /** 2581 * Prints out out state for debugging. 2582 */ 2583 public void dumpState() { 2584 Log.d(TAG, "BEGIN launcher2 dump state for launcher " + this); 2585 Log.d(TAG, "mSavedState=" + mSavedState); 2586 Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading); 2587 Log.d(TAG, "mRestoring=" + mRestoring); 2588 Log.d(TAG, "mWaitingForResult=" + mWaitingForResult); 2589 Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState); 2590 Log.d(TAG, "mDesktopItems.size=" + mDesktopItems.size()); 2591 Log.d(TAG, "mFolders.size=" + mFolders.size()); 2592 mModel.dumpState(); 2593 mAllAppsGrid.dumpState(); 2594 Log.d(TAG, "END launcher2 dump state"); 2595 } 2596} 2597