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