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