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