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