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