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