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