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