Launcher.java revision 77e1f47520958edd7ec04e203db65b932537bd1c
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.launcher3; 19 20import android.accounts.Account; 21import android.accounts.AccountManager; 22import android.animation.Animator; 23import android.animation.AnimatorListenerAdapter; 24import android.animation.AnimatorSet; 25import android.animation.ObjectAnimator; 26import android.animation.PropertyValuesHolder; 27import android.animation.ValueAnimator; 28import android.animation.ValueAnimator.AnimatorUpdateListener; 29import android.app.Activity; 30import android.app.ActivityManager; 31import android.app.ActivityOptions; 32import android.app.SearchManager; 33import android.appwidget.AppWidgetHostView; 34import android.appwidget.AppWidgetManager; 35import android.appwidget.AppWidgetProviderInfo; 36import android.content.ActivityNotFoundException; 37import android.content.BroadcastReceiver; 38import android.content.ComponentCallbacks2; 39import android.content.ComponentName; 40import android.content.ContentResolver; 41import android.content.Context; 42import android.content.Intent; 43import android.content.IntentFilter; 44import android.content.SharedPreferences; 45import android.content.pm.ActivityInfo; 46import android.content.pm.PackageManager; 47import android.content.pm.PackageManager.NameNotFoundException; 48import android.content.pm.ResolveInfo; 49import android.content.res.Configuration; 50import android.content.res.Resources; 51import android.database.ContentObserver; 52import android.graphics.Bitmap; 53import android.graphics.Canvas; 54import android.graphics.Point; 55import android.graphics.PorterDuff; 56import android.graphics.Rect; 57import android.graphics.drawable.Drawable; 58import android.net.Uri; 59import android.os.AsyncTask; 60import android.os.Bundle; 61import android.os.Environment; 62import android.os.Handler; 63import android.os.Message; 64import android.os.StrictMode; 65import android.os.SystemClock; 66import android.provider.Settings; 67import android.speech.RecognizerIntent; 68import android.text.Selection; 69import android.text.SpannableStringBuilder; 70import android.text.TextUtils; 71import android.text.method.TextKeyListener; 72import android.util.DisplayMetrics; 73import android.util.Log; 74import android.view.Display; 75import android.view.Gravity; 76import android.view.HapticFeedbackConstants; 77import android.view.KeyEvent; 78import android.view.LayoutInflater; 79import android.view.Menu; 80import android.view.MenuItem; 81import android.view.MotionEvent; 82import android.view.Surface; 83import android.view.View; 84import android.view.View.OnClickListener; 85import android.view.View.OnLongClickListener; 86import android.view.ViewGroup; 87import android.view.ViewTreeObserver; 88import android.view.ViewTreeObserver.OnGlobalLayoutListener; 89import android.view.WindowManager; 90import android.view.accessibility.AccessibilityEvent; 91import android.view.animation.AccelerateDecelerateInterpolator; 92import android.view.animation.AccelerateInterpolator; 93import android.view.animation.DecelerateInterpolator; 94import android.view.inputmethod.InputMethodManager; 95import android.widget.Advanceable; 96import android.widget.FrameLayout; 97import android.widget.ImageView; 98import android.widget.TextView; 99import android.widget.Toast; 100 101import com.android.launcher3.DropTarget.DragObject; 102 103import java.io.DataInputStream; 104import java.io.DataOutputStream; 105import java.io.File; 106import java.io.FileDescriptor; 107import java.io.FileInputStream; 108import java.io.FileNotFoundException; 109import java.io.FileOutputStream; 110import java.io.IOException; 111import java.io.InputStream; 112import java.io.OutputStream; 113import java.io.PrintWriter; 114import java.text.DateFormat; 115import java.util.ArrayList; 116import java.util.Collection; 117import java.util.Date; 118import java.util.HashMap; 119import java.util.List; 120 121/** 122 * Default launcher application. 123 */ 124public class Launcher extends Activity 125 implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, 126 View.OnTouchListener { 127 static final String TAG = "Launcher"; 128 static final boolean LOGD = false; 129 130 static final boolean PROFILE_STARTUP = false; 131 static final boolean DEBUG_WIDGETS = false; 132 static final boolean DEBUG_STRICT_MODE = false; 133 static final boolean DEBUG_RESUME_TIME = false; 134 135 private static final int MENU_GROUP_WALLPAPER = 1; 136 private static final int MENU_WALLPAPER_SETTINGS = Menu.FIRST + 1; 137 private static final int MENU_MANAGE_APPS = MENU_WALLPAPER_SETTINGS + 1; 138 private static final int MENU_SYSTEM_SETTINGS = MENU_MANAGE_APPS + 1; 139 private static final int MENU_HELP = MENU_SYSTEM_SETTINGS + 1; 140 141 private static final int REQUEST_CREATE_SHORTCUT = 1; 142 private static final int REQUEST_CREATE_APPWIDGET = 5; 143 private static final int REQUEST_PICK_APPLICATION = 6; 144 private static final int REQUEST_PICK_SHORTCUT = 7; 145 private static final int REQUEST_PICK_APPWIDGET = 9; 146 private static final int REQUEST_PICK_WALLPAPER = 10; 147 148 private static final int REQUEST_BIND_APPWIDGET = 11; 149 150 /** 151 * IntentStarter uses request codes starting with this. This must be greater than all activity 152 * request codes used internally. 153 */ 154 protected static final int REQUEST_LAST = 100; 155 156 static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate"; 157 158 static final int SCREEN_COUNT = 5; 159 static final int DEFAULT_SCREEN = 2; 160 161 private static final String PREFERENCES = "launcher.preferences"; 162 // To turn on these properties, type 163 // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] 164 static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate"; 165 static final String DUMP_STATE_PROPERTY = "launcher_dump_state"; 166 167 // The Intent extra that defines whether to ignore the launch animation 168 static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION = 169 "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION"; 170 171 // Type: int 172 private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; 173 // Type: int 174 private static final String RUNTIME_STATE = "launcher.state"; 175 // Type: int 176 private static final String RUNTIME_STATE_PENDING_ADD_CONTAINER = "launcher.add_container"; 177 // Type: int 178 private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen"; 179 // Type: int 180 private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x"; 181 // Type: int 182 private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y"; 183 // Type: boolean 184 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder"; 185 // Type: long 186 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id"; 187 // Type: int 188 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x"; 189 // Type: int 190 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y"; 191 // Type: parcelable 192 private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info"; 193 194 private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon"; 195 private static final String TOOLBAR_SEARCH_ICON_METADATA_NAME = 196 "com.android.launcher.toolbar_search_icon"; 197 private static final String TOOLBAR_VOICE_SEARCH_ICON_METADATA_NAME = 198 "com.android.launcher.toolbar_voice_search_icon"; 199 200 public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem"; 201 public static final boolean SHOW_WEIGHT_WATCHER_DEFAULT = false; 202 203 /** The different states that Launcher can be in. */ 204 private enum State { NONE, WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED }; 205 private State mState = State.WORKSPACE; 206 private AnimatorSet mStateAnimation; 207 208 static final int APPWIDGET_HOST_ID = 1024; 209 private static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300; 210 private static final int EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT = 600; 211 private static final int SHOW_CLING_DURATION = 550; 212 private static final int DISMISS_CLING_DURATION = 250; 213 214 private static final Object sLock = new Object(); 215 private static int sScreen = DEFAULT_SCREEN; 216 217 // How long to wait before the new-shortcut animation automatically pans the workspace 218 private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 10; 219 private static int NEW_APPS_ANIMATION_DELAY = 500; 220 221 private final BroadcastReceiver mCloseSystemDialogsReceiver 222 = new CloseSystemDialogsIntentReceiver(); 223 private final ContentObserver mWidgetObserver = new AppWidgetResetObserver(); 224 225 private LayoutInflater mInflater; 226 227 private Workspace mWorkspace; 228 private View mLauncherView; 229 private DragLayer mDragLayer; 230 private DragController mDragController; 231 private View mWeightWatcher; 232 233 private AppWidgetManager mAppWidgetManager; 234 private LauncherAppWidgetHost mAppWidgetHost; 235 236 private ItemInfo mPendingAddInfo = new ItemInfo(); 237 private AppWidgetProviderInfo mPendingAddWidgetInfo; 238 239 private int[] mTmpAddItemCellCoordinates = new int[2]; 240 241 private FolderInfo mFolderInfo; 242 243 private Hotseat mHotseat; 244 private View mOverviewPanel; 245 246 private View mAllAppsButton; 247 248 private SearchDropTargetBar mSearchDropTargetBar; 249 private AppsCustomizeTabHost mAppsCustomizeTabHost; 250 private AppsCustomizePagedView mAppsCustomizeContent; 251 private boolean mAutoAdvanceRunning = false; 252 private View mQsbBar; 253 254 private Bundle mSavedState; 255 // We set the state in both onCreate and then onNewIntent in some cases, which causes both 256 // scroll issues (because the workspace may not have been measured yet) and extra work. 257 // Instead, just save the state that we need to restore Launcher to, and commit it in onResume. 258 private State mOnResumeState = State.NONE; 259 260 private SpannableStringBuilder mDefaultKeySsb = null; 261 262 private boolean mWorkspaceLoading = true; 263 264 private boolean mPaused = true; 265 private boolean mRestoring; 266 private boolean mWaitingForResult; 267 private boolean mOnResumeNeedsLoad; 268 269 private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>(); 270 private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>(); 271 272 // Keep track of whether the user has left launcher 273 private static boolean sPausedFromUserAction = false; 274 275 private Bundle mSavedInstanceState; 276 277 private LauncherModel mModel; 278 private IconCache mIconCache; 279 private boolean mUserPresent = true; 280 private boolean mVisible = false; 281 private boolean mAttached = false; 282 private static final boolean DISABLE_CLINGS = true; 283 284 private static LocaleConfiguration sLocaleConfiguration = null; 285 286 private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>(); 287 288 private Intent mAppMarketIntent = null; 289 290 // Related to the auto-advancing of widgets 291 private final int ADVANCE_MSG = 1; 292 private final int mAdvanceInterval = 20000; 293 private final int mAdvanceStagger = 250; 294 private long mAutoAdvanceSentTime; 295 private long mAutoAdvanceTimeLeft = -1; 296 private HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance = 297 new HashMap<View, AppWidgetProviderInfo>(); 298 299 // Determines how long to wait after a rotation before restoring the screen orientation to 300 // match the sensor state. 301 private final int mRestoreScreenOrientationDelay = 500; 302 303 // External icons saved in case of resource changes, orientation, etc. 304 private static Drawable.ConstantState[] sGlobalSearchIcon = new Drawable.ConstantState[2]; 305 private static Drawable.ConstantState[] sVoiceSearchIcon = new Drawable.ConstantState[2]; 306 private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2]; 307 308 private Drawable mWorkspaceBackgroundDrawable; 309 310 private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>(); 311 312 static final ArrayList<String> sDumpLogs = new ArrayList<String>(); 313 static Date sDateStamp = new Date(); 314 static DateFormat sDateFormat = 315 DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); 316 static long sRunStart = System.currentTimeMillis(); 317 static final String CORRUPTION_EMAIL_SENT_KEY = "corruptionEmailSent"; 318 319 // We only want to get the SharedPreferences once since it does an FS stat each time we get 320 // it from the context. 321 private SharedPreferences mSharedPrefs; 322 323 private static ArrayList<ComponentName> mIntentsOnWorkspaceFromUpgradePath = null; 324 325 // Holds the page that we need to animate to, and the icon views that we need to animate up 326 // when we scroll to that page on resume. 327 private ImageView mFolderIconImageView; 328 private Bitmap mFolderIconBitmap; 329 private Canvas mFolderIconCanvas; 330 private Rect mRectForFolderAnimation = new Rect(); 331 332 private BubbleTextView mWaitingForResume; 333 334 private HideFromAccessibilityHelper mHideFromAccessibilityHelper 335 = new HideFromAccessibilityHelper(); 336 337 private Runnable mBuildLayersRunnable = new Runnable() { 338 public void run() { 339 if (mWorkspace != null) { 340 mWorkspace.buildPageHardwareLayers(); 341 } 342 } 343 }; 344 345 private static ArrayList<PendingAddArguments> sPendingAddList 346 = new ArrayList<PendingAddArguments>(); 347 348 private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); 349 350 private static class PendingAddArguments { 351 int requestCode; 352 Intent intent; 353 long container; 354 long screenId; 355 int cellX; 356 int cellY; 357 } 358 359 private Stats mStats; 360 361 private static boolean isPropertyEnabled(String propertyName) { 362 return Log.isLoggable(propertyName, Log.VERBOSE); 363 } 364 365 @Override 366 protected void onCreate(Bundle savedInstanceState) { 367 if (DEBUG_STRICT_MODE) { 368 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() 369 .detectDiskReads() 370 .detectDiskWrites() 371 .detectNetwork() // or .detectAll() for all detectable problems 372 .penaltyLog() 373 .build()); 374 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 375 .detectLeakedSqlLiteObjects() 376 .detectLeakedClosableObjects() 377 .penaltyLog() 378 .penaltyDeath() 379 .build()); 380 } 381 382 super.onCreate(savedInstanceState); 383 384 LauncherAppState.setApplicationContext(getApplicationContext()); 385 LauncherAppState app = LauncherAppState.getInstance(); 386 387 // Determine the dynamic grid properties 388 Point smallestSize = new Point(); 389 Point largestSize = new Point(); 390 Point realSize = new Point(); 391 Display display = getWindowManager().getDefaultDisplay(); 392 display.getCurrentSizeRange(smallestSize, largestSize); 393 display.getRealSize(realSize); 394 DisplayMetrics dm = new DisplayMetrics(); 395 display.getMetrics(dm); 396 // Lazy-initialize the dynamic grid 397 DeviceProfile grid = app.initDynamicGrid(this, 398 Math.min(smallestSize.x, smallestSize.y), 399 Math.min(largestSize.x, largestSize.y), 400 realSize.x, realSize.y, 401 dm.widthPixels, dm.heightPixels); 402 403 // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet 404 mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), 405 Context.MODE_PRIVATE); 406 mModel = app.setLauncher(this); 407 mIconCache = app.getIconCache(); 408 mDragController = new DragController(this); 409 mInflater = getLayoutInflater(); 410 411 mStats = new Stats(this); 412 413 mAppWidgetManager = AppWidgetManager.getInstance(this); 414 mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); 415 mAppWidgetHost.startListening(); 416 417 // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here, 418 // this also ensures that any synchronous binding below doesn't re-trigger another 419 // LauncherModel load. 420 mPaused = false; 421 422 if (PROFILE_STARTUP) { 423 android.os.Debug.startMethodTracing( 424 Environment.getExternalStorageDirectory() + "/launcher"); 425 } 426 427 checkForLocaleChange(); 428 setContentView(R.layout.launcher); 429 setupViews(); 430 grid.layout(this); 431 showFirstRunWorkspaceCling(); 432 433 registerContentObservers(); 434 435 lockAllApps(); 436 437 mSavedState = savedInstanceState; 438 restoreState(mSavedState); 439 440 // Update customization drawer _after_ restoring the states 441 if (mAppsCustomizeContent != null) { 442 mAppsCustomizeContent.onPackagesUpdated( 443 LauncherModel.getSortedWidgetsAndShortcuts(this)); 444 } 445 446 if (PROFILE_STARTUP) { 447 android.os.Debug.stopMethodTracing(); 448 } 449 450 if (!mRestoring) { 451 if (sPausedFromUserAction) { 452 // If the user leaves launcher, then we should just load items asynchronously when 453 // they return. 454 mModel.startLoader(true, -1); 455 } else { 456 // We only load the page synchronously if the user rotates (or triggers a 457 // configuration change) while launcher is in the foreground 458 mModel.startLoader(true, mWorkspace.getCurrentPage()); 459 } 460 } 461 462 // For handling default keys 463 mDefaultKeySsb = new SpannableStringBuilder(); 464 Selection.setSelection(mDefaultKeySsb, 0); 465 466 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 467 registerReceiver(mCloseSystemDialogsReceiver, filter); 468 469 updateGlobalIcons(); 470 471 // On large interfaces, we want the screen to auto-rotate based on the current orientation 472 unlockScreenOrientation(true); 473 } 474 475 protected void onUserLeaveHint() { 476 super.onUserLeaveHint(); 477 sPausedFromUserAction = true; 478 } 479 480 /** To be overriden by subclasses to hint to Launcher that we have custom content */ 481 protected boolean hasCustomContentToLeft() { 482 return false; 483 } 484 485 private void updateGlobalIcons() { 486 boolean searchVisible = false; 487 boolean voiceVisible = false; 488 // If we have a saved version of these external icons, we load them up immediately 489 int coi = getCurrentOrientationIndexForGlobalIcons(); 490 if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null || 491 sAppMarketIcon[coi] == null) { 492 updateAppMarketIcon(); 493 searchVisible = updateGlobalSearchIcon(); 494 voiceVisible = updateVoiceSearchIcon(searchVisible); 495 } 496 if (sGlobalSearchIcon[coi] != null) { 497 updateGlobalSearchIcon(sGlobalSearchIcon[coi]); 498 searchVisible = true; 499 } 500 if (sVoiceSearchIcon[coi] != null) { 501 updateVoiceSearchIcon(sVoiceSearchIcon[coi]); 502 voiceVisible = true; 503 } 504 if (sAppMarketIcon[coi] != null) { 505 updateAppMarketIcon(sAppMarketIcon[coi]); 506 } 507 if (mSearchDropTargetBar != null) { 508 mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible); 509 } 510 } 511 512 private void checkForLocaleChange() { 513 if (sLocaleConfiguration == null) { 514 new AsyncTask<Void, Void, LocaleConfiguration>() { 515 @Override 516 protected LocaleConfiguration doInBackground(Void... unused) { 517 LocaleConfiguration localeConfiguration = new LocaleConfiguration(); 518 readConfiguration(Launcher.this, localeConfiguration); 519 return localeConfiguration; 520 } 521 522 @Override 523 protected void onPostExecute(LocaleConfiguration result) { 524 sLocaleConfiguration = result; 525 checkForLocaleChange(); // recursive, but now with a locale configuration 526 } 527 }.execute(); 528 return; 529 } 530 531 final Configuration configuration = getResources().getConfiguration(); 532 533 final String previousLocale = sLocaleConfiguration.locale; 534 final String locale = configuration.locale.toString(); 535 536 final int previousMcc = sLocaleConfiguration.mcc; 537 final int mcc = configuration.mcc; 538 539 final int previousMnc = sLocaleConfiguration.mnc; 540 final int mnc = configuration.mnc; 541 542 boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc; 543 544 if (localeChanged) { 545 sLocaleConfiguration.locale = locale; 546 sLocaleConfiguration.mcc = mcc; 547 sLocaleConfiguration.mnc = mnc; 548 549 mIconCache.flush(); 550 551 final LocaleConfiguration localeConfiguration = sLocaleConfiguration; 552 new Thread("WriteLocaleConfiguration") { 553 @Override 554 public void run() { 555 writeConfiguration(Launcher.this, localeConfiguration); 556 } 557 }.start(); 558 } 559 } 560 561 private static class LocaleConfiguration { 562 public String locale; 563 public int mcc = -1; 564 public int mnc = -1; 565 } 566 567 private static void readConfiguration(Context context, LocaleConfiguration configuration) { 568 DataInputStream in = null; 569 try { 570 in = new DataInputStream(context.openFileInput(PREFERENCES)); 571 configuration.locale = in.readUTF(); 572 configuration.mcc = in.readInt(); 573 configuration.mnc = in.readInt(); 574 } catch (FileNotFoundException e) { 575 // Ignore 576 } catch (IOException e) { 577 // Ignore 578 } finally { 579 if (in != null) { 580 try { 581 in.close(); 582 } catch (IOException e) { 583 // Ignore 584 } 585 } 586 } 587 } 588 589 private static void writeConfiguration(Context context, LocaleConfiguration configuration) { 590 DataOutputStream out = null; 591 try { 592 out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE)); 593 out.writeUTF(configuration.locale); 594 out.writeInt(configuration.mcc); 595 out.writeInt(configuration.mnc); 596 out.flush(); 597 } catch (FileNotFoundException e) { 598 // Ignore 599 } catch (IOException e) { 600 //noinspection ResultOfMethodCallIgnored 601 context.getFileStreamPath(PREFERENCES).delete(); 602 } finally { 603 if (out != null) { 604 try { 605 out.close(); 606 } catch (IOException e) { 607 // Ignore 608 } 609 } 610 } 611 } 612 613 public LayoutInflater getInflater() { 614 return mInflater; 615 } 616 617 public DragLayer getDragLayer() { 618 return mDragLayer; 619 } 620 621 boolean isDraggingEnabled() { 622 // We prevent dragging when we are loading the workspace as it is possible to pick up a view 623 // that is subsequently removed from the workspace in startBinding(). 624 return !mModel.isLoadingWorkspace(); 625 } 626 627 static int getScreen() { 628 synchronized (sLock) { 629 return sScreen; 630 } 631 } 632 633 static void setScreen(int screen) { 634 synchronized (sLock) { 635 sScreen = screen; 636 } 637 } 638 639 /** 640 * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have 641 * a configuration step, this allows the proper animations to run after other transitions. 642 */ 643 private boolean completeAdd(PendingAddArguments args) { 644 boolean result = false; 645 switch (args.requestCode) { 646 case REQUEST_PICK_APPLICATION: 647 completeAddApplication(args.intent, args.container, args.screenId, args.cellX, 648 args.cellY); 649 break; 650 case REQUEST_PICK_SHORTCUT: 651 processShortcut(args.intent); 652 break; 653 case REQUEST_CREATE_SHORTCUT: 654 completeAddShortcut(args.intent, args.container, args.screenId, args.cellX, 655 args.cellY); 656 result = true; 657 break; 658 case REQUEST_CREATE_APPWIDGET: 659 int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 660 completeAddAppWidget(appWidgetId, args.container, args.screenId, null, null); 661 result = true; 662 break; 663 case REQUEST_PICK_WALLPAPER: 664 // We just wanted the activity result here so we can clear mWaitingForResult 665 break; 666 } 667 // Before adding this resetAddInfo(), after a shortcut was added to a workspace screen, 668 // if you turned the screen off and then back while in All Apps, Launcher would not 669 // return to the workspace. Clearing mAddInfo.container here fixes this issue 670 resetAddInfo(); 671 return result; 672 } 673 674 @Override 675 protected void onActivityResult( 676 final int requestCode, final int resultCode, final Intent data) { 677 // Reset the startActivity waiting flag 678 mWaitingForResult = false; 679 680 if (requestCode == REQUEST_BIND_APPWIDGET) { 681 int appWidgetId = data != null ? 682 data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; 683 if (resultCode == RESULT_CANCELED) { 684 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId); 685 } else if (resultCode == RESULT_OK) { 686 addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo); 687 } 688 return; 689 } 690 boolean delayExitSpringLoadedMode = false; 691 boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET || 692 requestCode == REQUEST_CREATE_APPWIDGET); 693 694 // We have special handling for widgets 695 if (isWidgetDrop) { 696 int appWidgetId = data != null ? 697 data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; 698 if (appWidgetId < 0) { 699 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \\" + 700 "widget configuration activity."); 701 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId); 702 } else { 703 completeTwoStageWidgetDrop(resultCode, appWidgetId); 704 } 705 return; 706 } 707 708 // The pattern used here is that a user PICKs a specific application, 709 // which, depending on the target, might need to CREATE the actual target. 710 711 // For example, the user would PICK_SHORTCUT for "Music playlist", and we 712 // launch over to the Music app to actually CREATE_SHORTCUT. 713 if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) { 714 final PendingAddArguments args = new PendingAddArguments(); 715 args.requestCode = requestCode; 716 args.intent = data; 717 args.container = mPendingAddInfo.container; 718 args.screenId = mPendingAddInfo.screenId; 719 args.cellX = mPendingAddInfo.cellX; 720 args.cellY = mPendingAddInfo.cellY; 721 if (isWorkspaceLocked()) { 722 sPendingAddList.add(args); 723 } else { 724 delayExitSpringLoadedMode = completeAdd(args); 725 } 726 } 727 mDragLayer.clearAnimatedView(); 728 // Exit spring loaded mode if necessary after cancelling the configuration of a widget 729 exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode, 730 null); 731 } 732 733 private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) { 734 CellLayout cellLayout = 735 (CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId); 736 Runnable onCompleteRunnable = null; 737 int animationType = 0; 738 739 AppWidgetHostView boundWidget = null; 740 if (resultCode == RESULT_OK) { 741 animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION; 742 final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId, 743 mPendingAddWidgetInfo); 744 boundWidget = layout; 745 onCompleteRunnable = new Runnable() { 746 @Override 747 public void run() { 748 completeAddAppWidget(appWidgetId, mPendingAddInfo.container, 749 mPendingAddInfo.screenId, layout, null); 750 exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false, 751 null); 752 } 753 }; 754 } else if (resultCode == RESULT_CANCELED) { 755 animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION; 756 onCompleteRunnable = new Runnable() { 757 @Override 758 public void run() { 759 exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false, 760 null); 761 } 762 }; 763 } 764 if (mDragLayer.getAnimatedView() != null) { 765 mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout, 766 (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable, 767 animationType, boundWidget, true); 768 } else { 769 // The animated view may be null in the case of a rotation during widget configuration 770 onCompleteRunnable.run(); 771 } 772 } 773 774 @Override 775 protected void onStop() { 776 super.onStop(); 777 FirstFrameAnimatorHelper.setIsVisible(false); 778 } 779 780 @Override 781 protected void onStart() { 782 super.onStart(); 783 FirstFrameAnimatorHelper.setIsVisible(true); 784 } 785 786 @Override 787 protected void onResume() { 788 long startTime = 0; 789 if (DEBUG_RESUME_TIME) { 790 startTime = System.currentTimeMillis(); 791 Log.v(TAG, "Launcher.onResume()"); 792 } 793 super.onResume(); 794 795 // Restore the previous launcher state 796 if (mOnResumeState == State.WORKSPACE) { 797 showWorkspace(false); 798 } else if (mOnResumeState == State.APPS_CUSTOMIZE) { 799 showAllApps(false, AppsCustomizePagedView.ContentType.Applications); 800 } 801 mOnResumeState = State.NONE; 802 803 // Background was set to gradient in onPause(), restore to black if in all apps. 804 setWorkspaceBackground(mState == State.WORKSPACE); 805 806 // Process any items that were added while Launcher was away 807 InstallShortcutReceiver.disableAndFlushInstallQueue(this); 808 809 mPaused = false; 810 sPausedFromUserAction = false; 811 if (mRestoring || mOnResumeNeedsLoad) { 812 mWorkspaceLoading = true; 813 mModel.startLoader(true, -1); 814 mRestoring = false; 815 mOnResumeNeedsLoad = false; 816 } 817 if (mBindOnResumeCallbacks.size() > 0) { 818 // We might have postponed some bind calls until onResume (see waitUntilResume) -- 819 // execute them here 820 long startTimeCallbacks = 0; 821 if (DEBUG_RESUME_TIME) { 822 startTimeCallbacks = System.currentTimeMillis(); 823 } 824 825 if (mAppsCustomizeContent != null) { 826 mAppsCustomizeContent.setBulkBind(true); 827 } 828 for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) { 829 mBindOnResumeCallbacks.get(i).run(); 830 } 831 if (mAppsCustomizeContent != null) { 832 mAppsCustomizeContent.setBulkBind(false); 833 } 834 mBindOnResumeCallbacks.clear(); 835 if (DEBUG_RESUME_TIME) { 836 Log.d(TAG, "Time spent processing callbacks in onResume: " + 837 (System.currentTimeMillis() - startTimeCallbacks)); 838 } 839 } 840 if (mOnResumeCallbacks.size() > 0) { 841 for (int i = 0; i < mOnResumeCallbacks.size(); i++) { 842 mOnResumeCallbacks.get(i).run(); 843 } 844 mOnResumeCallbacks.clear(); 845 } 846 847 // Reset the pressed state of icons that were locked in the press state while activities 848 // were launching 849 if (mWaitingForResume != null) { 850 // Resets the previous workspace icon press state 851 mWaitingForResume.setStayPressed(false); 852 } 853 if (mAppsCustomizeContent != null) { 854 // Resets the previous all apps icon press state 855 mAppsCustomizeContent.resetDrawableState(); 856 } 857 // It is possible that widgets can receive updates while launcher is not in the foreground. 858 // Consequently, the widgets will be inflated in the orientation of the foreground activity 859 // (framework issue). On resuming, we ensure that any widgets are inflated for the current 860 // orientation. 861 getWorkspace().reinflateWidgetsIfNecessary(); 862 863 // Again, as with the above scenario, it's possible that one or more of the global icons 864 // were updated in the wrong orientation. 865 updateGlobalIcons(); 866 if (DEBUG_RESUME_TIME) { 867 Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime)); 868 } 869 870 // Write all the logs to disk 871 Launcher.addDumpLog(TAG, "10249126 - onResume() - dumping logs to disk", true); 872 dumpLogsToLocalData(false); 873 } 874 875 @Override 876 protected void onPause() { 877 // NOTE: We want all transitions from launcher to act as if the wallpaper were enabled 878 // to be consistent. So re-enable the flag here, and we will re-disable it as necessary 879 // when Launcher resumes and we are still in AllApps. 880 updateWallpaperVisibility(true); 881 882 // Ensure that items added to Launcher are queued until Launcher returns 883 InstallShortcutReceiver.enableInstallQueue(); 884 885 super.onPause(); 886 mPaused = true; 887 mDragController.cancelDrag(); 888 mDragController.resetLastGestureUpTime(); 889 890 // Write all the logs to disk 891 Launcher.addDumpLog(TAG, "10249126 - onPause() - dumping logs to disk", true); 892 dumpLogsToLocalData(false); 893 } 894 895 protected void onFinishBindingItems() { 896 } 897 898 QSBScroller mQsbScroller = new QSBScroller() { 899 int scrollY = 0; 900 901 @Override 902 public void setScrollY(int scroll) { 903 scrollY = scroll; 904 905 if (mWorkspace.isOnOrMovingToCustomContent()) { 906 mSearchDropTargetBar.setTranslationY(- scrollY); 907 } 908 } 909 }; 910 911 public void resetQSBScroll() { 912 mSearchDropTargetBar.animate().translationY(0).start(); 913 } 914 915 public interface CustomContentCallbacks { 916 // Custom content is completely shown 917 public void onShow(); 918 919 // Custom content is completely hidden 920 public void onHide(); 921 922 // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing). 923 public void onScrollProgressChanged(float progress); 924 } 925 926 protected void startSettings() { 927 } 928 929 public interface QSBScroller { 930 public void setScrollY(int scrollY); 931 } 932 933 // Add a fullscreen unpadded view to the workspace to the left all other screens. 934 public QSBScroller addToCustomContentPage(View customContent) { 935 return addToCustomContentPage(customContent, null); 936 } 937 938 public QSBScroller addToCustomContentPage(View customContent, 939 CustomContentCallbacks callbacks) { 940 mWorkspace.addToCustomContentPage(customContent, callbacks); 941 return mQsbScroller; 942 } 943 944 // The custom content needs to offset its content to account for the QSB 945 public int getTopOffsetForCustomContent() { 946 return mWorkspace.getPaddingTop(); 947 } 948 949 @Override 950 public Object onRetainNonConfigurationInstance() { 951 // Flag the loader to stop early before switching 952 mModel.stopLoader(); 953 if (mAppsCustomizeContent != null) { 954 mAppsCustomizeContent.surrender(); 955 } 956 return Boolean.TRUE; 957 } 958 959 // We can't hide the IME if it was forced open. So don't bother 960 /* 961 @Override 962 public void onWindowFocusChanged(boolean hasFocus) { 963 super.onWindowFocusChanged(hasFocus); 964 965 if (hasFocus) { 966 final InputMethodManager inputManager = (InputMethodManager) 967 getSystemService(Context.INPUT_METHOD_SERVICE); 968 WindowManager.LayoutParams lp = getWindow().getAttributes(); 969 inputManager.hideSoftInputFromWindow(lp.token, 0, new android.os.ResultReceiver(new 970 android.os.Handler()) { 971 protected void onReceiveResult(int resultCode, Bundle resultData) { 972 Log.d(TAG, "ResultReceiver got resultCode=" + resultCode); 973 } 974 }); 975 Log.d(TAG, "called hideSoftInputFromWindow from onWindowFocusChanged"); 976 } 977 } 978 */ 979 980 private boolean acceptFilter() { 981 final InputMethodManager inputManager = (InputMethodManager) 982 getSystemService(Context.INPUT_METHOD_SERVICE); 983 return !inputManager.isFullscreenMode(); 984 } 985 986 @Override 987 public boolean onKeyDown(int keyCode, KeyEvent event) { 988 final int uniChar = event.getUnicodeChar(); 989 final boolean handled = super.onKeyDown(keyCode, event); 990 final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar); 991 if (!handled && acceptFilter() && isKeyNotWhitespace) { 992 boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb, 993 keyCode, event); 994 if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) { 995 // something usable has been typed - start a search 996 // the typed text will be retrieved and cleared by 997 // showSearchDialog() 998 // If there are multiple keystrokes before the search dialog takes focus, 999 // onSearchRequested() will be called for every keystroke, 1000 // but it is idempotent, so it's fine. 1001 return onSearchRequested(); 1002 } 1003 } 1004 1005 // Eat the long press event so the keyboard doesn't come up. 1006 if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) { 1007 return true; 1008 } 1009 1010 return handled; 1011 } 1012 1013 private String getTypedText() { 1014 return mDefaultKeySsb.toString(); 1015 } 1016 1017 private void clearTypedText() { 1018 mDefaultKeySsb.clear(); 1019 mDefaultKeySsb.clearSpans(); 1020 Selection.setSelection(mDefaultKeySsb, 0); 1021 } 1022 1023 /** 1024 * Given the integer (ordinal) value of a State enum instance, convert it to a variable of type 1025 * State 1026 */ 1027 private static State intToState(int stateOrdinal) { 1028 State state = State.WORKSPACE; 1029 final State[] stateValues = State.values(); 1030 for (int i = 0; i < stateValues.length; i++) { 1031 if (stateValues[i].ordinal() == stateOrdinal) { 1032 state = stateValues[i]; 1033 break; 1034 } 1035 } 1036 return state; 1037 } 1038 1039 /** 1040 * Restores the previous state, if it exists. 1041 * 1042 * @param savedState The previous state. 1043 */ 1044 private void restoreState(Bundle savedState) { 1045 if (savedState == null) { 1046 return; 1047 } 1048 1049 State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal())); 1050 if (state == State.APPS_CUSTOMIZE) { 1051 mOnResumeState = State.APPS_CUSTOMIZE; 1052 } 1053 1054 int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1); 1055 if (currentScreen > -1) { 1056 mWorkspace.setRestorePage(currentScreen); 1057 } 1058 1059 final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1); 1060 final long pendingAddScreen = savedState.getLong(RUNTIME_STATE_PENDING_ADD_SCREEN, -1); 1061 1062 if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) { 1063 mPendingAddInfo.container = pendingAddContainer; 1064 mPendingAddInfo.screenId = pendingAddScreen; 1065 mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X); 1066 mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y); 1067 mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X); 1068 mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y); 1069 mPendingAddWidgetInfo = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO); 1070 mWaitingForResult = true; 1071 mRestoring = true; 1072 } 1073 1074 boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false); 1075 if (renameFolder) { 1076 long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID); 1077 mFolderInfo = mModel.getFolderById(this, sFolders, id); 1078 mRestoring = true; 1079 } 1080 1081 // Restore the AppsCustomize tab 1082 if (mAppsCustomizeTabHost != null) { 1083 String curTab = savedState.getString("apps_customize_currentTab"); 1084 if (curTab != null) { 1085 mAppsCustomizeTabHost.setContentTypeImmediate( 1086 mAppsCustomizeTabHost.getContentTypeForTabTag(curTab)); 1087 mAppsCustomizeContent.loadAssociatedPages( 1088 mAppsCustomizeContent.getCurrentPage()); 1089 } 1090 1091 int currentIndex = savedState.getInt("apps_customize_currentIndex"); 1092 mAppsCustomizeContent.restorePageForIndex(currentIndex); 1093 } 1094 } 1095 1096 /** 1097 * Finds all the views we need and configure them properly. 1098 */ 1099 private void setupViews() { 1100 final DragController dragController = mDragController; 1101 1102 mLauncherView = findViewById(R.id.launcher); 1103 mDragLayer = (DragLayer) findViewById(R.id.drag_layer); 1104 mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace); 1105 1106 mLauncherView.setSystemUiVisibility( 1107 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 1108 mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg); 1109 1110 // Setup the drag layer 1111 mDragLayer.setup(this, dragController); 1112 1113 // Setup the hotseat 1114 mHotseat = (Hotseat) findViewById(R.id.hotseat); 1115 if (mHotseat != null) { 1116 mHotseat.setup(this); 1117 } 1118 1119 mOverviewPanel = findViewById(R.id.overview_panel); 1120 findViewById(R.id.widget_button).setOnClickListener(new OnClickListener() { 1121 @Override 1122 public void onClick(View arg0) { 1123 showAllApps(true, AppsCustomizePagedView.ContentType.Widgets); 1124 } 1125 }); 1126 findViewById(R.id.wallpaper_button).setOnClickListener(new OnClickListener() { 1127 @Override 1128 public void onClick(View arg0) { 1129 startWallpaper(); 1130 } 1131 }); 1132 findViewById(R.id.settings_button).setOnClickListener(new OnClickListener() { 1133 @Override 1134 public void onClick(View arg0) { 1135 startSettings(); 1136 } 1137 }); 1138 1139 // Setup the workspace 1140 mWorkspace.setHapticFeedbackEnabled(false); 1141 mWorkspace.setOnLongClickListener(this); 1142 mWorkspace.setup(dragController); 1143 dragController.addDragListener(mWorkspace); 1144 1145 // Get the search/delete bar 1146 mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.qsb_bar); 1147 1148 // Setup AppsCustomize 1149 mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane); 1150 mAppsCustomizeContent = (AppsCustomizePagedView) 1151 mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content); 1152 mAppsCustomizeContent.setup(this, dragController); 1153 1154 // Setup the drag controller (drop targets have to be added in reverse order in priority) 1155 dragController.setDragScoller(mWorkspace); 1156 dragController.setScrollView(mDragLayer); 1157 dragController.setMoveTarget(mWorkspace); 1158 dragController.addDropTarget(mWorkspace); 1159 if (mSearchDropTargetBar != null) { 1160 mSearchDropTargetBar.setup(this, dragController); 1161 } 1162 1163 if (getResources().getBoolean(R.bool.debug_memory_enabled)) { 1164 Log.v(TAG, "adding WeightWatcher"); 1165 mWeightWatcher = new WeightWatcher(this); 1166 mWeightWatcher.setAlpha(0.5f); 1167 ((FrameLayout) mLauncherView).addView(mWeightWatcher, 1168 new FrameLayout.LayoutParams( 1169 FrameLayout.LayoutParams.MATCH_PARENT, 1170 FrameLayout.LayoutParams.WRAP_CONTENT, 1171 Gravity.BOTTOM) 1172 ); 1173 1174 boolean show = shouldShowWeightWatcher(); 1175 mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE); 1176 } 1177 } 1178 1179 /** 1180 * Creates a view representing a shortcut. 1181 * 1182 * @param info The data structure describing the shortcut. 1183 * 1184 * @return A View inflated from R.layout.application. 1185 */ 1186 View createShortcut(ShortcutInfo info) { 1187 return createShortcut(R.layout.application, 1188 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info); 1189 } 1190 1191 /** 1192 * Creates a view representing a shortcut inflated from the specified resource. 1193 * 1194 * @param layoutResId The id of the XML layout used to create the shortcut. 1195 * @param parent The group the shortcut belongs to. 1196 * @param info The data structure describing the shortcut. 1197 * 1198 * @return A View inflated from layoutResId. 1199 */ 1200 View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) { 1201 BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false); 1202 favorite.applyFromShortcutInfo(info, mIconCache); 1203 favorite.setOnClickListener(this); 1204 return favorite; 1205 } 1206 1207 /** 1208 * Add an application shortcut to the workspace. 1209 * 1210 * @param data The intent describing the application. 1211 * @param cellInfo The position on screen where to create the shortcut. 1212 */ 1213 void completeAddApplication(Intent data, long container, long screenId, int cellX, int cellY) { 1214 final int[] cellXY = mTmpAddItemCellCoordinates; 1215 final CellLayout layout = getCellLayout(container, screenId); 1216 1217 // First we check if we already know the exact location where we want to add this item. 1218 if (cellX >= 0 && cellY >= 0) { 1219 cellXY[0] = cellX; 1220 cellXY[1] = cellY; 1221 } else if (!layout.findCellForSpan(cellXY, 1, 1)) { 1222 showOutOfSpaceMessage(isHotseatLayout(layout)); 1223 return; 1224 } 1225 1226 final ShortcutInfo info = mModel.getShortcutInfo(getPackageManager(), data, this); 1227 1228 if (info != null) { 1229 info.setActivity(this, data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK | 1230 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 1231 info.container = ItemInfo.NO_ID; 1232 mWorkspace.addApplicationShortcut(info, layout, container, screenId, cellXY[0], cellXY[1], 1233 isWorkspaceLocked(), cellX, cellY); 1234 } else { 1235 Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data); 1236 } 1237 } 1238 1239 /** 1240 * Add a shortcut to the workspace. 1241 * 1242 * @param data The intent describing the shortcut. 1243 * @param cellInfo The position on screen where to create the shortcut. 1244 */ 1245 private void completeAddShortcut(Intent data, long container, long screenId, int cellX, 1246 int cellY) { 1247 int[] cellXY = mTmpAddItemCellCoordinates; 1248 int[] touchXY = mPendingAddInfo.dropPos; 1249 CellLayout layout = getCellLayout(container, screenId); 1250 1251 boolean foundCellSpan = false; 1252 1253 ShortcutInfo info = mModel.infoFromShortcutIntent(this, data, null); 1254 if (info == null) { 1255 return; 1256 } 1257 final View view = createShortcut(info); 1258 1259 // First we check if we already know the exact location where we want to add this item. 1260 if (cellX >= 0 && cellY >= 0) { 1261 cellXY[0] = cellX; 1262 cellXY[1] = cellY; 1263 foundCellSpan = true; 1264 1265 // If appropriate, either create a folder or add to an existing folder 1266 if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0, 1267 true, null,null)) { 1268 return; 1269 } 1270 DragObject dragObject = new DragObject(); 1271 dragObject.dragInfo = info; 1272 if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject, 1273 true)) { 1274 return; 1275 } 1276 } else if (touchXY != null) { 1277 // when dragging and dropping, just find the closest free spot 1278 int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY); 1279 foundCellSpan = (result != null); 1280 } else { 1281 foundCellSpan = layout.findCellForSpan(cellXY, 1, 1); 1282 } 1283 1284 if (!foundCellSpan) { 1285 showOutOfSpaceMessage(isHotseatLayout(layout)); 1286 return; 1287 } 1288 1289 LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1], false); 1290 1291 if (!mRestoring) { 1292 mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, 1293 isWorkspaceLocked()); 1294 } 1295 } 1296 1297 static int[] getSpanForWidget(Context context, ComponentName component, int minWidth, 1298 int minHeight) { 1299 Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null); 1300 // We want to account for the extra amount of padding that we are adding to the widget 1301 // to ensure that it gets the full amount of space that it has requested 1302 int requiredWidth = minWidth + padding.left + padding.right; 1303 int requiredHeight = minHeight + padding.top + padding.bottom; 1304 return CellLayout.rectToCell(requiredWidth, requiredHeight, null); 1305 } 1306 1307 static int[] getSpanForWidget(Context context, AppWidgetProviderInfo info) { 1308 return getSpanForWidget(context, info.provider, info.minWidth, info.minHeight); 1309 } 1310 1311 static int[] getMinSpanForWidget(Context context, AppWidgetProviderInfo info) { 1312 return getSpanForWidget(context, info.provider, info.minResizeWidth, info.minResizeHeight); 1313 } 1314 1315 static int[] getSpanForWidget(Context context, PendingAddWidgetInfo info) { 1316 return getSpanForWidget(context, info.componentName, info.minWidth, info.minHeight); 1317 } 1318 1319 static int[] getMinSpanForWidget(Context context, PendingAddWidgetInfo info) { 1320 return getSpanForWidget(context, info.componentName, info.minResizeWidth, 1321 info.minResizeHeight); 1322 } 1323 1324 /** 1325 * Add a widget to the workspace. 1326 * 1327 * @param appWidgetId The app widget id 1328 * @param cellInfo The position on screen where to create the widget. 1329 */ 1330 private void completeAddAppWidget(final int appWidgetId, long container, long screenId, 1331 AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) { 1332 if (appWidgetInfo == null) { 1333 appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 1334 } 1335 1336 // Calculate the grid spans needed to fit this widget 1337 CellLayout layout = getCellLayout(container, screenId); 1338 1339 int[] minSpanXY = getMinSpanForWidget(this, appWidgetInfo); 1340 int[] spanXY = getSpanForWidget(this, appWidgetInfo); 1341 1342 // Try finding open space on Launcher screen 1343 // We have saved the position to which the widget was dragged-- this really only matters 1344 // if we are placing widgets on a "spring-loaded" screen 1345 int[] cellXY = mTmpAddItemCellCoordinates; 1346 int[] touchXY = mPendingAddInfo.dropPos; 1347 int[] finalSpan = new int[2]; 1348 boolean foundCellSpan = false; 1349 if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) { 1350 cellXY[0] = mPendingAddInfo.cellX; 1351 cellXY[1] = mPendingAddInfo.cellY; 1352 spanXY[0] = mPendingAddInfo.spanX; 1353 spanXY[1] = mPendingAddInfo.spanY; 1354 foundCellSpan = true; 1355 } else if (touchXY != null) { 1356 // when dragging and dropping, just find the closest free spot 1357 int[] result = layout.findNearestVacantArea( 1358 touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0], 1359 spanXY[1], cellXY, finalSpan); 1360 spanXY[0] = finalSpan[0]; 1361 spanXY[1] = finalSpan[1]; 1362 foundCellSpan = (result != null); 1363 } else { 1364 foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]); 1365 } 1366 1367 if (!foundCellSpan) { 1368 if (appWidgetId != -1) { 1369 // Deleting an app widget ID is a void call but writes to disk before returning 1370 // to the caller... 1371 new Thread("deleteAppWidgetId") { 1372 public void run() { 1373 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 1374 } 1375 }.start(); 1376 } 1377 showOutOfSpaceMessage(isHotseatLayout(layout)); 1378 return; 1379 } 1380 1381 // Build Launcher-specific widget info and save to database 1382 LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId, 1383 appWidgetInfo.provider); 1384 launcherInfo.spanX = spanXY[0]; 1385 launcherInfo.spanY = spanXY[1]; 1386 launcherInfo.minSpanX = mPendingAddInfo.minSpanX; 1387 launcherInfo.minSpanY = mPendingAddInfo.minSpanY; 1388 1389 LauncherModel.addItemToDatabase(this, launcherInfo, 1390 container, screenId, cellXY[0], cellXY[1], false); 1391 1392 if (!mRestoring) { 1393 if (hostView == null) { 1394 // Perform actual inflation because we're live 1395 launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 1396 launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo); 1397 } else { 1398 // The AppWidgetHostView has already been inflated and instantiated 1399 launcherInfo.hostView = hostView; 1400 } 1401 1402 launcherInfo.hostView.setTag(launcherInfo); 1403 launcherInfo.hostView.setVisibility(View.VISIBLE); 1404 launcherInfo.notifyWidgetSizeChanged(this); 1405 1406 mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, cellXY[0], cellXY[1], 1407 launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked()); 1408 1409 addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo); 1410 } 1411 resetAddInfo(); 1412 } 1413 1414 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 1415 @Override 1416 public void onReceive(Context context, Intent intent) { 1417 final String action = intent.getAction(); 1418 if (Intent.ACTION_SCREEN_OFF.equals(action)) { 1419 mUserPresent = false; 1420 mDragLayer.clearAllResizeFrames(); 1421 updateRunning(); 1422 1423 // Reset AllApps to its initial state only if we are not in the middle of 1424 // processing a multi-step drop 1425 if (mAppsCustomizeTabHost != null && mPendingAddInfo.container == ItemInfo.NO_ID) { 1426 mAppsCustomizeTabHost.reset(); 1427 showWorkspace(false); 1428 } 1429 } else if (Intent.ACTION_USER_PRESENT.equals(action)) { 1430 mUserPresent = true; 1431 updateRunning(); 1432 } 1433 } 1434 }; 1435 1436 @Override 1437 public void onAttachedToWindow() { 1438 super.onAttachedToWindow(); 1439 1440 // Listen for broadcasts related to user-presence 1441 final IntentFilter filter = new IntentFilter(); 1442 filter.addAction(Intent.ACTION_SCREEN_OFF); 1443 filter.addAction(Intent.ACTION_USER_PRESENT); 1444 registerReceiver(mReceiver, filter); 1445 FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView()); 1446 mAttached = true; 1447 mVisible = true; 1448 } 1449 1450 @Override 1451 public void onDetachedFromWindow() { 1452 super.onDetachedFromWindow(); 1453 mVisible = false; 1454 1455 if (mAttached) { 1456 unregisterReceiver(mReceiver); 1457 mAttached = false; 1458 } 1459 updateRunning(); 1460 } 1461 1462 public void onWindowVisibilityChanged(int visibility) { 1463 mVisible = visibility == View.VISIBLE; 1464 updateRunning(); 1465 // The following code used to be in onResume, but it turns out onResume is called when 1466 // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged 1467 // is a more appropriate event to handle 1468 if (mVisible) { 1469 mAppsCustomizeTabHost.onWindowVisible(); 1470 if (!mWorkspaceLoading) { 1471 final ViewTreeObserver observer = mWorkspace.getViewTreeObserver(); 1472 // We want to let Launcher draw itself at least once before we force it to build 1473 // layers on all the workspace pages, so that transitioning to Launcher from other 1474 // apps is nice and speedy. 1475 observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() { 1476 private boolean mStarted = false; 1477 public void onDraw() { 1478 if (mStarted) return; 1479 mStarted = true; 1480 // We delay the layer building a bit in order to give 1481 // other message processing a time to run. In particular 1482 // this avoids a delay in hiding the IME if it was 1483 // currently shown, because doing that may involve 1484 // some communication back with the app. 1485 mWorkspace.postDelayed(mBuildLayersRunnable, 500); 1486 final ViewTreeObserver.OnDrawListener listener = this; 1487 mWorkspace.post(new Runnable() { 1488 public void run() { 1489 if (mWorkspace != null && 1490 mWorkspace.getViewTreeObserver() != null) { 1491 mWorkspace.getViewTreeObserver(). 1492 removeOnDrawListener(listener); 1493 } 1494 } 1495 }); 1496 return; 1497 } 1498 }); 1499 } 1500 // When Launcher comes back to foreground, a different Activity might be responsible for 1501 // the app market intent, so refresh the icon 1502 updateAppMarketIcon(); 1503 clearTypedText(); 1504 } 1505 } 1506 1507 private void sendAdvanceMessage(long delay) { 1508 mHandler.removeMessages(ADVANCE_MSG); 1509 Message msg = mHandler.obtainMessage(ADVANCE_MSG); 1510 mHandler.sendMessageDelayed(msg, delay); 1511 mAutoAdvanceSentTime = System.currentTimeMillis(); 1512 } 1513 1514 private void updateRunning() { 1515 boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty(); 1516 if (autoAdvanceRunning != mAutoAdvanceRunning) { 1517 mAutoAdvanceRunning = autoAdvanceRunning; 1518 if (autoAdvanceRunning) { 1519 long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft; 1520 sendAdvanceMessage(delay); 1521 } else { 1522 if (!mWidgetsToAdvance.isEmpty()) { 1523 mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval - 1524 (System.currentTimeMillis() - mAutoAdvanceSentTime)); 1525 } 1526 mHandler.removeMessages(ADVANCE_MSG); 1527 mHandler.removeMessages(0); // Remove messages sent using postDelayed() 1528 } 1529 } 1530 } 1531 1532 private final Handler mHandler = new Handler() { 1533 @Override 1534 public void handleMessage(Message msg) { 1535 if (msg.what == ADVANCE_MSG) { 1536 int i = 0; 1537 for (View key: mWidgetsToAdvance.keySet()) { 1538 final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId); 1539 final int delay = mAdvanceStagger * i; 1540 if (v instanceof Advanceable) { 1541 postDelayed(new Runnable() { 1542 public void run() { 1543 ((Advanceable) v).advance(); 1544 } 1545 }, delay); 1546 } 1547 i++; 1548 } 1549 sendAdvanceMessage(mAdvanceInterval); 1550 } 1551 } 1552 }; 1553 1554 void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) { 1555 if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return; 1556 View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId); 1557 if (v instanceof Advanceable) { 1558 mWidgetsToAdvance.put(hostView, appWidgetInfo); 1559 ((Advanceable) v).fyiWillBeAdvancedByHostKThx(); 1560 updateRunning(); 1561 } 1562 } 1563 1564 void removeWidgetToAutoAdvance(View hostView) { 1565 if (mWidgetsToAdvance.containsKey(hostView)) { 1566 mWidgetsToAdvance.remove(hostView); 1567 updateRunning(); 1568 } 1569 } 1570 1571 public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) { 1572 removeWidgetToAutoAdvance(launcherInfo.hostView); 1573 launcherInfo.hostView = null; 1574 } 1575 1576 void showOutOfSpaceMessage(boolean isHotseatLayout) { 1577 int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space); 1578 Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show(); 1579 } 1580 1581 public LauncherAppWidgetHost getAppWidgetHost() { 1582 return mAppWidgetHost; 1583 } 1584 1585 public LauncherModel getModel() { 1586 return mModel; 1587 } 1588 1589 public void closeSystemDialogs() { 1590 getWindow().closeAllPanels(); 1591 1592 // Whatever we were doing is hereby canceled. 1593 mWaitingForResult = false; 1594 } 1595 1596 @Override 1597 protected void onNewIntent(Intent intent) { 1598 long startTime = 0; 1599 if (DEBUG_RESUME_TIME) { 1600 startTime = System.currentTimeMillis(); 1601 } 1602 super.onNewIntent(intent); 1603 1604 // Close the menu 1605 if (Intent.ACTION_MAIN.equals(intent.getAction())) { 1606 // also will cancel mWaitingForResult. 1607 closeSystemDialogs(); 1608 1609 final boolean alreadyOnHome = 1610 ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) 1611 != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); 1612 1613 Runnable processIntent = new Runnable() { 1614 public void run() { 1615 if (mWorkspace == null) { 1616 // Can be cases where mWorkspace is null, this prevents a NPE 1617 return; 1618 } 1619 Folder openFolder = mWorkspace.getOpenFolder(); 1620 // In all these cases, only animate if we're already on home 1621 mWorkspace.exitWidgetResizeMode(); 1622 if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() && 1623 openFolder == null) { 1624 mWorkspace.moveToDefaultScreen(true); 1625 } 1626 1627 closeFolder(); 1628 exitSpringLoadedDragMode(); 1629 1630 // If we are already on home, then just animate back to the workspace, 1631 // otherwise, just wait until onResume to set the state back to Workspace 1632 if (alreadyOnHome) { 1633 showWorkspace(true); 1634 if (mWorkspace.isInOverviewMode()) { 1635 mWorkspace.exitOverviewMode(); 1636 } 1637 } else { 1638 mOnResumeState = State.WORKSPACE; 1639 } 1640 1641 final View v = getWindow().peekDecorView(); 1642 if (v != null && v.getWindowToken() != null) { 1643 InputMethodManager imm = (InputMethodManager)getSystemService( 1644 INPUT_METHOD_SERVICE); 1645 imm.hideSoftInputFromWindow(v.getWindowToken(), 0); 1646 } 1647 1648 // Reset AllApps to its initial state 1649 if (!alreadyOnHome && mAppsCustomizeTabHost != null) { 1650 mAppsCustomizeTabHost.reset(); 1651 } 1652 } 1653 }; 1654 1655 if (alreadyOnHome && !mWorkspace.hasWindowFocus()) { 1656 // Delay processing of the intent to allow the status bar animation to finish 1657 // first in order to avoid janky animations. 1658 mWorkspace.postDelayed(processIntent, 350); 1659 } else { 1660 // Process the intent immediately. 1661 processIntent.run(); 1662 } 1663 1664 } 1665 if (DEBUG_RESUME_TIME) { 1666 Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime)); 1667 } 1668 } 1669 1670 @Override 1671 public void onRestoreInstanceState(Bundle state) { 1672 super.onRestoreInstanceState(state); 1673 for (int page: mSynchronouslyBoundPages) { 1674 mWorkspace.restoreInstanceStateForChild(page); 1675 } 1676 } 1677 1678 @Override 1679 protected void onSaveInstanceState(Bundle outState) { 1680 outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage()); 1681 super.onSaveInstanceState(outState); 1682 1683 outState.putInt(RUNTIME_STATE, mState.ordinal()); 1684 // We close any open folder since it will not be re-opened, and we need to make sure 1685 // this state is reflected. 1686 closeFolder(); 1687 1688 if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 && 1689 mWaitingForResult) { 1690 outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container); 1691 outState.putLong(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screenId); 1692 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX); 1693 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY); 1694 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX); 1695 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY); 1696 outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo); 1697 } 1698 1699 if (mFolderInfo != null && mWaitingForResult) { 1700 outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true); 1701 outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id); 1702 } 1703 1704 // Save the current AppsCustomize tab 1705 if (mAppsCustomizeTabHost != null) { 1706 String currentTabTag = mAppsCustomizeTabHost.getCurrentTabTag(); 1707 if (currentTabTag != null) { 1708 outState.putString("apps_customize_currentTab", currentTabTag); 1709 } 1710 int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex(); 1711 outState.putInt("apps_customize_currentIndex", currentIndex); 1712 } 1713 } 1714 1715 @Override 1716 public void onDestroy() { 1717 super.onDestroy(); 1718 1719 // Remove all pending runnables 1720 mHandler.removeMessages(ADVANCE_MSG); 1721 mHandler.removeMessages(0); 1722 mWorkspace.removeCallbacks(mBuildLayersRunnable); 1723 1724 // Stop callbacks from LauncherModel 1725 LauncherAppState app = (LauncherAppState.getInstance()); 1726 mModel.stopLoader(); 1727 app.setLauncher(null); 1728 1729 try { 1730 mAppWidgetHost.stopListening(); 1731 } catch (NullPointerException ex) { 1732 Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex); 1733 } 1734 mAppWidgetHost = null; 1735 1736 mWidgetsToAdvance.clear(); 1737 1738 TextKeyListener.getInstance().release(); 1739 1740 // Disconnect any of the callbacks and drawables associated with ItemInfos on the workspace 1741 // to prevent leaking Launcher activities on orientation change. 1742 if (mModel != null) { 1743 mModel.unbindItemInfosAndClearQueuedBindRunnables(); 1744 } 1745 1746 getContentResolver().unregisterContentObserver(mWidgetObserver); 1747 unregisterReceiver(mCloseSystemDialogsReceiver); 1748 1749 mDragLayer.clearAllResizeFrames(); 1750 ((ViewGroup) mWorkspace.getParent()).removeAllViews(); 1751 mWorkspace.removeAllViews(); 1752 mWorkspace = null; 1753 mDragController = null; 1754 1755 LauncherAnimUtils.onDestroyActivity(); 1756 } 1757 1758 public DragController getDragController() { 1759 return mDragController; 1760 } 1761 1762 @Override 1763 public void startActivityForResult(Intent intent, int requestCode) { 1764 if (requestCode >= 0) mWaitingForResult = true; 1765 super.startActivityForResult(intent, requestCode); 1766 } 1767 1768 /** 1769 * Indicates that we want global search for this activity by setting the globalSearch 1770 * argument for {@link #startSearch} to true. 1771 */ 1772 @Override 1773 public void startSearch(String initialQuery, boolean selectInitialQuery, 1774 Bundle appSearchData, boolean globalSearch) { 1775 1776 showWorkspace(true); 1777 1778 if (initialQuery == null) { 1779 // Use any text typed in the launcher as the initial query 1780 initialQuery = getTypedText(); 1781 } 1782 if (appSearchData == null) { 1783 appSearchData = new Bundle(); 1784 appSearchData.putString("source", "launcher-search"); 1785 } 1786 Rect sourceBounds = new Rect(); 1787 if (mSearchDropTargetBar != null) { 1788 sourceBounds = mSearchDropTargetBar.getSearchBarBounds(); 1789 } 1790 1791 startSearch(initialQuery, selectInitialQuery, 1792 appSearchData, sourceBounds); 1793 } 1794 1795 public void startSearch(String initialQuery, 1796 boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) { 1797 startGlobalSearch(initialQuery, selectInitialQuery, 1798 appSearchData, sourceBounds); 1799 } 1800 1801 /** 1802 * Starts the global search activity. This code is a copied from SearchManager 1803 */ 1804 private void startGlobalSearch(String initialQuery, 1805 boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) { 1806 final SearchManager searchManager = 1807 (SearchManager) getSystemService(Context.SEARCH_SERVICE); 1808 ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity(); 1809 if (globalSearchActivity == null) { 1810 Log.w(TAG, "No global search activity found."); 1811 return; 1812 } 1813 Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); 1814 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1815 intent.setComponent(globalSearchActivity); 1816 // Make sure that we have a Bundle to put source in 1817 if (appSearchData == null) { 1818 appSearchData = new Bundle(); 1819 } else { 1820 appSearchData = new Bundle(appSearchData); 1821 } 1822 // Set source to package name of app that starts global search, if not set already. 1823 if (!appSearchData.containsKey("source")) { 1824 appSearchData.putString("source", getPackageName()); 1825 } 1826 intent.putExtra(SearchManager.APP_DATA, appSearchData); 1827 if (!TextUtils.isEmpty(initialQuery)) { 1828 intent.putExtra(SearchManager.QUERY, initialQuery); 1829 } 1830 if (selectInitialQuery) { 1831 intent.putExtra(SearchManager.EXTRA_SELECT_QUERY, selectInitialQuery); 1832 } 1833 intent.setSourceBounds(sourceBounds); 1834 try { 1835 startActivity(intent); 1836 } catch (ActivityNotFoundException ex) { 1837 Log.e(TAG, "Global search activity not found: " + globalSearchActivity); 1838 } 1839 } 1840 1841 @Override 1842 public boolean onCreateOptionsMenu(Menu menu) { 1843 if (isWorkspaceLocked()) { 1844 return false; 1845 } 1846 1847 super.onCreateOptionsMenu(menu); 1848 1849 Intent manageApps = new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS); 1850 manageApps.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1851 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 1852 Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS); 1853 settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1854 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 1855 String helpUrl = getString(R.string.help_url); 1856 Intent help = new Intent(Intent.ACTION_VIEW, Uri.parse(helpUrl)); 1857 help.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1858 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 1859 1860 menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper) 1861 .setIcon(android.R.drawable.ic_menu_gallery) 1862 .setAlphabeticShortcut('W'); 1863 menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps) 1864 .setIcon(android.R.drawable.ic_menu_manage) 1865 .setIntent(manageApps) 1866 .setAlphabeticShortcut('M'); 1867 menu.add(0, MENU_SYSTEM_SETTINGS, 0, R.string.menu_settings) 1868 .setIcon(android.R.drawable.ic_menu_preferences) 1869 .setIntent(settings) 1870 .setAlphabeticShortcut('P'); 1871 if (!helpUrl.isEmpty()) { 1872 menu.add(0, MENU_HELP, 0, R.string.menu_help) 1873 .setIcon(android.R.drawable.ic_menu_help) 1874 .setIntent(help) 1875 .setAlphabeticShortcut('H'); 1876 } 1877 return true; 1878 } 1879 1880 @Override 1881 public boolean onPrepareOptionsMenu(Menu menu) { 1882 super.onPrepareOptionsMenu(menu); 1883 1884 if (mAppsCustomizeTabHost.isTransitioning()) { 1885 return false; 1886 } 1887 boolean allAppsVisible = (mAppsCustomizeTabHost.getVisibility() == View.VISIBLE); 1888 menu.setGroupVisible(MENU_GROUP_WALLPAPER, !allAppsVisible); 1889 1890 return true; 1891 } 1892 1893 @Override 1894 public boolean onOptionsItemSelected(MenuItem item) { 1895 switch (item.getItemId()) { 1896 case MENU_WALLPAPER_SETTINGS: 1897 startWallpaper(); 1898 return true; 1899 } 1900 1901 return super.onOptionsItemSelected(item); 1902 } 1903 1904 @Override 1905 public boolean onSearchRequested() { 1906 startSearch(null, false, null, true); 1907 // Use a custom animation for launching search 1908 return true; 1909 } 1910 1911 public boolean isWorkspaceLocked() { 1912 return mWorkspaceLoading || mWaitingForResult; 1913 } 1914 1915 private void resetAddInfo() { 1916 mPendingAddInfo.container = ItemInfo.NO_ID; 1917 mPendingAddInfo.screenId = -1; 1918 mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1; 1919 mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1; 1920 mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1; 1921 mPendingAddInfo.dropPos = null; 1922 } 1923 1924 void addAppWidgetImpl(final int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, 1925 AppWidgetProviderInfo appWidgetInfo) { 1926 if (appWidgetInfo.configure != null) { 1927 mPendingAddWidgetInfo = appWidgetInfo; 1928 1929 // Launch over to configure widget, if needed 1930 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); 1931 intent.setComponent(appWidgetInfo.configure); 1932 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 1933 Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_APPWIDGET); 1934 } else { 1935 // Otherwise just add it 1936 completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget, 1937 appWidgetInfo); 1938 // Exit spring loaded mode if necessary after adding the widget 1939 exitSpringLoadedDragModeDelayed(true, false, null); 1940 } 1941 } 1942 1943 /** 1944 * Process a shortcut drop. 1945 * 1946 * @param componentName The name of the component 1947 * @param screenId The ID of the screen where it should be added 1948 * @param cell The cell it should be added to, optional 1949 * @param position The location on the screen where it was dropped, optional 1950 */ 1951 void processShortcutFromDrop(ComponentName componentName, long container, long screenId, 1952 int[] cell, int[] loc) { 1953 resetAddInfo(); 1954 mPendingAddInfo.container = container; 1955 mPendingAddInfo.screenId = screenId; 1956 mPendingAddInfo.dropPos = loc; 1957 1958 if (cell != null) { 1959 mPendingAddInfo.cellX = cell[0]; 1960 mPendingAddInfo.cellY = cell[1]; 1961 } 1962 1963 Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); 1964 createShortcutIntent.setComponent(componentName); 1965 processShortcut(createShortcutIntent); 1966 } 1967 1968 /** 1969 * Process a widget drop. 1970 * 1971 * @param info The PendingAppWidgetInfo of the widget being added. 1972 * @param screenId The ID of the screen where it should be added 1973 * @param cell The cell it should be added to, optional 1974 * @param position The location on the screen where it was dropped, optional 1975 */ 1976 void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId, 1977 int[] cell, int[] span, int[] loc) { 1978 resetAddInfo(); 1979 mPendingAddInfo.container = info.container = container; 1980 mPendingAddInfo.screenId = info.screenId = screenId; 1981 mPendingAddInfo.dropPos = loc; 1982 mPendingAddInfo.minSpanX = info.minSpanX; 1983 mPendingAddInfo.minSpanY = info.minSpanY; 1984 1985 if (cell != null) { 1986 mPendingAddInfo.cellX = cell[0]; 1987 mPendingAddInfo.cellY = cell[1]; 1988 } 1989 if (span != null) { 1990 mPendingAddInfo.spanX = span[0]; 1991 mPendingAddInfo.spanY = span[1]; 1992 } 1993 1994 AppWidgetHostView hostView = info.boundWidget; 1995 int appWidgetId; 1996 if (hostView != null) { 1997 appWidgetId = hostView.getAppWidgetId(); 1998 addAppWidgetImpl(appWidgetId, info, hostView, info.info); 1999 } else { 2000 // In this case, we either need to start an activity to get permission to bind 2001 // the widget, or we need to start an activity to configure the widget, or both. 2002 appWidgetId = getAppWidgetHost().allocateAppWidgetId(); 2003 Bundle options = info.bindOptions; 2004 2005 boolean success = false; 2006 if (options != null) { 2007 success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, 2008 info.componentName, options); 2009 } else { 2010 success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, 2011 info.componentName); 2012 } 2013 if (success) { 2014 addAppWidgetImpl(appWidgetId, info, null, info.info); 2015 } else { 2016 mPendingAddWidgetInfo = info.info; 2017 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND); 2018 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 2019 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName); 2020 // TODO: we need to make sure that this accounts for the options bundle. 2021 // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); 2022 startActivityForResult(intent, REQUEST_BIND_APPWIDGET); 2023 } 2024 } 2025 } 2026 2027 void processShortcut(Intent intent) { 2028 // Handle case where user selected "Applications" 2029 String applicationName = getResources().getString(R.string.group_applications); 2030 String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 2031 2032 if (applicationName != null && applicationName.equals(shortcutName)) { 2033 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 2034 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 2035 2036 Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); 2037 pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent); 2038 pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application)); 2039 Utilities.startActivityForResultSafely(this, pickIntent, REQUEST_PICK_APPLICATION); 2040 } else { 2041 Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT); 2042 } 2043 } 2044 2045 void processWallpaper(Intent intent) { 2046 startActivityForResult(intent, REQUEST_PICK_WALLPAPER); 2047 } 2048 2049 FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX, 2050 int cellY) { 2051 final FolderInfo folderInfo = new FolderInfo(); 2052 folderInfo.title = getText(R.string.folder_name); 2053 2054 // Update the model 2055 LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId, cellX, cellY, 2056 false); 2057 sFolders.put(folderInfo.id, folderInfo); 2058 2059 // Create the view 2060 FolderIcon newFolder = 2061 FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache); 2062 mWorkspace.addInScreen(newFolder, container, screenId, cellX, cellY, 1, 1, 2063 isWorkspaceLocked()); 2064 // Force measure the new folder icon 2065 CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder); 2066 parent.getShortcutsAndWidgets().measureChild(newFolder); 2067 return newFolder; 2068 } 2069 2070 void removeFolder(FolderInfo folder) { 2071 sFolders.remove(folder.id); 2072 } 2073 2074 protected void startWallpaper() { 2075 showWorkspace(true); 2076 final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); 2077 pickWallpaper.setComponent(getWallpaperPickerComponent()); 2078 startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER); 2079 } 2080 2081 protected ComponentName getWallpaperPickerComponent() { 2082 return new ComponentName(getPackageName(), WallpaperPickerActivity.class.getName()); 2083 } 2084 2085 /** 2086 * Registers various content observers. The current implementation registers 2087 * only a favorites observer to keep track of the favorites applications. 2088 */ 2089 private void registerContentObservers() { 2090 ContentResolver resolver = getContentResolver(); 2091 resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI, 2092 true, mWidgetObserver); 2093 } 2094 2095 @Override 2096 public boolean dispatchKeyEvent(KeyEvent event) { 2097 if (event.getAction() == KeyEvent.ACTION_DOWN) { 2098 switch (event.getKeyCode()) { 2099 case KeyEvent.KEYCODE_HOME: 2100 return true; 2101 case KeyEvent.KEYCODE_VOLUME_DOWN: 2102 if (isPropertyEnabled(DUMP_STATE_PROPERTY)) { 2103 dumpState(); 2104 return true; 2105 } 2106 break; 2107 } 2108 } else if (event.getAction() == KeyEvent.ACTION_UP) { 2109 switch (event.getKeyCode()) { 2110 case KeyEvent.KEYCODE_HOME: 2111 return true; 2112 } 2113 } 2114 2115 return super.dispatchKeyEvent(event); 2116 } 2117 2118 @Override 2119 public void onBackPressed() { 2120 if (isAllAppsVisible()) { 2121 showWorkspace(true); 2122 } else if (mWorkspace.isInOverviewMode()) { 2123 mWorkspace.exitOverviewMode(); 2124 } else if (mWorkspace.getOpenFolder() != null) { 2125 Folder openFolder = mWorkspace.getOpenFolder(); 2126 if (openFolder.isEditingName()) { 2127 openFolder.dismissEditingName(); 2128 } else { 2129 closeFolder(); 2130 } 2131 } else { 2132 mWorkspace.exitWidgetResizeMode(); 2133 2134 // Back button is a no-op here, but give at least some feedback for the button press 2135 mWorkspace.showOutlinesTemporarily(); 2136 } 2137 } 2138 2139 /** 2140 * Re-listen when widgets are reset. 2141 */ 2142 private void onAppWidgetReset() { 2143 if (mAppWidgetHost != null) { 2144 mAppWidgetHost.startListening(); 2145 } 2146 } 2147 2148 /** 2149 * Launches the intent referred by the clicked shortcut. 2150 * 2151 * @param v The view representing the clicked shortcut. 2152 */ 2153 public void onClick(View v) { 2154 // Make sure that rogue clicks don't get through while allapps is launching, or after the 2155 // view has detached (it's possible for this to happen if the view is removed mid touch). 2156 if (v.getWindowToken() == null) { 2157 return; 2158 } 2159 2160 if (!mWorkspace.isFinishedSwitchingState()) { 2161 return; 2162 } 2163 2164 if (v instanceof PageIndicator) { 2165 if (!mWorkspace.isInOverviewMode()) { 2166 mWorkspace.enterOverviewMode(); 2167 } 2168 return; 2169 } 2170 2171 if (v instanceof CellLayout) { 2172 if (mWorkspace.isInOverviewMode()) { 2173 mWorkspace.exitOverviewMode(mWorkspace.indexOfChild(v)); 2174 } 2175 } 2176 2177 Object tag = v.getTag(); 2178 if (tag instanceof ShortcutInfo) { 2179 // Open shortcut 2180 final ShortcutInfo shortcut = (ShortcutInfo) tag; 2181 final Intent intent = shortcut.intent; 2182 2183 // Check for special shortcuts 2184 if (intent.getComponent() != null) { 2185 final String shortcutClass = intent.getComponent().getClassName(); 2186 2187 if (shortcutClass.equals(WidgetAdder.class.getName())) { 2188 showAllApps(true, AppsCustomizePagedView.ContentType.Widgets); 2189 return; 2190 } else if (shortcutClass.equals(MemoryDumpActivity.class.getName())) { 2191 MemoryDumpActivity.startDump(this); 2192 return; 2193 } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) { 2194 toggleShowWeightWatcher(); 2195 return; 2196 } 2197 } 2198 2199 // Start activities 2200 int[] pos = new int[2]; 2201 v.getLocationOnScreen(pos); 2202 intent.setSourceBounds(new Rect(pos[0], pos[1], 2203 pos[0] + v.getWidth(), pos[1] + v.getHeight())); 2204 2205 boolean success = startActivitySafely(v, intent, tag); 2206 2207 mStats.recordLaunch(intent, shortcut); 2208 2209 if (success && v instanceof BubbleTextView) { 2210 mWaitingForResume = (BubbleTextView) v; 2211 mWaitingForResume.setStayPressed(true); 2212 } 2213 } else if (tag instanceof FolderInfo) { 2214 if (v instanceof FolderIcon) { 2215 FolderIcon fi = (FolderIcon) v; 2216 handleFolderClick(fi); 2217 } 2218 } else if (v == mAllAppsButton) { 2219 if (isAllAppsVisible()) { 2220 showWorkspace(true); 2221 } else { 2222 onClickAllAppsButton(v); 2223 } 2224 } 2225 } 2226 2227 public boolean onTouch(View v, MotionEvent event) { 2228 return false; 2229 } 2230 2231 /** 2232 * Event handler for the search button 2233 * 2234 * @param v The view that was clicked. 2235 */ 2236 public void onClickSearchButton(View v) { 2237 v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 2238 2239 onSearchRequested(); 2240 } 2241 2242 /** 2243 * Event handler for the voice button 2244 * 2245 * @param v The view that was clicked. 2246 */ 2247 public void onClickVoiceButton(View v) { 2248 v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 2249 2250 startVoice(); 2251 } 2252 2253 public void startVoice() { 2254 try { 2255 final SearchManager searchManager = 2256 (SearchManager) getSystemService(Context.SEARCH_SERVICE); 2257 ComponentName activityName = searchManager.getGlobalSearchActivity(); 2258 Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); 2259 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2260 if (activityName != null) { 2261 intent.setPackage(activityName.getPackageName()); 2262 } 2263 startActivity(null, intent, "onClickVoiceButton"); 2264 } catch (ActivityNotFoundException e) { 2265 Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); 2266 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2267 startActivitySafely(null, intent, "onClickVoiceButton"); 2268 } 2269 } 2270 2271 /** 2272 * Event handler for the "grid" button that appears on the home screen, which 2273 * enters all apps mode. 2274 * 2275 * @param v The view that was clicked. 2276 */ 2277 public void onClickAllAppsButton(View v) { 2278 showAllApps(true, AppsCustomizePagedView.ContentType.Applications); 2279 } 2280 2281 public void onTouchDownAllAppsButton(View v) { 2282 // Provide the same haptic feedback that the system offers for virtual keys. 2283 v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 2284 } 2285 2286 public void onClickAppMarketButton(View v) { 2287 if (mAppMarketIntent != null) { 2288 startActivitySafely(v, mAppMarketIntent, "app market"); 2289 } else { 2290 Log.e(TAG, "Invalid app market intent."); 2291 } 2292 } 2293 2294 void startApplicationDetailsActivity(ComponentName componentName) { 2295 String packageName = componentName.getPackageName(); 2296 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 2297 Uri.fromParts("package", packageName, null)); 2298 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 2299 startActivitySafely(null, intent, "startApplicationDetailsActivity"); 2300 } 2301 2302 // returns true if the activity was started 2303 boolean startApplicationUninstallActivity(ComponentName componentName, int flags) { 2304 if ((flags & AppInfo.DOWNLOADED_FLAG) == 0) { 2305 // System applications cannot be installed. For now, show a toast explaining that. 2306 // We may give them the option of disabling apps this way. 2307 int messageId = R.string.uninstall_system_app_text; 2308 Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show(); 2309 return false; 2310 } else { 2311 String packageName = componentName.getPackageName(); 2312 String className = componentName.getClassName(); 2313 Intent intent = new Intent( 2314 Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className)); 2315 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 2316 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 2317 startActivity(intent); 2318 return true; 2319 } 2320 } 2321 2322 boolean startActivity(View v, Intent intent, Object tag) { 2323 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2324 2325 try { 2326 // Only launch using the new animation if the shortcut has not opted out (this is a 2327 // private contract between launcher and may be ignored in the future). 2328 boolean useLaunchAnimation = (v != null) && 2329 !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION); 2330 if (useLaunchAnimation) { 2331 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0, 2332 v.getMeasuredWidth(), v.getMeasuredHeight()); 2333 2334 startActivity(intent, opts.toBundle()); 2335 } else { 2336 startActivity(intent); 2337 } 2338 return true; 2339 } catch (SecurityException e) { 2340 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 2341 Log.e(TAG, "Launcher does not have the permission to launch " + intent + 2342 ". Make sure to create a MAIN intent-filter for the corresponding activity " + 2343 "or use the exported attribute for this activity. " 2344 + "tag="+ tag + " intent=" + intent, e); 2345 } 2346 return false; 2347 } 2348 2349 boolean startActivitySafely(View v, Intent intent, Object tag) { 2350 boolean success = false; 2351 try { 2352 success = startActivity(v, intent, tag); 2353 } catch (ActivityNotFoundException e) { 2354 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 2355 Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e); 2356 } 2357 return success; 2358 } 2359 2360 private void handleFolderClick(FolderIcon folderIcon) { 2361 final FolderInfo info = folderIcon.getFolderInfo(); 2362 Folder openFolder = mWorkspace.getFolderForTag(info); 2363 2364 // If the folder info reports that the associated folder is open, then verify that 2365 // it is actually opened. There have been a few instances where this gets out of sync. 2366 if (info.opened && openFolder == null) { 2367 Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: " 2368 + info.screenId + " (" + info.cellX + ", " + info.cellY + ")"); 2369 info.opened = false; 2370 } 2371 2372 if (!info.opened && !folderIcon.getFolder().isDestroyed()) { 2373 // Close any open folder 2374 closeFolder(); 2375 // Open the requested folder 2376 openFolder(folderIcon); 2377 } else { 2378 // Find the open folder... 2379 int folderScreen; 2380 if (openFolder != null) { 2381 folderScreen = mWorkspace.getPageForView(openFolder); 2382 // .. and close it 2383 closeFolder(openFolder); 2384 if (folderScreen != mWorkspace.getCurrentPage()) { 2385 // Close any folder open on the current screen 2386 closeFolder(); 2387 // Pull the folder onto this screen 2388 openFolder(folderIcon); 2389 } 2390 } 2391 } 2392 } 2393 2394 /** 2395 * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView 2396 * in the DragLayer in the exact absolute location of the original FolderIcon. 2397 */ 2398 private void copyFolderIconToImage(FolderIcon fi) { 2399 final int width = fi.getMeasuredWidth(); 2400 final int height = fi.getMeasuredHeight(); 2401 2402 // Lazy load ImageView, Bitmap and Canvas 2403 if (mFolderIconImageView == null) { 2404 mFolderIconImageView = new ImageView(this); 2405 } 2406 if (mFolderIconBitmap == null || mFolderIconBitmap.getWidth() != width || 2407 mFolderIconBitmap.getHeight() != height) { 2408 mFolderIconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 2409 mFolderIconCanvas = new Canvas(mFolderIconBitmap); 2410 } 2411 2412 DragLayer.LayoutParams lp; 2413 if (mFolderIconImageView.getLayoutParams() instanceof DragLayer.LayoutParams) { 2414 lp = (DragLayer.LayoutParams) mFolderIconImageView.getLayoutParams(); 2415 } else { 2416 lp = new DragLayer.LayoutParams(width, height); 2417 } 2418 2419 // The layout from which the folder is being opened may be scaled, adjust the starting 2420 // view size by this scale factor. 2421 float scale = mDragLayer.getDescendantRectRelativeToSelf(fi, mRectForFolderAnimation); 2422 lp.customPosition = true; 2423 lp.x = mRectForFolderAnimation.left; 2424 lp.y = mRectForFolderAnimation.top; 2425 lp.width = (int) (scale * width); 2426 lp.height = (int) (scale * height); 2427 2428 mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR); 2429 fi.draw(mFolderIconCanvas); 2430 mFolderIconImageView.setImageBitmap(mFolderIconBitmap); 2431 if (fi.getFolder() != null) { 2432 mFolderIconImageView.setPivotX(fi.getFolder().getPivotXForIconAnimation()); 2433 mFolderIconImageView.setPivotY(fi.getFolder().getPivotYForIconAnimation()); 2434 } 2435 // Just in case this image view is still in the drag layer from a previous animation, 2436 // we remove it and re-add it. 2437 if (mDragLayer.indexOfChild(mFolderIconImageView) != -1) { 2438 mDragLayer.removeView(mFolderIconImageView); 2439 } 2440 mDragLayer.addView(mFolderIconImageView, lp); 2441 if (fi.getFolder() != null) { 2442 fi.getFolder().bringToFront(); 2443 } 2444 } 2445 2446 private void growAndFadeOutFolderIcon(FolderIcon fi) { 2447 if (fi == null) return; 2448 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0); 2449 PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f); 2450 PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f); 2451 2452 FolderInfo info = (FolderInfo) fi.getTag(); 2453 if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 2454 CellLayout cl = (CellLayout) fi.getParent().getParent(); 2455 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) fi.getLayoutParams(); 2456 cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY); 2457 } 2458 2459 // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original 2460 copyFolderIconToImage(fi); 2461 fi.setVisibility(View.INVISIBLE); 2462 2463 ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha, 2464 scaleX, scaleY); 2465 oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration)); 2466 oa.start(); 2467 } 2468 2469 private void shrinkAndFadeInFolderIcon(final FolderIcon fi) { 2470 if (fi == null) return; 2471 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f); 2472 PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f); 2473 PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f); 2474 2475 final CellLayout cl = (CellLayout) fi.getParent().getParent(); 2476 2477 // We remove and re-draw the FolderIcon in-case it has changed 2478 mDragLayer.removeView(mFolderIconImageView); 2479 copyFolderIconToImage(fi); 2480 ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha, 2481 scaleX, scaleY); 2482 oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration)); 2483 oa.addListener(new AnimatorListenerAdapter() { 2484 @Override 2485 public void onAnimationEnd(Animator animation) { 2486 if (cl != null) { 2487 cl.clearFolderLeaveBehind(); 2488 // Remove the ImageView copy of the FolderIcon and make the original visible. 2489 mDragLayer.removeView(mFolderIconImageView); 2490 fi.setVisibility(View.VISIBLE); 2491 } 2492 } 2493 }); 2494 oa.start(); 2495 } 2496 2497 /** 2498 * Opens the user folder described by the specified tag. The opening of the folder 2499 * is animated relative to the specified View. If the View is null, no animation 2500 * is played. 2501 * 2502 * @param folderInfo The FolderInfo describing the folder to open. 2503 */ 2504 public void openFolder(FolderIcon folderIcon) { 2505 Folder folder = folderIcon.getFolder(); 2506 FolderInfo info = folder.mInfo; 2507 2508 info.opened = true; 2509 2510 // Just verify that the folder hasn't already been added to the DragLayer. 2511 // There was a one-off crash where the folder had a parent already. 2512 if (folder.getParent() == null) { 2513 mDragLayer.addView(folder); 2514 mDragController.addDropTarget((DropTarget) folder); 2515 } else { 2516 Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" + 2517 folder.getParent() + ")."); 2518 } 2519 folder.animateOpen(); 2520 growAndFadeOutFolderIcon(folderIcon); 2521 2522 // Notify the accessibility manager that this folder "window" has appeared and occluded 2523 // the workspace items 2524 folder.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 2525 getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 2526 } 2527 2528 public void closeFolder() { 2529 Folder folder = mWorkspace.getOpenFolder(); 2530 if (folder != null) { 2531 if (folder.isEditingName()) { 2532 folder.dismissEditingName(); 2533 } 2534 closeFolder(folder); 2535 2536 // Dismiss the folder cling 2537 dismissFolderCling(null); 2538 } 2539 } 2540 2541 void closeFolder(Folder folder) { 2542 folder.getInfo().opened = false; 2543 2544 ViewGroup parent = (ViewGroup) folder.getParent().getParent(); 2545 if (parent != null) { 2546 FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo); 2547 shrinkAndFadeInFolderIcon(fi); 2548 } 2549 folder.animateClosed(); 2550 2551 // Notify the accessibility manager that this folder "window" has disappeard and no 2552 // longer occludeds the workspace items 2553 getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 2554 } 2555 2556 public boolean onLongClick(View v) { 2557 if (!isDraggingEnabled()) return false; 2558 if (isWorkspaceLocked()) return false; 2559 if (mState != State.WORKSPACE) return false; 2560 2561 if (!(v instanceof CellLayout)) { 2562 v = (View) v.getParent().getParent(); 2563 } 2564 2565 resetAddInfo(); 2566 CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag(); 2567 // This happens when long clicking an item with the dpad/trackball 2568 if (longClickCellInfo == null) { 2569 return true; 2570 } 2571 2572 // The hotseat touch handling does not go through Workspace, and we always allow long press 2573 // on hotseat items. 2574 final View itemUnderLongClick = longClickCellInfo.cell; 2575 boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress(); 2576 if (allowLongPress && !mDragController.isDragging()) { 2577 if (itemUnderLongClick == null) { 2578 if (mWorkspace.hasNonCustomEmptyScreens()) { 2579 // User long pressed on empty space 2580 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, 2581 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 2582 // Disabling reordering until we sort out some issues. 2583 if (mWorkspace.isInOverviewMode()) { 2584 mWorkspace.startReordering(v); 2585 } else { 2586 mWorkspace.enterOverviewMode(); 2587 } 2588 } 2589 } else { 2590 if (!(itemUnderLongClick instanceof Folder)) { 2591 // User long pressed on an item 2592 mWorkspace.startDrag(longClickCellInfo); 2593 } 2594 } 2595 } 2596 return true; 2597 } 2598 2599 boolean isHotseatLayout(View layout) { 2600 return mHotseat != null && layout != null && 2601 (layout instanceof CellLayout) && (layout == mHotseat.getLayout()); 2602 } 2603 Hotseat getHotseat() { 2604 return mHotseat; 2605 } 2606 View getOverviewPanel() { 2607 return mOverviewPanel; 2608 } 2609 SearchDropTargetBar getSearchBar() { 2610 return mSearchDropTargetBar; 2611 } 2612 2613 /** 2614 * Returns the CellLayout of the specified container at the specified screen. 2615 */ 2616 CellLayout getCellLayout(long container, long screenId) { 2617 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 2618 if (mHotseat != null) { 2619 return mHotseat.getLayout(); 2620 } else { 2621 return null; 2622 } 2623 } else { 2624 return (CellLayout) mWorkspace.getScreenWithId(screenId); 2625 } 2626 } 2627 2628 Workspace getWorkspace() { 2629 return mWorkspace; 2630 } 2631 2632 public boolean isAllAppsVisible() { 2633 return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE); 2634 } 2635 2636 /** 2637 * Helper method for the cameraZoomIn/cameraZoomOut animations 2638 * @param view The view being animated 2639 * @param scaleFactor The scale factor used for the zoom 2640 */ 2641 private void setPivotsForZoom(View view, float scaleFactor) { 2642 view.setPivotX(view.getWidth() / 2.0f); 2643 view.setPivotY(view.getHeight() / 2.0f); 2644 } 2645 2646 void disableWallpaperIfInAllApps() { 2647 // Only disable it if we are in all apps 2648 if (isAllAppsVisible()) { 2649 if (mAppsCustomizeTabHost != null && 2650 !mAppsCustomizeTabHost.isTransitioning()) { 2651 updateWallpaperVisibility(false); 2652 } 2653 } 2654 } 2655 2656 private void setWorkspaceBackground(boolean workspace) { 2657 mLauncherView.setBackground(workspace ? 2658 mWorkspaceBackgroundDrawable : null); 2659 } 2660 2661 void updateWallpaperVisibility(boolean visible) { 2662 int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0; 2663 int curflags = getWindow().getAttributes().flags 2664 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 2665 if (wpflags != curflags) { 2666 getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER); 2667 } 2668 setWorkspaceBackground(visible); 2669 } 2670 2671 private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) { 2672 if (v instanceof LauncherTransitionable) { 2673 ((LauncherTransitionable) v).onLauncherTransitionPrepare(this, animated, toWorkspace); 2674 } 2675 } 2676 2677 private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) { 2678 if (v instanceof LauncherTransitionable) { 2679 ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace); 2680 } 2681 2682 // Update the workspace transition step as well 2683 dispatchOnLauncherTransitionStep(v, 0f); 2684 } 2685 2686 private void dispatchOnLauncherTransitionStep(View v, float t) { 2687 if (v instanceof LauncherTransitionable) { 2688 ((LauncherTransitionable) v).onLauncherTransitionStep(this, t); 2689 } 2690 } 2691 2692 private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) { 2693 if (v instanceof LauncherTransitionable) { 2694 ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace); 2695 } 2696 2697 // Update the workspace transition step as well 2698 dispatchOnLauncherTransitionStep(v, 1f); 2699 } 2700 2701 /** 2702 * Things to test when changing the following seven functions. 2703 * - Home from workspace 2704 * - from center screen 2705 * - from other screens 2706 * - Home from all apps 2707 * - from center screen 2708 * - from other screens 2709 * - Back from all apps 2710 * - from center screen 2711 * - from other screens 2712 * - Launch app from workspace and quit 2713 * - with back 2714 * - with home 2715 * - Launch app from all apps and quit 2716 * - with back 2717 * - with home 2718 * - Go to a screen that's not the default, then all 2719 * apps, and launch and app, and go back 2720 * - with back 2721 * -with home 2722 * - On workspace, long press power and go back 2723 * - with back 2724 * - with home 2725 * - On all apps, long press power and go back 2726 * - with back 2727 * - with home 2728 * - On workspace, power off 2729 * - On all apps, power off 2730 * - Launch an app and turn off the screen while in that app 2731 * - Go back with home key 2732 * - Go back with back key TODO: make this not go to workspace 2733 * - From all apps 2734 * - From workspace 2735 * - Enter and exit car mode (becuase it causes an extra configuration changed) 2736 * - From all apps 2737 * - From the center workspace 2738 * - From another workspace 2739 */ 2740 2741 /** 2742 * Zoom the camera out from the workspace to reveal 'toView'. 2743 * Assumes that the view to show is anchored at either the very top or very bottom 2744 * of the screen. 2745 */ 2746 private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) { 2747 AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType(); 2748 showAppsCustomizeHelper(animated, springLoaded, contentType); 2749 } 2750 private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded, 2751 final AppsCustomizePagedView.ContentType contentType) { 2752 if (mStateAnimation != null) { 2753 mStateAnimation.setDuration(0); 2754 mStateAnimation.cancel(); 2755 mStateAnimation = null; 2756 } 2757 final Resources res = getResources(); 2758 2759 final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime); 2760 final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime); 2761 final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor); 2762 final View fromView = mWorkspace; 2763 final AppsCustomizeTabHost toView = mAppsCustomizeTabHost; 2764 final int startDelay = 2765 res.getInteger(R.integer.config_workspaceAppsCustomizeAnimationStagger); 2766 2767 setPivotsForZoom(toView, scale); 2768 2769 // Shrink workspaces away if going to AppsCustomize from workspace 2770 Animator workspaceAnim = 2771 mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated); 2772 if (!AppsCustomizePagedView.DISABLE_ALL_APPS) { 2773 // Set the content type for the all apps space 2774 mAppsCustomizeTabHost.setContentTypeImmediate(contentType); 2775 } 2776 2777 if (animated) { 2778 toView.setScaleX(scale); 2779 toView.setScaleY(scale); 2780 final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView); 2781 scaleAnim. 2782 scaleX(1f).scaleY(1f). 2783 setDuration(duration). 2784 setInterpolator(new Workspace.ZoomOutInterpolator()); 2785 2786 toView.setVisibility(View.VISIBLE); 2787 toView.setAlpha(0f); 2788 final ObjectAnimator alphaAnim = LauncherAnimUtils 2789 .ofFloat(toView, "alpha", 0f, 1f) 2790 .setDuration(fadeDuration); 2791 alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f)); 2792 alphaAnim.addUpdateListener(new AnimatorUpdateListener() { 2793 @Override 2794 public void onAnimationUpdate(ValueAnimator animation) { 2795 if (animation == null) { 2796 throw new RuntimeException("animation is null"); 2797 } 2798 float t = (Float) animation.getAnimatedValue(); 2799 dispatchOnLauncherTransitionStep(fromView, t); 2800 dispatchOnLauncherTransitionStep(toView, t); 2801 } 2802 }); 2803 2804 // toView should appear right at the end of the workspace shrink 2805 // animation 2806 mStateAnimation = LauncherAnimUtils.createAnimatorSet(); 2807 mStateAnimation.play(scaleAnim).after(startDelay); 2808 mStateAnimation.play(alphaAnim).after(startDelay); 2809 2810 mStateAnimation.addListener(new AnimatorListenerAdapter() { 2811 boolean animationCancelled = false; 2812 2813 @Override 2814 public void onAnimationStart(Animator animation) { 2815 updateWallpaperVisibility(true); 2816 // Prepare the position 2817 toView.setTranslationX(0.0f); 2818 toView.setTranslationY(0.0f); 2819 toView.setVisibility(View.VISIBLE); 2820 toView.bringToFront(); 2821 } 2822 @Override 2823 public void onAnimationEnd(Animator animation) { 2824 dispatchOnLauncherTransitionEnd(fromView, animated, false); 2825 dispatchOnLauncherTransitionEnd(toView, animated, false); 2826 2827 if (!animationCancelled) { 2828 updateWallpaperVisibility(false); 2829 } 2830 2831 // Hide the search bar 2832 if (mSearchDropTargetBar != null) { 2833 mSearchDropTargetBar.hideSearchBar(false); 2834 } 2835 } 2836 2837 @Override 2838 public void onAnimationCancel(Animator animation) { 2839 animationCancelled = true; 2840 } 2841 }); 2842 2843 if (workspaceAnim != null) { 2844 mStateAnimation.play(workspaceAnim); 2845 } 2846 2847 boolean delayAnim = false; 2848 2849 dispatchOnLauncherTransitionPrepare(fromView, animated, false); 2850 dispatchOnLauncherTransitionPrepare(toView, animated, false); 2851 2852 // If any of the objects being animated haven't been measured/laid out 2853 // yet, delay the animation until we get a layout pass 2854 if ((((LauncherTransitionable) toView).getContent().getMeasuredWidth() == 0) || 2855 (mWorkspace.getMeasuredWidth() == 0) || 2856 (toView.getMeasuredWidth() == 0)) { 2857 delayAnim = true; 2858 } 2859 2860 final AnimatorSet stateAnimation = mStateAnimation; 2861 final Runnable startAnimRunnable = new Runnable() { 2862 public void run() { 2863 // Check that mStateAnimation hasn't changed while 2864 // we waited for a layout/draw pass 2865 if (mStateAnimation != stateAnimation) 2866 return; 2867 setPivotsForZoom(toView, scale); 2868 dispatchOnLauncherTransitionStart(fromView, animated, false); 2869 dispatchOnLauncherTransitionStart(toView, animated, false); 2870 LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView); 2871 } 2872 }; 2873 if (delayAnim) { 2874 final ViewTreeObserver observer = toView.getViewTreeObserver(); 2875 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 2876 public void onGlobalLayout() { 2877 startAnimRunnable.run(); 2878 toView.getViewTreeObserver().removeOnGlobalLayoutListener(this); 2879 } 2880 }); 2881 } else { 2882 startAnimRunnable.run(); 2883 } 2884 } else { 2885 toView.setTranslationX(0.0f); 2886 toView.setTranslationY(0.0f); 2887 toView.setScaleX(1.0f); 2888 toView.setScaleY(1.0f); 2889 toView.setVisibility(View.VISIBLE); 2890 toView.bringToFront(); 2891 2892 if (!springLoaded && !LauncherAppState.getInstance().isScreenLarge()) { 2893 // Hide the search bar 2894 if (mSearchDropTargetBar != null) { 2895 mSearchDropTargetBar.hideSearchBar(false); 2896 } 2897 } 2898 dispatchOnLauncherTransitionPrepare(fromView, animated, false); 2899 dispatchOnLauncherTransitionStart(fromView, animated, false); 2900 dispatchOnLauncherTransitionEnd(fromView, animated, false); 2901 dispatchOnLauncherTransitionPrepare(toView, animated, false); 2902 dispatchOnLauncherTransitionStart(toView, animated, false); 2903 dispatchOnLauncherTransitionEnd(toView, animated, false); 2904 updateWallpaperVisibility(false); 2905 } 2906 } 2907 2908 /** 2909 * Zoom the camera back into the workspace, hiding 'fromView'. 2910 * This is the opposite of showAppsCustomizeHelper. 2911 * @param animated If true, the transition will be animated. 2912 */ 2913 private void hideAppsCustomizeHelper(State toState, final boolean animated, 2914 final boolean springLoaded, final Runnable onCompleteRunnable) { 2915 2916 if (mStateAnimation != null) { 2917 mStateAnimation.setDuration(0); 2918 mStateAnimation.cancel(); 2919 mStateAnimation = null; 2920 } 2921 Resources res = getResources(); 2922 2923 final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime); 2924 final int fadeOutDuration = 2925 res.getInteger(R.integer.config_appsCustomizeFadeOutTime); 2926 final float scaleFactor = (float) 2927 res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor); 2928 final View fromView = mAppsCustomizeTabHost; 2929 final View toView = mWorkspace; 2930 Animator workspaceAnim = null; 2931 if (toState == State.WORKSPACE) { 2932 int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger); 2933 workspaceAnim = mWorkspace.getChangeStateAnimation( 2934 Workspace.State.NORMAL, animated, stagger, -1); 2935 } else if (toState == State.APPS_CUSTOMIZE_SPRING_LOADED) { 2936 workspaceAnim = mWorkspace.getChangeStateAnimation( 2937 Workspace.State.SPRING_LOADED, animated); 2938 } 2939 2940 setPivotsForZoom(fromView, scaleFactor); 2941 updateWallpaperVisibility(true); 2942 showHotseat(animated); 2943 if (animated) { 2944 final LauncherViewPropertyAnimator scaleAnim = 2945 new LauncherViewPropertyAnimator(fromView); 2946 scaleAnim. 2947 scaleX(scaleFactor).scaleY(scaleFactor). 2948 setDuration(duration). 2949 setInterpolator(new Workspace.ZoomInInterpolator()); 2950 2951 final ObjectAnimator alphaAnim = LauncherAnimUtils 2952 .ofFloat(fromView, "alpha", 1f, 0f) 2953 .setDuration(fadeOutDuration); 2954 alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator()); 2955 alphaAnim.addUpdateListener(new AnimatorUpdateListener() { 2956 @Override 2957 public void onAnimationUpdate(ValueAnimator animation) { 2958 float t = 1f - (Float) animation.getAnimatedValue(); 2959 dispatchOnLauncherTransitionStep(fromView, t); 2960 dispatchOnLauncherTransitionStep(toView, t); 2961 } 2962 }); 2963 2964 mStateAnimation = LauncherAnimUtils.createAnimatorSet(); 2965 2966 dispatchOnLauncherTransitionPrepare(fromView, animated, true); 2967 dispatchOnLauncherTransitionPrepare(toView, animated, true); 2968 mAppsCustomizeContent.pauseScrolling(); 2969 2970 mStateAnimation.addListener(new AnimatorListenerAdapter() { 2971 @Override 2972 public void onAnimationEnd(Animator animation) { 2973 updateWallpaperVisibility(true); 2974 fromView.setVisibility(View.GONE); 2975 dispatchOnLauncherTransitionEnd(fromView, animated, true); 2976 dispatchOnLauncherTransitionEnd(toView, animated, true); 2977 if (onCompleteRunnable != null) { 2978 onCompleteRunnable.run(); 2979 } 2980 mAppsCustomizeContent.updateCurrentPageScroll(); 2981 mAppsCustomizeContent.resumeScrolling(); 2982 } 2983 }); 2984 2985 mStateAnimation.playTogether(scaleAnim, alphaAnim); 2986 if (workspaceAnim != null) { 2987 mStateAnimation.play(workspaceAnim); 2988 } 2989 dispatchOnLauncherTransitionStart(fromView, animated, true); 2990 dispatchOnLauncherTransitionStart(toView, animated, true); 2991 LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView); 2992 } else { 2993 fromView.setVisibility(View.GONE); 2994 dispatchOnLauncherTransitionPrepare(fromView, animated, true); 2995 dispatchOnLauncherTransitionStart(fromView, animated, true); 2996 dispatchOnLauncherTransitionEnd(fromView, animated, true); 2997 dispatchOnLauncherTransitionPrepare(toView, animated, true); 2998 dispatchOnLauncherTransitionStart(toView, animated, true); 2999 dispatchOnLauncherTransitionEnd(toView, animated, true); 3000 } 3001 } 3002 3003 @Override 3004 public void onTrimMemory(int level) { 3005 super.onTrimMemory(level); 3006 if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) { 3007 mAppsCustomizeTabHost.onTrimMemory(); 3008 } 3009 } 3010 3011 @Override 3012 public void onWindowFocusChanged(boolean hasFocus) { 3013 if (!hasFocus) { 3014 // When another window occludes launcher (like the notification shade, or recents), 3015 // ensure that we enable the wallpaper flag so that transitions are done correctly. 3016 updateWallpaperVisibility(true); 3017 } else { 3018 // When launcher has focus again, disable the wallpaper if we are in AllApps 3019 mWorkspace.postDelayed(new Runnable() { 3020 @Override 3021 public void run() { 3022 disableWallpaperIfInAllApps(); 3023 } 3024 }, 500); 3025 } 3026 } 3027 3028 void showWorkspace(boolean animated) { 3029 showWorkspace(animated, null); 3030 } 3031 3032 void showWorkspace(boolean animated, Runnable onCompleteRunnable) { 3033 if (mState != State.WORKSPACE) { 3034 boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED); 3035 mWorkspace.setVisibility(View.VISIBLE); 3036 hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable); 3037 3038 // Show the search bar (only animate if we were showing the drop target bar in spring 3039 // loaded mode) 3040 if (mSearchDropTargetBar != null) { 3041 mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode); 3042 } 3043 3044 // Set focus to the AppsCustomize button 3045 if (mAllAppsButton != null) { 3046 mAllAppsButton.requestFocus(); 3047 } 3048 } 3049 3050 // Change the state *after* we've called all the transition code 3051 mState = State.WORKSPACE; 3052 3053 // Resume the auto-advance of widgets 3054 mUserPresent = true; 3055 updateRunning(); 3056 3057 // Send an accessibility event to announce the context change 3058 getWindow().getDecorView() 3059 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 3060 3061 onWorkspaceShown(animated); 3062 } 3063 3064 public void onWorkspaceShown(boolean animated) { 3065 } 3066 3067 void showAllApps(boolean animated, 3068 AppsCustomizePagedView.ContentType contentType) { 3069 if (mState != State.WORKSPACE) return; 3070 3071 showAppsCustomizeHelper(animated, false, contentType); 3072 mAppsCustomizeTabHost.requestFocus(); 3073 3074 // Change the state *after* we've called all the transition code 3075 mState = State.APPS_CUSTOMIZE; 3076 3077 // Pause the auto-advance of widgets until we are out of AllApps 3078 mUserPresent = false; 3079 updateRunning(); 3080 closeFolder(); 3081 3082 // Send an accessibility event to announce the context change 3083 getWindow().getDecorView() 3084 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 3085 } 3086 3087 void enterSpringLoadedDragMode() { 3088 if (isAllAppsVisible()) { 3089 hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true, null); 3090 mState = State.APPS_CUSTOMIZE_SPRING_LOADED; 3091 } 3092 } 3093 3094 void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay, 3095 final Runnable onCompleteRunnable) { 3096 if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return; 3097 3098 mHandler.postDelayed(new Runnable() { 3099 @Override 3100 public void run() { 3101 if (successfulDrop) { 3102 // Before we show workspace, hide all apps again because 3103 // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should 3104 // clean up our state transition functions 3105 mAppsCustomizeTabHost.setVisibility(View.GONE); 3106 showWorkspace(true, onCompleteRunnable); 3107 } else { 3108 exitSpringLoadedDragMode(); 3109 } 3110 } 3111 }, (extendedDelay ? 3112 EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT : 3113 EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT)); 3114 } 3115 3116 void exitSpringLoadedDragMode() { 3117 if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) { 3118 final boolean animated = true; 3119 final boolean springLoaded = true; 3120 showAppsCustomizeHelper(animated, springLoaded); 3121 mState = State.APPS_CUSTOMIZE; 3122 } 3123 // Otherwise, we are not in spring loaded mode, so don't do anything. 3124 } 3125 3126 void lockAllApps() { 3127 // TODO 3128 } 3129 3130 void unlockAllApps() { 3131 // TODO 3132 } 3133 3134 /** 3135 * Shows the hotseat area. 3136 */ 3137 void showHotseat(boolean animated) { 3138 if (!LauncherAppState.getInstance().isScreenLarge()) { 3139 if (animated) { 3140 if (mHotseat.getAlpha() != 1f) { 3141 int duration = 0; 3142 if (mSearchDropTargetBar != null) { 3143 duration = mSearchDropTargetBar.getTransitionInDuration(); 3144 } 3145 mHotseat.animate().alpha(1f).setDuration(duration); 3146 } 3147 } else { 3148 mHotseat.setAlpha(1f); 3149 } 3150 } 3151 } 3152 3153 /** 3154 * Hides the hotseat area. 3155 */ 3156 void hideHotseat(boolean animated) { 3157 if (!LauncherAppState.getInstance().isScreenLarge()) { 3158 if (animated) { 3159 if (mHotseat.getAlpha() != 0f) { 3160 int duration = 0; 3161 if (mSearchDropTargetBar != null) { 3162 duration = mSearchDropTargetBar.getTransitionOutDuration(); 3163 } 3164 mHotseat.animate().alpha(0f).setDuration(duration); 3165 } 3166 } else { 3167 mHotseat.setAlpha(0f); 3168 } 3169 } 3170 } 3171 3172 /** 3173 * Add an item from all apps or customize onto the given workspace screen. 3174 * If layout is null, add to the current screen. 3175 */ 3176 void addExternalItemToScreen(ItemInfo itemInfo, final CellLayout layout) { 3177 if (!mWorkspace.addExternalItemToScreen(itemInfo, layout)) { 3178 showOutOfSpaceMessage(isHotseatLayout(layout)); 3179 } 3180 } 3181 3182 /** Maps the current orientation to an index for referencing orientation correct global icons */ 3183 private int getCurrentOrientationIndexForGlobalIcons() { 3184 // default - 0, landscape - 1 3185 switch (getResources().getConfiguration().orientation) { 3186 case Configuration.ORIENTATION_LANDSCAPE: 3187 return 1; 3188 default: 3189 return 0; 3190 } 3191 } 3192 3193 private Drawable getExternalPackageToolbarIcon(ComponentName activityName, String resourceName) { 3194 try { 3195 PackageManager packageManager = getPackageManager(); 3196 // Look for the toolbar icon specified in the activity meta-data 3197 Bundle metaData = packageManager.getActivityInfo( 3198 activityName, PackageManager.GET_META_DATA).metaData; 3199 if (metaData != null) { 3200 int iconResId = metaData.getInt(resourceName); 3201 if (iconResId != 0) { 3202 Resources res = packageManager.getResourcesForActivity(activityName); 3203 return res.getDrawable(iconResId); 3204 } 3205 } 3206 } catch (NameNotFoundException e) { 3207 // This can happen if the activity defines an invalid drawable 3208 Log.w(TAG, "Failed to load toolbar icon; " + activityName.flattenToShortString() + 3209 " not found", e); 3210 } catch (Resources.NotFoundException nfe) { 3211 // This can happen if the activity defines an invalid drawable 3212 Log.w(TAG, "Failed to load toolbar icon from " + activityName.flattenToShortString(), 3213 nfe); 3214 } 3215 return null; 3216 } 3217 3218 // if successful in getting icon, return it; otherwise, set button to use default drawable 3219 private Drawable.ConstantState updateTextButtonWithIconFromExternalActivity( 3220 int buttonId, ComponentName activityName, int fallbackDrawableId, 3221 String toolbarResourceName) { 3222 Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName); 3223 Resources r = getResources(); 3224 int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width); 3225 int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height); 3226 3227 TextView button = (TextView) findViewById(buttonId); 3228 // If we were unable to find the icon via the meta-data, use a generic one 3229 if (toolbarIcon == null) { 3230 toolbarIcon = r.getDrawable(fallbackDrawableId); 3231 toolbarIcon.setBounds(0, 0, w, h); 3232 if (button != null) { 3233 button.setCompoundDrawables(toolbarIcon, null, null, null); 3234 } 3235 return null; 3236 } else { 3237 toolbarIcon.setBounds(0, 0, w, h); 3238 if (button != null) { 3239 button.setCompoundDrawables(toolbarIcon, null, null, null); 3240 } 3241 return toolbarIcon.getConstantState(); 3242 } 3243 } 3244 3245 // if successful in getting icon, return it; otherwise, set button to use default drawable 3246 private Drawable.ConstantState updateButtonWithIconFromExternalActivity( 3247 int buttonId, ComponentName activityName, int fallbackDrawableId, 3248 String toolbarResourceName) { 3249 ImageView button = (ImageView) findViewById(buttonId); 3250 Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName); 3251 3252 if (button != null) { 3253 // If we were unable to find the icon via the meta-data, use a 3254 // generic one 3255 if (toolbarIcon == null) { 3256 button.setImageResource(fallbackDrawableId); 3257 } else { 3258 button.setImageDrawable(toolbarIcon); 3259 } 3260 } 3261 3262 return toolbarIcon != null ? toolbarIcon.getConstantState() : null; 3263 3264 } 3265 3266 private void updateTextButtonWithDrawable(int buttonId, Drawable d) { 3267 TextView button = (TextView) findViewById(buttonId); 3268 button.setCompoundDrawables(d, null, null, null); 3269 } 3270 3271 private void updateButtonWithDrawable(int buttonId, Drawable.ConstantState d) { 3272 ImageView button = (ImageView) findViewById(buttonId); 3273 button.setImageDrawable(d.newDrawable(getResources())); 3274 } 3275 3276 private void invalidatePressedFocusedStates(View container, View button) { 3277 if (container instanceof HolographicLinearLayout) { 3278 HolographicLinearLayout layout = (HolographicLinearLayout) container; 3279 layout.invalidatePressedFocusedStates(); 3280 } else if (button instanceof HolographicImageView) { 3281 HolographicImageView view = (HolographicImageView) button; 3282 view.invalidatePressedFocusedStates(); 3283 } 3284 } 3285 3286 public View getQsbBar() { 3287 if (mQsbBar == null) { 3288 mQsbBar = mInflater.inflate(R.layout.qsb_bar, mSearchDropTargetBar); 3289 } 3290 return mQsbBar; 3291 } 3292 3293 protected boolean updateGlobalSearchIcon() { 3294 final View searchButtonContainer = findViewById(R.id.search_button_container); 3295 final ImageView searchButton = (ImageView) findViewById(R.id.search_button); 3296 final View voiceButtonContainer = findViewById(R.id.voice_button_container); 3297 final View voiceButton = findViewById(R.id.voice_button); 3298 3299 final SearchManager searchManager = 3300 (SearchManager) getSystemService(Context.SEARCH_SERVICE); 3301 ComponentName activityName = searchManager.getGlobalSearchActivity(); 3302 if (activityName != null) { 3303 int coi = getCurrentOrientationIndexForGlobalIcons(); 3304 sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity( 3305 R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo, 3306 TOOLBAR_SEARCH_ICON_METADATA_NAME); 3307 if (sGlobalSearchIcon[coi] == null) { 3308 sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity( 3309 R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo, 3310 TOOLBAR_ICON_METADATA_NAME); 3311 } 3312 3313 if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.VISIBLE); 3314 searchButton.setVisibility(View.VISIBLE); 3315 invalidatePressedFocusedStates(searchButtonContainer, searchButton); 3316 return true; 3317 } else { 3318 // We disable both search and voice search when there is no global search provider 3319 if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.GONE); 3320 if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE); 3321 searchButton.setVisibility(View.GONE); 3322 voiceButton.setVisibility(View.GONE); 3323 setVoiceButtonProxyVisible(false); 3324 return false; 3325 } 3326 } 3327 3328 protected void updateGlobalSearchIcon(Drawable.ConstantState d) { 3329 final View searchButtonContainer = findViewById(R.id.search_button_container); 3330 final View searchButton = (ImageView) findViewById(R.id.search_button); 3331 updateButtonWithDrawable(R.id.search_button, d); 3332 invalidatePressedFocusedStates(searchButtonContainer, searchButton); 3333 } 3334 3335 protected boolean updateVoiceSearchIcon(boolean searchVisible) { 3336 final View voiceButtonContainer = findViewById(R.id.voice_button_container); 3337 final View voiceButton = findViewById(R.id.voice_button); 3338 3339 // We only show/update the voice search icon if the search icon is enabled as well 3340 final SearchManager searchManager = 3341 (SearchManager) getSystemService(Context.SEARCH_SERVICE); 3342 ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity(); 3343 3344 ComponentName activityName = null; 3345 if (globalSearchActivity != null) { 3346 // Check if the global search activity handles voice search 3347 Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); 3348 intent.setPackage(globalSearchActivity.getPackageName()); 3349 activityName = intent.resolveActivity(getPackageManager()); 3350 } 3351 3352 if (activityName == null) { 3353 // Fallback: check if an activity other than the global search activity 3354 // resolves this 3355 Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); 3356 activityName = intent.resolveActivity(getPackageManager()); 3357 } 3358 if (searchVisible && activityName != null) { 3359 int coi = getCurrentOrientationIndexForGlobalIcons(); 3360 sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity( 3361 R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo, 3362 TOOLBAR_VOICE_SEARCH_ICON_METADATA_NAME); 3363 if (sVoiceSearchIcon[coi] == null) { 3364 sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity( 3365 R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo, 3366 TOOLBAR_ICON_METADATA_NAME); 3367 } 3368 if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.VISIBLE); 3369 voiceButton.setVisibility(View.VISIBLE); 3370 setVoiceButtonProxyVisible(true); 3371 invalidatePressedFocusedStates(voiceButtonContainer, voiceButton); 3372 return true; 3373 } else { 3374 if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE); 3375 voiceButton.setVisibility(View.GONE); 3376 setVoiceButtonProxyVisible(false); 3377 return false; 3378 } 3379 } 3380 3381 protected void updateVoiceSearchIcon(Drawable.ConstantState d) { 3382 final View voiceButtonContainer = findViewById(R.id.voice_button_container); 3383 final View voiceButton = findViewById(R.id.voice_button); 3384 updateButtonWithDrawable(R.id.voice_button, d); 3385 invalidatePressedFocusedStates(voiceButtonContainer, voiceButton); 3386 } 3387 3388 public void setVoiceButtonProxyVisible(boolean visible) { 3389 final View voiceButtonProxy = findViewById(R.id.voice_button_proxy); 3390 if (voiceButtonProxy != null) { 3391 voiceButtonProxy.setVisibility(visible ? View.VISIBLE : View.GONE); 3392 } 3393 } 3394 /** 3395 * Sets the app market icon 3396 */ 3397 private void updateAppMarketIcon() { 3398 final View marketButton = findViewById(R.id.market_button); 3399 Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET); 3400 // Find the app market activity by resolving an intent. 3401 // (If multiple app markets are installed, it will return the ResolverActivity.) 3402 ComponentName activityName = intent.resolveActivity(getPackageManager()); 3403 if (activityName != null) { 3404 int coi = getCurrentOrientationIndexForGlobalIcons(); 3405 mAppMarketIntent = intent; 3406 sAppMarketIcon[coi] = updateTextButtonWithIconFromExternalActivity( 3407 R.id.market_button, activityName, R.drawable.ic_launcher_market_holo, 3408 TOOLBAR_ICON_METADATA_NAME); 3409 marketButton.setVisibility(View.VISIBLE); 3410 } else { 3411 // We should hide and disable the view so that we don't try and restore the visibility 3412 // of it when we swap between drag & normal states from IconDropTarget subclasses. 3413 marketButton.setVisibility(View.GONE); 3414 marketButton.setEnabled(false); 3415 } 3416 } 3417 3418 private void updateAppMarketIcon(Drawable.ConstantState d) { 3419 // Ensure that the new drawable we are creating has the approprate toolbar icon bounds 3420 Resources r = getResources(); 3421 Drawable marketIconDrawable = d.newDrawable(r); 3422 int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width); 3423 int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height); 3424 marketIconDrawable.setBounds(0, 0, w, h); 3425 3426 updateTextButtonWithDrawable(R.id.market_button, marketIconDrawable); 3427 } 3428 3429 @Override 3430 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 3431 final boolean result = super.dispatchPopulateAccessibilityEvent(event); 3432 final List<CharSequence> text = event.getText(); 3433 text.clear(); 3434 // Populate event with a fake title based on the current state. 3435 if (mState == State.APPS_CUSTOMIZE) { 3436 text.add(getString(R.string.all_apps_button_label)); 3437 } else { 3438 text.add(getString(R.string.all_apps_home_button_label)); 3439 } 3440 return result; 3441 } 3442 3443 /** 3444 * Receives notifications when system dialogs are to be closed. 3445 */ 3446 private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver { 3447 @Override 3448 public void onReceive(Context context, Intent intent) { 3449 closeSystemDialogs(); 3450 } 3451 } 3452 3453 /** 3454 * Receives notifications whenever the appwidgets are reset. 3455 */ 3456 private class AppWidgetResetObserver extends ContentObserver { 3457 public AppWidgetResetObserver() { 3458 super(new Handler()); 3459 } 3460 3461 @Override 3462 public void onChange(boolean selfChange) { 3463 onAppWidgetReset(); 3464 } 3465 } 3466 3467 /** 3468 * If the activity is currently paused, signal that we need to run the passed Runnable 3469 * in onResume. 3470 * 3471 * This needs to be called from incoming places where resources might have been loaded 3472 * while we are paused. That is becaues the Configuration might be wrong 3473 * when we're not running, and if it comes back to what it was when we 3474 * were paused, we are not restarted. 3475 * 3476 * Implementation of the method from LauncherModel.Callbacks. 3477 * 3478 * @return true if we are currently paused. The caller might be able to 3479 * skip some work in that case since we will come back again. 3480 */ 3481 private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) { 3482 if (mPaused) { 3483 Log.i(TAG, "Deferring update until onResume"); 3484 if (deletePreviousRunnables) { 3485 while (mBindOnResumeCallbacks.remove(run)) { 3486 } 3487 } 3488 mBindOnResumeCallbacks.add(run); 3489 return true; 3490 } else { 3491 return false; 3492 } 3493 } 3494 3495 private boolean waitUntilResume(Runnable run) { 3496 return waitUntilResume(run, false); 3497 } 3498 3499 public void addOnResumeCallback(Runnable run) { 3500 mOnResumeCallbacks.add(run); 3501 } 3502 3503 /** 3504 * If the activity is currently paused, signal that we need to re-run the loader 3505 * in onResume. 3506 * 3507 * This needs to be called from incoming places where resources might have been loaded 3508 * while we are paused. That is becaues the Configuration might be wrong 3509 * when we're not running, and if it comes back to what it was when we 3510 * were paused, we are not restarted. 3511 * 3512 * Implementation of the method from LauncherModel.Callbacks. 3513 * 3514 * @return true if we are currently paused. The caller might be able to 3515 * skip some work in that case since we will come back again. 3516 */ 3517 public boolean setLoadOnResume() { 3518 if (mPaused) { 3519 Log.i(TAG, "setLoadOnResume"); 3520 mOnResumeNeedsLoad = true; 3521 return true; 3522 } else { 3523 return false; 3524 } 3525 } 3526 3527 /** 3528 * Implementation of the method from LauncherModel.Callbacks. 3529 */ 3530 public int getCurrentWorkspaceScreen() { 3531 if (mWorkspace != null) { 3532 return mWorkspace.getCurrentPage(); 3533 } else { 3534 return SCREEN_COUNT / 2; 3535 } 3536 } 3537 3538 /** 3539 * Refreshes the shortcuts shown on the workspace. 3540 * 3541 * Implementation of the method from LauncherModel.Callbacks. 3542 */ 3543 public void startBinding() { 3544 // If we're starting binding all over again, clear any bind calls we'd postponed in 3545 // the past (see waitUntilResume) -- we don't need them since we're starting binding 3546 // from scratch again 3547 mBindOnResumeCallbacks.clear(); 3548 3549 // Clear the workspace because it's going to be rebound 3550 mWorkspace.clearDropTargets(); 3551 mWorkspace.removeAllWorkspaceScreens(); 3552 3553 mWidgetsToAdvance.clear(); 3554 if (mHotseat != null) { 3555 mHotseat.resetLayout(); 3556 } 3557 } 3558 3559 @Override 3560 public void bindScreens(ArrayList<Long> orderedScreenIds) { 3561 bindAddScreens(orderedScreenIds); 3562 3563 // Create the new empty page 3564 mWorkspace.addExtraEmptyScreen(); 3565 3566 // Create the custom content page (this call updates mDefaultScreen which calls 3567 // setCurrentPage() so ensure that all pages are added before calling this) 3568 if (!mWorkspace.hasCustomContent() && hasCustomContentToLeft()) { 3569 mWorkspace.createCustomContentPage(); 3570 } 3571 } 3572 3573 @Override 3574 public void bindAddScreens(ArrayList<Long> orderedScreenIds) { 3575 int count = orderedScreenIds.size(); 3576 for (int i = 0; i < count; i++) { 3577 Launcher.addDumpLog(TAG, "10249126 - bindAddScreens(" + orderedScreenIds.get(i) + ")", true); 3578 mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i)); 3579 } 3580 } 3581 3582 private boolean shouldShowWeightWatcher() { 3583 String spKey = LauncherAppState.getSharedPreferencesKey(); 3584 SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); 3585 boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, SHOW_WEIGHT_WATCHER_DEFAULT); 3586 3587 return show; 3588 } 3589 3590 private boolean emailSent() { 3591 String spKey = LauncherAppState.getSharedPreferencesKey(); 3592 SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); 3593 boolean show = sp.getBoolean(CORRUPTION_EMAIL_SENT_KEY, false); 3594 return show; 3595 } 3596 3597 private void setEmailSent(boolean sent) { 3598 String spKey = LauncherAppState.getSharedPreferencesKey(); 3599 SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); 3600 3601 SharedPreferences.Editor editor = sp.edit(); 3602 editor.putBoolean(CORRUPTION_EMAIL_SENT_KEY, sent); 3603 editor.commit(); 3604 } 3605 3606 private void toggleShowWeightWatcher() { 3607 String spKey = LauncherAppState.getSharedPreferencesKey(); 3608 SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); 3609 boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, true); 3610 3611 show = !show; 3612 3613 SharedPreferences.Editor editor = sp.edit(); 3614 editor.putBoolean(SHOW_WEIGHT_WATCHER, show); 3615 editor.commit(); 3616 3617 if (mWeightWatcher != null) { 3618 mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE); 3619 } 3620 } 3621 3622 public void bindAppsAdded(final ArrayList<Long> newScreens, 3623 final ArrayList<ItemInfo> addNotAnimated, 3624 final ArrayList<ItemInfo> addAnimated, 3625 final ArrayList<AppInfo> addedApps) { 3626 Runnable r = new Runnable() { 3627 public void run() { 3628 bindAppsAdded(newScreens, addNotAnimated, addAnimated, addedApps); 3629 } 3630 }; 3631 if (waitUntilResume(r)) { 3632 return; 3633 } 3634 3635 Launcher.addDumpLog(TAG, "10249126 - bindAppsAdded(" + newScreens.size() + ")", true); 3636 3637 // Add the new screens 3638 bindAddScreens(newScreens); 3639 3640 // We add the items without animation on non-visible pages, and with 3641 // animations on the new page (which we will try and snap to). 3642 if (!addNotAnimated.isEmpty()) { 3643 bindItems(addNotAnimated, 0, 3644 addNotAnimated.size(), false); 3645 } 3646 if (!addAnimated.isEmpty()) { 3647 bindItems(addAnimated, 0, 3648 addAnimated.size(), true); 3649 } 3650 3651 if (!AppsCustomizePagedView.DISABLE_ALL_APPS && 3652 addedApps != null && mAppsCustomizeContent != null) { 3653 mAppsCustomizeContent.addApps(addedApps); 3654 } 3655 } 3656 3657 /** 3658 * Bind the items start-end from the list. 3659 * 3660 * Implementation of the method from LauncherModel.Callbacks. 3661 */ 3662 public void bindItems(final ArrayList<ItemInfo> shortcuts, final int start, final int end, 3663 final boolean forceAnimateIcons) { 3664 Runnable r = new Runnable() { 3665 public void run() { 3666 bindItems(shortcuts, start, end, forceAnimateIcons); 3667 } 3668 }; 3669 if (waitUntilResume(r)) { 3670 return; 3671 } 3672 3673 // Get the list of added shortcuts and intersect them with the set of shortcuts here 3674 final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet(); 3675 final Collection<Animator> bounceAnims = new ArrayList<Animator>(); 3676 final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation(); 3677 Workspace workspace = mWorkspace; 3678 long newShortcutsScreenId = -1; 3679 for (int i = start; i < end; i++) { 3680 final ItemInfo item = shortcuts.get(i); 3681 3682 // Short circuit if we are loading dock items for a configuration which has no dock 3683 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && 3684 mHotseat == null) { 3685 continue; 3686 } 3687 3688 switch (item.itemType) { 3689 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 3690 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 3691 ShortcutInfo info = (ShortcutInfo) item; 3692 View shortcut = createShortcut(info); 3693 3694 /* 3695 * TODO: FIX collision case 3696 */ 3697 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 3698 CellLayout cl = mWorkspace.getScreenWithId(item.screenId); 3699 if (cl != null && cl.isOccupied(item.cellX, item.cellY)) { 3700 throw new RuntimeException("OCCUPIED"); 3701 } 3702 } 3703 3704 workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX, 3705 item.cellY, 1, 1); 3706 if (animateIcons) { 3707 // Animate all the applications up now 3708 shortcut.setAlpha(0f); 3709 shortcut.setScaleX(0f); 3710 shortcut.setScaleY(0f); 3711 bounceAnims.add(createNewAppBounceAnimation(shortcut, i)); 3712 newShortcutsScreenId = item.screenId; 3713 } 3714 break; 3715 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 3716 FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, 3717 (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), 3718 (FolderInfo) item, mIconCache); 3719 workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX, 3720 item.cellY, 1, 1); 3721 break; 3722 default: 3723 throw new RuntimeException("Invalid Item Type"); 3724 } 3725 } 3726 3727 if (animateIcons) { 3728 // Animate to the correct page 3729 if (newShortcutsScreenId > -1) { 3730 long currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage()); 3731 int newScreenIndex = mWorkspace.getPageIndexForScreenId(newShortcutsScreenId); 3732 if (newShortcutsScreenId != currentScreenId) { 3733 mWorkspace.snapToPage(newScreenIndex); 3734 } 3735 } 3736 3737 // We post the animation slightly delayed to prevent slowdowns when we are loading 3738 // right after we return to launcher. 3739 mWorkspace.postDelayed(new Runnable() { 3740 public void run() { 3741 anim.playTogether(bounceAnims); 3742 anim.start(); 3743 } 3744 }, NEW_APPS_ANIMATION_DELAY); 3745 } 3746 workspace.requestLayout(); 3747 } 3748 3749 /** 3750 * Implementation of the method from LauncherModel.Callbacks. 3751 */ 3752 public void bindFolders(final HashMap<Long, FolderInfo> folders) { 3753 Runnable r = new Runnable() { 3754 public void run() { 3755 bindFolders(folders); 3756 } 3757 }; 3758 if (waitUntilResume(r)) { 3759 return; 3760 } 3761 sFolders.clear(); 3762 sFolders.putAll(folders); 3763 } 3764 3765 /** 3766 * Add the views for a widget to the workspace. 3767 * 3768 * Implementation of the method from LauncherModel.Callbacks. 3769 */ 3770 public void bindAppWidget(final LauncherAppWidgetInfo item) { 3771 Runnable r = new Runnable() { 3772 public void run() { 3773 bindAppWidget(item); 3774 } 3775 }; 3776 if (waitUntilResume(r)) { 3777 return; 3778 } 3779 3780 final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0; 3781 if (DEBUG_WIDGETS) { 3782 Log.d(TAG, "bindAppWidget: " + item); 3783 } 3784 final Workspace workspace = mWorkspace; 3785 3786 final int appWidgetId = item.appWidgetId; 3787 final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 3788 if (DEBUG_WIDGETS) { 3789 Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider); 3790 } 3791 3792 item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 3793 3794 item.hostView.setTag(item); 3795 item.onBindAppWidget(this); 3796 3797 workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX, 3798 item.cellY, item.spanX, item.spanY, false); 3799 addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo); 3800 3801 workspace.requestLayout(); 3802 3803 if (DEBUG_WIDGETS) { 3804 Log.d(TAG, "bound widget id="+item.appWidgetId+" in " 3805 + (SystemClock.uptimeMillis()-start) + "ms"); 3806 } 3807 } 3808 3809 public void onPageBoundSynchronously(int page) { 3810 mSynchronouslyBoundPages.add(page); 3811 } 3812 3813 /** 3814 * Callback saying that there aren't any more items to bind. 3815 * 3816 * Implementation of the method from LauncherModel.Callbacks. 3817 */ 3818 public void finishBindingItems(final boolean upgradePath) { 3819 Runnable r = new Runnable() { 3820 public void run() { 3821 finishBindingItems(upgradePath); 3822 } 3823 }; 3824 if (waitUntilResume(r)) { 3825 return; 3826 } 3827 if (mSavedState != null) { 3828 if (!mWorkspace.hasFocus()) { 3829 mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus(); 3830 } 3831 mSavedState = null; 3832 } 3833 3834 mWorkspace.restoreInstanceStateForRemainingPages(); 3835 3836 // If we received the result of any pending adds while the loader was running (e.g. the 3837 // widget configuration forced an orientation change), process them now. 3838 for (int i = 0; i < sPendingAddList.size(); i++) { 3839 completeAdd(sPendingAddList.get(i)); 3840 } 3841 sPendingAddList.clear(); 3842 3843 // Update the market app icon as necessary (the other icons will be managed in response to 3844 // package changes in bindSearchablesChanged() 3845 updateAppMarketIcon(); 3846 3847 mWorkspaceLoading = false; 3848 if (upgradePath) { 3849 mWorkspace.getUniqueComponents(true, null); 3850 mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null); 3851 } 3852 3853 mWorkspace.post(new Runnable() { 3854 @Override 3855 public void run() { 3856 onFinishBindingItems(); 3857 } 3858 }); 3859 3860 // Write all the logs to disk 3861 Launcher.addDumpLog(TAG, "10249126 - finishBindingItems() - dumping logs to disk", true); 3862 dumpLogsToLocalData(false); 3863 } 3864 3865 private boolean canRunNewAppsAnimation() { 3866 long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime(); 3867 return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000); 3868 } 3869 3870 private ValueAnimator createNewAppBounceAnimation(View v, int i) { 3871 ValueAnimator bounceAnim = LauncherAnimUtils.ofPropertyValuesHolder(v, 3872 PropertyValuesHolder.ofFloat("alpha", 1f), 3873 PropertyValuesHolder.ofFloat("scaleX", 1f), 3874 PropertyValuesHolder.ofFloat("scaleY", 1f)); 3875 bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION); 3876 bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY); 3877 bounceAnim.setInterpolator(new SmoothPagedView.OvershootInterpolator()); 3878 return bounceAnim; 3879 } 3880 3881 @Override 3882 public void bindSearchablesChanged() { 3883 boolean searchVisible = updateGlobalSearchIcon(); 3884 boolean voiceVisible = updateVoiceSearchIcon(searchVisible); 3885 if (mSearchDropTargetBar != null) { 3886 mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible); 3887 } 3888 } 3889 3890 /** 3891 * Add the icons for all apps. 3892 * 3893 * Implementation of the method from LauncherModel.Callbacks. 3894 */ 3895 public void bindAllApplications(final ArrayList<AppInfo> apps) { 3896 if (AppsCustomizePagedView.DISABLE_ALL_APPS) { 3897 if (mIntentsOnWorkspaceFromUpgradePath != null) { 3898 if (LauncherModel.UPGRADE_USE_MORE_APPS_FOLDER) { 3899 getHotseat().addAllAppsFolder(mIconCache, apps, 3900 mIntentsOnWorkspaceFromUpgradePath, Launcher.this, mWorkspace); 3901 } 3902 mIntentsOnWorkspaceFromUpgradePath = null; 3903 } 3904 } else { 3905 if (!AppsCustomizePagedView.DISABLE_ALL_APPS && 3906 mAppsCustomizeContent != null) { 3907 mAppsCustomizeContent.setApps(apps); 3908 } 3909 } 3910 } 3911 3912 @Override 3913 public boolean shouldShowApp(ResolveInfo app) { 3914 return true; 3915 } 3916 3917 /** 3918 * A package was updated. 3919 * 3920 * Implementation of the method from LauncherModel.Callbacks. 3921 */ 3922 public void bindAppsUpdated(final ArrayList<AppInfo> apps) { 3923 Runnable r = new Runnable() { 3924 public void run() { 3925 bindAppsUpdated(apps); 3926 } 3927 }; 3928 if (waitUntilResume(r)) { 3929 return; 3930 } 3931 3932 if (mWorkspace != null) { 3933 mWorkspace.updateShortcuts(apps); 3934 } 3935 3936 if (!AppsCustomizePagedView.DISABLE_ALL_APPS && 3937 mAppsCustomizeContent != null) { 3938 mAppsCustomizeContent.updateApps(apps); 3939 } 3940 } 3941 3942 /** 3943 * A package was uninstalled. We take both the super set of packageNames 3944 * in addition to specific applications to remove, the reason being that 3945 * this can be called when a package is updated as well. In that scenario, 3946 * we only remove specific components from the workspace, where as 3947 * package-removal should clear all items by package name. 3948 * 3949 * Implementation of the method from LauncherModel.Callbacks. 3950 */ 3951 public void bindComponentsRemoved(final ArrayList<String> packageNames, 3952 final ArrayList<AppInfo> appInfos, 3953 final boolean packageRemoved) { 3954 Runnable r = new Runnable() { 3955 public void run() { 3956 bindComponentsRemoved(packageNames, appInfos, packageRemoved); 3957 } 3958 }; 3959 if (waitUntilResume(r)) { 3960 return; 3961 } 3962 3963 if (packageRemoved) { 3964 mWorkspace.removeItemsByPackageName(packageNames); 3965 } else { 3966 mWorkspace.removeItemsByApplicationInfo(appInfos); 3967 } 3968 3969 // Notify the drag controller 3970 mDragController.onAppsRemoved(appInfos, this); 3971 3972 if (!AppsCustomizePagedView.DISABLE_ALL_APPS && 3973 mAppsCustomizeContent != null) { 3974 mAppsCustomizeContent.removeApps(appInfos); 3975 } 3976 } 3977 3978 /** 3979 * A number of packages were updated. 3980 */ 3981 private ArrayList<Object> mWidgetsAndShortcuts; 3982 private Runnable mBindPackagesUpdatedRunnable = new Runnable() { 3983 public void run() { 3984 bindPackagesUpdated(mWidgetsAndShortcuts); 3985 mWidgetsAndShortcuts = null; 3986 } 3987 }; 3988 3989 public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) { 3990 if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) { 3991 mWidgetsAndShortcuts = widgetsAndShortcuts; 3992 return; 3993 } 3994 3995 // Update the widgets pane 3996 if (!AppsCustomizePagedView.DISABLE_ALL_APPS && 3997 mAppsCustomizeContent != null) { 3998 mAppsCustomizeContent.onPackagesUpdated(widgetsAndShortcuts); 3999 } 4000 } 4001 4002 private int mapConfigurationOriActivityInfoOri(int configOri) { 4003 final Display d = getWindowManager().getDefaultDisplay(); 4004 int naturalOri = Configuration.ORIENTATION_LANDSCAPE; 4005 switch (d.getRotation()) { 4006 case Surface.ROTATION_0: 4007 case Surface.ROTATION_180: 4008 // We are currently in the same basic orientation as the natural orientation 4009 naturalOri = configOri; 4010 break; 4011 case Surface.ROTATION_90: 4012 case Surface.ROTATION_270: 4013 // We are currently in the other basic orientation to the natural orientation 4014 naturalOri = (configOri == Configuration.ORIENTATION_LANDSCAPE) ? 4015 Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; 4016 break; 4017 } 4018 4019 int[] oriMap = { 4020 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, 4021 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, 4022 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, 4023 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE 4024 }; 4025 // Since the map starts at portrait, we need to offset if this device's natural orientation 4026 // is landscape. 4027 int indexOffset = 0; 4028 if (naturalOri == Configuration.ORIENTATION_LANDSCAPE) { 4029 indexOffset = 1; 4030 } 4031 return oriMap[(d.getRotation() + indexOffset) % 4]; 4032 } 4033 4034 public boolean isRotationEnabled() { 4035 boolean enableRotation = sForceEnableRotation || 4036 getResources().getBoolean(R.bool.allow_rotation); 4037 return enableRotation; 4038 } 4039 public void lockScreenOrientation() { 4040 if (isRotationEnabled()) { 4041 setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources() 4042 .getConfiguration().orientation)); 4043 } 4044 } 4045 public void unlockScreenOrientation(boolean immediate) { 4046 if (isRotationEnabled()) { 4047 if (immediate) { 4048 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 4049 } else { 4050 mHandler.postDelayed(new Runnable() { 4051 public void run() { 4052 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 4053 } 4054 }, mRestoreScreenOrientationDelay); 4055 } 4056 } 4057 } 4058 4059 /* Cling related */ 4060 private boolean isClingsEnabled() { 4061 if (DISABLE_CLINGS) { 4062 return false; 4063 } 4064 4065 // disable clings when running in a test harness 4066 if(ActivityManager.isRunningInTestHarness()) return false; 4067 4068 // Restricted secondary users (child mode) will potentially have very few apps 4069 // seeded when they start up for the first time. Clings won't work well with that 4070// boolean supportsLimitedUsers = 4071// android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2; 4072// Account[] accounts = AccountManager.get(this).getAccounts(); 4073// if (supportsLimitedUsers && accounts.length == 0) { 4074// UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 4075// Bundle restrictions = um.getUserRestrictions(); 4076// if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) { 4077// return false; 4078// } 4079// } 4080 return true; 4081 } 4082 4083 private Cling initCling(int clingId, int[] positionData, boolean animate, int delay) { 4084 final Cling cling = (Cling) findViewById(clingId); 4085 if (cling != null) { 4086 cling.init(this, positionData); 4087 cling.setVisibility(View.VISIBLE); 4088 cling.setLayerType(View.LAYER_TYPE_HARDWARE, null); 4089 if (animate) { 4090 cling.buildLayer(); 4091 cling.setAlpha(0f); 4092 cling.animate() 4093 .alpha(1f) 4094 .setInterpolator(new AccelerateInterpolator()) 4095 .setDuration(SHOW_CLING_DURATION) 4096 .setStartDelay(delay) 4097 .start(); 4098 } else { 4099 cling.setAlpha(1f); 4100 } 4101 cling.setFocusableInTouchMode(true); 4102 cling.post(new Runnable() { 4103 public void run() { 4104 cling.setFocusable(true); 4105 cling.requestFocus(); 4106 } 4107 }); 4108 mHideFromAccessibilityHelper.setImportantForAccessibilityToNo( 4109 mDragLayer, clingId == R.id.all_apps_cling); 4110 } 4111 return cling; 4112 } 4113 4114 private void dismissCling(final Cling cling, final String flag, int duration) { 4115 // To catch cases where siblings of top-level views are made invisible, just check whether 4116 // the cling is directly set to GONE before dismissing it. 4117 if (cling != null && cling.getVisibility() != View.GONE) { 4118 ObjectAnimator anim = LauncherAnimUtils.ofFloat(cling, "alpha", 0f); 4119 anim.setDuration(duration); 4120 anim.addListener(new AnimatorListenerAdapter() { 4121 public void onAnimationEnd(Animator animation) { 4122 cling.setVisibility(View.GONE); 4123 cling.cleanup(); 4124 // We should update the shared preferences on a background thread 4125 new Thread("dismissClingThread") { 4126 public void run() { 4127 SharedPreferences.Editor editor = mSharedPrefs.edit(); 4128 editor.putBoolean(flag, true); 4129 editor.commit(); 4130 } 4131 }.start(); 4132 }; 4133 }); 4134 anim.start(); 4135 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mDragLayer); 4136 } 4137 } 4138 4139 private void removeCling(int id) { 4140 final View cling = findViewById(id); 4141 if (cling != null) { 4142 final ViewGroup parent = (ViewGroup) cling.getParent(); 4143 parent.post(new Runnable() { 4144 @Override 4145 public void run() { 4146 parent.removeView(cling); 4147 } 4148 }); 4149 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mDragLayer); 4150 } 4151 } 4152 4153 private boolean skipCustomClingIfNoAccounts() { 4154 Cling cling = (Cling) findViewById(R.id.workspace_cling); 4155 boolean customCling = cling.getDrawIdentifier().equals("workspace_custom"); 4156 if (customCling) { 4157 AccountManager am = AccountManager.get(this); 4158 if (am == null) return false; 4159 Account[] accounts = am.getAccountsByType("com.google"); 4160 return accounts.length == 0; 4161 } 4162 return false; 4163 } 4164 4165 public void showFirstRunWorkspaceCling() { 4166 // Enable the clings only if they have not been dismissed before 4167 if (isClingsEnabled() && 4168 !mSharedPrefs.getBoolean(Cling.WORKSPACE_CLING_DISMISSED_KEY, false) && 4169 !skipCustomClingIfNoAccounts() ) { 4170 // If we're not using the default workspace layout, replace workspace cling 4171 // with a custom workspace cling (usually specified in an overlay) 4172 // For now, only do this on tablets 4173 if (mSharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 && 4174 getResources().getBoolean(R.bool.config_useCustomClings)) { 4175 // Use a custom cling 4176 View cling = findViewById(R.id.workspace_cling); 4177 ViewGroup clingParent = (ViewGroup) cling.getParent(); 4178 int clingIndex = clingParent.indexOfChild(cling); 4179 clingParent.removeViewAt(clingIndex); 4180 View customCling = mInflater.inflate(R.layout.custom_workspace_cling, clingParent, false); 4181 clingParent.addView(customCling, clingIndex); 4182 customCling.setId(R.id.workspace_cling); 4183 } 4184 initCling(R.id.workspace_cling, null, false, 0); 4185 } else { 4186 removeCling(R.id.workspace_cling); 4187 } 4188 } 4189 public void showFirstRunAllAppsCling(int[] position) { 4190 // Enable the clings only if they have not been dismissed before 4191 if (isClingsEnabled() && 4192 !mSharedPrefs.getBoolean(Cling.ALLAPPS_CLING_DISMISSED_KEY, false)) { 4193 initCling(R.id.all_apps_cling, position, true, 0); 4194 } else { 4195 removeCling(R.id.all_apps_cling); 4196 } 4197 } 4198 public Cling showFirstRunFoldersCling() { 4199 // Enable the clings only if they have not been dismissed before 4200 if (isClingsEnabled() && 4201 !mSharedPrefs.getBoolean(Cling.FOLDER_CLING_DISMISSED_KEY, false)) { 4202 return initCling(R.id.folder_cling, null, true, 0); 4203 } else { 4204 removeCling(R.id.folder_cling); 4205 return null; 4206 } 4207 } 4208 protected SharedPreferences getSharedPrefs() { 4209 return mSharedPrefs; 4210 } 4211 public boolean isFolderClingVisible() { 4212 Cling cling = (Cling) findViewById(R.id.folder_cling); 4213 if (cling != null) { 4214 return cling.getVisibility() == View.VISIBLE; 4215 } 4216 return false; 4217 } 4218 public void dismissWorkspaceCling(View v) { 4219 Cling cling = (Cling) findViewById(R.id.workspace_cling); 4220 dismissCling(cling, Cling.WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION); 4221 } 4222 public void dismissAllAppsCling(View v) { 4223 Cling cling = (Cling) findViewById(R.id.all_apps_cling); 4224 dismissCling(cling, Cling.ALLAPPS_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION); 4225 } 4226 public void dismissFolderCling(View v) { 4227 Cling cling = (Cling) findViewById(R.id.folder_cling); 4228 dismissCling(cling, Cling.FOLDER_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION); 4229 } 4230 4231 /** 4232 * Prints out out state for debugging. 4233 */ 4234 public void dumpState() { 4235 Log.d(TAG, "BEGIN launcher3 dump state for launcher " + this); 4236 Log.d(TAG, "mSavedState=" + mSavedState); 4237 Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading); 4238 Log.d(TAG, "mRestoring=" + mRestoring); 4239 Log.d(TAG, "mWaitingForResult=" + mWaitingForResult); 4240 Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState); 4241 Log.d(TAG, "sFolders.size=" + sFolders.size()); 4242 mModel.dumpState(); 4243 4244 if (mAppsCustomizeContent != null) { 4245 mAppsCustomizeContent.dumpState(); 4246 } 4247 Log.d(TAG, "END launcher3 dump state"); 4248 } 4249 4250 @Override 4251 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 4252 super.dump(prefix, fd, writer, args); 4253 synchronized (sDumpLogs) { 4254 writer.println(" "); 4255 writer.println("Debug logs: "); 4256 for (int i = 0; i < sDumpLogs.size(); i++) { 4257 writer.println(" " + sDumpLogs.get(i)); 4258 } 4259 } 4260 } 4261 4262 public static void dumpDebugLogsToConsole() { 4263 synchronized (sDumpLogs) { 4264 Log.d(TAG, ""); 4265 Log.d(TAG, "*********************"); 4266 Log.d(TAG, "Launcher debug logs: "); 4267 for (int i = 0; i < sDumpLogs.size(); i++) { 4268 Log.d(TAG, " " + sDumpLogs.get(i)); 4269 } 4270 Log.d(TAG, "*********************"); 4271 Log.d(TAG, ""); 4272 } 4273 } 4274 4275 public static void addDumpLog(String tag, String log, boolean debugLog) { 4276 if (debugLog) { 4277 Log.d(tag, log); 4278 } 4279 sDateStamp.setTime(System.currentTimeMillis()); 4280 synchronized (sDumpLogs) { 4281 sDumpLogs.add(sDateFormat.format(sDateStamp) + ": " + tag + ", " + log); 4282 } 4283 } 4284 4285 public void dumpLogsToLocalData(final boolean email) { 4286 new Thread("DumpLogsToLocalData") { 4287 @Override 4288 public void run() { 4289 boolean success = false; 4290 sDateStamp.setTime(sRunStart); 4291 String FILENAME = sDateStamp.getMonth() + "-" 4292 + sDateStamp.getDay() + "_" 4293 + sDateStamp.getHours() + "-" 4294 + sDateStamp.getMinutes() + "_" 4295 + sDateStamp.getSeconds() + ".txt"; 4296 4297 FileOutputStream fos = null; 4298 File outFile = null; 4299 try { 4300 outFile = new File(getFilesDir(), FILENAME); 4301 outFile.createNewFile(); 4302 fos = new FileOutputStream(outFile); 4303 } catch (Exception e) { 4304 e.printStackTrace(); 4305 } 4306 if (fos != null) { 4307 PrintWriter writer = new PrintWriter(fos); 4308 4309 writer.println(" "); 4310 writer.println("Debug logs: "); 4311 synchronized (sDumpLogs) { 4312 for (int i = 0; i < sDumpLogs.size(); i++) { 4313 writer.println(" " + sDumpLogs.get(i)); 4314 } 4315 } 4316 writer.close(); 4317 } 4318 try { 4319 if (fos != null) { 4320 fos.close(); 4321 success = true; 4322 } 4323 } catch (IOException e) { 4324 e.printStackTrace(); 4325 } 4326 4327 if (success && email) { 4328 if (!emailSent()) { 4329 emailFile(outFile); 4330 } 4331 } 4332 } 4333 }.start(); 4334 } 4335 4336 private void emailFile(File file) { 4337 File publicCopy = new File(Environment.getExternalStorageDirectory(), file.getName()); 4338 try { 4339 copyFile(file, publicCopy); 4340 } catch (IOException e) { 4341 e.printStackTrace(); 4342 return; 4343 } 4344 4345 Intent intent = new Intent(Intent.ACTION_SEND); 4346 intent.setType("text/plain"); 4347 intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"adamcohen@google.com, winsonc@google.com," + 4348 "mikejurka@google"}); 4349 intent.putExtra(Intent.EXTRA_SUBJECT, "Data corruption " + file.getName()); 4350 intent.putExtra(Intent.EXTRA_TEXT, "Data corruption has occurred, logs attached"); 4351 4352 if (!file.exists() || !file.canRead()) { 4353 Toast.makeText(this, "Attachment Error", Toast.LENGTH_SHORT).show(); 4354 finish(); 4355 return; 4356 } 4357 4358 Toast.makeText(this, "Data corruption has occurred, please send e-mail", Toast.LENGTH_LONG); 4359 Uri uri = Uri.fromFile(publicCopy); 4360 intent.putExtra(Intent.EXTRA_STREAM, uri); 4361 startActivity(Intent.createChooser(intent, "Please send logs, consider clearing data")); 4362 4363 setEmailSent(true); 4364 } 4365 4366 public void copyFile(File src, File dst) throws IOException { 4367 InputStream in = new FileInputStream(src); 4368 OutputStream out = new FileOutputStream(dst); 4369 4370 // Transfer bytes from in to out 4371 byte[] buf = new byte[1024]; 4372 int len; 4373 while ((len = in.read(buf)) > 0) { 4374 out.write(buf, 0, len); 4375 } 4376 in.close(); 4377 out.close(); 4378 } 4379} 4380 4381interface LauncherTransitionable { 4382 View getContent(); 4383 void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace); 4384 void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace); 4385 void onLauncherTransitionStep(Launcher l, float t); 4386 void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace); 4387} 4388