1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3; 18 19 import static android.content.pm.ActivityInfo.CONFIG_LOCALE; 20 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; 21 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; 22 23 import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR; 24 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; 25 import static com.android.launcher3.LauncherState.ALL_APPS; 26 import static com.android.launcher3.LauncherState.NORMAL; 27 import static com.android.launcher3.LauncherState.OVERVIEW; 28 import static com.android.launcher3.LauncherState.OVERVIEW_PEEK; 29 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD; 30 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; 31 import static com.android.launcher3.logging.LoggerUtils.newTarget; 32 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; 33 import static com.android.launcher3.util.RaceConditionTracker.ENTER; 34 import static com.android.launcher3.util.RaceConditionTracker.EXIT; 35 36 import android.animation.Animator; 37 import android.animation.AnimatorListenerAdapter; 38 import android.animation.AnimatorSet; 39 import android.animation.ObjectAnimator; 40 import android.animation.ValueAnimator; 41 import android.annotation.TargetApi; 42 import android.app.ActivityOptions; 43 import android.appwidget.AppWidgetHostView; 44 import android.appwidget.AppWidgetManager; 45 import android.content.ActivityNotFoundException; 46 import android.content.BroadcastReceiver; 47 import android.content.ComponentCallbacks2; 48 import android.content.Context; 49 import android.content.Intent; 50 import android.content.IntentFilter; 51 import android.content.IntentSender; 52 import android.content.SharedPreferences; 53 import android.content.pm.PackageManager; 54 import android.content.res.Configuration; 55 import android.database.sqlite.SQLiteDatabase; 56 import android.graphics.Point; 57 import android.graphics.Rect; 58 import android.os.Build; 59 import android.os.Bundle; 60 import android.os.Handler; 61 import android.os.Parcelable; 62 import android.os.Process; 63 import android.os.StrictMode; 64 import android.text.TextUtils; 65 import android.text.method.TextKeyListener; 66 import android.util.Log; 67 import android.util.SparseArray; 68 import android.view.Display; 69 import android.view.KeyEvent; 70 import android.view.KeyboardShortcutGroup; 71 import android.view.KeyboardShortcutInfo; 72 import android.view.LayoutInflater; 73 import android.view.Menu; 74 import android.view.View; 75 import android.view.ViewGroup; 76 import android.view.accessibility.AccessibilityEvent; 77 import android.view.animation.OvershootInterpolator; 78 import android.widget.Toast; 79 80 import com.android.launcher3.DropTarget.DragObject; 81 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; 82 import com.android.launcher3.allapps.AllAppsContainerView; 83 import com.android.launcher3.allapps.AllAppsStore; 84 import com.android.launcher3.allapps.AllAppsTransitionController; 85 import com.android.launcher3.allapps.DiscoveryBounce; 86 import com.android.launcher3.anim.PropertyListBuilder; 87 import com.android.launcher3.compat.AppWidgetManagerCompat; 88 import com.android.launcher3.compat.LauncherAppsCompatVO; 89 import com.android.launcher3.config.FeatureFlags; 90 import com.android.launcher3.dot.DotInfo; 91 import com.android.launcher3.dragndrop.DragController; 92 import com.android.launcher3.dragndrop.DragLayer; 93 import com.android.launcher3.dragndrop.DragView; 94 import com.android.launcher3.folder.Folder; 95 import com.android.launcher3.folder.FolderIcon; 96 import com.android.launcher3.folder.FolderIconPreviewVerifier; 97 import com.android.launcher3.graphics.RotationMode; 98 import com.android.launcher3.icons.IconCache; 99 import com.android.launcher3.keyboard.CustomActionsPopup; 100 import com.android.launcher3.keyboard.ViewGroupFocusHelper; 101 import com.android.launcher3.logging.FileLog; 102 import com.android.launcher3.logging.StatsLogUtils; 103 import com.android.launcher3.logging.UserEventDispatcher; 104 import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate; 105 import com.android.launcher3.model.AppLaunchTracker; 106 import com.android.launcher3.model.ModelWriter; 107 import com.android.launcher3.notification.NotificationListener; 108 import com.android.launcher3.popup.PopupContainerWithArrow; 109 import com.android.launcher3.popup.PopupDataProvider; 110 import com.android.launcher3.shortcuts.DeepShortcutManager; 111 import com.android.launcher3.states.InternalStateHandler; 112 import com.android.launcher3.states.RotationHelper; 113 import com.android.launcher3.testing.TestProtocol; 114 import com.android.launcher3.touch.ItemClickHandler; 115 import com.android.launcher3.uioverrides.UiFactory; 116 import com.android.launcher3.userevent.nano.LauncherLogProto; 117 import com.android.launcher3.userevent.nano.LauncherLogProto.Action; 118 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; 119 import com.android.launcher3.userevent.nano.LauncherLogProto.Target; 120 import com.android.launcher3.util.ActivityResultInfo; 121 import com.android.launcher3.util.ComponentKey; 122 import com.android.launcher3.util.IntArray; 123 import com.android.launcher3.util.ItemInfoMatcher; 124 import com.android.launcher3.util.MultiValueAlpha; 125 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; 126 import com.android.launcher3.util.PackageManagerHelper; 127 import com.android.launcher3.util.PackageUserKey; 128 import com.android.launcher3.util.PendingRequestArgs; 129 import com.android.launcher3.util.RaceConditionTracker; 130 import com.android.launcher3.util.SystemUiController; 131 import com.android.launcher3.util.Themes; 132 import com.android.launcher3.util.Thunk; 133 import com.android.launcher3.util.TraceHelper; 134 import com.android.launcher3.util.UiThreadHelper; 135 import com.android.launcher3.util.ViewOnDrawExecutor; 136 import com.android.launcher3.views.ActivityContext; 137 import com.android.launcher3.views.OptionsPopupView; 138 import com.android.launcher3.views.ScrimView; 139 import com.android.launcher3.widget.LauncherAppWidgetHostView; 140 import com.android.launcher3.widget.PendingAddShortcutInfo; 141 import com.android.launcher3.widget.PendingAddWidgetInfo; 142 import com.android.launcher3.widget.PendingAppWidgetHostView; 143 import com.android.launcher3.widget.WidgetAddFlowHandler; 144 import com.android.launcher3.widget.WidgetHostViewLoader; 145 import com.android.launcher3.widget.WidgetListRowEntry; 146 import com.android.launcher3.widget.WidgetsFullSheet; 147 import com.android.launcher3.widget.custom.CustomWidgetParser; 148 149 import java.io.FileDescriptor; 150 import java.io.PrintWriter; 151 import java.util.ArrayList; 152 import java.util.Collection; 153 import java.util.HashMap; 154 import java.util.HashSet; 155 import java.util.List; 156 import java.util.function.Predicate; 157 158 import androidx.annotation.Nullable; 159 160 /** 161 * Default launcher application. 162 */ 163 public class Launcher extends BaseDraggingActivity implements LauncherExterns, 164 LauncherModel.Callbacks, LauncherProviderChangeListener, UserEventDelegate, 165 InvariantDeviceProfile.OnIDPChangeListener { 166 public static final String TAG = "Launcher"; 167 static final boolean LOGD = false; 168 169 static final boolean DEBUG_STRICT_MODE = false; 170 171 private static final int REQUEST_CREATE_SHORTCUT = 1; 172 private static final int REQUEST_CREATE_APPWIDGET = 5; 173 174 private static final int REQUEST_PICK_APPWIDGET = 9; 175 176 private static final int REQUEST_BIND_APPWIDGET = 11; 177 public static final int REQUEST_BIND_PENDING_APPWIDGET = 12; 178 public static final int REQUEST_RECONFIGURE_APPWIDGET = 13; 179 180 private static final int REQUEST_PERMISSION_CALL_PHONE = 14; 181 182 private static final float BOUNCE_ANIMATION_TENSION = 1.3f; 183 184 /** 185 * IntentStarter uses request codes starting with this. This must be greater than all activity 186 * request codes used internally. 187 */ 188 protected static final int REQUEST_LAST = 100; 189 190 // Type: int 191 private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; 192 // Type: int 193 private static final String RUNTIME_STATE = "launcher.state"; 194 // Type: PendingRequestArgs 195 private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args"; 196 // Type: int 197 private static final String RUNTIME_STATE_PENDING_REQUEST_CODE = "launcher.request_code"; 198 // Type: ActivityResultInfo 199 private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result"; 200 // Type: SparseArray<Parcelable> 201 private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel"; 202 public static final String ON_CREATE_EVT = "Launcher.onCreate"; 203 private static final String ON_START_EVT = "Launcher.onStart"; 204 private static final String ON_RESUME_EVT = "Launcher.onResume"; 205 206 private LauncherStateManager mStateManager; 207 208 private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; 209 210 // How long to wait before the new-shortcut animation automatically pans the workspace 211 private static final int NEW_APPS_PAGE_MOVE_DELAY = 500; 212 private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; 213 @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500; 214 215 private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1; 216 private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0; 217 218 private LauncherAppTransitionManager mAppTransitionManager; 219 private Configuration mOldConfig; 220 221 @Thunk Workspace mWorkspace; 222 private View mLauncherView; 223 @Thunk DragLayer mDragLayer; 224 private DragController mDragController; 225 226 private AppWidgetManagerCompat mAppWidgetManager; 227 private LauncherAppWidgetHost mAppWidgetHost; 228 229 private final int[] mTmpAddItemCellCoordinates = new int[2]; 230 231 @Thunk Hotseat mHotseat; 232 233 private DropTargetBar mDropTargetBar; 234 235 // Main container view for the all apps screen. 236 @Thunk AllAppsContainerView mAppsView; 237 AllAppsTransitionController mAllAppsController; 238 239 // Scrim view for the all apps and overview state. 240 @Thunk ScrimView mScrimView; 241 242 // UI and state for the overview panel 243 private View mOverviewPanel; 244 245 @Thunk boolean mWorkspaceLoading = true; 246 247 private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>(); 248 249 private ViewOnDrawExecutor mPendingExecutor; 250 251 private LauncherModel mModel; 252 private ModelWriter mModelWriter; 253 private IconCache mIconCache; 254 private LauncherAccessibilityDelegate mAccessibilityDelegate; 255 256 private PopupDataProvider mPopupDataProvider; 257 258 private int mSynchronouslyBoundPage = PagedView.INVALID_PAGE; 259 260 // We only want to get the SharedPreferences once since it does an FS stat each time we get 261 // it from the context. 262 private SharedPreferences mSharedPrefs; 263 264 // Activity result which needs to be processed after workspace has loaded. 265 private ActivityResultInfo mPendingActivityResult; 266 /** 267 * Holds extra information required to handle a result from an external call, like 268 * {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)} 269 */ 270 private PendingRequestArgs mPendingRequestArgs; 271 // Request id for any pending activity result 272 private int mPendingActivityRequestCode = -1; 273 274 public ViewGroupFocusHelper mFocusHandler; 275 276 private RotationHelper mRotationHelper; 277 private Runnable mCancelTouchController; 278 279 final Handler mHandler = new Handler(); 280 private final Runnable mHandleDeferredResume = this::handleDeferredResume; 281 private boolean mDeferredResumePending; 282 283 private float mCurrentAssistantVisibility = 0f; 284 285 private DeviceProfile mStableDeviceProfile; 286 private RotationMode mRotationMode = RotationMode.NORMAL; 287 288 @Override onCreate(Bundle savedInstanceState)289 protected void onCreate(Bundle savedInstanceState) { 290 RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER); 291 if (DEBUG_STRICT_MODE) { 292 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() 293 .detectDiskReads() 294 .detectDiskWrites() 295 .detectNetwork() // or .detectAll() for all detectable problems 296 .penaltyLog() 297 .build()); 298 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 299 .detectLeakedSqlLiteObjects() 300 .detectLeakedClosableObjects() 301 .penaltyLog() 302 .penaltyDeath() 303 .build()); 304 } 305 TraceHelper.beginSection("Launcher-onCreate"); 306 307 super.onCreate(savedInstanceState); 308 TraceHelper.partitionSection("Launcher-onCreate", "super call"); 309 310 LauncherAppState app = LauncherAppState.getInstance(this); 311 mOldConfig = new Configuration(getResources().getConfiguration()); 312 mModel = app.setLauncher(this); 313 mRotationHelper = new RotationHelper(this); 314 InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); 315 initDeviceProfile(idp); 316 idp.addOnChangeListener(this); 317 mSharedPrefs = Utilities.getPrefs(this); 318 mIconCache = app.getIconCache(); 319 mAccessibilityDelegate = new LauncherAccessibilityDelegate(this); 320 321 mDragController = new DragController(this); 322 mAllAppsController = new AllAppsTransitionController(this); 323 mStateManager = new LauncherStateManager(this); 324 UiFactory.onCreate(this); 325 326 mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); 327 328 mAppWidgetHost = new LauncherAppWidgetHost(this); 329 mAppWidgetHost.startListening(); 330 331 mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null); 332 333 setupViews(); 334 mPopupDataProvider = new PopupDataProvider(this); 335 336 mAppTransitionManager = LauncherAppTransitionManager.newInstance(this); 337 338 boolean internalStateHandled = InternalStateHandler.handleCreate(this, getIntent()); 339 if (internalStateHandled) { 340 if (savedInstanceState != null) { 341 // InternalStateHandler has already set the appropriate state. 342 // We dont need to do anything. 343 savedInstanceState.remove(RUNTIME_STATE); 344 } 345 } 346 restoreState(savedInstanceState); 347 mStateManager.reapplyState(); 348 349 // We only load the page synchronously if the user rotates (or triggers a 350 // configuration change) while launcher is in the foreground 351 int currentScreen = PagedView.INVALID_RESTORE_PAGE; 352 if (savedInstanceState != null) { 353 currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen); 354 } 355 356 if (!mModel.startLoader(currentScreen)) { 357 if (!internalStateHandled) { 358 // If we are not binding synchronously, show a fade in animation when 359 // the first page bind completes. 360 mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).setValue(0); 361 } 362 } else { 363 // Pages bound synchronously. 364 mWorkspace.setCurrentPage(currentScreen); 365 366 setWorkspaceLoading(true); 367 } 368 369 // For handling default keys 370 setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); 371 372 setContentView(mLauncherView); 373 getRootView().dispatchInsets(); 374 375 // Listen for broadcasts 376 registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); 377 378 getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW, 379 Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)); 380 381 if (mLauncherCallbacks != null) { 382 mLauncherCallbacks.onCreate(savedInstanceState); 383 } 384 mRotationHelper.initialize(); 385 386 TraceHelper.endSection("Launcher-onCreate"); 387 RaceConditionTracker.onEvent(ON_CREATE_EVT, EXIT); 388 mStateManager.addStateListener(new LauncherStateManager.StateListener() { 389 @Override 390 public void onStateTransitionStart(LauncherState toState) {} 391 392 @Override 393 public void onStateTransitionComplete(LauncherState finalState) { 394 float alpha = 1f - mCurrentAssistantVisibility; 395 if (finalState == NORMAL) { 396 mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); 397 } else if (finalState == OVERVIEW || finalState == OVERVIEW_PEEK) { 398 mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); 399 mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); 400 } else { 401 mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f); 402 mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f); 403 } 404 } 405 }); 406 } 407 408 @Override onEnterAnimationComplete()409 public void onEnterAnimationComplete() { 410 super.onEnterAnimationComplete(); 411 UiFactory.onEnterAnimationComplete(this); 412 mAllAppsController.highlightWorkTabIfNecessary(); 413 mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE); 414 } 415 416 @Override onConfigurationChanged(Configuration newConfig)417 public void onConfigurationChanged(Configuration newConfig) { 418 int diff = newConfig.diff(mOldConfig); 419 420 if ((diff & CONFIG_LOCALE) != 0) { 421 Folder.setLocaleDependentFields(getResources(), true /* force */); 422 } 423 424 if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) { 425 onIdpChanged(mDeviceProfile.inv); 426 } 427 428 mOldConfig.setTo(newConfig); 429 UiFactory.onLauncherStateOrResumeChanged(this); 430 super.onConfigurationChanged(newConfig); 431 } 432 reload()433 public void reload() { 434 onIdpChanged(mDeviceProfile.inv); 435 } 436 supportsFakeLandscapeUI()437 private boolean supportsFakeLandscapeUI() { 438 return FeatureFlags.FAKE_LANDSCAPE_UI.get() && !mRotationHelper.homeScreenCanRotate(); 439 } 440 441 @Override reapplyUi()442 public void reapplyUi() { 443 if (supportsFakeLandscapeUI()) { 444 mRotationMode = mStableDeviceProfile == null 445 ? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile); 446 } 447 getRootView().dispatchInsets(); 448 getStateManager().reapplyState(true /* cancelCurrentAnimation */); 449 } 450 451 @Override rebindModel()452 public void rebindModel() { 453 int currentPage = mWorkspace.getNextPage(); 454 if (mModel.startLoader(currentPage)) { 455 mWorkspace.setCurrentPage(currentPage); 456 setWorkspaceLoading(true); 457 } 458 } 459 460 @Override onIdpChanged(int changeFlags, InvariantDeviceProfile idp)461 public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) { 462 onIdpChanged(idp); 463 } 464 onIdpChanged(InvariantDeviceProfile idp)465 private void onIdpChanged(InvariantDeviceProfile idp) { 466 mUserEventDispatcher = null; 467 468 DeviceProfile oldWallpaperProfile = getWallpaperDeviceProfile(); 469 initDeviceProfile(idp); 470 dispatchDeviceProfileChanged(); 471 reapplyUi(); 472 mDragLayer.recreateControllers(); 473 474 // Calling onSaveInstanceState ensures that static cache used by listWidgets is 475 // initialized properly. 476 onSaveInstanceState(new Bundle()); 477 if (oldWallpaperProfile != getWallpaperDeviceProfile()) { 478 rebindModel(); 479 } 480 } 481 onAssistantVisibilityChanged(float visibility)482 public void onAssistantVisibilityChanged(float visibility) { 483 mCurrentAssistantVisibility = visibility; 484 float alpha = 1f - visibility; 485 LauncherState state = mStateManager.getState(); 486 if (state == NORMAL) { 487 mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); 488 } else if (state == OVERVIEW || state == OVERVIEW_PEEK) { 489 mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); 490 mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); 491 } 492 } 493 initDeviceProfile(InvariantDeviceProfile idp)494 private void initDeviceProfile(InvariantDeviceProfile idp) { 495 // Load configuration-specific DeviceProfile 496 mDeviceProfile = idp.getDeviceProfile(this); 497 if (isInMultiWindowMode()) { 498 Display display = getWindowManager().getDefaultDisplay(); 499 Point mwSize = new Point(); 500 display.getSize(mwSize); 501 mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize); 502 } 503 504 if (supportsFakeLandscapeUI() && mDeviceProfile.isVerticalBarLayout()) { 505 mStableDeviceProfile = mDeviceProfile.inv.portraitProfile; 506 mRotationMode = UiFactory.getRotationMode(mDeviceProfile); 507 } else { 508 mStableDeviceProfile = null; 509 mRotationMode = RotationMode.NORMAL; 510 } 511 512 mRotationHelper.updateRotationAnimation(); 513 onDeviceProfileInitiated(); 514 mModelWriter = mModel.getWriter(getWallpaperDeviceProfile().isVerticalBarLayout(), true); 515 } 516 updateInsets(Rect insets)517 public void updateInsets(Rect insets) { 518 mDeviceProfile.updateInsets(insets); 519 if (mStableDeviceProfile != null) { 520 Rect r = mStableDeviceProfile.getInsets(); 521 mRotationMode.mapInsets(this, insets, r); 522 mStableDeviceProfile.updateInsets(r); 523 } 524 } 525 526 @Override getRotationMode()527 public RotationMode getRotationMode() { 528 return mRotationMode; 529 } 530 531 /** 532 * Device profile to be used by UI elements which are shown directly on top of the wallpaper 533 * and whose presentation is tied to the wallpaper (and physical device) and not the activity 534 * configuration. 535 */ 536 @Override getWallpaperDeviceProfile()537 public DeviceProfile getWallpaperDeviceProfile() { 538 return mStableDeviceProfile == null ? mDeviceProfile : mStableDeviceProfile; 539 } 540 getRotationHelper()541 public RotationHelper getRotationHelper() { 542 return mRotationHelper; 543 } 544 getStateManager()545 public LauncherStateManager getStateManager() { 546 return mStateManager; 547 } 548 549 @Override findViewById(int id)550 public <T extends View> T findViewById(int id) { 551 return mLauncherView.findViewById(id); 552 } 553 554 @Override onAppWidgetHostReset()555 public void onAppWidgetHostReset() { 556 if (mAppWidgetHost != null) { 557 mAppWidgetHost.startListening(); 558 } 559 } 560 561 private LauncherCallbacks mLauncherCallbacks; 562 563 /** 564 * Call this after onCreate to set or clear overlay. 565 */ setLauncherOverlay(LauncherOverlay overlay)566 public void setLauncherOverlay(LauncherOverlay overlay) { 567 if (overlay != null) { 568 overlay.setOverlayCallbacks(new LauncherOverlayCallbacksImpl()); 569 } 570 mWorkspace.setLauncherOverlay(overlay); 571 } 572 setLauncherCallbacks(LauncherCallbacks callbacks)573 public boolean setLauncherCallbacks(LauncherCallbacks callbacks) { 574 mLauncherCallbacks = callbacks; 575 return true; 576 } 577 578 @Override onLauncherProviderChanged()579 public void onLauncherProviderChanged() { 580 if (mLauncherCallbacks != null) { 581 mLauncherCallbacks.onLauncherProviderChange(); 582 } 583 } 584 isDraggingEnabled()585 public boolean isDraggingEnabled() { 586 // We prevent dragging when we are loading the workspace as it is possible to pick up a view 587 // that is subsequently removed from the workspace in startBinding(). 588 return !isWorkspaceLoading(); 589 } 590 getPopupDataProvider()591 public PopupDataProvider getPopupDataProvider() { 592 return mPopupDataProvider; 593 } 594 595 @Override getDotInfoForItem(ItemInfo info)596 public DotInfo getDotInfoForItem(ItemInfo info) { 597 return mPopupDataProvider.getDotInfoForItem(info); 598 } 599 600 @Override invalidateParent(ItemInfo info)601 public void invalidateParent(ItemInfo info) { 602 if (info.container >= 0) { 603 View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container); 604 if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) { 605 FolderIconPreviewVerifier verifier = 606 new FolderIconPreviewVerifier(getDeviceProfile().inv); 607 verifier.setFolderInfo((FolderInfo) folderIcon.getTag()); 608 if (verifier.isItemInPreview(info.rank)) { 609 folderIcon.invalidate(); 610 } 611 } 612 } 613 } 614 615 /** 616 * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have 617 * a configuration step, this allows the proper animations to run after other transitions. 618 */ completeAdd( int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info)619 private int completeAdd( 620 int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info) { 621 int screenId = info.screenId; 622 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 623 // When the screen id represents an actual screen (as opposed to a rank) we make sure 624 // that the drop page actually exists. 625 screenId = ensurePendingDropLayoutExists(info.screenId); 626 } 627 628 switch (requestCode) { 629 case REQUEST_CREATE_SHORTCUT: 630 completeAddShortcut(intent, info.container, screenId, info.cellX, info.cellY, info); 631 break; 632 case REQUEST_CREATE_APPWIDGET: 633 completeAddAppWidget(appWidgetId, info, null, null); 634 break; 635 case REQUEST_RECONFIGURE_APPWIDGET: 636 completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED); 637 break; 638 case REQUEST_BIND_PENDING_APPWIDGET: { 639 int widgetId = appWidgetId; 640 LauncherAppWidgetInfo widgetInfo = 641 completeRestoreAppWidget(widgetId, LauncherAppWidgetInfo.FLAG_UI_NOT_READY); 642 if (widgetInfo != null) { 643 // Since the view was just bound, also launch the configure activity if needed 644 LauncherAppWidgetProviderInfo provider = mAppWidgetManager 645 .getLauncherAppWidgetInfo(widgetId); 646 if (provider != null) { 647 new WidgetAddFlowHandler(provider) 648 .startConfigActivity(this, widgetInfo, REQUEST_RECONFIGURE_APPWIDGET); 649 } 650 } 651 break; 652 } 653 } 654 655 return screenId; 656 } 657 handleActivityResult( final int requestCode, final int resultCode, final Intent data)658 private void handleActivityResult( 659 final int requestCode, final int resultCode, final Intent data) { 660 if (isWorkspaceLoading()) { 661 // process the result once the workspace has loaded. 662 mPendingActivityResult = new ActivityResultInfo(requestCode, resultCode, data); 663 return; 664 } 665 mPendingActivityResult = null; 666 667 // Reset the startActivity waiting flag 668 final PendingRequestArgs requestArgs = mPendingRequestArgs; 669 setWaitingForResult(null); 670 if (requestArgs == null) { 671 return; 672 } 673 674 final int pendingAddWidgetId = requestArgs.getWidgetId(); 675 676 Runnable exitSpringLoaded = new Runnable() { 677 @Override 678 public void run() { 679 mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); 680 } 681 }; 682 683 if (requestCode == REQUEST_BIND_APPWIDGET) { 684 // This is called only if the user did not previously have permissions to bind widgets 685 final int appWidgetId = data != null ? 686 data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; 687 if (resultCode == RESULT_CANCELED) { 688 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId, requestArgs); 689 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, 690 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); 691 } else if (resultCode == RESULT_OK) { 692 addAppWidgetImpl( 693 appWidgetId, requestArgs, null, 694 requestArgs.getWidgetHandler(), 695 ON_ACTIVITY_RESULT_ANIMATION_DELAY); 696 } 697 return; 698 } 699 700 boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET || 701 requestCode == REQUEST_CREATE_APPWIDGET); 702 703 // We have special handling for widgets 704 if (isWidgetDrop) { 705 final int appWidgetId; 706 int widgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) 707 : -1; 708 if (widgetId < 0) { 709 appWidgetId = pendingAddWidgetId; 710 } else { 711 appWidgetId = widgetId; 712 } 713 714 final int result; 715 if (appWidgetId < 0 || resultCode == RESULT_CANCELED) { 716 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " + 717 "returned from the widget configuration activity."); 718 result = RESULT_CANCELED; 719 completeTwoStageWidgetDrop(result, appWidgetId, requestArgs); 720 final Runnable onComplete = new Runnable() { 721 @Override 722 public void run() { 723 getStateManager().goToState(NORMAL); 724 } 725 }; 726 727 mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, 728 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); 729 } else { 730 if (requestArgs.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 731 // When the screen id represents an actual screen (as opposed to a rank) 732 // we make sure that the drop page actually exists. 733 requestArgs.screenId = 734 ensurePendingDropLayoutExists(requestArgs.screenId); 735 } 736 final CellLayout dropLayout = 737 mWorkspace.getScreenWithId(requestArgs.screenId); 738 739 dropLayout.setDropPending(true); 740 final Runnable onComplete = new Runnable() { 741 @Override 742 public void run() { 743 completeTwoStageWidgetDrop(resultCode, appWidgetId, requestArgs); 744 dropLayout.setDropPending(false); 745 } 746 }; 747 mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, 748 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); 749 } 750 return; 751 } 752 753 if (requestCode == REQUEST_RECONFIGURE_APPWIDGET 754 || requestCode == REQUEST_BIND_PENDING_APPWIDGET) { 755 if (resultCode == RESULT_OK) { 756 // Update the widget view. 757 completeAdd(requestCode, data, pendingAddWidgetId, requestArgs); 758 } 759 // Leave the widget in the pending state if the user canceled the configure. 760 return; 761 } 762 763 if (requestCode == REQUEST_CREATE_SHORTCUT) { 764 // Handle custom shortcuts created using ACTION_CREATE_SHORTCUT. 765 if (resultCode == RESULT_OK && requestArgs.container != ItemInfo.NO_ID) { 766 completeAdd(requestCode, data, -1, requestArgs); 767 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, 768 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); 769 770 } else if (resultCode == RESULT_CANCELED) { 771 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, 772 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); 773 } 774 } 775 mDragLayer.clearAnimatedView(); 776 } 777 778 @Override onActivityResult( final int requestCode, final int resultCode, final Intent data)779 public void onActivityResult( 780 final int requestCode, final int resultCode, final Intent data) { 781 mPendingActivityRequestCode = -1; 782 handleActivityResult(requestCode, resultCode, data); 783 if (mLauncherCallbacks != null) { 784 mLauncherCallbacks.onActivityResult(requestCode, resultCode, data); 785 } 786 } 787 788 @Override onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)789 public void onRequestPermissionsResult(int requestCode, String[] permissions, 790 int[] grantResults) { 791 PendingRequestArgs pendingArgs = mPendingRequestArgs; 792 if (requestCode == REQUEST_PERMISSION_CALL_PHONE && pendingArgs != null 793 && pendingArgs.getRequestCode() == REQUEST_PERMISSION_CALL_PHONE) { 794 setWaitingForResult(null); 795 796 View v = null; 797 CellLayout layout = getCellLayout(pendingArgs.container, pendingArgs.screenId); 798 if (layout != null) { 799 v = layout.getChildAt(pendingArgs.cellX, pendingArgs.cellY); 800 } 801 Intent intent = pendingArgs.getPendingIntent(); 802 803 if (grantResults.length > 0 804 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 805 startActivitySafely(v, intent, null, null); 806 } else { 807 // TODO: Show a snack bar with link to settings 808 Toast.makeText(this, getString(R.string.msg_no_phone_permission, 809 getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show(); 810 } 811 } 812 if (mLauncherCallbacks != null) { 813 mLauncherCallbacks.onRequestPermissionsResult(requestCode, permissions, 814 grantResults); 815 } 816 } 817 818 /** 819 * Check to see if a given screen id exists. If not, create it at the end, return the new id. 820 * 821 * @param screenId the screen id to check 822 * @return the new screen, or screenId if it exists 823 */ ensurePendingDropLayoutExists(int screenId)824 private int ensurePendingDropLayoutExists(int screenId) { 825 CellLayout dropLayout = mWorkspace.getScreenWithId(screenId); 826 if (dropLayout == null) { 827 // it's possible that the add screen was removed because it was 828 // empty and a re-bind occurred 829 mWorkspace.addExtraEmptyScreen(); 830 return mWorkspace.commitExtraEmptyScreen(); 831 } else { 832 return screenId; 833 } 834 } 835 completeTwoStageWidgetDrop( final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs)836 @Thunk void completeTwoStageWidgetDrop( 837 final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) { 838 CellLayout cellLayout = mWorkspace.getScreenWithId(requestArgs.screenId); 839 Runnable onCompleteRunnable = null; 840 int animationType = 0; 841 842 AppWidgetHostView boundWidget = null; 843 if (resultCode == RESULT_OK) { 844 animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION; 845 final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId, 846 requestArgs.getWidgetHandler().getProviderInfo(this)); 847 boundWidget = layout; 848 onCompleteRunnable = new Runnable() { 849 @Override 850 public void run() { 851 completeAddAppWidget(appWidgetId, requestArgs, layout, null); 852 mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); 853 } 854 }; 855 } else if (resultCode == RESULT_CANCELED) { 856 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 857 animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION; 858 } 859 if (mDragLayer.getAnimatedView() != null) { 860 mWorkspace.animateWidgetDrop(requestArgs, cellLayout, 861 (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable, 862 animationType, boundWidget, true); 863 } else if (onCompleteRunnable != null) { 864 // The animated view may be null in the case of a rotation during widget configuration 865 onCompleteRunnable.run(); 866 } 867 } 868 869 @Override onStop()870 protected void onStop() { 871 super.onStop(); 872 873 if (mLauncherCallbacks != null) { 874 mLauncherCallbacks.onStop(); 875 } 876 877 getUserEventDispatcher().logActionCommand(Action.Command.STOP, 878 mStateManager.getState().containerType, -1); 879 880 mAppWidgetHost.setListenIfResumed(false); 881 882 NotificationListener.removeNotificationsChangedListener(); 883 getStateManager().moveToRestState(); 884 885 UiFactory.onLauncherStateOrResumeChanged(this); 886 887 // Workaround for b/78520668, explicitly trim memory once UI is hidden 888 onTrimMemory(TRIM_MEMORY_UI_HIDDEN); 889 } 890 891 @Override onStart()892 protected void onStart() { 893 if (TestProtocol.sDebugTracing) { 894 Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "Launcher.onStart"); 895 } 896 RaceConditionTracker.onEvent(ON_START_EVT, ENTER); 897 super.onStart(); 898 if (mLauncherCallbacks != null) { 899 mLauncherCallbacks.onStart(); 900 } 901 mAppWidgetHost.setListenIfResumed(true); 902 RaceConditionTracker.onEvent(ON_START_EVT, EXIT); 903 } 904 handleDeferredResume()905 private void handleDeferredResume() { 906 if (hasBeenResumed() && !mStateManager.getState().disableInteraction) { 907 getUserEventDispatcher().logActionCommand(Action.Command.RESUME, 908 mStateManager.getState().containerType, -1); 909 getUserEventDispatcher().startSession(); 910 911 UiFactory.onLauncherStateOrResumeChanged(this); 912 AppLaunchTracker.INSTANCE.get(this).onReturnedToHome(); 913 914 // Process any items that were added while Launcher was away. 915 InstallShortcutReceiver.disableAndFlushInstallQueue( 916 InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this); 917 918 // Refresh shortcuts if the permission changed. 919 mModel.refreshShortcutsIfRequired(); 920 921 // Set the notification listener and fetch updated notifications when we resume 922 NotificationListener.setNotificationsChangedListener(mPopupDataProvider); 923 924 DiscoveryBounce.showForHomeIfNeeded(this); 925 926 if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) { 927 UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode); 928 } 929 mDeferredResumePending = false; 930 } else { 931 mDeferredResumePending = true; 932 } 933 } 934 onStateSet(LauncherState state)935 protected void onStateSet(LauncherState state) { 936 getAppWidgetHost().setResumed(state == LauncherState.NORMAL); 937 if (mDeferredResumePending) { 938 handleDeferredResume(); 939 } 940 if (mLauncherCallbacks != null) { 941 mLauncherCallbacks.onStateChanged(); 942 } 943 } 944 945 @Override onResume()946 protected void onResume() { 947 RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER); 948 TraceHelper.beginSection("ON_RESUME"); 949 super.onResume(); 950 TraceHelper.partitionSection("ON_RESUME", "superCall"); 951 952 mHandler.removeCallbacks(mHandleDeferredResume); 953 Utilities.postAsyncCallback(mHandler, mHandleDeferredResume); 954 955 if (!mOnResumeCallbacks.isEmpty()) { 956 final ArrayList<OnResumeCallback> resumeCallbacks = new ArrayList<>(mOnResumeCallbacks); 957 mOnResumeCallbacks.clear(); 958 for (int i = resumeCallbacks.size() - 1; i >= 0; i--) { 959 resumeCallbacks.get(i).onLauncherResume(); 960 } 961 resumeCallbacks.clear(); 962 } 963 964 if (mLauncherCallbacks != null) { 965 mLauncherCallbacks.onResume(); 966 } 967 968 TraceHelper.endSection("ON_RESUME"); 969 RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT); 970 } 971 972 @Override onPause()973 protected void onPause() { 974 // Ensure that items added to Launcher are queued until Launcher returns 975 InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED); 976 977 super.onPause(); 978 mDragController.cancelDrag(); 979 mDragController.resetLastGestureUpTime(); 980 mDropTargetBar.animateToVisibility(false); 981 if (mLauncherCallbacks != null) { 982 mLauncherCallbacks.onPause(); 983 } 984 } 985 986 @Override onUserLeaveHint()987 protected void onUserLeaveHint() { 988 super.onUserLeaveHint(); 989 UiFactory.onLauncherStateOrResumeChanged(this); 990 } 991 992 @Override onWindowFocusChanged(boolean hasFocus)993 public void onWindowFocusChanged(boolean hasFocus) { 994 super.onWindowFocusChanged(hasFocus); 995 mStateManager.onWindowFocusChanged(); 996 } 997 998 public interface LauncherOverlay { 999 1000 /** 1001 * Touch interaction leading to overscroll has begun 1002 */ onScrollInteractionBegin()1003 void onScrollInteractionBegin(); 1004 1005 /** 1006 * Touch interaction related to overscroll has ended 1007 */ onScrollInteractionEnd()1008 void onScrollInteractionEnd(); 1009 1010 /** 1011 * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost 1012 * screen (or in the case of RTL, the rightmost screen). 1013 */ onScrollChange(float progress, boolean rtl)1014 void onScrollChange(float progress, boolean rtl); 1015 1016 /** 1017 * Called when the launcher is ready to use the overlay 1018 * @param callbacks A set of callbacks provided by Launcher in relation to the overlay 1019 */ setOverlayCallbacks(LauncherOverlayCallbacks callbacks)1020 void setOverlayCallbacks(LauncherOverlayCallbacks callbacks); 1021 } 1022 1023 public interface LauncherOverlayCallbacks { onScrollChanged(float progress)1024 void onScrollChanged(float progress); 1025 } 1026 1027 class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks { 1028 onScrollChanged(float progress)1029 public void onScrollChanged(float progress) { 1030 if (mWorkspace != null) { 1031 mWorkspace.onOverlayScrollChanged(progress); 1032 } 1033 } 1034 } 1035 isInState(LauncherState state)1036 public boolean isInState(LauncherState state) { 1037 return mStateManager.getState() == state; 1038 } 1039 1040 /** 1041 * Restores the previous state, if it exists. 1042 * 1043 * @param savedState The previous state. 1044 */ restoreState(Bundle savedState)1045 private void restoreState(Bundle savedState) { 1046 if (savedState == null) { 1047 return; 1048 } 1049 1050 int stateOrdinal = savedState.getInt(RUNTIME_STATE, NORMAL.ordinal); 1051 LauncherState[] stateValues = LauncherState.values(); 1052 LauncherState state = stateValues[stateOrdinal]; 1053 if (!state.disableRestore) { 1054 mStateManager.goToState(state, false /* animated */); 1055 } 1056 1057 PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS); 1058 if (requestArgs != null) { 1059 setWaitingForResult(requestArgs); 1060 } 1061 mPendingActivityRequestCode = savedState.getInt(RUNTIME_STATE_PENDING_REQUEST_CODE); 1062 1063 mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT); 1064 1065 SparseArray<Parcelable> widgetsState = 1066 savedState.getSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL); 1067 if (widgetsState != null) { 1068 WidgetsFullSheet.show(this, false).restoreHierarchyState(widgetsState); 1069 } 1070 } 1071 1072 /** 1073 * Finds all the views we need and configure them properly. 1074 */ setupViews()1075 private void setupViews() { 1076 mDragLayer = findViewById(R.id.drag_layer); 1077 mFocusHandler = mDragLayer.getFocusIndicatorHelper(); 1078 mWorkspace = mDragLayer.findViewById(R.id.workspace); 1079 mWorkspace.initParentViews(mDragLayer); 1080 mOverviewPanel = findViewById(R.id.overview_panel); 1081 mHotseat = findViewById(R.id.hotseat); 1082 1083 mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 1084 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 1085 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 1086 1087 // Setup the drag layer 1088 mDragLayer.setup(mDragController, mWorkspace); 1089 mCancelTouchController = UiFactory.enableLiveUIChanges(this); 1090 1091 mWorkspace.setup(mDragController); 1092 // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the 1093 // default state, otherwise we will update to the wrong offsets in RTL 1094 mWorkspace.lockWallpaperToDefaultPage(); 1095 mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */); 1096 mDragController.addDragListener(mWorkspace); 1097 1098 // Get the search/delete/uninstall bar 1099 mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar); 1100 1101 // Setup Apps 1102 mAppsView = findViewById(R.id.apps_view); 1103 1104 // Setup Scrim 1105 mScrimView = findViewById(R.id.scrim_view); 1106 1107 // Setup the drag controller (drop targets have to be added in reverse order in priority) 1108 mDragController.setMoveTarget(mWorkspace); 1109 mDropTargetBar.setup(mDragController); 1110 1111 mAllAppsController.setupViews(mAppsView); 1112 } 1113 1114 /** 1115 * Creates a view representing a shortcut. 1116 * 1117 * @param info The data structure describing the shortcut. 1118 */ createShortcut(WorkspaceItemInfo info)1119 View createShortcut(WorkspaceItemInfo info) { 1120 return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info); 1121 } 1122 1123 /** 1124 * Creates a view representing a shortcut inflated from the specified resource. 1125 * 1126 * @param parent The group the shortcut belongs to. 1127 * @param info The data structure describing the shortcut. 1128 * 1129 * @return A View inflated from layoutResId. 1130 */ createShortcut(ViewGroup parent, WorkspaceItemInfo info)1131 public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) { 1132 BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext()) 1133 .inflate(R.layout.app_icon, parent, false); 1134 favorite.applyFromWorkspaceItem(info); 1135 favorite.setOnClickListener(ItemClickHandler.INSTANCE); 1136 favorite.setOnFocusChangeListener(mFocusHandler); 1137 return favorite; 1138 } 1139 1140 /** 1141 * Add a shortcut to the workspace or to a Folder. 1142 * 1143 * @param data The intent describing the shortcut. 1144 */ completeAddShortcut(Intent data, int container, int screenId, int cellX, int cellY, PendingRequestArgs args)1145 private void completeAddShortcut(Intent data, int container, int screenId, int cellX, 1146 int cellY, PendingRequestArgs args) { 1147 if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT 1148 || args.getPendingIntent().getComponent() == null) { 1149 return; 1150 } 1151 1152 int[] cellXY = mTmpAddItemCellCoordinates; 1153 CellLayout layout = getCellLayout(container, screenId); 1154 1155 WorkspaceItemInfo info = null; 1156 if (Utilities.ATLEAST_OREO) { 1157 info = LauncherAppsCompatVO.createWorkspaceItemFromPinItemRequest( 1158 this, LauncherAppsCompatVO.getPinItemRequest(data), 0); 1159 } 1160 1161 if (info == null) { 1162 // Legacy shortcuts are only supported for primary profile. 1163 info = Process.myUserHandle().equals(args.user) 1164 ? InstallShortcutReceiver.fromShortcutIntent(this, data) : null; 1165 1166 if (info == null) { 1167 Log.e(TAG, "Unable to parse a valid custom shortcut result"); 1168 return; 1169 } else if (!new PackageManagerHelper(this).hasPermissionForActivity( 1170 info.intent, args.getPendingIntent().getComponent().getPackageName())) { 1171 // The app is trying to add a shortcut without sufficient permissions 1172 Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0)); 1173 return; 1174 } 1175 } 1176 1177 if (container < 0) { 1178 // Adding a shortcut to the Workspace. 1179 final View view = createShortcut(info); 1180 boolean foundCellSpan = false; 1181 // First we check if we already know the exact location where we want to add this item. 1182 if (cellX >= 0 && cellY >= 0) { 1183 cellXY[0] = cellX; 1184 cellXY[1] = cellY; 1185 foundCellSpan = true; 1186 1187 // If appropriate, either create a folder or add to an existing folder 1188 if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0, 1189 true, null)) { 1190 return; 1191 } 1192 DragObject dragObject = new DragObject(); 1193 dragObject.dragInfo = info; 1194 if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject, 1195 true)) { 1196 return; 1197 } 1198 } else { 1199 foundCellSpan = layout.findCellForSpan(cellXY, 1, 1); 1200 } 1201 1202 if (!foundCellSpan) { 1203 mWorkspace.onNoCellFound(layout); 1204 return; 1205 } 1206 1207 getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]); 1208 mWorkspace.addInScreen(view, info); 1209 } else { 1210 // Adding a shortcut to a Folder. 1211 FolderIcon folderIcon = findFolderIcon(container); 1212 if (folderIcon != null) { 1213 FolderInfo folderInfo = (FolderInfo) folderIcon.getTag(); 1214 folderInfo.add(info, args.rank, false); 1215 } else { 1216 Log.e(TAG, "Could not find folder with id " + container + " to add shortcut."); 1217 } 1218 } 1219 } 1220 findFolderIcon(final int folderIconId)1221 public FolderIcon findFolderIcon(final int folderIconId) { 1222 return (FolderIcon) mWorkspace.getHomescreenIconByItemId(folderIconId); 1223 } 1224 1225 /** 1226 * Add a widget to the workspace. 1227 * 1228 * @param appWidgetId The app widget id 1229 */ completeAddAppWidget(int appWidgetId, ItemInfo itemInfo, AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo)1230 @Thunk void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo, 1231 AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) { 1232 1233 if (appWidgetInfo == null) { 1234 appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId); 1235 } 1236 1237 LauncherAppWidgetInfo launcherInfo; 1238 launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider); 1239 launcherInfo.spanX = itemInfo.spanX; 1240 launcherInfo.spanY = itemInfo.spanY; 1241 launcherInfo.minSpanX = itemInfo.minSpanX; 1242 launcherInfo.minSpanY = itemInfo.minSpanY; 1243 launcherInfo.user = appWidgetInfo.getProfile(); 1244 1245 getModelWriter().addItemToDatabase(launcherInfo, 1246 itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY); 1247 1248 if (hostView == null) { 1249 // Perform actual inflation because we're live 1250 hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 1251 } 1252 hostView.setVisibility(View.VISIBLE); 1253 prepareAppWidget(hostView, launcherInfo); 1254 mWorkspace.addInScreen(hostView, launcherInfo); 1255 } 1256 prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item)1257 private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) { 1258 hostView.setTag(item); 1259 item.onBindAppWidget(this, hostView); 1260 hostView.setFocusable(true); 1261 hostView.setOnFocusChangeListener(mFocusHandler); 1262 } 1263 1264 private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 1265 @Override 1266 public void onReceive(Context context, Intent intent) { 1267 // Reset AllApps to its initial state only if we are not in the middle of 1268 // processing a multi-step drop 1269 if (mPendingRequestArgs == null) { 1270 mStateManager.goToState(NORMAL); 1271 } 1272 } 1273 }; 1274 updateNotificationDots(Predicate<PackageUserKey> updatedDots)1275 public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) { 1276 mWorkspace.updateNotificationDots(updatedDots); 1277 mAppsView.getAppsStore().updateNotificationDots(updatedDots); 1278 } 1279 1280 @Override onAttachedToWindow()1281 public void onAttachedToWindow() { 1282 super.onAttachedToWindow(); 1283 1284 if (mLauncherCallbacks != null) { 1285 mLauncherCallbacks.onAttachedToWindow(); 1286 } 1287 } 1288 1289 @Override onDetachedFromWindow()1290 public void onDetachedFromWindow() { 1291 super.onDetachedFromWindow(); 1292 1293 if (mLauncherCallbacks != null) { 1294 mLauncherCallbacks.onDetachedFromWindow(); 1295 } 1296 } 1297 getAllAppsController()1298 public AllAppsTransitionController getAllAppsController() { 1299 return mAllAppsController; 1300 } 1301 1302 @Override getRootView()1303 public LauncherRootView getRootView() { 1304 return (LauncherRootView) mLauncherView; 1305 } 1306 1307 @Override getDragLayer()1308 public DragLayer getDragLayer() { 1309 return mDragLayer; 1310 } 1311 getAppsView()1312 public AllAppsContainerView getAppsView() { 1313 return mAppsView; 1314 } 1315 getWorkspace()1316 public Workspace getWorkspace() { 1317 return mWorkspace; 1318 } 1319 getHotseat()1320 public Hotseat getHotseat() { 1321 return mHotseat; 1322 } 1323 getOverviewPanel()1324 public <T extends View> T getOverviewPanel() { 1325 return (T) mOverviewPanel; 1326 } 1327 getDropTargetBar()1328 public DropTargetBar getDropTargetBar() { 1329 return mDropTargetBar; 1330 } 1331 getAppWidgetHost()1332 public LauncherAppWidgetHost getAppWidgetHost() { 1333 return mAppWidgetHost; 1334 } 1335 getModel()1336 public LauncherModel getModel() { 1337 return mModel; 1338 } 1339 getModelWriter()1340 public ModelWriter getModelWriter() { 1341 return mModelWriter; 1342 } 1343 getSharedPrefs()1344 public SharedPreferences getSharedPrefs() { 1345 return mSharedPrefs; 1346 } 1347 getOrientation()1348 public int getOrientation() { return mOldConfig.orientation; } 1349 1350 @Override onNewIntent(Intent intent)1351 protected void onNewIntent(Intent intent) { 1352 TraceHelper.beginSection("NEW_INTENT"); 1353 super.onNewIntent(intent); 1354 1355 boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags() & 1356 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) 1357 != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); 1358 1359 // Check this condition before handling isActionMain, as this will get reset. 1360 boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL) 1361 && AbstractFloatingView.getTopOpenView(this) == null; 1362 boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction()); 1363 boolean internalStateHandled = InternalStateHandler 1364 .handleNewIntent(this, intent, isStarted()); 1365 1366 if (isActionMain) { 1367 if (!internalStateHandled) { 1368 // In all these cases, only animate if we're already on home 1369 AbstractFloatingView.closeAllOpenViews(this, isStarted()); 1370 1371 if (!isInState(NORMAL)) { 1372 // Only change state, if not already the same. This prevents cancelling any 1373 // animations running as part of resume 1374 mStateManager.goToState(NORMAL); 1375 } 1376 1377 // Reset the apps view 1378 if (!alreadyOnHome) { 1379 mAppsView.reset(isStarted() /* animate */); 1380 } 1381 1382 if (shouldMoveToDefaultScreen && !mWorkspace.isHandlingTouch()) { 1383 mWorkspace.post(mWorkspace::moveToDefaultScreen); 1384 } 1385 } 1386 1387 // Handle HOME_INTENT 1388 UserEventDispatcher ued = getUserEventDispatcher(); 1389 Target target = newContainerTarget(mStateManager.getState().containerType); 1390 target.pageIndex = mWorkspace.getCurrentPage(); 1391 ued.logActionCommand(Action.Command.HOME_INTENT, target, 1392 newContainerTarget(ContainerType.WORKSPACE)); 1393 1394 final View v = getWindow().peekDecorView(); 1395 if (v != null && v.getWindowToken() != null) { 1396 UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken()); 1397 } 1398 1399 if (mLauncherCallbacks != null) { 1400 mLauncherCallbacks.onHomeIntent(internalStateHandled); 1401 } 1402 } 1403 1404 TraceHelper.endSection("NEW_INTENT"); 1405 } 1406 1407 @Override onRestoreInstanceState(Bundle state)1408 public void onRestoreInstanceState(Bundle state) { 1409 super.onRestoreInstanceState(state); 1410 mWorkspace.restoreInstanceStateForChild(mSynchronouslyBoundPage); 1411 } 1412 1413 @Override onSaveInstanceState(Bundle outState)1414 protected void onSaveInstanceState(Bundle outState) { 1415 if (mWorkspace.getChildCount() > 0) { 1416 outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage()); 1417 1418 } 1419 outState.putInt(RUNTIME_STATE, mStateManager.getState().ordinal); 1420 1421 1422 AbstractFloatingView widgets = AbstractFloatingView 1423 .getOpenView(this, AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET); 1424 if (widgets != null) { 1425 SparseArray<Parcelable> widgetsState = new SparseArray<>(); 1426 widgets.saveHierarchyState(widgetsState); 1427 outState.putSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL, widgetsState); 1428 } else { 1429 outState.remove(RUNTIME_STATE_WIDGET_PANEL); 1430 } 1431 1432 // We close any open folders and shortcut containers since they will not be re-opened, 1433 // and we need to make sure this state is reflected. 1434 AbstractFloatingView.closeAllOpenViews(this, false); 1435 1436 if (mPendingRequestArgs != null) { 1437 outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs); 1438 } 1439 outState.putInt(RUNTIME_STATE_PENDING_REQUEST_CODE, mPendingActivityRequestCode); 1440 1441 if (mPendingActivityResult != null) { 1442 outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult); 1443 } 1444 1445 super.onSaveInstanceState(outState); 1446 1447 if (mLauncherCallbacks != null) { 1448 mLauncherCallbacks.onSaveInstanceState(outState); 1449 } 1450 } 1451 1452 @Override onDestroy()1453 public void onDestroy() { 1454 super.onDestroy(); 1455 1456 unregisterReceiver(mScreenOffReceiver); 1457 mWorkspace.removeFolderListeners(); 1458 1459 if (mCancelTouchController != null) { 1460 mCancelTouchController.run(); 1461 mCancelTouchController = null; 1462 } 1463 1464 // Stop callbacks from LauncherModel 1465 // It's possible to receive onDestroy after a new Launcher activity has 1466 // been created. In this case, don't interfere with the new Launcher. 1467 if (mModel.isCurrentCallbacks(this)) { 1468 mModel.stopLoader(); 1469 LauncherAppState.getInstance(this).setLauncher(null); 1470 } 1471 mRotationHelper.destroy(); 1472 1473 try { 1474 mAppWidgetHost.stopListening(); 1475 } catch (NullPointerException ex) { 1476 Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex); 1477 } 1478 1479 TextKeyListener.getInstance().release(); 1480 clearPendingBinds(); 1481 LauncherAppState.getIDP(this).removeOnChangeListener(this); 1482 if (mLauncherCallbacks != null) { 1483 mLauncherCallbacks.onDestroy(); 1484 } 1485 } 1486 getAccessibilityDelegate()1487 public LauncherAccessibilityDelegate getAccessibilityDelegate() { 1488 return mAccessibilityDelegate; 1489 } 1490 getDragController()1491 public DragController getDragController() { 1492 return mDragController; 1493 } 1494 1495 @Override startActivityForResult(Intent intent, int requestCode, Bundle options)1496 public void startActivityForResult(Intent intent, int requestCode, Bundle options) { 1497 if (requestCode != -1) { 1498 mPendingActivityRequestCode = requestCode; 1499 } 1500 if (requestCode == -1 1501 || !UiFactory.startActivityForResult(this, intent, requestCode, options)) { 1502 super.startActivityForResult(intent, requestCode, options); 1503 } 1504 } 1505 1506 @Override startIntentSenderForResult(IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options)1507 public void startIntentSenderForResult(IntentSender intent, int requestCode, 1508 Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) { 1509 if (requestCode != -1) { 1510 mPendingActivityRequestCode = requestCode; 1511 } 1512 if (requestCode == -1 || !UiFactory.startIntentSenderForResult(this, intent, requestCode, 1513 fillInIntent, flagsMask, flagsValues, extraFlags, options)) { 1514 try { 1515 super.startIntentSenderForResult(intent, requestCode, 1516 fillInIntent, flagsMask, flagsValues, extraFlags, options); 1517 } catch (IntentSender.SendIntentException e) { 1518 throw new ActivityNotFoundException(); 1519 } 1520 } 1521 } 1522 1523 /** 1524 * Indicates that we want global search for this activity by setting the globalSearch 1525 * argument for {@link #startSearch} to true. 1526 */ 1527 @Override startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch)1528 public void startSearch(String initialQuery, boolean selectInitialQuery, 1529 Bundle appSearchData, boolean globalSearch) { 1530 if (appSearchData == null) { 1531 appSearchData = new Bundle(); 1532 appSearchData.putString("source", "launcher-search"); 1533 } 1534 1535 if (mLauncherCallbacks == null || 1536 !mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData)) { 1537 // Starting search from the callbacks failed. Start the default global search. 1538 super.startSearch(initialQuery, selectInitialQuery, appSearchData, true); 1539 } 1540 1541 // We need to show the workspace after starting the search 1542 mStateManager.goToState(NORMAL); 1543 } 1544 isWorkspaceLocked()1545 public boolean isWorkspaceLocked() { 1546 return mWorkspaceLoading || mPendingRequestArgs != null; 1547 } 1548 isWorkspaceLoading()1549 public boolean isWorkspaceLoading() { 1550 return mWorkspaceLoading; 1551 } 1552 setWorkspaceLoading(boolean value)1553 private void setWorkspaceLoading(boolean value) { 1554 mWorkspaceLoading = value; 1555 } 1556 setWaitingForResult(PendingRequestArgs args)1557 public void setWaitingForResult(PendingRequestArgs args) { 1558 mPendingRequestArgs = args; 1559 } 1560 addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler)1561 void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, 1562 WidgetAddFlowHandler addFlowHandler) { 1563 if (LOGD) { 1564 Log.d(TAG, "Adding widget from drop"); 1565 } 1566 addAppWidgetImpl(appWidgetId, info, boundWidget, addFlowHandler, 0); 1567 } 1568 addAppWidgetImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay)1569 void addAppWidgetImpl(int appWidgetId, ItemInfo info, 1570 AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay) { 1571 if (!addFlowHandler.startConfigActivity(this, appWidgetId, info, REQUEST_CREATE_APPWIDGET)) { 1572 // If the configuration flow was not started, add the widget 1573 1574 Runnable onComplete = new Runnable() { 1575 @Override 1576 public void run() { 1577 // Exit spring loaded mode if necessary after adding the widget 1578 mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); 1579 } 1580 }; 1581 completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this)); 1582 mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false); 1583 } 1584 } 1585 addPendingItem(PendingAddItemInfo info, int container, int screenId, int[] cell, int spanX, int spanY)1586 public void addPendingItem(PendingAddItemInfo info, int container, int screenId, 1587 int[] cell, int spanX, int spanY) { 1588 info.container = container; 1589 info.screenId = screenId; 1590 if (cell != null) { 1591 info.cellX = cell[0]; 1592 info.cellY = cell[1]; 1593 } 1594 info.spanX = spanX; 1595 info.spanY = spanY; 1596 1597 switch (info.itemType) { 1598 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: 1599 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1600 addAppWidgetFromDrop((PendingAddWidgetInfo) info); 1601 break; 1602 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1603 processShortcutFromDrop((PendingAddShortcutInfo) info); 1604 break; 1605 default: 1606 throw new IllegalStateException("Unknown item type: " + info.itemType); 1607 } 1608 } 1609 1610 /** 1611 * Process a shortcut drop. 1612 */ processShortcutFromDrop(PendingAddShortcutInfo info)1613 private void processShortcutFromDrop(PendingAddShortcutInfo info) { 1614 Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(info.componentName); 1615 setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_CREATE_SHORTCUT, intent, info)); 1616 if (!info.activityInfo.startConfigActivity(this, REQUEST_CREATE_SHORTCUT)) { 1617 handleActivityResult(REQUEST_CREATE_SHORTCUT, RESULT_CANCELED, null); 1618 } 1619 } 1620 1621 /** 1622 * Process a widget drop. 1623 */ addAppWidgetFromDrop(PendingAddWidgetInfo info)1624 private void addAppWidgetFromDrop(PendingAddWidgetInfo info) { 1625 AppWidgetHostView hostView = info.boundWidget; 1626 final int appWidgetId; 1627 WidgetAddFlowHandler addFlowHandler = info.getHandler(); 1628 if (hostView != null) { 1629 // In the case where we've prebound the widget, we remove it from the DragLayer 1630 if (LOGD) { 1631 Log.d(TAG, "Removing widget view from drag layer and setting boundWidget to null"); 1632 } 1633 getDragLayer().removeView(hostView); 1634 1635 appWidgetId = hostView.getAppWidgetId(); 1636 addAppWidgetFromDropImpl(appWidgetId, info, hostView, addFlowHandler); 1637 1638 // Clear the boundWidget so that it doesn't get destroyed. 1639 info.boundWidget = null; 1640 } else { 1641 // In this case, we either need to start an activity to get permission to bind 1642 // the widget, or we need to start an activity to configure the widget, or both. 1643 if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && 1644 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) { 1645 appWidgetId = CustomWidgetParser.getWidgetIdForCustomProvider( 1646 this, info.componentName); 1647 } else { 1648 appWidgetId = getAppWidgetHost().allocateAppWidgetId(); 1649 } 1650 Bundle options = info.bindOptions; 1651 1652 boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( 1653 appWidgetId, info.info, options); 1654 if (success) { 1655 addAppWidgetFromDropImpl(appWidgetId, info, null, addFlowHandler); 1656 } else { 1657 addFlowHandler.startBindFlow(this, appWidgetId, info, REQUEST_BIND_APPWIDGET); 1658 } 1659 } 1660 } 1661 addFolder(CellLayout layout, int container, final int screenId, int cellX, int cellY)1662 FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX, 1663 int cellY) { 1664 final FolderInfo folderInfo = new FolderInfo(); 1665 folderInfo.title = getText(R.string.folder_name); 1666 1667 // Update the model 1668 getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY); 1669 1670 // Create the view 1671 FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo); 1672 mWorkspace.addInScreen(newFolder, folderInfo); 1673 // Force measure the new folder icon 1674 CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder); 1675 parent.getShortcutsAndWidgets().measureChild(newFolder); 1676 return newFolder; 1677 } 1678 1679 /** 1680 * Unbinds the view for the specified item, and removes the item and all its children. 1681 * 1682 * @param v the view being removed. 1683 * @param itemInfo the {@link ItemInfo} for this view. 1684 * @param deleteFromDb whether or not to delete this item from the db. 1685 */ removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb)1686 public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) { 1687 if (itemInfo instanceof WorkspaceItemInfo) { 1688 // Remove the shortcut from the folder before removing it from launcher 1689 View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container); 1690 if (folderIcon instanceof FolderIcon) { 1691 ((FolderInfo) folderIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true); 1692 } else { 1693 mWorkspace.removeWorkspaceItem(v); 1694 } 1695 if (deleteFromDb) { 1696 getModelWriter().deleteItemFromDatabase(itemInfo); 1697 } 1698 } else if (itemInfo instanceof FolderInfo) { 1699 final FolderInfo folderInfo = (FolderInfo) itemInfo; 1700 if (v instanceof FolderIcon) { 1701 ((FolderIcon) v).removeListeners(); 1702 } 1703 mWorkspace.removeWorkspaceItem(v); 1704 if (deleteFromDb) { 1705 getModelWriter().deleteFolderAndContentsFromDatabase(folderInfo); 1706 } 1707 } else if (itemInfo instanceof LauncherAppWidgetInfo) { 1708 final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo; 1709 mWorkspace.removeWorkspaceItem(v); 1710 if (deleteFromDb) { 1711 getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost()); 1712 } 1713 } else { 1714 return false; 1715 } 1716 return true; 1717 } 1718 1719 1720 1721 @Override dispatchKeyEvent(KeyEvent event)1722 public boolean dispatchKeyEvent(KeyEvent event) { 1723 return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event); 1724 } 1725 1726 @Override onBackPressed()1727 public void onBackPressed() { 1728 if (finishAutoCancelActionMode()) { 1729 return; 1730 } 1731 if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) { 1732 return; 1733 } 1734 1735 if (mDragController.isDragging()) { 1736 mDragController.cancelDrag(); 1737 return; 1738 } 1739 1740 // Note: There should be at most one log per method call. This is enforced implicitly 1741 // by using if-else statements. 1742 UserEventDispatcher ued = getUserEventDispatcher(); 1743 AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); 1744 if (topView != null && topView.onBackPressed()) { 1745 // Handled by the floating view. 1746 } else { 1747 mStateManager.getState().onBackPressed(this); 1748 } 1749 } 1750 1751 @TargetApi(Build.VERSION_CODES.M) 1752 @Override getActivityLaunchOptions(View v)1753 public ActivityOptions getActivityLaunchOptions(View v) { 1754 return mAppTransitionManager.getActivityLaunchOptions(this, v); 1755 } 1756 getAppTransitionManager()1757 public LauncherAppTransitionManager getAppTransitionManager() { 1758 return mAppTransitionManager; 1759 } 1760 1761 @TargetApi(Build.VERSION_CODES.M) 1762 @Override onErrorStartingShortcut(Intent intent, ItemInfo info)1763 protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) { 1764 // Due to legacy reasons, direct call shortcuts require Launchers to have the 1765 // corresponding permission. Show the appropriate permission prompt if that 1766 // is the case. 1767 if (intent.getComponent() == null 1768 && Intent.ACTION_CALL.equals(intent.getAction()) 1769 && checkSelfPermission(android.Manifest.permission.CALL_PHONE) != 1770 PackageManager.PERMISSION_GRANTED) { 1771 1772 setWaitingForResult(PendingRequestArgs 1773 .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info)); 1774 requestPermissions(new String[]{android.Manifest.permission.CALL_PHONE}, 1775 REQUEST_PERMISSION_CALL_PHONE); 1776 return true; 1777 } else { 1778 return false; 1779 } 1780 } 1781 1782 @Override getCurrentState()1783 public int getCurrentState() { 1784 if(mStateManager.getState() == LauncherState.ALL_APPS) { 1785 return StatsLogUtils.LAUNCHER_STATE_ALLAPPS; 1786 } else if (mStateManager.getState() == OVERVIEW) { 1787 return StatsLogUtils.LAUNCHER_STATE_OVERVIEW; 1788 } 1789 return StatsLogUtils.LAUNCHER_STATE_HOME; 1790 } 1791 1792 @Override modifyUserEvent(LauncherLogProto.LauncherEvent event)1793 public void modifyUserEvent(LauncherLogProto.LauncherEvent event) { 1794 if (event.srcTarget != null && event.srcTarget.length > 0 && 1795 event.srcTarget[1].containerType == ContainerType.PREDICTION) { 1796 Target[] targets = new Target[3]; 1797 targets[0] = event.srcTarget[0]; 1798 targets[1] = event.srcTarget[1]; 1799 targets[2] = newTarget(Target.Type.CONTAINER); 1800 event.srcTarget = targets; 1801 LauncherState state = mStateManager.getState(); 1802 if (state == LauncherState.ALL_APPS) { 1803 event.srcTarget[2].containerType = ContainerType.ALLAPPS; 1804 } else if (state == OVERVIEW) { 1805 event.srcTarget[2].containerType = ContainerType.TASKSWITCHER; 1806 } 1807 } 1808 } 1809 startActivitySafely(View v, Intent intent, ItemInfo item, @Nullable String sourceContainer)1810 public boolean startActivitySafely(View v, Intent intent, ItemInfo item, 1811 @Nullable String sourceContainer) { 1812 if (TestProtocol.sDebugTracing) { 1813 android.util.Log.d(TestProtocol.NO_START_TAG, 1814 "startActivitySafely outer"); 1815 } 1816 1817 if (!hasBeenResumed()) { 1818 // Workaround an issue where the WM launch animation is clobbered when finishing the 1819 // recents animation into launcher. Defer launching the activity until Launcher is 1820 // next resumed. 1821 addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer)); 1822 UiFactory.clearSwipeSharedState(true /* finishAnimation */); 1823 return true; 1824 } 1825 1826 boolean success = super.startActivitySafely(v, intent, item, sourceContainer); 1827 if (success && v instanceof BubbleTextView) { 1828 // This is set to the view that launched the activity that navigated the user away 1829 // from launcher. Since there is no callback for when the activity has finished 1830 // launching, enable the press state and keep this reference to reset the press 1831 // state when we return to launcher. 1832 BubbleTextView btv = (BubbleTextView) v; 1833 btv.setStayPressed(true); 1834 addOnResumeCallback(btv); 1835 } 1836 return success; 1837 } 1838 isHotseatLayout(View layout)1839 boolean isHotseatLayout(View layout) { 1840 // TODO: Remove this method 1841 return mHotseat != null && (layout == mHotseat); 1842 } 1843 1844 /** 1845 * Returns the CellLayout of the specified container at the specified screen. 1846 */ getCellLayout(int container, int screenId)1847 public CellLayout getCellLayout(int container, int screenId) { 1848 return (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) 1849 ? mHotseat : mWorkspace.getScreenWithId(screenId); 1850 } 1851 1852 @Override onTrimMemory(int level)1853 public void onTrimMemory(int level) { 1854 super.onTrimMemory(level); 1855 if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { 1856 // The widget preview db can result in holding onto over 1857 // 3MB of memory for caching which isn't necessary. 1858 SQLiteDatabase.releaseMemory(); 1859 1860 // This clears all widget bitmaps from the widget tray 1861 // TODO(hyunyoungs) 1862 } 1863 if (mLauncherCallbacks != null) { 1864 mLauncherCallbacks.onTrimMemory(level); 1865 } 1866 UiFactory.onTrimMemory(this, level); 1867 } 1868 1869 @Override dispatchPopulateAccessibilityEvent(AccessibilityEvent event)1870 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 1871 final boolean result = super.dispatchPopulateAccessibilityEvent(event); 1872 final List<CharSequence> text = event.getText(); 1873 text.clear(); 1874 // Populate event with a fake title based on the current state. 1875 // TODO: When can workspace be null? 1876 text.add(mWorkspace == null 1877 ? getString(R.string.all_apps_home_button_label) 1878 : mStateManager.getState().getDescription(this)); 1879 return result; 1880 } 1881 addOnResumeCallback(OnResumeCallback callback)1882 public void addOnResumeCallback(OnResumeCallback callback) { 1883 mOnResumeCallbacks.add(callback); 1884 } 1885 1886 /** 1887 * Implementation of the method from LauncherModel.Callbacks. 1888 */ 1889 @Override getCurrentWorkspaceScreen()1890 public int getCurrentWorkspaceScreen() { 1891 if (mWorkspace != null) { 1892 return mWorkspace.getCurrentPage(); 1893 } else { 1894 return 0; 1895 } 1896 } 1897 1898 /** 1899 * Clear any pending bind callbacks. This is called when is loader is planning to 1900 * perform a full rebind from scratch. 1901 */ 1902 @Override clearPendingBinds()1903 public void clearPendingBinds() { 1904 if (mPendingExecutor != null) { 1905 mPendingExecutor.markCompleted(); 1906 mPendingExecutor = null; 1907 } 1908 } 1909 1910 /** 1911 * Refreshes the shortcuts shown on the workspace. 1912 * 1913 * Implementation of the method from LauncherModel.Callbacks. 1914 */ startBinding()1915 public void startBinding() { 1916 TraceHelper.beginSection("startBinding"); 1917 // Floating panels (except the full widget sheet) are associated with individual icons. If 1918 // we are starting a fresh bind, close all such panels as all the icons are about 1919 // to go away. 1920 AbstractFloatingView.closeOpenViews(this, true, 1921 AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); 1922 1923 setWorkspaceLoading(true); 1924 1925 // Clear the workspace because it's going to be rebound 1926 mDragController.cancelDrag(); 1927 1928 mWorkspace.clearDropTargets(); 1929 mWorkspace.removeAllWorkspaceScreens(); 1930 mAppWidgetHost.clearViews(); 1931 1932 if (mHotseat != null) { 1933 mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout()); 1934 } 1935 TraceHelper.endSection("startBinding"); 1936 } 1937 1938 @Override bindScreens(IntArray orderedScreenIds)1939 public void bindScreens(IntArray orderedScreenIds) { 1940 // Make sure the first screen is always at the start. 1941 if (FeatureFlags.QSB_ON_FIRST_SCREEN && 1942 orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) { 1943 orderedScreenIds.removeValue(Workspace.FIRST_SCREEN_ID); 1944 orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID); 1945 } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) { 1946 // If there are no screens, we need to have an empty screen 1947 mWorkspace.addExtraEmptyScreen(); 1948 } 1949 bindAddScreens(orderedScreenIds); 1950 1951 // After we have added all the screens, if the wallpaper was locked to the default state, 1952 // then notify to indicate that it can be released and a proper wallpaper offset can be 1953 // computed before the next layout 1954 mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout(); 1955 } 1956 bindAddScreens(IntArray orderedScreenIds)1957 private void bindAddScreens(IntArray orderedScreenIds) { 1958 int count = orderedScreenIds.size(); 1959 for (int i = 0; i < count; i++) { 1960 int screenId = orderedScreenIds.get(i); 1961 if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) { 1962 // No need to bind the first screen, as its always bound. 1963 mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId); 1964 } 1965 } 1966 } 1967 1968 @Override preAddApps()1969 public void preAddApps() { 1970 // If there's an undo snackbar, force it to complete to ensure empty screens are removed 1971 // before trying to add new items. 1972 mModelWriter.commitDelete(); 1973 AbstractFloatingView snackbar = AbstractFloatingView.getOpenView(this, TYPE_SNACKBAR); 1974 if (snackbar != null) { 1975 snackbar.post(() -> snackbar.close(true)); 1976 } 1977 } 1978 1979 @Override bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated)1980 public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, 1981 ArrayList<ItemInfo> addAnimated) { 1982 // Add the new screens 1983 if (newScreens != null) { 1984 bindAddScreens(newScreens); 1985 } 1986 1987 // We add the items without animation on non-visible pages, and with 1988 // animations on the new page (which we will try and snap to). 1989 if (addNotAnimated != null && !addNotAnimated.isEmpty()) { 1990 bindItems(addNotAnimated, false); 1991 } 1992 if (addAnimated != null && !addAnimated.isEmpty()) { 1993 bindItems(addAnimated, true); 1994 } 1995 1996 // Remove the extra empty screen 1997 mWorkspace.removeExtraEmptyScreen(false, false); 1998 } 1999 2000 /** 2001 * Bind the items start-end from the list. 2002 * 2003 * Implementation of the method from LauncherModel.Callbacks. 2004 */ 2005 @Override bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons)2006 public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) { 2007 // Get the list of added items and intersect them with the set of items here 2008 final Collection<Animator> bounceAnims = new ArrayList<>(); 2009 final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation(); 2010 Workspace workspace = mWorkspace; 2011 int newItemsScreenId = -1; 2012 int end = items.size(); 2013 for (int i = 0; i < end; i++) { 2014 final ItemInfo item = items.get(i); 2015 2016 // Short circuit if we are loading dock items for a configuration which has no dock 2017 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && 2018 mHotseat == null) { 2019 continue; 2020 } 2021 2022 final View view; 2023 switch (item.itemType) { 2024 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 2025 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 2026 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: { 2027 WorkspaceItemInfo info = (WorkspaceItemInfo) item; 2028 view = createShortcut(info); 2029 break; 2030 } 2031 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: { 2032 view = FolderIcon.fromXml(R.layout.folder_icon, this, 2033 (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), 2034 (FolderInfo) item); 2035 break; 2036 } 2037 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 2038 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: { 2039 view = inflateAppWidget((LauncherAppWidgetInfo) item); 2040 if (view == null) { 2041 continue; 2042 } 2043 break; 2044 } 2045 default: 2046 throw new RuntimeException("Invalid Item Type"); 2047 } 2048 2049 /* 2050 * Remove colliding items. 2051 */ 2052 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 2053 CellLayout cl = mWorkspace.getScreenWithId(item.screenId); 2054 if (cl != null && cl.isOccupied(item.cellX, item.cellY)) { 2055 View v = cl.getChildAt(item.cellX, item.cellY); 2056 Object tag = v.getTag(); 2057 String desc = "Collision while binding workspace item: " + item 2058 + ". Collides with " + tag; 2059 if (FeatureFlags.IS_DOGFOOD_BUILD) { 2060 throw (new RuntimeException(desc)); 2061 } else { 2062 Log.d(TAG, desc); 2063 getModelWriter().deleteItemFromDatabase(item); 2064 continue; 2065 } 2066 } 2067 } 2068 workspace.addInScreenFromBind(view, item); 2069 if (animateIcons) { 2070 // Animate all the applications up now 2071 view.setAlpha(0f); 2072 view.setScaleX(0f); 2073 view.setScaleY(0f); 2074 bounceAnims.add(createNewAppBounceAnimation(view, i)); 2075 newItemsScreenId = item.screenId; 2076 } 2077 } 2078 2079 // Animate to the correct page 2080 if (animateIcons && newItemsScreenId > -1) { 2081 AnimatorSet anim = new AnimatorSet(); 2082 anim.playTogether(bounceAnims); 2083 2084 int currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage()); 2085 final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId); 2086 final Runnable startBounceAnimRunnable = anim::start; 2087 2088 if (newItemsScreenId != currentScreenId) { 2089 // We post the animation slightly delayed to prevent slowdowns 2090 // when we are loading right after we return to launcher. 2091 mWorkspace.postDelayed(new Runnable() { 2092 public void run() { 2093 if (mWorkspace != null) { 2094 AbstractFloatingView.closeAllOpenViews(Launcher.this, false); 2095 2096 mWorkspace.snapToPage(newScreenIndex); 2097 mWorkspace.postDelayed(startBounceAnimRunnable, 2098 NEW_APPS_ANIMATION_DELAY); 2099 } 2100 } 2101 }, NEW_APPS_PAGE_MOVE_DELAY); 2102 } else { 2103 mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); 2104 } 2105 } 2106 workspace.requestLayout(); 2107 } 2108 2109 /** 2110 * Add the views for a widget to the workspace. 2111 */ bindAppWidget(LauncherAppWidgetInfo item)2112 public void bindAppWidget(LauncherAppWidgetInfo item) { 2113 View view = inflateAppWidget(item); 2114 if (view != null) { 2115 mWorkspace.addInScreen(view, item); 2116 mWorkspace.requestLayout(); 2117 } 2118 } 2119 inflateAppWidget(LauncherAppWidgetInfo item)2120 private View inflateAppWidget(LauncherAppWidgetInfo item) { 2121 if (mIsSafeModeEnabled) { 2122 PendingAppWidgetHostView view = 2123 new PendingAppWidgetHostView(this, item, mIconCache, true); 2124 prepareAppWidget(view, item); 2125 return view; 2126 } 2127 2128 TraceHelper.beginSection("BIND_WIDGET"); 2129 2130 final LauncherAppWidgetProviderInfo appWidgetInfo; 2131 2132 if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { 2133 // If the provider is not ready, bind as a pending widget. 2134 appWidgetInfo = null; 2135 } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { 2136 // The widget id is not valid. Try to find the widget based on the provider info. 2137 appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user); 2138 } else { 2139 appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId); 2140 } 2141 2142 // If the provider is ready, but the width is not yet restored, try to restore it. 2143 if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && 2144 (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) { 2145 if (appWidgetInfo == null) { 2146 Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId 2147 + " belongs to component " + item.providerName 2148 + ", as the provider is null"); 2149 getModelWriter().deleteItemFromDatabase(item); 2150 return null; 2151 } 2152 2153 // If we do not have a valid id, try to bind an id. 2154 if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) { 2155 if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) { 2156 // Id has not been allocated yet. Allocate a new id. 2157 item.appWidgetId = mAppWidgetHost.allocateAppWidgetId(); 2158 item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED; 2159 2160 // Also try to bind the widget. If the bind fails, the user will be shown 2161 // a click to setup UI, which will ask for the bind permission. 2162 PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo); 2163 pendingInfo.spanX = item.spanX; 2164 pendingInfo.spanY = item.spanY; 2165 pendingInfo.minSpanX = item.minSpanX; 2166 pendingInfo.minSpanY = item.minSpanY; 2167 Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo); 2168 2169 boolean isDirectConfig = 2170 item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG); 2171 if (isDirectConfig && item.bindOptions != null) { 2172 Bundle newOptions = item.bindOptions.getExtras(); 2173 if (options != null) { 2174 newOptions.putAll(options); 2175 } 2176 options = newOptions; 2177 } 2178 boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( 2179 item.appWidgetId, appWidgetInfo, options); 2180 2181 // We tried to bind once. If we were not able to bind, we would need to 2182 // go through the permission dialog, which means we cannot skip the config 2183 // activity. 2184 item.bindOptions = null; 2185 item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG; 2186 2187 // Bind succeeded 2188 if (success) { 2189 // If the widget has a configure activity, it is still needs to set it up, 2190 // otherwise the widget is ready to go. 2191 item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig 2192 ? LauncherAppWidgetInfo.RESTORE_COMPLETED 2193 : LauncherAppWidgetInfo.FLAG_UI_NOT_READY; 2194 } 2195 2196 getModelWriter().updateItemInDatabase(item); 2197 } 2198 } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) 2199 && (appWidgetInfo.configure == null)) { 2200 // The widget was marked as UI not ready, but there is no configure activity to 2201 // update the UI. 2202 item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; 2203 getModelWriter().updateItemInDatabase(item); 2204 } 2205 } 2206 2207 final AppWidgetHostView view; 2208 if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { 2209 // Verify that we own the widget 2210 if (appWidgetInfo == null) { 2211 FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId); 2212 getModelWriter().deleteWidgetInfo(item, getAppWidgetHost()); 2213 return null; 2214 } 2215 2216 item.minSpanX = appWidgetInfo.minSpanX; 2217 item.minSpanY = appWidgetInfo.minSpanY; 2218 view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo); 2219 } else { 2220 view = new PendingAppWidgetHostView(this, item, mIconCache, false); 2221 } 2222 prepareAppWidget(view, item); 2223 2224 TraceHelper.endSection("BIND_WIDGET", "id=" + item.appWidgetId); 2225 return view; 2226 } 2227 2228 /** 2229 * Restores a pending widget. 2230 * 2231 * @param appWidgetId The app widget id 2232 */ completeRestoreAppWidget(int appWidgetId, int finalRestoreFlag)2233 private LauncherAppWidgetInfo completeRestoreAppWidget(int appWidgetId, int finalRestoreFlag) { 2234 LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId); 2235 if ((view == null) || !(view instanceof PendingAppWidgetHostView)) { 2236 Log.e(TAG, "Widget update called, when the widget no longer exists."); 2237 return null; 2238 } 2239 2240 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag(); 2241 info.restoreStatus = finalRestoreFlag; 2242 if (info.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { 2243 info.pendingItemInfo = null; 2244 } 2245 2246 if (((PendingAppWidgetHostView) view).isReinflateIfNeeded()) { 2247 view.reInflate(); 2248 } 2249 2250 getModelWriter().updateItemInDatabase(info); 2251 return info; 2252 } 2253 onPageBoundSynchronously(int page)2254 public void onPageBoundSynchronously(int page) { 2255 mSynchronouslyBoundPage = page; 2256 } 2257 2258 @Override executeOnNextDraw(ViewOnDrawExecutor executor)2259 public void executeOnNextDraw(ViewOnDrawExecutor executor) { 2260 if (mPendingExecutor != null) { 2261 mPendingExecutor.markCompleted(); 2262 } 2263 mPendingExecutor = executor; 2264 if (!isInState(ALL_APPS)) { 2265 mAppsView.getAppsStore().enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW); 2266 mPendingExecutor.execute(() -> mAppsView.getAppsStore().disableDeferUpdates( 2267 AllAppsStore.DEFER_UPDATES_NEXT_DRAW)); 2268 } 2269 2270 executor.attachTo(this); 2271 } 2272 clearPendingExecutor(ViewOnDrawExecutor executor)2273 public void clearPendingExecutor(ViewOnDrawExecutor executor) { 2274 if (mPendingExecutor == executor) { 2275 mPendingExecutor = null; 2276 } 2277 } 2278 2279 @Override finishFirstPageBind(final ViewOnDrawExecutor executor)2280 public void finishFirstPageBind(final ViewOnDrawExecutor executor) { 2281 AlphaProperty property = mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD); 2282 if (property.getValue() < 1) { 2283 ObjectAnimator anim = ObjectAnimator.ofFloat(property, MultiValueAlpha.VALUE, 1); 2284 if (executor != null) { 2285 anim.addListener(new AnimatorListenerAdapter() { 2286 @Override 2287 public void onAnimationEnd(Animator animation) { 2288 executor.onLoadAnimationCompleted(); 2289 } 2290 }); 2291 } 2292 anim.start(); 2293 } else if (executor != null) { 2294 executor.onLoadAnimationCompleted(); 2295 } 2296 } 2297 2298 /** 2299 * Callback saying that there aren't any more items to bind. 2300 * 2301 * Implementation of the method from LauncherModel.Callbacks. 2302 */ finishBindingItems(int pageBoundFirst)2303 public void finishBindingItems(int pageBoundFirst) { 2304 TraceHelper.beginSection("finishBindingItems"); 2305 mWorkspace.restoreInstanceStateForRemainingPages(); 2306 2307 setWorkspaceLoading(false); 2308 2309 if (mPendingActivityResult != null) { 2310 handleActivityResult(mPendingActivityResult.requestCode, 2311 mPendingActivityResult.resultCode, mPendingActivityResult.data); 2312 mPendingActivityResult = null; 2313 } 2314 2315 InstallShortcutReceiver.disableAndFlushInstallQueue( 2316 InstallShortcutReceiver.FLAG_LOADER_RUNNING, this); 2317 2318 // When undoing the removal of the last item on a page, return to that page. 2319 // Since we are just resetting the current page without user interaction, 2320 // override the previous page so we don't log the page switch. 2321 mWorkspace.setCurrentPage(pageBoundFirst, pageBoundFirst /* overridePrevPage */); 2322 2323 TraceHelper.endSection("finishBindingItems"); 2324 } 2325 canRunNewAppsAnimation()2326 private boolean canRunNewAppsAnimation() { 2327 long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime(); 2328 return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000); 2329 } 2330 createNewAppBounceAnimation(View v, int i)2331 private ValueAnimator createNewAppBounceAnimation(View v, int i) { 2332 ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v) 2333 .setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION); 2334 bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY); 2335 bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION)); 2336 return bounceAnim; 2337 } 2338 2339 /** 2340 * Add the icons for all apps. 2341 * 2342 * Implementation of the method from LauncherModel.Callbacks. 2343 */ bindAllApplications(ArrayList<AppInfo> apps)2344 public void bindAllApplications(ArrayList<AppInfo> apps) { 2345 mAppsView.getAppsStore().setApps(apps); 2346 } 2347 2348 /** 2349 * Copies LauncherModel's map of activities to shortcut counts to Launcher's. This is necessary 2350 * because LauncherModel's map is updated in the background, while Launcher runs on the UI. 2351 */ 2352 @Override bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy)2353 public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) { 2354 mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy); 2355 } 2356 2357 /** 2358 * A package was updated. 2359 * 2360 * Implementation of the method from LauncherModel.Callbacks. 2361 */ 2362 @Override bindAppsAddedOrUpdated(ArrayList<AppInfo> apps)2363 public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps) { 2364 mAppsView.getAppsStore().addOrUpdateApps(apps); 2365 } 2366 2367 @Override bindPromiseAppProgressUpdated(PromiseAppInfo app)2368 public void bindPromiseAppProgressUpdated(PromiseAppInfo app) { 2369 mAppsView.getAppsStore().updatePromiseAppProgress(app); 2370 } 2371 2372 @Override bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets)2373 public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { 2374 mWorkspace.widgetsRestored(widgets); 2375 } 2376 2377 /** 2378 * Some shortcuts were updated in the background. 2379 * Implementation of the method from LauncherModel.Callbacks. 2380 * 2381 * @param updated list of shortcuts which have changed. 2382 */ 2383 @Override bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated)2384 public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) { 2385 if (!updated.isEmpty()) { 2386 mWorkspace.updateShortcuts(updated); 2387 } 2388 } 2389 2390 /** 2391 * Update the state of a package, typically related to install state. 2392 * 2393 * Implementation of the method from LauncherModel.Callbacks. 2394 */ 2395 @Override bindRestoreItemsChange(HashSet<ItemInfo> updates)2396 public void bindRestoreItemsChange(HashSet<ItemInfo> updates) { 2397 mWorkspace.updateRestoreItems(updates); 2398 } 2399 2400 /** 2401 * A package was uninstalled/updated. We take both the super set of packageNames 2402 * in addition to specific applications to remove, the reason being that 2403 * this can be called when a package is updated as well. In that scenario, 2404 * we only remove specific components from the workspace and hotseat, where as 2405 * package-removal should clear all items by package name. 2406 */ 2407 @Override bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher)2408 public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) { 2409 mWorkspace.removeItemsByMatcher(matcher); 2410 mDragController.onAppsRemoved(matcher); 2411 } 2412 2413 @Override bindAppInfosRemoved(final ArrayList<AppInfo> appInfos)2414 public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) { 2415 mAppsView.getAppsStore().removeApps(appInfos); 2416 } 2417 2418 @Override bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets)2419 public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) { 2420 mPopupDataProvider.setAllWidgets(allWidgets); 2421 } 2422 2423 /** 2424 * @param packageUser if null, refreshes all widgets and shortcuts, otherwise only 2425 * refreshes the widgets and shortcuts associated with the given package/user 2426 */ refreshAndBindWidgetsForPackageUser(@ullable PackageUserKey packageUser)2427 public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) { 2428 mModel.refreshAndBindWidgetsAndShortcuts(packageUser); 2429 } 2430 2431 /** 2432 * $ adb shell dumpsys activity com.android.launcher3.Launcher [--all] 2433 */ 2434 @Override dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)2435 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 2436 super.dump(prefix, fd, writer, args); 2437 2438 if (args.length > 0 && TextUtils.equals(args[0], "--all")) { 2439 writer.println(prefix + "Workspace Items"); 2440 for (int i = 0; i < mWorkspace.getPageCount(); i++) { 2441 writer.println(prefix + " Homescreen " + i); 2442 2443 ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets(); 2444 for (int j = 0; j < layout.getChildCount(); j++) { 2445 Object tag = layout.getChildAt(j).getTag(); 2446 if (tag != null) { 2447 writer.println(prefix + " " + tag.toString()); 2448 } 2449 } 2450 } 2451 2452 writer.println(prefix + " Hotseat"); 2453 ViewGroup layout = mHotseat.getShortcutsAndWidgets(); 2454 for (int j = 0; j < layout.getChildCount(); j++) { 2455 Object tag = layout.getChildAt(j).getTag(); 2456 if (tag != null) { 2457 writer.println(prefix + " " + tag.toString()); 2458 } 2459 } 2460 } 2461 2462 writer.println(prefix + "Misc:"); 2463 writer.print(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading); 2464 writer.print(" mPendingRequestArgs=" + mPendingRequestArgs); 2465 writer.println(" mPendingActivityResult=" + mPendingActivityResult); 2466 writer.println(" mRotationHelper: " + mRotationHelper); 2467 // Extra logging for b/116853349 2468 mDragLayer.dump(prefix, writer); 2469 mStateManager.dump(prefix, writer); 2470 dumpMisc(writer); 2471 2472 try { 2473 FileLog.flushAll(writer); 2474 } catch (Exception e) { 2475 // Ignore 2476 } 2477 2478 mModel.dumpState(prefix, fd, writer, args); 2479 2480 if (mLauncherCallbacks != null) { 2481 mLauncherCallbacks.dump(prefix, fd, writer, args); 2482 } 2483 } 2484 2485 @Override onProvideKeyboardShortcuts( List<KeyboardShortcutGroup> data, Menu menu, int deviceId)2486 public void onProvideKeyboardShortcuts( 2487 List<KeyboardShortcutGroup> data, Menu menu, int deviceId) { 2488 2489 ArrayList<KeyboardShortcutInfo> shortcutInfos = new ArrayList<>(); 2490 if (isInState(NORMAL)) { 2491 shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label), 2492 KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON)); 2493 shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.widget_button_text), 2494 KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON)); 2495 } 2496 final View currentFocus = getCurrentFocus(); 2497 if (currentFocus != null) { 2498 if (new CustomActionsPopup(this, currentFocus).canShow()) { 2499 shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.custom_actions), 2500 KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON)); 2501 } 2502 if (currentFocus.getTag() instanceof ItemInfo 2503 && DeepShortcutManager.supportsShortcuts((ItemInfo) currentFocus.getTag())) { 2504 shortcutInfos.add(new KeyboardShortcutInfo( 2505 getString(R.string.shortcuts_menu_with_notifications_description), 2506 KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON)); 2507 } 2508 } 2509 if (!shortcutInfos.isEmpty()) { 2510 data.add(new KeyboardShortcutGroup(getString(R.string.home_screen), shortcutInfos)); 2511 } 2512 2513 super.onProvideKeyboardShortcuts(data, menu, deviceId); 2514 } 2515 2516 @Override onKeyShortcut(int keyCode, KeyEvent event)2517 public boolean onKeyShortcut(int keyCode, KeyEvent event) { 2518 if (event.hasModifiers(KeyEvent.META_CTRL_ON)) { 2519 switch (keyCode) { 2520 case KeyEvent.KEYCODE_A: 2521 if (isInState(NORMAL)) { 2522 getStateManager().goToState(ALL_APPS); 2523 return true; 2524 } 2525 break; 2526 case KeyEvent.KEYCODE_S: { 2527 View focusedView = getCurrentFocus(); 2528 if (focusedView instanceof BubbleTextView 2529 && focusedView.getTag() instanceof ItemInfo 2530 && mAccessibilityDelegate.performAction(focusedView, 2531 (ItemInfo) focusedView.getTag(), 2532 LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) { 2533 PopupContainerWithArrow.getOpen(this).requestFocus(); 2534 return true; 2535 } 2536 break; 2537 } 2538 case KeyEvent.KEYCODE_O: 2539 if (new CustomActionsPopup(this, getCurrentFocus()).show()) { 2540 return true; 2541 } 2542 break; 2543 case KeyEvent.KEYCODE_W: 2544 if (isInState(NORMAL)) { 2545 OptionsPopupView.openWidgets(this); 2546 return true; 2547 } 2548 break; 2549 } 2550 } 2551 return super.onKeyShortcut(keyCode, event); 2552 } 2553 2554 @Override onKeyUp(int keyCode, KeyEvent event)2555 public boolean onKeyUp(int keyCode, KeyEvent event) { 2556 if (keyCode == KeyEvent.KEYCODE_MENU) { 2557 // KEYCODE_MENU is sent by some tests, for example 2558 // LauncherJankTests#testWidgetsContainerFling. Don't just remove its handling. 2559 if (!mDragController.isDragging() && !mWorkspace.isSwitchingState() && 2560 isInState(NORMAL)) { 2561 // Close any open floating views. 2562 AbstractFloatingView.closeAllOpenViews(this); 2563 2564 // Setting the touch point to (-1, -1) will show the options popup in the center of 2565 // the screen. 2566 OptionsPopupView.showDefaultOptions(this, -1, -1); 2567 } 2568 return true; 2569 } 2570 return super.onKeyUp(keyCode, event); 2571 } 2572 getLauncher(Context context)2573 public static Launcher getLauncher(Context context) { 2574 return (Launcher) fromContext(context); 2575 } 2576 2577 /** 2578 * Just a wrapper around the type cast to allow easier tracking of calls. 2579 */ cast(ActivityContext activityContext)2580 public static <T extends Launcher> T cast(ActivityContext activityContext) { 2581 return (T) activityContext; 2582 } 2583 2584 /** 2585 * Callback for listening for onResume 2586 */ 2587 public interface OnResumeCallback { 2588 onLauncherResume()2589 void onLauncherResume(); 2590 } 2591 } 2592