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