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