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