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