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.app.PendingIntent.FLAG_IMMUTABLE; 20 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; 21 import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; 22 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE; 23 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; 24 25 import static com.android.app.animation.Interpolators.EMPHASIZED; 26 import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER; 27 import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE; 28 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; 29 import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; 30 import static com.android.launcher3.Flags.enableAddAppWidgetViaConfigActivityV2; 31 import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle; 32 import static com.android.launcher3.Flags.enableStrictMode; 33 import static com.android.launcher3.Flags.enableWorkspaceInflation; 34 import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY; 35 import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WIDGET_TRANSITION; 36 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; 37 import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY; 38 import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_BIND_APPWIDGET; 39 import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_BIND_PENDING_APPWIDGET; 40 import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_CREATE_APPWIDGET; 41 import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_CREATE_SHORTCUT; 42 import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_HOME_ROLE; 43 import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_PICK_APPWIDGET; 44 import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_RECONFIGURE_APPWIDGET; 45 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE; 46 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_CURRENT_SCREEN_IDS; 47 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_PENDING_ACTIVITY_RESULT; 48 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_PENDING_REQUEST_ARGS; 49 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_PENDING_REQUEST_CODE; 50 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_RECREATE_TO_UPDATE_THEME; 51 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_WIDGET_PANEL; 52 import static com.android.launcher3.LauncherConstants.TraceEvents.COLD_STARTUP_TRACE_COOKIE; 53 import static com.android.launcher3.LauncherConstants.TraceEvents.COLD_STARTUP_TRACE_METHOD_NAME; 54 import static com.android.launcher3.LauncherConstants.TraceEvents.DISPLAY_ALL_APPS_TRACE_COOKIE; 55 import static com.android.launcher3.LauncherConstants.TraceEvents.DISPLAY_ALL_APPS_TRACE_METHOD_NAME; 56 import static com.android.launcher3.LauncherConstants.TraceEvents.DISPLAY_WORKSPACE_TRACE_COOKIE; 57 import static com.android.launcher3.LauncherConstants.TraceEvents.DISPLAY_WORKSPACE_TRACE_METHOD_NAME; 58 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_CREATE_EVT; 59 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_NEW_INTENT_EVT; 60 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_RESUME_EVT; 61 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_START_EVT; 62 import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE; 63 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; 64 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 65 import static com.android.launcher3.LauncherState.ALL_APPS; 66 import static com.android.launcher3.LauncherState.EDIT_MODE; 67 import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE; 68 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE; 69 import static com.android.launcher3.LauncherState.NORMAL; 70 import static com.android.launcher3.LauncherState.NO_OFFSET; 71 import static com.android.launcher3.LauncherState.NO_SCALE; 72 import static com.android.launcher3.LauncherState.SPRING_LOADED; 73 import static com.android.launcher3.Utilities.postAsyncCallback; 74 import static com.android.launcher3.Workspace.mapOverCellLayouts; 75 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; 76 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE; 77 import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE; 78 import static com.android.launcher3.icons.BitmapRenderer.createHardwareBitmap; 79 import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE; 80 import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW; 81 import static com.android.launcher3.logging.StatsLogManager.EventEnum; 82 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; 83 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; 84 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH; 85 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT; 86 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME; 87 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP; 88 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME; 89 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT; 90 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT; 91 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED; 92 import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE; 93 import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION; 94 import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION; 95 import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.COLD; 96 import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.COLD_DEVICE_REBOOTING; 97 import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.WARM; 98 import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED; 99 import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP; 100 import static com.android.launcher3.popup.SystemShortcut.APP_INFO; 101 import static com.android.launcher3.popup.SystemShortcut.INSTALL; 102 import static com.android.launcher3.popup.SystemShortcut.WIDGETS; 103 import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK; 104 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; 105 import static com.android.launcher3.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE; 106 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 107 import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch; 108 import static com.android.launcher3.util.SettingsCache.TOUCHPAD_NATURAL_SCROLLING; 109 import static com.android.launcher3.util.WallpaperThemeManager.setWallpaperDependentTheme; 110 111 import android.animation.Animator; 112 import android.animation.AnimatorSet; 113 import android.animation.ValueAnimator; 114 import android.annotation.TargetApi; 115 import android.app.Notification; 116 import android.app.NotificationChannel; 117 import android.app.NotificationManager; 118 import android.app.PendingIntent; 119 import android.appwidget.AppWidgetHostView; 120 import android.appwidget.AppWidgetManager; 121 import android.content.ActivityNotFoundException; 122 import android.content.ComponentCallbacks2; 123 import android.content.Context; 124 import android.content.Intent; 125 import android.content.IntentSender; 126 import android.content.SharedPreferences; 127 import android.content.res.Configuration; 128 import android.database.sqlite.SQLiteDatabase; 129 import android.graphics.Bitmap; 130 import android.graphics.Color; 131 import android.graphics.Rect; 132 import android.graphics.RectF; 133 import android.os.Build; 134 import android.os.Bundle; 135 import android.os.Parcelable; 136 import android.os.StrictMode; 137 import android.os.SystemClock; 138 import android.os.Trace; 139 import android.os.UserHandle; 140 import android.text.TextUtils; 141 import android.text.method.TextKeyListener; 142 import android.util.FloatProperty; 143 import android.util.Log; 144 import android.util.Pair; 145 import android.util.SparseArray; 146 import android.view.KeyEvent; 147 import android.view.KeyboardShortcutGroup; 148 import android.view.Menu; 149 import android.view.MotionEvent; 150 import android.view.View; 151 import android.view.View.OnClickListener; 152 import android.view.ViewGroup; 153 import android.view.ViewTreeObserver.OnPreDrawListener; 154 import android.view.WindowInsets; 155 import android.view.WindowInsetsAnimation; 156 import android.view.WindowManager.LayoutParams; 157 import android.view.accessibility.AccessibilityEvent; 158 import android.view.animation.OvershootInterpolator; 159 import android.widget.Toast; 160 import android.window.BackEvent; 161 import android.window.OnBackAnimationCallback; 162 163 import androidx.annotation.CallSuper; 164 import androidx.annotation.NonNull; 165 import androidx.annotation.Nullable; 166 import androidx.annotation.RequiresApi; 167 import androidx.annotation.UiThread; 168 import androidx.annotation.VisibleForTesting; 169 import androidx.core.os.BuildCompat; 170 import androidx.window.embedding.RuleController; 171 172 import com.android.launcher3.DropTarget.DragObject; 173 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; 174 import com.android.launcher3.allapps.ActivityAllAppsContainerView; 175 import com.android.launcher3.allapps.AllAppsTransitionController; 176 import com.android.launcher3.allapps.DiscoveryBounce; 177 import com.android.launcher3.anim.AnimationSuccessListener; 178 import com.android.launcher3.anim.PropertyListBuilder; 179 import com.android.launcher3.apppairs.AppPairIcon; 180 import com.android.launcher3.celllayout.CellPosMapper; 181 import com.android.launcher3.celllayout.CellPosMapper.CellPos; 182 import com.android.launcher3.celllayout.CellPosMapper.TwoPanelCellPosMapper; 183 import com.android.launcher3.compat.AccessibilityManagerCompat; 184 import com.android.launcher3.config.FeatureFlags; 185 import com.android.launcher3.debug.TestEventEmitter; 186 import com.android.launcher3.debug.TestEventEmitter.TestEvent; 187 import com.android.launcher3.dragndrop.DragController; 188 import com.android.launcher3.dragndrop.DragLayer; 189 import com.android.launcher3.dragndrop.DragView; 190 import com.android.launcher3.dragndrop.LauncherDragController; 191 import com.android.launcher3.folder.Folder; 192 import com.android.launcher3.folder.FolderIcon; 193 import com.android.launcher3.icons.IconCache; 194 import com.android.launcher3.keyboard.ViewGroupFocusHelper; 195 import com.android.launcher3.logger.LauncherAtom; 196 import com.android.launcher3.logger.LauncherAtom.ContainerInfo; 197 import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer; 198 import com.android.launcher3.logging.ColdRebootStartupLatencyLogger; 199 import com.android.launcher3.logging.FileLog; 200 import com.android.launcher3.logging.InstanceId; 201 import com.android.launcher3.logging.InstanceIdSequence; 202 import com.android.launcher3.logging.StartupLatencyLogger; 203 import com.android.launcher3.logging.StatsLogManager; 204 import com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent; 205 import com.android.launcher3.model.BgDataModel.Callbacks; 206 import com.android.launcher3.model.ItemInstallQueue; 207 import com.android.launcher3.model.ModelWriter; 208 import com.android.launcher3.model.StringCache; 209 import com.android.launcher3.model.data.AppInfo; 210 import com.android.launcher3.model.data.CollectionInfo; 211 import com.android.launcher3.model.data.FolderInfo; 212 import com.android.launcher3.model.data.ItemInfo; 213 import com.android.launcher3.model.data.LauncherAppWidgetInfo; 214 import com.android.launcher3.model.data.WorkspaceItemInfo; 215 import com.android.launcher3.notification.NotificationListener; 216 import com.android.launcher3.pm.PinRequestHelper; 217 import com.android.launcher3.popup.ArrowPopup; 218 import com.android.launcher3.popup.PopupDataProvider; 219 import com.android.launcher3.popup.SystemShortcut; 220 import com.android.launcher3.statemanager.StateManager; 221 import com.android.launcher3.statemanager.StateManager.StateHandler; 222 import com.android.launcher3.statemanager.StatefulActivity; 223 import com.android.launcher3.states.RotationHelper; 224 import com.android.launcher3.testing.TestLogging; 225 import com.android.launcher3.testing.shared.TestProtocol; 226 import com.android.launcher3.touch.AllAppsSwipeController; 227 import com.android.launcher3.touch.ItemClickHandler; 228 import com.android.launcher3.touch.ItemLongClickListener; 229 import com.android.launcher3.util.ActivityResultInfo; 230 import com.android.launcher3.util.BackPressHandler; 231 import com.android.launcher3.util.CannedAnimationCoordinator; 232 import com.android.launcher3.util.ComponentKey; 233 import com.android.launcher3.util.ContextTracker; 234 import com.android.launcher3.util.IntArray; 235 import com.android.launcher3.util.IntSet; 236 import com.android.launcher3.util.ItemInflater; 237 import com.android.launcher3.util.KeyboardShortcutsDelegate; 238 import com.android.launcher3.util.LauncherBindableItemsContainer; 239 import com.android.launcher3.util.LockedUserState; 240 import com.android.launcher3.util.MSDLPlayerWrapper; 241 import com.android.launcher3.util.PackageUserKey; 242 import com.android.launcher3.util.PendingRequestArgs; 243 import com.android.launcher3.util.PluginManagerWrapper; 244 import com.android.launcher3.util.RunnableList; 245 import com.android.launcher3.util.ScreenOnTracker; 246 import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener; 247 import com.android.launcher3.util.SettingsCache; 248 import com.android.launcher3.util.StableViewInfo; 249 import com.android.launcher3.util.SystemUiController; 250 import com.android.launcher3.util.Themes; 251 import com.android.launcher3.util.Thunk; 252 import com.android.launcher3.util.TouchController; 253 import com.android.launcher3.util.TraceHelper; 254 import com.android.launcher3.views.ActivityContext; 255 import com.android.launcher3.views.FloatingIconView; 256 import com.android.launcher3.views.FloatingSurfaceView; 257 import com.android.launcher3.views.OptionsPopupView; 258 import com.android.launcher3.views.ScrimView; 259 import com.android.launcher3.widget.LauncherAppWidgetHostView; 260 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; 261 import com.android.launcher3.widget.LauncherWidgetHolder; 262 import com.android.launcher3.widget.PendingAddShortcutInfo; 263 import com.android.launcher3.widget.PendingAddWidgetInfo; 264 import com.android.launcher3.widget.PendingAppWidgetHostView; 265 import com.android.launcher3.widget.WidgetAddFlowHandler; 266 import com.android.launcher3.widget.WidgetManagerHelper; 267 import com.android.launcher3.widget.custom.CustomWidgetManager; 268 import com.android.launcher3.widget.model.WidgetsListBaseEntry; 269 import com.android.launcher3.widget.picker.WidgetsFullSheet; 270 import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider; 271 import com.android.launcher3.widget.util.WidgetSizes; 272 import com.android.systemui.plugins.LauncherOverlayPlugin; 273 import com.android.systemui.plugins.PluginListener; 274 import com.android.systemui.plugins.shared.LauncherOverlayManager; 275 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayTouchProxy; 276 import com.android.window.flags.Flags; 277 278 import java.io.FileDescriptor; 279 import java.io.PrintWriter; 280 import java.util.ArrayList; 281 import java.util.Collections; 282 import java.util.HashMap; 283 import java.util.List; 284 import java.util.Map; 285 import java.util.Objects; 286 import java.util.Optional; 287 import java.util.Set; 288 import java.util.function.Predicate; 289 import java.util.function.Supplier; 290 import java.util.stream.Collectors; 291 import java.util.stream.Stream; 292 293 /** 294 * Default launcher application. 295 */ 296 public class Launcher extends StatefulActivity<LauncherState> 297 implements Callbacks, InvariantDeviceProfile.OnIDPChangeListener, 298 PluginListener<LauncherOverlayPlugin> { 299 public static final String TAG = "Launcher"; 300 301 public static final ContextTracker.ActivityTracker<Launcher> ACTIVITY_TRACKER = 302 new ContextTracker.ActivityTracker<>(); 303 304 static final boolean LOGD = false; 305 306 static final boolean DEBUG_STRICT_MODE = false; 307 308 private static final float BOUNCE_ANIMATION_TENSION = 1.3f; 309 310 /** 311 * IntentStarter uses request codes starting with this. This must be greater than all activity 312 * request codes used internally. 313 */ 314 protected static final int REQUEST_LAST = 100; 315 316 public static final String INTENT_ACTION_ALL_APPS_TOGGLE = 317 "launcher.intent_action_all_apps_toggle"; 318 319 private static boolean sIsNewProcess = true; 320 321 private StateManager<LauncherState, Launcher> mStateManager; 322 323 private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; 324 325 // How long to wait before the new-shortcut animation automatically pans the workspace 326 @VisibleForTesting public static final int NEW_APPS_PAGE_MOVE_DELAY = 500; 327 private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; 328 @Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500; 329 330 private static final FloatProperty<Workspace<?>> WORKSPACE_WIDGET_SCALE = 331 WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION); 332 private static final FloatProperty<Hotseat> HOTSEAT_WIDGET_SCALE = 333 HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION); 334 335 private final ModelCallbacks mModelCallbacks = createModelCallbacks(); 336 337 private final KeyboardShortcutsDelegate mKeyboardShortcutsDelegate = 338 new KeyboardShortcutsDelegate(this); 339 340 @Thunk 341 Workspace<?> mWorkspace; 342 @Thunk 343 DragLayer mDragLayer; 344 345 private WidgetManagerHelper mAppWidgetManager; 346 private LauncherWidgetHolder mAppWidgetHolder; 347 private ItemInflater<Launcher> mItemInflater; 348 349 private final int[] mTmpAddItemCellCoordinates = new int[2]; 350 351 @Thunk 352 Hotseat mHotseat; 353 354 private DropTargetBar mDropTargetBar; 355 356 // Main container view for the all apps screen. 357 @Thunk 358 ActivityAllAppsContainerView<Launcher> mAppsView; 359 AllAppsTransitionController mAllAppsController; 360 361 // Scrim view for the all apps and overview state. 362 @Thunk 363 ScrimView mScrimView; 364 365 // UI and state for the overview panel 366 private View mOverviewPanel; 367 368 // Used to notify when an activity launch has been deferred because launcher is not yet resumed 369 // TODO: See if we can remove this later 370 private Runnable mOnDeferredActivityLaunchCallback; 371 private OnPreDrawListener mOnInitialBindListener; 372 373 private LauncherModel mModel; 374 private ModelWriter mModelWriter; 375 private IconCache mIconCache; 376 private LauncherAccessibilityDelegate mAccessibilityDelegate; 377 378 private PopupDataProvider mPopupDataProvider; 379 private WidgetPickerDataProvider mWidgetPickerDataProvider; 380 381 // We only want to get the SharedPreferences once since it does an FS stat each time we get 382 // it from the context. 383 private SharedPreferences mSharedPrefs; 384 385 // Activity result which needs to be processed after workspace has loaded. 386 private ActivityResultInfo mPendingActivityResult; 387 /** 388 * Holds extra information required to handle a result from an external call, like 389 * {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)} 390 */ 391 private PendingRequestArgs mPendingRequestArgs; 392 // Request id for any pending activity result 393 protected int mPendingActivityRequestCode = -1; 394 395 private ViewGroupFocusHelper mFocusHandler; 396 397 private RotationHelper mRotationHelper; 398 399 protected LauncherOverlayManager mOverlayManager; 400 protected DragController mDragController; 401 // If true, overlay callbacks are deferred 402 private boolean mDeferOverlayCallbacks; 403 private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred; 404 405 protected long mLastTouchUpTime = -1; 406 private boolean mTouchInProgress; 407 408 // New InstanceId is assigned to mAllAppsSessionLogId for each AllApps sessions. 409 // When Launcher is not in AllApps state mAllAppsSessionLogId will be null. 410 // User actions within AllApps state are logged with this InstanceId, to recreate AllApps 411 // session on the server side. 412 protected InstanceId mAllAppsSessionLogId; 413 private LauncherState mPrevLauncherState; 414 private StartupLatencyLogger mStartupLatencyLogger; 415 private CellPosMapper mCellPosMapper = CellPosMapper.DEFAULT; 416 417 private final CannedAnimationCoordinator mAnimationCoordinator = 418 new CannedAnimationCoordinator(this); 419 420 private final List<BackPressHandler> mBackPressedHandlers = new ArrayList<>(); 421 private boolean mIsColdStartupAfterReboot; 422 423 private boolean mIsNaturalScrollingEnabled; 424 425 private final SettingsCache.OnChangeListener mNaturalScrollingChangedListener = 426 enabled -> mIsNaturalScrollingEnabled = enabled; 427 getLauncher(Context context)428 public static Launcher getLauncher(Context context) { 429 return fromContext(context); 430 } 431 432 @Override 433 @TargetApi(Build.VERSION_CODES.S) onCreate(Bundle savedInstanceState)434 protected void onCreate(Bundle savedInstanceState) { 435 mStartupLatencyLogger = createStartupLatencyLogger( 436 sIsNewProcess 437 ? LockedUserState.get(this).isUserUnlockedAtLauncherStartup() 438 ? COLD 439 : COLD_DEVICE_REBOOTING 440 : WARM); 441 442 mIsColdStartupAfterReboot = sIsNewProcess 443 && !LockedUserState.get(this).isUserUnlockedAtLauncherStartup(); 444 if (mIsColdStartupAfterReboot) { 445 /* 446 * This trace is used to calculate the time from create to the point that icons are 447 * visible. 448 */ 449 Trace.beginAsyncSection( 450 COLD_STARTUP_TRACE_METHOD_NAME, COLD_STARTUP_TRACE_COOKIE); 451 } 452 453 sIsNewProcess = false; 454 mStartupLatencyLogger 455 .logStart(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION) 456 .logStart(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE); 457 // Only use a hard-coded cookie since we only want to trace this once. 458 Trace.beginAsyncSection( 459 DISPLAY_WORKSPACE_TRACE_METHOD_NAME, DISPLAY_WORKSPACE_TRACE_COOKIE); 460 Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME, 461 DISPLAY_ALL_APPS_TRACE_COOKIE); 462 TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT); 463 if (DEBUG_STRICT_MODE 464 || (FeatureFlags.IS_STUDIO_BUILD && enableStrictMode())) { 465 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() 466 .detectDiskReads() 467 .detectDiskWrites() 468 .detectNetwork() // or .detectAll() for all detectable problems 469 .penaltyLog() 470 .build()); 471 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 472 .detectLeakedSqlLiteObjects() 473 .detectLeakedClosableObjects() 474 .detectActivityLeaks() 475 .penaltyLog() 476 .penaltyDeath() 477 .build()); 478 } 479 480 if (Utilities.IS_DEBUG_DEVICE && FeatureFlags.NOTIFY_CRASHES.get()) { 481 final String notificationChannelId = "com.android.launcher3.Debug"; 482 final String notificationChannelName = "Debug"; 483 final String notificationTag = "Debug"; 484 final int notificationId = 0; 485 486 NotificationManager notificationManager = getSystemService(NotificationManager.class); 487 notificationManager.createNotificationChannel(new NotificationChannel( 488 notificationChannelId, notificationChannelName, 489 NotificationManager.IMPORTANCE_HIGH)); 490 491 Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> { 492 String stackTrace = Log.getStackTraceString(throwable); 493 494 Intent shareIntent = new Intent(Intent.ACTION_SEND); 495 shareIntent.setType("text/plain"); 496 shareIntent.putExtra(Intent.EXTRA_TEXT, stackTrace); 497 shareIntent = Intent.createChooser(shareIntent, null); 498 PendingIntent sharePendingIntent = PendingIntent.getActivity( 499 this, 0, shareIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE); 500 501 Notification notification = new Notification.Builder(this, notificationChannelId) 502 .setSmallIcon(android.R.drawable.ic_menu_close_clear_cancel) 503 .setContentTitle("Launcher crash detected!") 504 .setStyle(new Notification.BigTextStyle().bigText(stackTrace)) 505 .addAction(android.R.drawable.ic_menu_share, "Share", sharePendingIntent) 506 .build(); 507 notificationManager.notify(notificationTag, notificationId, notification); 508 509 Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = 510 Thread.getDefaultUncaughtExceptionHandler(); 511 if (defaultUncaughtExceptionHandler != null) { 512 defaultUncaughtExceptionHandler.uncaughtException(thread, throwable); 513 } 514 }); 515 } 516 517 super.onCreate(savedInstanceState); 518 setWallpaperDependentTheme(this); 519 520 LauncherAppState app = LauncherAppState.getInstance(this); 521 mModel = app.getModel(); 522 523 mRotationHelper = new RotationHelper(this); 524 InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); 525 initDeviceProfile(idp); 526 idp.addOnChangeListener(this); 527 mSharedPrefs = LauncherPrefs.getPrefs(this); 528 mIconCache = app.getIconCache(); 529 mAccessibilityDelegate = createAccessibilityDelegate(); 530 531 initDragController(); 532 mAllAppsController = new AllAppsTransitionController(this); 533 mStateManager = new StateManager<>(this, NORMAL); 534 535 mAppWidgetManager = new WidgetManagerHelper(this); 536 mAppWidgetHolder = LauncherWidgetHolder.newInstance(this); 537 mAppWidgetHolder.setAppWidgetRemovedCallback( 538 appWidgetId -> getWorkspace().removeWidget(appWidgetId)); 539 540 setupViews(); 541 updateDisallowBack(); 542 543 mAppWidgetHolder.startListening(); 544 mAppWidgetHolder.addProviderChangeListener(() -> refreshAndBindWidgetsForPackageUser(null)); 545 mItemInflater = new ItemInflater<>(this, mAppWidgetHolder, getItemOnClickListener(), 546 mFocusHandler, new CellLayout(mWorkspace.getContext(), mWorkspace)); 547 548 mPopupDataProvider = new PopupDataProvider(this); 549 mWidgetPickerDataProvider = new WidgetPickerDataProvider(this); 550 PillColorProvider.getInstance(mWorkspace.getContext()).registerObserver(); 551 552 boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this); 553 if (internalStateHandled) { 554 if (savedInstanceState != null) { 555 // InternalStateHandler has already set the appropriate state. 556 // We dont need to do anything. 557 savedInstanceState.remove(RUNTIME_STATE); 558 } 559 } 560 restoreState(savedInstanceState); 561 mStateManager.reapplyState(); 562 563 if (savedInstanceState != null) { 564 int[] pageIds = savedInstanceState.getIntArray(RUNTIME_STATE_CURRENT_SCREEN_IDS); 565 if (pageIds != null) { 566 mModelCallbacks.setPagesToBindSynchronously(IntSet.wrap(pageIds)); 567 } 568 } 569 570 mStartupLatencyLogger.logWorkspaceLoadStartTime(); 571 if (!mModel.addCallbacksAndLoad(this)) { 572 if (!internalStateHandled) { 573 // If we are not binding synchronously, pause drawing until initial bind complete, 574 // so that the system could continue to show the device loading prompt 575 mOnInitialBindListener = Boolean.FALSE::booleanValue; 576 } 577 } 578 579 // For handling default keys 580 setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); 581 582 setContentView(getRootView()); 583 584 if (mOnInitialBindListener != null) { 585 getRootView().getViewTreeObserver().addOnPreDrawListener(mOnInitialBindListener); 586 } 587 getRootView().dispatchInsets(); 588 589 final SettingsCache settingsCache = SettingsCache.INSTANCE.get(this); 590 settingsCache.register(TOUCHPAD_NATURAL_SCROLLING, mNaturalScrollingChangedListener); 591 mIsNaturalScrollingEnabled = settingsCache.getValue(TOUCHPAD_NATURAL_SCROLLING); 592 593 // Listen for screen turning off 594 ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener); 595 getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW, 596 Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)); 597 598 mOverlayManager = getDefaultOverlay(); 599 PluginManagerWrapper.INSTANCE.get(this) 600 .addPluginListener(this, LauncherOverlayPlugin.class); 601 602 mRotationHelper.initialize(); 603 TraceHelper.INSTANCE.endSection(); 604 605 getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); 606 setTitle(R.string.home_screen); 607 mStartupLatencyLogger.logEnd(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE); 608 609 if (BuildCompat.isAtLeastV() 610 && com.android.launcher3.Flags.enableTwoPaneLauncherSettings()) { 611 RuleController.getInstance(this).setRules( 612 RuleController.parseRules(this, R.xml.split_configuration)); 613 } 614 TestEventEmitter.sendEvent(TestEvent.LAUNCHER_ON_CREATE); 615 } 616 createModelCallbacks()617 protected ModelCallbacks createModelCallbacks() { 618 return new ModelCallbacks(this); 619 } 620 621 /** 622 * We only log startup latency in {@link COLD_DEVICE_REBOOTING} type. For other latency types, 623 * create a no op implementation. 624 */ createStartupLatencyLogger( StatsLogManager.StatsLatencyLogger.LatencyType latencyType)625 private StartupLatencyLogger createStartupLatencyLogger( 626 StatsLogManager.StatsLatencyLogger.LatencyType latencyType) { 627 if (latencyType == COLD_DEVICE_REBOOTING) { 628 return createColdRebootStartupLatencyLogger(); 629 } 630 return StartupLatencyLogger.Companion.getNO_OP(); 631 } 632 633 /** 634 * Create {@link ColdRebootStartupLatencyLogger} that only collects launcher startup latency 635 * metrics without sending them anywhere. Child class can override this method to create logger 636 * that overrides {@link StartupLatencyLogger#log()} to report those metrics. 637 */ createColdRebootStartupLatencyLogger()638 protected ColdRebootStartupLatencyLogger createColdRebootStartupLatencyLogger() { 639 return new ColdRebootStartupLatencyLogger(); 640 } 641 getAccessibilityActionView()642 @NonNull View getAccessibilityActionView() { 643 return findViewById(R.id.accessibility_action_view); 644 } 645 646 /** 647 * Provide {@link OnBackAnimationCallback} in below order: 648 * <ol> 649 * <li> auto cancel action mode handler 650 * <li> drag handler 651 * <li> view handler 652 * <li> registered {@link BackPressHandler} 653 * <li> state handler 654 * </ol> 655 * 656 * A back gesture (a single click on back button, or a swipe back gesture that contains a series 657 * of swipe events) should be handled by the same handler from above list. For a new back 658 * gesture, a new handler should be regenerated. 659 * 660 * Note that state handler will always be handling the back press event if the previous 3 don't. 661 */ 662 @NonNull 663 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) getOnBackAnimationCallback()664 protected OnBackAnimationCallback getOnBackAnimationCallback() { 665 // #1 auto cancel action mode handler 666 if (isInAutoCancelActionMode()) { 667 return this::finishAutoCancelActionMode; 668 } 669 670 // #2 drag handler 671 if (mDragController.isDragging()) { 672 return mDragController::cancelDrag; 673 } 674 675 // #3 view handler 676 AbstractFloatingView topView = 677 AbstractFloatingView.getTopOpenView(Launcher.this); 678 if (topView != null && topView.canHandleBack()) { 679 return topView; 680 } 681 682 // #4 Custom back handlers 683 for (BackPressHandler handler : mBackPressedHandlers) { 684 if (handler.canHandleBack()) { 685 return handler; 686 } 687 } 688 689 // #5 state handler 690 return new OnBackAnimationCallback() { 691 @Override 692 public void onBackStarted(BackEvent backEvent) { 693 Launcher.this.onBackStarted(); 694 } 695 696 @Override 697 public void onBackInvoked() { 698 onStateBack(); 699 } 700 701 @Override 702 public void onBackProgressed(@NonNull BackEvent backEvent) { 703 mStateManager.getState().onBackProgressed( 704 Launcher.this, backEvent.getProgress()); 705 } 706 707 @Override 708 public void onBackCancelled() { 709 Launcher.this.onBackCancelled(); 710 } 711 }; 712 } 713 714 protected LauncherOverlayManager getDefaultOverlay() { 715 return new LauncherOverlayManager() { }; 716 } 717 718 @Override 719 public void onPluginConnected(LauncherOverlayPlugin overlayManager, Context context) { 720 switchOverlay(() -> overlayManager.createOverlayManager(this)); 721 } 722 723 @Override 724 public void onPluginDisconnected(LauncherOverlayPlugin plugin) { 725 switchOverlay(this::getDefaultOverlay); 726 } 727 728 private void switchOverlay(Supplier<LauncherOverlayManager> overlaySupplier) { 729 if (mOverlayManager != null) { 730 mOverlayManager.onActivityDestroyed(); 731 } 732 mOverlayManager = overlaySupplier.get(); 733 if (getRootView().isAttachedToWindow()) { 734 mOverlayManager.onAttachedToWindow(); 735 } 736 mDeferOverlayCallbacks = true; 737 checkIfOverlayStillDeferred(); 738 } 739 740 @Override 741 public void dispatchDeviceProfileChanged() { 742 super.dispatchDeviceProfileChanged(); 743 mOverlayManager.onDeviceProvideChanged(); 744 } 745 746 @Override 747 public void onEnterAnimationComplete() { 748 super.onEnterAnimationComplete(); 749 mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE); 750 } 751 752 @Override 753 public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { 754 super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); 755 // Always update device profile when multi window mode changed. 756 initDeviceProfile(mDeviceProfile.inv); 757 dispatchDeviceProfileChanged(); 758 } 759 760 /** 761 * Initializes the drag controller. 762 */ 763 protected void initDragController() { 764 mDragController = new LauncherDragController(this); 765 } 766 767 @Override 768 public void onIdpChanged(boolean modelPropertiesChanged) { 769 onHandleConfigurationChanged(); 770 } 771 772 @Override 773 protected void onHandleConfigurationChanged() { 774 Trace.beginSection("Launcher#onHandleconfigurationChanged"); 775 try { 776 if (!initDeviceProfile(mDeviceProfile.inv)) { 777 return; 778 } 779 dispatchDeviceProfileChanged(); 780 reapplyUi(); 781 mDragLayer.recreateControllers(); 782 783 // Calling onSaveInstanceState ensures that static cache used by listWidgets is 784 // initialized properly. 785 onSaveInstanceState(new Bundle()); 786 mModel.rebindCallbacks(); 787 updateDisallowBack(); 788 } finally { 789 Trace.endSection(); 790 } 791 } 792 793 private void updateFixedLandscape() { 794 if (!com.android.launcher3.Flags.oneGridSpecs()) { 795 return; 796 } 797 // When the flag oneGridSpecs is on we want to disable ALLOW_ROTATION which is replaced 798 // by FIXED_LANDSCAPE_MODE, ALLOW_ROTATION will only be used on Tablets and foldables 799 // afterwards. 800 if (getDeviceProfile().isPhone) { 801 LauncherPrefs.get(this).put(LauncherPrefs.ALLOW_ROTATION, false); 802 } else if (getDeviceProfile().isTablet) { 803 // Tablet do not use fixed landscape mode, make sure it can't be activated by mistake 804 LauncherPrefs.get(this).put(FIXED_LANDSCAPE_MODE, false); 805 } 806 getRotationHelper().setFixedLandscape( 807 Objects.requireNonNull(mDeviceProfile.inv).isFixedLandscape 808 ); 809 } 810 811 public void onAssistantVisibilityChanged(float visibility) { 812 mHotseat.getQsb().setAlpha(1f - visibility); 813 } 814 815 /** 816 * Returns {@code true} if a new DeviceProfile is initialized, and {@code false} otherwise. 817 */ 818 protected boolean initDeviceProfile(InvariantDeviceProfile idp) { 819 // Load configuration-specific DeviceProfile 820 DeviceProfile deviceProfile = idp.getDeviceProfile(this); 821 if (mDeviceProfile == deviceProfile) { 822 return false; 823 } 824 825 mDeviceProfile = deviceProfile; 826 if (isInMultiWindowMode()) { 827 mDeviceProfile = mDeviceProfile.getMultiWindowProfile( 828 this, getMultiWindowDisplaySize()); 829 } 830 831 if (FOLDABLE_SINGLE_PAGE.get() && mDeviceProfile.isTwoPanels) { 832 mCellPosMapper = new TwoPanelCellPosMapper(mDeviceProfile.inv.numColumns); 833 } else { 834 mCellPosMapper = new CellPosMapper(mDeviceProfile.isVerticalBarLayout(), 835 mDeviceProfile.numShownHotseatIcons); 836 } 837 mModelWriter = mModel.getWriter(true, mCellPosMapper, this); 838 updateFixedLandscape(); 839 return true; 840 } 841 842 /** 843 * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have 844 * a configuration step, this allows the proper animations to run after other transitions. 845 */ 846 private int completeAdd( 847 int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info) { 848 CellPos cellPos = getCellPosMapper().mapModelToPresenter(info); 849 int screenId = cellPos.screenId; 850 if (info.container == CONTAINER_DESKTOP) { 851 // When the screen id represents an actual screen (as opposed to a rank) we make sure 852 // that the drop page actually exists. 853 screenId = ensurePendingDropLayoutExists(cellPos.screenId); 854 } 855 856 switch (requestCode) { 857 case REQUEST_CREATE_SHORTCUT: 858 completeAddShortcut(intent, info.container, screenId, 859 cellPos.cellX, cellPos.cellY, info); 860 break; 861 case REQUEST_CREATE_APPWIDGET: 862 completeAddAppWidget(appWidgetId, info, null, null, false, true, null); 863 break; 864 case REQUEST_RECONFIGURE_APPWIDGET: 865 getStatsLogManager().logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED); 866 completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED); 867 break; 868 case REQUEST_BIND_PENDING_APPWIDGET: { 869 int widgetId = appWidgetId; 870 LauncherAppWidgetInfo widgetInfo = 871 completeRestoreAppWidget(widgetId, LauncherAppWidgetInfo.FLAG_UI_NOT_READY); 872 if (widgetInfo != null) { 873 // Since the view was just bound, also launch the configure activity if needed 874 LauncherAppWidgetProviderInfo provider = mAppWidgetManager 875 .getLauncherAppWidgetInfo(widgetId, info.getTargetComponent()); 876 if (provider != null) { 877 new WidgetAddFlowHandler(provider) 878 .startConfigActivity(this, widgetInfo, 879 REQUEST_RECONFIGURE_APPWIDGET); 880 } 881 } 882 break; 883 } 884 } 885 return screenId; 886 } 887 888 /** 889 * Process any pending activity result if it was put on hold for any reason like item binding. 890 */ 891 public void processActivityResult() { 892 if (mPendingActivityResult != null) { 893 handleActivityResult(mPendingActivityResult.requestCode, 894 mPendingActivityResult.resultCode, mPendingActivityResult.data); 895 mPendingActivityResult = null; 896 } 897 } 898 899 private void handleActivityResult( 900 final int requestCode, final int resultCode, final Intent data) { 901 if (isWorkspaceLoading()) { 902 // process the result once the workspace has loaded. 903 mPendingActivityResult = new ActivityResultInfo(requestCode, resultCode, data); 904 return; 905 } 906 mPendingActivityResult = null; 907 908 if (requestCode == REQUEST_HOME_ROLE) { 909 if (resultCode != RESULT_OK) { 910 Toast.makeText( 911 this, 912 this.getString(R.string.set_default_home_app, 913 this.getString(R.string.derived_app_name)), 914 Toast.LENGTH_LONG).show(); 915 } 916 return; 917 } 918 919 // Reset the startActivity waiting flag 920 final PendingRequestArgs requestArgs = mPendingRequestArgs; 921 setWaitingForResult(null); 922 if (requestArgs == null) { 923 return; 924 } 925 926 final int pendingAddWidgetId = requestArgs.getWidgetId(); 927 928 Runnable exitSpringLoaded = MULTI_SELECT_EDIT_MODE.get() ? null 929 : () -> mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); 930 931 if (requestCode == REQUEST_BIND_APPWIDGET) { 932 // This is called only if the user did not previously have permissions to bind widgets 933 final int appWidgetId = data != null ? 934 data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; 935 if (resultCode == RESULT_CANCELED) { 936 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId, requestArgs); 937 mWorkspace.removeExtraEmptyScreenDelayed( 938 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, exitSpringLoaded); 939 } else if (resultCode == RESULT_OK) { 940 addAppWidgetImpl( 941 appWidgetId, requestArgs, null, 942 requestArgs.getWidgetHandler(), 943 ON_ACTIVITY_RESULT_ANIMATION_DELAY); 944 } 945 return; 946 } 947 948 boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET || 949 requestCode == REQUEST_CREATE_APPWIDGET); 950 951 // We have special handling for widgets 952 if (isWidgetDrop) { 953 final int appWidgetId; 954 int widgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) 955 : -1; 956 if (widgetId < 0) { 957 appWidgetId = pendingAddWidgetId; 958 } else { 959 appWidgetId = widgetId; 960 } 961 962 final int result; 963 if (appWidgetId < 0 || resultCode == RESULT_CANCELED) { 964 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " + 965 "returned from the widget configuration activity."); 966 result = RESULT_CANCELED; 967 completeTwoStageWidgetDrop(result, appWidgetId, requestArgs); 968 mWorkspace.removeExtraEmptyScreenDelayed( 969 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, 970 () -> getStateManager().goToState(NORMAL)); 971 } else { 972 CellPos presenterPos = getCellPosMapper().mapModelToPresenter(requestArgs); 973 if (requestArgs.container == CONTAINER_DESKTOP) { 974 // When the screen id represents an actual screen (as opposed to a rank) 975 // we make sure that the drop page actually exists. 976 int newScreenId = ensurePendingDropLayoutExists(presenterPos.screenId); 977 requestArgs.screenId = getCellPosMapper().mapPresenterToModel( 978 presenterPos.cellX, presenterPos.cellY, newScreenId, CONTAINER_DESKTOP) 979 .screenId; 980 } 981 final CellLayout dropLayout = 982 mWorkspace.getScreenWithId(presenterPos.screenId); 983 984 dropLayout.setDropPending(true); 985 final Runnable onComplete = new Runnable() { 986 @Override 987 public void run() { 988 completeTwoStageWidgetDrop(resultCode, appWidgetId, requestArgs); 989 dropLayout.setDropPending(false); 990 } 991 }; 992 mWorkspace.removeExtraEmptyScreenDelayed( 993 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, onComplete); 994 } 995 return; 996 } 997 998 if (requestCode == REQUEST_RECONFIGURE_APPWIDGET 999 || requestCode == REQUEST_BIND_PENDING_APPWIDGET) { 1000 if (resultCode == RESULT_OK) { 1001 // Update the widget view. 1002 completeAdd(requestCode, data, pendingAddWidgetId, requestArgs); 1003 } 1004 // Leave the widget in the pending state if the user canceled the configure. 1005 return; 1006 } 1007 1008 if (requestCode == REQUEST_CREATE_SHORTCUT) { 1009 // Handle custom shortcuts created using ACTION_CREATE_SHORTCUT. 1010 if (resultCode == RESULT_OK && requestArgs.container != ItemInfo.NO_ID) { 1011 completeAdd(requestCode, data, -1, requestArgs); 1012 mWorkspace.removeExtraEmptyScreenDelayed( 1013 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, exitSpringLoaded); 1014 1015 } else if (resultCode == RESULT_CANCELED) { 1016 mWorkspace.removeExtraEmptyScreenDelayed( 1017 ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, exitSpringLoaded); 1018 } 1019 } 1020 1021 mDragLayer.clearAnimatedView(); 1022 } 1023 1024 @Override 1025 public void onActivityResult( 1026 final int requestCode, final int resultCode, final Intent data) { 1027 mPendingActivityRequestCode = -1; 1028 handleActivityResult(requestCode, resultCode, data); 1029 } 1030 1031 /** 1032 * Check to see if a given screen id exists. If not, create it at the end, return the new id. 1033 * 1034 * @param screenId the screen id to check 1035 * @return the new screen, or screenId if it exists 1036 */ 1037 private int ensurePendingDropLayoutExists(int screenId) { 1038 CellLayout dropLayout = mWorkspace.getScreenWithId(screenId); 1039 if (dropLayout == null) { 1040 // it's possible that the add screen was removed because it was 1041 // empty and a re-bind occurred 1042 mWorkspace.addExtraEmptyScreens(); 1043 IntSet emptyPagesAdded = mWorkspace.commitExtraEmptyScreens(); 1044 return emptyPagesAdded.isEmpty() ? -1 : emptyPagesAdded.getArray().get(0); 1045 } 1046 return screenId; 1047 } 1048 1049 @Thunk 1050 void completeTwoStageWidgetDrop( 1051 final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) { 1052 CellLayout cellLayout = mWorkspace.getScreenWithId( 1053 getCellPosMapper().mapModelToPresenter(requestArgs).screenId); 1054 Runnable onCompleteRunnable = null; 1055 int animationType = 0; 1056 1057 AppWidgetHostView boundWidget = null; 1058 if (resultCode == RESULT_OK) { 1059 animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION; 1060 1061 // Now that we are exiting the config activity with RESULT_OK. 1062 // If FLAG_ENABLE_ADD_APP_WIDGET_VIA_CONFIG_ACTIVITY_V2 is enabled, we can retrieve the 1063 // PendingAppWidgetHostView from LauncherWidgetHolder (it was added to 1064 // LauncherWidgetHolder when starting the config activity). 1065 final AppWidgetHostView layout = enableAddAppWidgetViaConfigActivityV2() 1066 ? getWorkspace().getWidgetForAppWidgetId(appWidgetId) 1067 : mAppWidgetHolder.createView(appWidgetId, 1068 requestArgs.getWidgetHandler().getProviderInfo(this)); 1069 boundWidget = layout; 1070 onCompleteRunnable = () -> { 1071 completeAddAppWidget(appWidgetId, requestArgs, layout, null, false, true, null); 1072 if (!isInState(EDIT_MODE)) { 1073 mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); 1074 } 1075 }; 1076 } else if (resultCode == RESULT_CANCELED) { 1077 mAppWidgetHolder.deleteAppWidgetId(appWidgetId); 1078 animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION; 1079 } 1080 if (mDragLayer.getAnimatedView() != null) { 1081 mWorkspace.animateWidgetDrop(requestArgs, cellLayout, 1082 (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable, 1083 animationType, boundWidget, true); 1084 } else if (onCompleteRunnable != null) { 1085 // The animated view may be null in the case of a rotation during widget configuration 1086 onCompleteRunnable.run(); 1087 } 1088 } 1089 1090 @Override 1091 protected void onStop() { 1092 super.onStop(); 1093 if (mDeferOverlayCallbacks) { 1094 checkIfOverlayStillDeferred(); 1095 } else { 1096 mOverlayManager.onActivityStopped(); 1097 } 1098 hideKeyboard(); 1099 logStopAndResume(false /* isResume */); 1100 mAppWidgetHolder.setActivityStarted(false); 1101 NotificationListener.removeNotificationsChangedListener(getPopupDataProvider()); 1102 FloatingIconView.resetIconLoadResult(); 1103 AccessibilityManagerCompat.sendTestProtocolEventToTest( 1104 this, LAUNCHER_ACTIVITY_STOPPED_MESSAGE); 1105 } 1106 1107 @Override 1108 protected void onStart() { 1109 TraceHelper.INSTANCE.beginSection(ON_START_EVT); 1110 super.onStart(); 1111 if (!mDeferOverlayCallbacks) { 1112 mOverlayManager.onActivityStarted(); 1113 } 1114 1115 mAppWidgetHolder.setActivityStarted(true); 1116 TraceHelper.INSTANCE.endSection(); 1117 } 1118 1119 @Override 1120 @CallSuper 1121 protected void onDeferredResumed() { 1122 logStopAndResume(true /* isResume */); 1123 1124 // Process any items that were added while Launcher was away. 1125 ItemInstallQueue.INSTANCE.get(this) 1126 .resumeModelPush(FLAG_ACTIVITY_PAUSED); 1127 1128 // Refresh shortcuts if the permission changed. 1129 mModel.validateModelDataOnResume(); 1130 1131 // Set the notification listener and fetch updated notifications when we resume 1132 NotificationListener.addNotificationsChangedListener(mPopupDataProvider); 1133 1134 DiscoveryBounce.showForHomeIfNeeded(this); 1135 mAppWidgetHolder.setActivityResumed(true); 1136 1137 // Listen for IME changes to keep state up to date. 1138 getRootView().setWindowInsetsAnimationCallback( 1139 new WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) { 1140 @Override 1141 public WindowInsets onProgress(WindowInsets windowInsets, 1142 List<WindowInsetsAnimation> windowInsetsAnimations) { 1143 return windowInsets; 1144 } 1145 1146 @Override 1147 public void onEnd(WindowInsetsAnimation animation) { 1148 WindowInsets insets = getRootView().getRootWindowInsets(); 1149 boolean isImeVisible = 1150 insets != null && insets.isVisible(WindowInsets.Type.ime()); 1151 getStatsLogManager().keyboardStateManager().setKeyboardState( 1152 isImeVisible ? SHOW : HIDE); 1153 } 1154 }); 1155 } 1156 1157 private void logStopAndResume(boolean isResume) { 1158 if (mModelCallbacks.getPendingExecutor() != null) return; 1159 int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage(); 1160 int statsLogOrdinal = mStateManager.getState().statsLogOrdinal; 1161 1162 StatsLogManager.EventEnum event; 1163 StatsLogManager.StatsLogger logger = getStatsLogManager().logger(); 1164 if (isResume) { 1165 logger.withSrcState(LAUNCHER_STATE_BACKGROUND) 1166 .withDstState(mStateManager.getState().statsLogOrdinal); 1167 event = LAUNCHER_ONRESUME; 1168 } else { /* command == Action.Command.STOP */ 1169 logger.withSrcState(mStateManager.getState().statsLogOrdinal) 1170 .withDstState(LAUNCHER_STATE_BACKGROUND); 1171 event = LAUNCHER_ONSTOP; 1172 } 1173 1174 if (statsLogOrdinal == LAUNCHER_STATE_HOME && mWorkspace != null) { 1175 logger.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder() 1176 .setWorkspace( 1177 LauncherAtom.WorkspaceContainer.newBuilder() 1178 .setPageIndex(pageIndex)).build()); 1179 } 1180 logger.log(event); 1181 } 1182 1183 private void scheduleDeferredCheck() { 1184 mHandler.removeCallbacks(mDeferredOverlayCallbacks); 1185 postAsyncCallback(mHandler, mDeferredOverlayCallbacks); 1186 } 1187 1188 private void checkIfOverlayStillDeferred() { 1189 if (!mDeferOverlayCallbacks) { 1190 return; 1191 } 1192 if (isStarted() && (!hasBeenResumed() 1193 || mStateManager.getState().hasFlag(FLAG_NON_INTERACTIVE))) { 1194 return; 1195 } 1196 mDeferOverlayCallbacks = false; 1197 1198 // Move the client to the correct state. Calling the same method twice is no-op. 1199 if (isStarted()) { 1200 mOverlayManager.onActivityStarted(); 1201 } 1202 if (hasBeenResumed()) { 1203 mOverlayManager.onActivityResumed(); 1204 } else { 1205 mOverlayManager.onActivityPaused(); 1206 } 1207 if (!isStarted()) { 1208 mOverlayManager.onActivityStopped(); 1209 } 1210 } 1211 1212 public void deferOverlayCallbacksUntilNextResumeOrStop() { 1213 mDeferOverlayCallbacks = true; 1214 } 1215 1216 @Override 1217 public void onStateSetStart(LauncherState state) { 1218 super.onStateSetStart(state); 1219 if (mDeferOverlayCallbacks) { 1220 scheduleDeferredCheck(); 1221 } 1222 addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE); 1223 1224 if (state == SPRING_LOADED || state == EDIT_MODE) { 1225 // Prevent any Un/InstallShortcutReceivers from updating the db while we are 1226 // not on homescreen 1227 ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_DRAG_AND_DROP); 1228 getRotationHelper().setCurrentStateRequest(REQUEST_LOCK); 1229 1230 mWorkspace.showPageIndicatorAtCurrentScroll(); 1231 mWorkspace.setClipChildren(false); 1232 } 1233 // When multiple pages are visible, show persistent page indicator 1234 mWorkspace.getPageIndicator().setShouldAutoHide(!state.hasFlag(FLAG_MULTI_PAGE)); 1235 1236 mPrevLauncherState = mStateManager.getCurrentStableState(); 1237 if (mPrevLauncherState != state && ALL_APPS.equals(state) 1238 // Making sure mAllAppsSessionLogId is null to avoid double logging. 1239 && mAllAppsSessionLogId == null) { 1240 // creates new instance ID since new all apps session is started. 1241 mAllAppsSessionLogId = new InstanceIdSequence().newInstanceId(); 1242 if (getAllAppsEntryEvent().isPresent()) { 1243 getStatsLogManager().logger() 1244 .withContainerInfo(ContainerInfo.newBuilder() 1245 .setWorkspace(WorkspaceContainer.newBuilder() 1246 .setPageIndex(getWorkspace().getCurrentPage())).build()) 1247 .log(getAllAppsEntryEvent().get()); 1248 } 1249 } 1250 updateDisallowBack(); 1251 } 1252 1253 /** 1254 * Returns {@link EventEnum} that should be logged when Launcher enters into AllApps state. 1255 */ 1256 protected Optional<EventEnum> getAllAppsEntryEvent() { 1257 return Optional.of(LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH); 1258 } 1259 1260 @Override 1261 public void onStateSetEnd(LauncherState state) { 1262 super.onStateSetEnd(state); 1263 getAppWidgetHolder().setStateIsNormal(state == LauncherState.NORMAL); 1264 getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE)); 1265 1266 finishAutoCancelActionMode(); 1267 removeActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE); 1268 1269 // dispatch window state changed 1270 getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED); 1271 AccessibilityManagerCompat.sendStateEventToTest(this, state.ordinal); 1272 1273 if (state == NORMAL) { 1274 // Re-enable any Un/InstallShortcutReceiver and now process any queued items 1275 ItemInstallQueue.INSTANCE.get(this) 1276 .resumeModelPush(FLAG_DRAG_AND_DROP); 1277 1278 // Clear any rotation locks when going to normal state 1279 getRotationHelper().setCurrentStateRequest(REQUEST_NONE); 1280 } 1281 1282 if (ALL_APPS.equals(mPrevLauncherState) && !ALL_APPS.equals(state) 1283 // Making sure mAllAppsSessionLogId is not null to avoid double logging. 1284 && mAllAppsSessionLogId != null) { 1285 getAppsView().reset(false); 1286 getAllAppsExitEvent().ifPresent(getStatsLogManager().logger()::log); 1287 mAllAppsSessionLogId = null; 1288 } 1289 1290 // Set screen title for Talkback 1291 setTitle(state.getTitle()); 1292 } 1293 1294 /** 1295 * Returns {@link EventEnum} that should be logged when Launcher exists from AllApps state. 1296 */ 1297 protected Optional<EventEnum> getAllAppsExitEvent() { 1298 return Optional.of(LAUNCHER_ALLAPPS_EXIT); 1299 } 1300 1301 @Override 1302 protected void onResume() { 1303 TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT); 1304 super.onResume(); 1305 1306 if (mDeferOverlayCallbacks) { 1307 scheduleDeferredCheck(); 1308 } else { 1309 mOverlayManager.onActivityResumed(); 1310 } 1311 1312 DragView.removeAllViews(this); 1313 TraceHelper.INSTANCE.endSection(); 1314 } 1315 1316 @Override 1317 protected void onPause() { 1318 // Ensure that items added to Launcher are queued until Launcher returns 1319 ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED); 1320 1321 super.onPause(); 1322 mDragController.cancelDrag(); 1323 mLastTouchUpTime = -1; 1324 mDropTargetBar.animateToVisibility(false); 1325 1326 if (!mDeferOverlayCallbacks) { 1327 mOverlayManager.onActivityPaused(); 1328 } 1329 mAppWidgetHolder.setActivityResumed(false); 1330 } 1331 1332 /** 1333 * Restores the previous state, if it exists. 1334 * 1335 * @param savedState The previous state. 1336 */ 1337 private void restoreState(Bundle savedState) { 1338 if (savedState == null) { 1339 return; 1340 } 1341 1342 int stateOrdinal = savedState.getInt(RUNTIME_STATE, NORMAL.ordinal); 1343 LauncherState[] stateValues = LauncherState.values(); 1344 LauncherState state = stateValues[stateOrdinal]; 1345 1346 NonConfigInstance lastInstance = (NonConfigInstance) getLastNonConfigurationInstance(); 1347 boolean forceRestore = lastInstance != null 1348 && ((lastInstance.config.diff(mOldConfig) & CONFIG_UI_MODE) != 0 1349 || savedState.getBoolean(RUNTIME_STATE_RECREATE_TO_UPDATE_THEME)); 1350 if (forceRestore || !state.shouldDisableRestore()) { 1351 mStateManager.goToState(state, false /* animated */); 1352 } 1353 1354 PendingRequestArgs requestArgs = savedState.getParcelable( 1355 RUNTIME_STATE_PENDING_REQUEST_ARGS); 1356 if (requestArgs != null) { 1357 setWaitingForResult(requestArgs); 1358 } 1359 mPendingActivityRequestCode = savedState.getInt( 1360 RUNTIME_STATE_PENDING_REQUEST_CODE, mPendingActivityRequestCode); 1361 1362 mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT); 1363 1364 SparseArray<Parcelable> widgetsState = 1365 savedState.getSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL); 1366 if (widgetsState != null) { 1367 WidgetsFullSheet.show(this, false).restoreHierarchyState(widgetsState); 1368 } 1369 } 1370 1371 /** 1372 * Finds all the views we need and configure them properly. 1373 */ 1374 protected void setupViews() { 1375 mStartupLatencyLogger.logStart(LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION); 1376 inflateRootView(R.layout.launcher); 1377 mStartupLatencyLogger.logEnd(LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION); 1378 1379 mDragLayer = findViewById(R.id.drag_layer); 1380 mFocusHandler = mDragLayer.getFocusIndicatorHelper(); 1381 mWorkspace = mDragLayer.findViewById(R.id.workspace); 1382 mWorkspace.initParentViews(mDragLayer); 1383 mOverviewPanel = findViewById(R.id.overview_panel); 1384 mHotseat = findViewById(R.id.hotseat); 1385 mHotseat.setWorkspace(mWorkspace); 1386 1387 // Setup the drag layer 1388 mDragLayer.setup(mDragController, mWorkspace); 1389 1390 mWorkspace.setup(mDragController); 1391 // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the 1392 // default state, otherwise we will update to the wrong offsets in RTL 1393 mWorkspace.lockWallpaperToDefaultPage(); 1394 if (!enableSmartspaceRemovalToggle()) { 1395 mWorkspace.bindAndInitFirstWorkspaceScreen(); 1396 } 1397 mDragController.addDragListener(mWorkspace); 1398 1399 // Get the search/delete/uninstall bar 1400 mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar); 1401 1402 // Setup Apps 1403 mAppsView = findViewById(R.id.apps_view); 1404 mAppsView.setAllAppsTransitionController(mAllAppsController); 1405 1406 // Setup Scrim 1407 mScrimView = findViewById(R.id.scrim_view); 1408 1409 // Setup the drag controller (drop targets have to be added in reverse order in priority) 1410 mDropTargetBar.setup(mDragController); 1411 mAllAppsController.setupViews(mScrimView, mAppsView); 1412 1413 mWorkspace.getPageIndicator().setShouldAutoHide(true); 1414 mWorkspace.getPageIndicator().setPaintColor(Themes.getAttrBoolean( 1415 this, R.attr.isWorkspaceDarkText) ? Color.BLACK : Color.WHITE); 1416 } 1417 1418 /** 1419 * Add a shortcut to the workspace or to a Folder. 1420 * 1421 * @param data The intent describing the shortcut. 1422 */ 1423 protected void completeAddShortcut(Intent data, int container, int screenId, int cellX, 1424 int cellY, PendingRequestArgs args) { 1425 if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT) { 1426 return; 1427 } 1428 1429 int[] cellXY = mTmpAddItemCellCoordinates; 1430 CellLayout layout = getCellLayout(container, screenId); 1431 1432 WorkspaceItemInfo info = PinRequestHelper.createWorkspaceItemFromPinItemRequest( 1433 this, PinRequestHelper.getPinItemRequest(data), 0); 1434 if (info == null) { 1435 Log.e(TAG, "Unable to parse a valid shortcut result"); 1436 return; 1437 } 1438 1439 if (container < 0) { 1440 // Adding a shortcut to the Workspace. 1441 final View view = mItemInflater.inflateItem(info, getModelWriter()); 1442 boolean foundCellSpan = false; 1443 // First we check if we already know the exact location where we want to add this item. 1444 if (cellX >= 0 && cellY >= 0) { 1445 cellXY[0] = cellX; 1446 cellXY[1] = cellY; 1447 foundCellSpan = true; 1448 1449 DragObject dragObject = new DragObject(getApplicationContext()); 1450 dragObject.dragInfo = info; 1451 // If appropriate, either create a folder or add to an existing folder 1452 if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0, 1453 true, dragObject)) { 1454 return; 1455 } 1456 if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject, 1457 true)) { 1458 return; 1459 } 1460 } else { 1461 foundCellSpan = layout.findCellForSpan(cellXY, 1, 1); 1462 } 1463 1464 if (!foundCellSpan) { 1465 mWorkspace.onNoCellFound(layout, info, /* logInstanceId= */ null); 1466 return; 1467 } 1468 1469 getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]); 1470 AnimatorSet anim = new AnimatorSet(); 1471 anim.addListener(forEndCallback(() -> 1472 view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED))); 1473 bindInflatedItems(Collections.singletonList(Pair.create(info, view)), anim); 1474 } else { 1475 // Adding a shortcut to a Folder. 1476 FolderIcon folderIcon = findFolderIcon(container); 1477 if (folderIcon != null) { 1478 folderIcon.getFolder().addFolderContent(info, args.rank, false); 1479 } else { 1480 Log.e(TAG, "Could not find folder with id " + container + " to add shortcut."); 1481 } 1482 } 1483 } 1484 1485 @Override 1486 public @Nullable FolderIcon findFolderIcon(final int folderIconId) { 1487 return (FolderIcon) mWorkspace.getViewByItemId(folderIconId); 1488 } 1489 1490 /** 1491 * Add a widget to the workspace. 1492 * 1493 * @param appWidgetId The app widget id 1494 */ 1495 @Thunk 1496 void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo, 1497 @Nullable AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo, 1498 boolean showPendingWidget, boolean updateWidgetSize, 1499 @Nullable Bitmap widgetPreviewBitmap) { 1500 1501 if (appWidgetInfo == null) { 1502 appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId, 1503 itemInfo.getTargetComponent()); 1504 } 1505 1506 if (hostView == null && !showPendingWidget) { 1507 // Perform actual inflation because we're live 1508 hostView = mAppWidgetHolder.createView(appWidgetId, appWidgetInfo); 1509 } 1510 1511 LauncherAppWidgetInfo launcherInfo; 1512 launcherInfo = 1513 new LauncherAppWidgetInfo( 1514 appWidgetId, appWidgetInfo.provider, appWidgetInfo, hostView); 1515 launcherInfo.spanX = itemInfo.spanX; 1516 launcherInfo.spanY = itemInfo.spanY; 1517 launcherInfo.minSpanX = itemInfo.minSpanX; 1518 launcherInfo.minSpanY = itemInfo.minSpanY; 1519 launcherInfo.user = appWidgetInfo.getProfile(); 1520 CellPos presenterPos = getCellPosMapper().mapModelToPresenter(itemInfo); 1521 if (showPendingWidget) { 1522 launcherInfo.restoreStatus = LauncherAppWidgetInfo.FLAG_UI_NOT_READY; 1523 PendingAppWidgetHostView pendingAppWidgetHostView = new PendingAppWidgetHostView( 1524 this, mAppWidgetHolder, launcherInfo, appWidgetInfo, widgetPreviewBitmap); 1525 hostView = pendingAppWidgetHostView; 1526 } else if (hostView instanceof PendingAppWidgetHostView) { 1527 ((PendingAppWidgetHostView) hostView).setPreviewBitmapAndUpdateBackground(null); 1528 // User has selected a widget config and exited the config activity, we can trigger 1529 // re-inflation of PendingAppWidgetHostView to replace it with 1530 // LauncherAppWidgetHostView in workspace. 1531 completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED); 1532 1533 // Show resize frame on the newly inflated LauncherAppWidgetHostView. 1534 LauncherAppWidgetHostView reInflatedHostView = 1535 getWorkspace().getWidgetForAppWidgetId(appWidgetId); 1536 showWidgetResizeFrame( 1537 reInflatedHostView, 1538 (LauncherAppWidgetInfo) reInflatedHostView.getTag(), 1539 presenterPos); 1540 // We always update widget size after re-inflating PendingAppWidgetHostView 1541 WidgetSizes.updateWidgetSizeRanges( 1542 reInflatedHostView, this, itemInfo.spanX, itemInfo.spanY); 1543 return; 1544 } 1545 if (updateWidgetSize) { 1546 WidgetSizes.updateWidgetSizeRanges(hostView, this, itemInfo.spanX, itemInfo.spanY); 1547 } 1548 if (itemInfo instanceof PendingAddWidgetInfo) { 1549 launcherInfo.sourceContainer = ((PendingAddWidgetInfo) itemInfo).sourceContainer; 1550 } else if (itemInfo instanceof PendingRequestArgs) { 1551 launcherInfo.sourceContainer = 1552 ((PendingRequestArgs) itemInfo).getWidgetSourceContainer(); 1553 } 1554 getModelWriter().addItemToDatabase(launcherInfo, 1555 itemInfo.container, presenterPos.screenId, presenterPos.cellX, presenterPos.cellY); 1556 1557 hostView.setVisibility(View.VISIBLE); 1558 mItemInflater.prepareAppWidget(hostView, launcherInfo); 1559 if (!enableAddAppWidgetViaConfigActivityV2() || hostView.getParent() == null) { 1560 mWorkspace.addInScreen(hostView, launcherInfo); 1561 } 1562 1563 // Show the widget resize frame. 1564 if (hostView instanceof LauncherAppWidgetHostView) { 1565 final LauncherAppWidgetHostView launcherHostView = (LauncherAppWidgetHostView) hostView; 1566 showWidgetResizeFrame(launcherHostView, launcherInfo, presenterPos); 1567 } 1568 } 1569 1570 /** Show widget resize frame. */ 1571 private void showWidgetResizeFrame( 1572 LauncherAppWidgetHostView launcherHostView, 1573 LauncherAppWidgetInfo launcherInfo, 1574 CellPos presenterPos) { 1575 CellLayout cellLayout = getCellLayout(launcherInfo.container, presenterPos.screenId); 1576 // We should wait until launcher is not animating to show resize frame so that 1577 // {@link View#hasIdentityMatrix()} returns true (no scale effect) from CellLayout and 1578 // Workspace (they are widget's parent view). Otherwise widget's 1579 // {@link View#getLocationInWindow(int[])} will set skewed location, causing resize 1580 // frame not showing at skewed location in 1581 // {@link AppWidgetResizeFrame#snapToWidget(boolean)}. 1582 if (mStateManager.getState() == NORMAL && !mStateManager.isInTransition()) { 1583 AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout); 1584 } else { 1585 mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() { 1586 @Override 1587 public void onStateTransitionComplete(LauncherState finalState) { 1588 if ((mPrevLauncherState == SPRING_LOADED || mPrevLauncherState == EDIT_MODE) 1589 && finalState == NORMAL) { 1590 AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout); 1591 mStateManager.removeStateListener(this); 1592 } 1593 } 1594 }); 1595 } 1596 } 1597 1598 private final ScreenOnListener mScreenOnListener = this::onScreenOnChanged; 1599 1600 @Override 1601 public void onAttachedToWindow() { 1602 super.onAttachedToWindow(); 1603 mOverlayManager.onAttachedToWindow(); 1604 } 1605 1606 @Override 1607 public void onDetachedFromWindow() { 1608 super.onDetachedFromWindow(); 1609 mOverlayManager.onDetachedFromWindow(); 1610 closeContextMenu(); 1611 } 1612 1613 @Override 1614 public Object onRetainNonConfigurationInstance() { 1615 NonConfigInstance instance = new NonConfigInstance(); 1616 instance.config = new Configuration(mOldConfig); 1617 return instance; 1618 } 1619 1620 @Override 1621 protected void onNewIntent(Intent intent) { 1622 if (Utilities.isRunningInTestHarness()) { 1623 Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Launcher.onNewIntent: " + intent); 1624 } 1625 TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT); 1626 super.onNewIntent(intent); 1627 1628 boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags() & 1629 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) 1630 != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); 1631 1632 // Check this condition before handling isActionMain, as this will get reset. 1633 boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL) 1634 && AbstractFloatingView.getTopOpenView(this) == null; 1635 boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction()); 1636 boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this); 1637 1638 if (isActionMain) { 1639 if (!internalStateHandled) { 1640 // In all these cases, only animate if we're already on home 1641 AbstractFloatingView.closeAllOpenViewsExcept( 1642 this, isStarted(), AbstractFloatingView.TYPE_LISTENER); 1643 1644 if (!isInState(NORMAL)) { 1645 // Only change state, if not already the same. This prevents cancelling any 1646 // animations running as part of resume 1647 mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange()); 1648 } 1649 1650 // Reset the apps view 1651 if (!alreadyOnHome) { 1652 mAppsView.reset(isStarted() /* animate */); 1653 } 1654 1655 if (shouldMoveToDefaultScreen && !mWorkspace.isHandlingTouch()) { 1656 mWorkspace.post(mWorkspace::moveToDefaultScreen); 1657 } 1658 } 1659 1660 handleSplitAnimationGoingToHome(LAUNCHER_SPLIT_SELECTION_EXIT_HOME); 1661 mOverlayManager.hideOverlay(isStarted()); 1662 handleGestureContract(intent); 1663 } else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) { 1664 showAllAppsFromIntent(alreadyOnHome); 1665 } else if (INTENT_ACTION_ALL_APPS_TOGGLE.equals(intent.getAction())) { 1666 toggleAllApps(alreadyOnHome, true); 1667 } else if (Intent.ACTION_SHOW_WORK_APPS.equals(intent.getAction())) { 1668 showAllAppsWithSelectedTabFromIntent(alreadyOnHome, 1669 ActivityAllAppsContainerView.AdapterHolder.WORK); 1670 } 1671 1672 TraceHelper.INSTANCE.endSection(); 1673 } 1674 1675 /** Handle animating away split placeholder view when user taps on home button */ 1676 protected void handleSplitAnimationGoingToHome(EventEnum splitDismissReason) { 1677 // Overridden 1678 } 1679 1680 /** 1681 * Toggles Launcher All Apps. 1682 * @param focusSearch Indicates whether to make All Apps keyboard ready for search. 1683 */ 1684 public void toggleAllApps(boolean focusSearch) { 1685 toggleAllApps(/* alreadyOnHome= */ true, focusSearch); 1686 } 1687 1688 private void toggleAllApps(boolean alreadyOnHome, boolean focusSearch) { 1689 if (getStateManager().isInStableState(ALL_APPS)) { 1690 getStateManager().goToState(NORMAL, alreadyOnHome); 1691 } else { 1692 if (mWorkspace.isOverlayShown()) { 1693 mOverlayManager.hideOverlay(/* animate */true); 1694 } 1695 AbstractFloatingView.closeAllOpenViews(this); 1696 getStateManager().goToState(ALL_APPS, true /* animated */, 1697 new AnimationSuccessListener() { 1698 @Override 1699 public void onAnimationSuccess(Animator animator) { 1700 if (focusSearch 1701 && mAppsView.getSearchUiManager().getEditText() != null) { 1702 mAppsView.getSearchUiManager().getEditText().requestFocus(); 1703 } 1704 } 1705 }); 1706 } 1707 } 1708 1709 protected void showAllAppsFromIntent(boolean alreadyOnHome) { 1710 showAllAppsWithSelectedTabFromIntent(alreadyOnHome, 1711 ActivityAllAppsContainerView.AdapterHolder.MAIN); 1712 } 1713 1714 private void showAllAppsWithSelectedTabFromIntent(boolean alreadyOnHome, int tab) { 1715 AbstractFloatingView.closeAllOpenViews(this); 1716 getStateManager().goToState(ALL_APPS, alreadyOnHome); 1717 if (mAppsView.isSearching()) { 1718 mAppsView.getSearchUiManager().resetSearch(); 1719 } 1720 if (mAppsView.getCurrentPage() != tab) { 1721 mAppsView.switchToTab(tab); 1722 } 1723 } 1724 1725 /** 1726 * Handles gesture nav contract 1727 */ 1728 protected void handleGestureContract(Intent intent) { 1729 GestureNavContract gnc = GestureNavContract.fromIntent(intent); 1730 if (gnc != null) { 1731 AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE); 1732 FloatingSurfaceView.show(this, gnc); 1733 } 1734 } 1735 1736 @Override 1737 public void onRestoreInstanceState(Bundle state) { 1738 super.onRestoreInstanceState(state); 1739 IntSet synchronouslyBoundPages = mModelCallbacks.getSynchronouslyBoundPages(); 1740 if (synchronouslyBoundPages != null) { 1741 synchronouslyBoundPages.forEach(screenId -> { 1742 int pageIndex = mWorkspace.getPageIndexForScreenId(screenId); 1743 if (pageIndex != PagedView.INVALID_PAGE) { 1744 mWorkspace.restoreInstanceStateForChild(pageIndex); 1745 } 1746 }); 1747 } 1748 } 1749 1750 @Override 1751 protected void onSaveInstanceState(Bundle outState) { 1752 outState.putIntArray(RUNTIME_STATE_CURRENT_SCREEN_IDS, 1753 mWorkspace.getCurrentPageScreenIds().getArray().toArray()); 1754 outState.putInt(RUNTIME_STATE, mStateManager.getState().ordinal); 1755 1756 AbstractFloatingView widgets = AbstractFloatingView 1757 .getOpenView(this, AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET); 1758 if (widgets != null) { 1759 SparseArray<Parcelable> widgetsState = new SparseArray<>(); 1760 widgets.saveHierarchyState(widgetsState); 1761 outState.putSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL, widgetsState); 1762 } else { 1763 outState.remove(RUNTIME_STATE_WIDGET_PANEL); 1764 } 1765 1766 // We close any open folders and shortcut containers that are not safe for rebind, 1767 // and we need to make sure this state is reflected. 1768 AbstractFloatingView.closeAllOpenViewsExcept( 1769 this, isStarted() && !isForceInvisible(), TYPE_REBIND_SAFE); 1770 finishAutoCancelActionMode(); 1771 1772 if (mPendingRequestArgs != null) { 1773 outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs); 1774 } 1775 outState.putInt(RUNTIME_STATE_PENDING_REQUEST_CODE, mPendingActivityRequestCode); 1776 1777 if (mPendingActivityResult != null) { 1778 outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult); 1779 } 1780 1781 super.onSaveInstanceState(outState); 1782 } 1783 1784 @Override 1785 public void onDestroy() { 1786 super.onDestroy(); 1787 ACTIVITY_TRACKER.onContextDestroyed(this); 1788 1789 SettingsCache.INSTANCE.get(this).unregister(TOUCHPAD_NATURAL_SCROLLING, 1790 mNaturalScrollingChangedListener); 1791 ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener); 1792 PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this); 1793 1794 mModel.removeCallbacks(this); 1795 mRotationHelper.destroy(); 1796 1797 mAppWidgetHolder.stopListening(); 1798 mAppWidgetHolder.destroy(); 1799 mWidgetPickerDataProvider.destroy(); 1800 1801 TextKeyListener.getInstance().release(); 1802 mModelCallbacks.clearPendingBinds(); 1803 LauncherAppState.getIDP(this).removeOnChangeListener(this); 1804 // if Launcher activity is recreated, {@link Window} including {@link ViewTreeObserver} 1805 // could be preserved in {@link ActivityThread#scheduleRelaunchActivity(IBinder)} if the 1806 // previous activity has not stopped, which could happen when wallpaper detects a color 1807 // changes while launcher is still loading. 1808 getRootView().getViewTreeObserver().removeOnPreDrawListener(mOnInitialBindListener); 1809 mOverlayManager.onActivityDestroyed(); 1810 PillColorProvider.getInstance(mWorkspace.getContext()).unregisterObserver(); 1811 } 1812 1813 public LauncherAccessibilityDelegate getAccessibilityDelegate() { 1814 return mAccessibilityDelegate; 1815 } 1816 1817 public DragController getDragController() { 1818 return mDragController; 1819 } 1820 1821 @Override 1822 public DropTargetHandler getDropTargetHandler() { 1823 return new DropTargetHandler(this); 1824 } 1825 1826 @Override 1827 public void startActivityForResult(Intent intent, int requestCode, Bundle options) { 1828 if (requestCode != -1) { 1829 mPendingActivityRequestCode = requestCode; 1830 } 1831 super.startActivityForResult(intent, requestCode, options); 1832 } 1833 1834 @Override 1835 public void startIntentSenderForResult(IntentSender intent, int requestCode, 1836 Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) { 1837 if (requestCode != -1) { 1838 mPendingActivityRequestCode = requestCode; 1839 } 1840 try { 1841 super.startIntentSenderForResult(intent, requestCode, 1842 fillInIntent, flagsMask, flagsValues, extraFlags, options); 1843 } catch (Exception e) { 1844 throw new ActivityNotFoundException(); 1845 } 1846 } 1847 1848 void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, 1849 WidgetAddFlowHandler addFlowHandler) { 1850 if (LOGD) { 1851 Log.d(TAG, "Adding widget from drop"); 1852 } 1853 addAppWidgetImpl(appWidgetId, info, boundWidget, addFlowHandler, 0); 1854 } 1855 1856 /** 1857 * If FLAG_ENABLE_ADD_APP_WIDGET_VIA_CONFIG_ACTIVITY_V2 is enabled, we always add widget 1858 * host view to workspace, otherwise we only add widget to host view if config activity is 1859 * not started. 1860 */ 1861 void addAppWidgetImpl(int appWidgetId, ItemInfo info, 1862 AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay) { 1863 final boolean isActivityStarted = addFlowHandler.startConfigActivity( 1864 this, appWidgetId, info, REQUEST_CREATE_APPWIDGET); 1865 1866 if (!enableAddAppWidgetViaConfigActivityV2() && isActivityStarted) { 1867 return; 1868 } 1869 1870 // If FLAG_ENABLE_ADD_APP_WIDGET_VIA_CONFIG_ACTIVITY_V2 is enabled and config activity is 1871 // started, we should remove the dropped AppWidgetHostView from drag layer and extract the 1872 // Bitmap that shows the preview. Then pass the Bitmap to completeAddAppWidget() to create 1873 // a PendingWidgetHostView. 1874 Bitmap widgetPreviewBitmap = null; 1875 if (isActivityStarted) { 1876 DragView dropView = getDragLayer().clearAnimatedView(); 1877 if (dropView != null && dropView.containsAppWidgetHostView()) { 1878 // Extracting Bitmap from dropView instead of its content view produces the correct 1879 // bitmap. 1880 widgetPreviewBitmap = createHardwareBitmap( 1881 dropView.getWidth(), dropView.getHeight(), dropView::draw); 1882 } 1883 } 1884 1885 // Exit spring loaded mode if necessary after adding the widget; unless config activity was 1886 // started. 1887 Runnable onComplete = MULTI_SELECT_EDIT_MODE.get() ? null : () -> mStateManager.goToState( 1888 NORMAL, SPRING_LOADED_EXIT_DELAY); 1889 completeAddAppWidget(appWidgetId, info, boundWidget, 1890 addFlowHandler.getProviderInfo(this), addFlowHandler.needsConfigure(), 1891 false, widgetPreviewBitmap); 1892 // Remove extra screen if widget drop concluded. If a config activity was started, extra 1893 // screen will be removed when we get back its result. 1894 if (!isActivityStarted) { 1895 mWorkspace.removeExtraEmptyScreenDelayed(delay, false, onComplete); 1896 } 1897 } 1898 1899 public void addPendingItem(PendingAddItemInfo info, int container, int screenId, 1900 int[] cell, int spanX, int spanY) { 1901 if (cell == null) { 1902 CellPos modelPos = getCellPosMapper().mapPresenterToModel(0, 0, screenId, container); 1903 info.screenId = modelPos.screenId; 1904 } else { 1905 CellPos modelPos = getCellPosMapper().mapPresenterToModel( 1906 cell[0], cell[1], screenId, container); 1907 info.screenId = modelPos.screenId; 1908 info.cellX = modelPos.cellX; 1909 info.cellY = modelPos.cellY; 1910 } 1911 info.container = container; 1912 info.spanX = spanX; 1913 info.spanY = spanY; 1914 1915 if (info instanceof PendingAddWidgetInfo) { 1916 addAppWidgetFromDrop((PendingAddWidgetInfo) info); 1917 } else { // info can only be PendingAddShortcutInfo 1918 processShortcutFromDrop((PendingAddShortcutInfo) info); 1919 } 1920 } 1921 1922 /** 1923 * Process a shortcut drop. 1924 */ 1925 private void processShortcutFromDrop(PendingAddShortcutInfo info) { 1926 Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(info.componentName); 1927 setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_CREATE_SHORTCUT, intent, info)); 1928 TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: processShortcutFromDrop"); 1929 if (!info.getActivityInfo(this).startConfigActivity(this, REQUEST_CREATE_SHORTCUT)) { 1930 handleActivityResult(REQUEST_CREATE_SHORTCUT, RESULT_CANCELED, null); 1931 } 1932 } 1933 1934 /** 1935 * Process a widget drop. 1936 */ 1937 private void addAppWidgetFromDrop(PendingAddWidgetInfo info) { 1938 AppWidgetHostView hostView = info.boundWidget; 1939 final int appWidgetId; 1940 WidgetAddFlowHandler addFlowHandler = info.getHandler(); 1941 if (hostView != null) { 1942 // In the case where we've prebound the widget, we remove it from the DragLayer 1943 if (LOGD) { 1944 Log.d(TAG, "Removing widget view from drag layer and setting boundWidget to null"); 1945 } 1946 getDragLayer().removeView(hostView); 1947 1948 appWidgetId = hostView.getAppWidgetId(); 1949 addAppWidgetFromDropImpl(appWidgetId, info, hostView, addFlowHandler); 1950 1951 // Clear the boundWidget so that it doesn't get destroyed. 1952 info.boundWidget = null; 1953 } else { 1954 // In this case, we either need to start an activity to get permission to bind 1955 // the widget, or we need to start an activity to configure the widget, or both. 1956 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) { 1957 appWidgetId = CustomWidgetManager.INSTANCE.get(this) 1958 .allocateCustomAppWidgetId(info.componentName); 1959 } else { 1960 appWidgetId = getAppWidgetHolder().allocateAppWidgetId(); 1961 } 1962 Bundle options = info.bindOptions; 1963 1964 boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( 1965 appWidgetId, info.info, options); 1966 if (success) { 1967 addAppWidgetFromDropImpl(appWidgetId, info, null, addFlowHandler); 1968 } else { 1969 addFlowHandler.startBindFlow(this, appWidgetId, info, REQUEST_BIND_APPWIDGET); 1970 } 1971 } 1972 } 1973 1974 /** 1975 * Creates and adds new folder to CellLayout 1976 */ 1977 public FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX, 1978 int cellY) { 1979 final FolderInfo folderInfo = new FolderInfo(); 1980 1981 // Update the model 1982 getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY); 1983 1984 // Create the view 1985 FolderIcon newFolder = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this, layout, 1986 folderInfo); 1987 mWorkspace.addInScreen(newFolder, folderInfo); 1988 // Force measure the new folder icon 1989 CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder); 1990 parent.getShortcutsAndWidgets().measureChild(newFolder); 1991 return newFolder; 1992 } 1993 1994 @Override 1995 public Rect getFolderBoundingBox() { 1996 // We need to bound the folder to the currently visible workspace area 1997 return getWorkspace().getPageAreaRelativeToDragLayer(); 1998 } 1999 2000 @Override 2001 public void updateOpenFolderPosition(int[] inOutPosition, Rect bounds, int width, int height) { 2002 int left = inOutPosition[0]; 2003 int top = inOutPosition[1]; 2004 DeviceProfile grid = getDeviceProfile(); 2005 int distFromEdgeOfScreen = getWorkspace().getPaddingLeft(); 2006 if (grid.isPhone && (grid.availableWidthPx - width) < 4 * distFromEdgeOfScreen) { 2007 // Center the folder if it is very close to being centered anyway, by virtue of 2008 // filling the majority of the viewport. ie. remove it from the uncanny valley 2009 // of centeredness. 2010 left = (grid.availableWidthPx - width) / 2; 2011 } else if (width >= bounds.width()) { 2012 // If the folder doesn't fit within the bounds, center it about the desired bounds 2013 left = bounds.left + (bounds.width() - width) / 2; 2014 } 2015 if (height >= bounds.height()) { 2016 // Folder height is greater than page height, center on page 2017 top = bounds.top + (bounds.height() - height) / 2; 2018 } else { 2019 // Folder height is less than page height, so bound it to the absolute open folder 2020 // bounds if necessary 2021 Rect folderBounds = grid.getAbsoluteOpenFolderBounds(); 2022 left = Math.max(folderBounds.left, Math.min(left, folderBounds.right - width)); 2023 top = Math.max(folderBounds.top, Math.min(top, folderBounds.bottom - height)); 2024 } 2025 inOutPosition[0] = left; 2026 inOutPosition[1] = top; 2027 } 2028 2029 /** 2030 * Unbinds the view for the specified item, and removes the item and all its children. 2031 * 2032 * @param v the view being removed. 2033 * @param itemInfo the {@link ItemInfo} for this view. 2034 * @param deleteFromDb whether or not to delete this item from the db. 2035 */ 2036 public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) { 2037 return removeItem(v, itemInfo, deleteFromDb, null); 2038 } 2039 2040 /** 2041 * Unbinds the view for the specified item, and removes the item and all its children. 2042 * 2043 * @param v the view being removed. 2044 * @param itemInfo the {@link ItemInfo} for this view. 2045 * @param deleteFromDb whether or not to delete this item from the db. 2046 * @param reason the resaon for removal. 2047 */ 2048 public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb, 2049 @Nullable final String reason) { 2050 if (itemInfo instanceof WorkspaceItemInfo) { 2051 View collectionIcon = mWorkspace.getViewByItemId(itemInfo.container); 2052 if (collectionIcon instanceof FolderIcon folderIcon) { 2053 // Remove the shortcut from the folder before removing it from launcher 2054 Folder folder = folderIcon.getFolder(); 2055 folder.removeFolderContent(true, itemInfo); 2056 } else if (collectionIcon instanceof AppPairIcon appPairIcon) { 2057 removeItem(appPairIcon, appPairIcon.getInfo(), deleteFromDb, 2058 "removing app pair because one of its member apps was removed"); 2059 } else { 2060 mWorkspace.removeWorkspaceItem(v); 2061 } 2062 if (deleteFromDb) { 2063 getModelWriter().deleteItemFromDatabase(itemInfo, reason); 2064 } 2065 } else if (itemInfo instanceof CollectionInfo ci) { 2066 mWorkspace.removeWorkspaceItem(v); 2067 if (deleteFromDb) { 2068 getModelWriter().deleteCollectionAndContentsFromDatabase(ci); 2069 } 2070 } else if (itemInfo instanceof LauncherAppWidgetInfo) { 2071 final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo; 2072 mWorkspace.removeWorkspaceItem(v); 2073 if (deleteFromDb) { 2074 getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHolder(), reason); 2075 } 2076 } else { 2077 return false; 2078 } 2079 return true; 2080 } 2081 2082 @Override 2083 public boolean dispatchKeyEvent(KeyEvent event) { 2084 TestLogging.recordKeyEvent(TestProtocol.SEQUENCE_MAIN, "Key event", event); 2085 return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event); 2086 } 2087 2088 @Override 2089 public boolean dispatchTouchEvent(MotionEvent ev) { 2090 switch (ev.getAction()) { 2091 case MotionEvent.ACTION_DOWN: 2092 mTouchInProgress = true; 2093 break; 2094 case MotionEvent.ACTION_UP: 2095 mLastTouchUpTime = SystemClock.uptimeMillis(); 2096 // Follow through 2097 case MotionEvent.ACTION_CANCEL: 2098 mTouchInProgress = false; 2099 break; 2100 } 2101 TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev); 2102 return super.dispatchTouchEvent(ev); 2103 } 2104 2105 @Override 2106 @TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 2107 public void onBackPressed() { 2108 getOnBackAnimationCallback().onBackInvoked(); 2109 } 2110 2111 protected void onBackStarted() { 2112 mStateManager.getState().onBackStarted(this); 2113 } 2114 2115 protected void onStateBack() { 2116 mStateManager.getState().onBackInvoked(this); 2117 } 2118 2119 protected void onBackCancelled() { 2120 mStateManager.getState().onBackCancelled(this); 2121 } 2122 2123 protected void onScreenOnChanged(boolean isOn) { 2124 // Reset AllApps to its initial state only if we are not in the middle of 2125 // processing a multi-step drop 2126 if (!isOn && mPendingRequestArgs == null) { 2127 if (!isInState(NORMAL)) { 2128 onUiChangedWhileSleeping(); 2129 } 2130 mStateManager.goToState(NORMAL); 2131 } 2132 } 2133 2134 @Override 2135 public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) { 2136 if (!hasBeenResumed()) { 2137 RunnableList result = new RunnableList(); 2138 // Workaround an issue where the WM launch animation is clobbered when finishing the 2139 // recents animation into launcher. Defer launching the activity until Launcher is 2140 // next resumed. 2141 addEventCallback(EVENT_RESUMED, () -> { 2142 RunnableList actualResult = startActivitySafely(v, intent, item); 2143 if (actualResult != null) { 2144 actualResult.add(result::executeAllAndDestroy); 2145 } else { 2146 result.executeAllAndDestroy(); 2147 } 2148 }); 2149 if (mOnDeferredActivityLaunchCallback != null) { 2150 mOnDeferredActivityLaunchCallback.run(); 2151 mOnDeferredActivityLaunchCallback = null; 2152 } 2153 return result; 2154 } 2155 2156 RunnableList result = super.startActivitySafely(v, intent, item); 2157 if (result != null && v instanceof BubbleTextView) { 2158 // This is set to the view that launched the activity that navigated the user away 2159 // from launcher. Since there is no callback for when the activity has finished 2160 // launching, enable the press state and keep this reference to reset the press 2161 // state when we return to launcher. 2162 BubbleTextView btv = (BubbleTextView) v; 2163 btv.setStayPressed(true); 2164 result.add(() -> btv.setStayPressed(false)); 2165 } 2166 return result; 2167 } 2168 2169 boolean isHotseatLayout(View layout) { 2170 // TODO: Remove this method 2171 return mHotseat != null && (layout == mHotseat); 2172 } 2173 2174 @Override 2175 public void onTrimMemory(int level) { 2176 super.onTrimMemory(level); 2177 if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { 2178 // The widget preview db can result in holding onto over 2179 // 3MB of memory for caching which isn't necessary. 2180 SQLiteDatabase.releaseMemory(); 2181 2182 // This clears all widget bitmaps from the widget tray 2183 // TODO(hyunyoungs) 2184 } 2185 } 2186 2187 @Override 2188 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 2189 final boolean result = super.dispatchPopulateAccessibilityEvent(event); 2190 final List<CharSequence> text = event.getText(); 2191 text.clear(); 2192 // Populate event with a fake title based on the current state. 2193 // TODO: When can workspace be null? 2194 text.add(mWorkspace == null 2195 ? getString(R.string.home_screen) 2196 : mStateManager.getState().getDescription(this)); 2197 return result; 2198 } 2199 2200 @Override 2201 public IntSet getPagesToBindSynchronously(IntArray orderedScreenIds) { 2202 return mModelCallbacks.getPagesToBindSynchronously(orderedScreenIds); 2203 } 2204 2205 @Override 2206 public void startBinding() { 2207 mModelCallbacks.startBinding(); 2208 } 2209 2210 @Override 2211 public void setIsFirstPagePinnedItemEnabled(boolean isFirstPagePinnedItemEnabled) { 2212 mModelCallbacks.setIsFirstPagePinnedItemEnabled(isFirstPagePinnedItemEnabled); 2213 } 2214 2215 @Override 2216 public void bindScreens(IntArray orderedScreenIds) { 2217 mModelCallbacks.bindScreens(orderedScreenIds); 2218 } 2219 2220 /** 2221 * Remove odd number because they are already included when isTwoPanels and add the pair screen 2222 * if not present. 2223 */ 2224 private IntArray filterTwoPanelScreenIds(IntArray orderedScreenIds) { 2225 IntSet screenIds = IntSet.wrap(orderedScreenIds); 2226 orderedScreenIds.forEach(screenId -> { 2227 if (screenId % 2 == 1) { 2228 screenIds.remove(screenId); 2229 // In case the pair is not added, add it 2230 if (!mWorkspace.containsScreenId(screenId - 1)) { 2231 screenIds.add(screenId - 1); 2232 } 2233 } 2234 }); 2235 return screenIds.getArray(); 2236 } 2237 2238 @Override 2239 public void preAddApps() { 2240 mModelCallbacks.preAddApps(); 2241 } 2242 2243 @Override 2244 public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, 2245 ArrayList<ItemInfo> addAnimated) { 2246 mModelCallbacks.bindAppsAdded(newScreens, addNotAnimated, addAnimated); 2247 } 2248 2249 /** 2250 * Bind the items start-end from the list. 2251 * 2252 * Implementation of the method from LauncherModel.Callbacks. 2253 */ 2254 @Override 2255 public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) { 2256 bindInflatedItems(items.stream() 2257 .map(i -> Pair.create(i, getItemInflater().inflateItem(i, getModelWriter()))) 2258 .collect(Collectors.toList()), 2259 forceAnimateIcons ? new AnimatorSet() : null); 2260 } 2261 2262 @Override 2263 public void bindInflatedItems(List<Pair<ItemInfo, View>> items) { 2264 bindInflatedItems(items, null); 2265 } 2266 2267 /** 2268 * Bind all the items in the map, ignoring any null views 2269 * 2270 * @param boundAnim if non-null, uses it to create and play the bounce animation for added views 2271 */ 2272 public void bindInflatedItems( 2273 List<Pair<ItemInfo, View>> shortcuts, @Nullable AnimatorSet boundAnim) { 2274 // Get the list of added items and intersect them with the set of items here 2275 Workspace<?> workspace = mWorkspace; 2276 int newItemsScreenId = -1; 2277 int index = 0; 2278 for (Pair<ItemInfo, View> e : shortcuts) { 2279 final ItemInfo item = e.first; 2280 2281 // Remove colliding items. 2282 CellPos presenterPos = getCellPosMapper().mapModelToPresenter(item); 2283 if (item.container == CONTAINER_DESKTOP) { 2284 CellLayout cl = mWorkspace.getScreenWithId(presenterPos.screenId); 2285 if (cl != null && cl.isOccupied(presenterPos.cellX, presenterPos.cellY)) { 2286 View occupiedView = cl.getChildAt(presenterPos.cellX, presenterPos.cellY); 2287 Object tag = occupiedView == null ? null : occupiedView.getTag(); 2288 String desc = "Collision while binding workspace item: " + item 2289 + ". Collides with " + tag; 2290 if (FeatureFlags.IS_STUDIO_BUILD) { 2291 throw (new RuntimeException(desc)); 2292 } else { 2293 getModelWriter().deleteItemFromDatabase(item, desc); 2294 continue; 2295 } 2296 } 2297 } 2298 2299 View view = e.second; 2300 if (view == null) { 2301 continue; 2302 } 2303 if (enableWorkspaceInflation() && view instanceof LauncherAppWidgetHostView lv) { 2304 view = getAppWidgetHolder().attachViewToHostAndGetAttachedView(lv); 2305 } 2306 workspace.addInScreenFromBind(view, item); 2307 if (boundAnim != null) { 2308 // Animate all the applications up now 2309 view.setAlpha(0f); 2310 view.setScaleX(0f); 2311 view.setScaleY(0f); 2312 boundAnim.play(createNewAppBounceAnimation(view, index++)); 2313 newItemsScreenId = presenterPos.screenId; 2314 } 2315 } 2316 2317 // Animate to the correct page 2318 if (boundAnim != null && newItemsScreenId > -1) { 2319 int currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage()); 2320 final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId); 2321 final Runnable startBounceAnimRunnable = boundAnim::start; 2322 2323 if (canAnimatePageChange() && newItemsScreenId != currentScreenId) { 2324 // We post the animation slightly delayed to prevent slowdowns 2325 // when we are loading right after we return to launcher. 2326 mWorkspace.postDelayed(() -> { 2327 closeOpenViews(false); 2328 mWorkspace.snapToPage(newScreenIndex); 2329 mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); 2330 }, NEW_APPS_PAGE_MOVE_DELAY); 2331 } else { 2332 mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); 2333 } 2334 } 2335 workspace.requestLayout(); 2336 } 2337 2338 /** 2339 * Add the views for a widget to the workspace. 2340 */ 2341 public void bindAppWidget(LauncherAppWidgetInfo item) { 2342 View view = mItemInflater.inflateItem(item, getModelWriter()); 2343 if (view != null) { 2344 mWorkspace.addInScreen(view, item); 2345 mWorkspace.requestLayout(); 2346 } 2347 } 2348 2349 /** 2350 * Restores a pending widget. 2351 * 2352 * @param appWidgetId The app widget id 2353 */ 2354 private LauncherAppWidgetInfo completeRestoreAppWidget(int appWidgetId, int finalRestoreFlag) { 2355 LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId); 2356 if (!(view instanceof PendingAppWidgetHostView)) { 2357 Log.e(TAG, "Widget update called, when the widget no longer exists."); 2358 return null; 2359 } 2360 2361 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag(); 2362 info.restoreStatus = finalRestoreFlag; 2363 if (info.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { 2364 info.pendingItemInfo = null; 2365 } 2366 2367 PendingAppWidgetHostView pv = (PendingAppWidgetHostView) view; 2368 if (pv.isReinflateIfNeeded()) { 2369 pv.reInflate(); 2370 } 2371 2372 getModelWriter().updateItemInDatabase(info); 2373 return info; 2374 } 2375 2376 /** 2377 * Call back when ModelCallbacks finish binding the Launcher data. 2378 */ 2379 @TargetApi(Build.VERSION_CODES.S) 2380 public void bindComplete(int workspaceItemCount, boolean isBindSync) { 2381 if (mOnInitialBindListener != null) { 2382 getRootView().getViewTreeObserver().removeOnPreDrawListener(mOnInitialBindListener); 2383 mOnInitialBindListener = null; 2384 } 2385 if (!isBindSync) { 2386 mStartupLatencyLogger 2387 .logCardinality(workspaceItemCount) 2388 .logEnd(LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC); 2389 } 2390 MAIN_EXECUTOR.getHandler().postAtFrontOfQueue(() -> { 2391 mStartupLatencyLogger 2392 .logEnd(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION) 2393 .log() 2394 .reset(); 2395 }); 2396 } 2397 2398 @Override 2399 public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks, 2400 RunnableList onCompleteSignal, int workspaceItemCount, boolean isBindSync) { 2401 mModelCallbacks.onInitialBindComplete(boundPages, pendingTasks, onCompleteSignal, 2402 workspaceItemCount, isBindSync); 2403 if (mIsColdStartupAfterReboot) { 2404 Trace.endAsyncSection(COLD_STARTUP_TRACE_METHOD_NAME, 2405 COLD_STARTUP_TRACE_COOKIE); 2406 } 2407 } 2408 2409 /** 2410 * Callback saying that there aren't any more items to bind. 2411 * <p> 2412 * Implementation of the method from LauncherModel.Callbacks. 2413 */ 2414 public void finishBindingItems(IntSet pagesBoundFirst) { 2415 mModelCallbacks.finishBindingItems(pagesBoundFirst); 2416 } 2417 2418 private boolean canAnimatePageChange() { 2419 if (mDragController.isDragging()) { 2420 return false; 2421 } else { 2422 return (SystemClock.uptimeMillis() - mLastTouchUpTime) 2423 > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000); 2424 } 2425 } 2426 2427 /** 2428 * Finds the first view on homescreen matching the provided parameters, optimized to finding a 2429 * suitable view for the app close animation. 2430 * 2431 * @param svi The StableViewInfo of the preferred item to match to if it exists or null 2432 * @param packageName The package name of the app to match. 2433 * @param user The user of the app to match. 2434 */ 2435 public @Nullable View getFirstHomeElementForAppClose( 2436 @Nullable StableViewInfo svi, String packageName, UserHandle user) { 2437 final Predicate<ItemInfo> preferredItem = svi == null ? i -> false : svi::matches; 2438 final Predicate<ItemInfo> packageAndUserAndApp = info -> info != null 2439 && info.itemType == ITEM_TYPE_APPLICATION 2440 && info.user.equals(user) 2441 && TextUtils.equals(info.getTargetPackage(), packageName); 2442 2443 // Look for the item inside the folder at the current page 2444 Folder folder = Folder.getOpen(this); 2445 if (folder != null) { 2446 View v = folder.getFirstMatch(preferredItem, packageAndUserAndApp); 2447 if (v == null) { 2448 folder.close(isStarted() && !isForceInvisible()); 2449 } else { 2450 return v; 2451 } 2452 } 2453 2454 List<CellLayout> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1); 2455 containers.add(mWorkspace.getHotseat()); 2456 mWorkspace.forEachVisiblePage(page -> containers.add((CellLayout) page)); 2457 CellLayout[] containerArray = containers.toArray(new CellLayout[0]); 2458 LauncherBindableItemsContainer visibleContainer = 2459 op -> mapOverCellLayouts(containerArray, op); 2460 2461 // Order: Preferred item by itself or in folder, then by matching package/user 2462 return visibleContainer.getFirstMatch( 2463 preferredItem, forFolderMatch(preferredItem), 2464 packageAndUserAndApp, forFolderMatch(packageAndUserAndApp)); 2465 } 2466 2467 private ValueAnimator createNewAppBounceAnimation(View v, int i) { 2468 ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v) 2469 .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION); 2470 bounceAnim.setStartDelay(i * ItemInstallQueue.NEW_SHORTCUT_STAGGER_DELAY); 2471 bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION)); 2472 return bounceAnim; 2473 } 2474 2475 /** 2476 * Informs us that the overlay (-1 screen, typically), has either become visible or invisible. 2477 */ 2478 public void onOverlayVisibilityChanged(boolean visible) { 2479 getStatsLogManager().logger() 2480 .withSrcState(LAUNCHER_STATE_HOME) 2481 .withDstState(LAUNCHER_STATE_HOME) 2482 .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder() 2483 .setWorkspace(WorkspaceContainer.newBuilder() 2484 .setPageIndex(visible ? 0 : -1)) 2485 .build()) 2486 .log(visible ? LAUNCHER_SWIPELEFT : LAUNCHER_SWIPERIGHT); 2487 } 2488 2489 /** 2490 * Informs us that the page transition has ended, so that we can react to the newly selected 2491 * page if we want to. 2492 */ 2493 public void onPageEndTransition() {} 2494 2495 /** 2496 * See {@code LauncherBindingDelegate} 2497 */ 2498 @Override 2499 @TargetApi(Build.VERSION_CODES.S) 2500 @UiThread 2501 public void bindAllApplications(AppInfo[] apps, int flags, 2502 Map<PackageUserKey, Integer> packageUserKeytoUidMap) { 2503 mModelCallbacks.bindAllApplications(apps, flags, packageUserKeytoUidMap); 2504 Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME, 2505 DISPLAY_ALL_APPS_TRACE_COOKIE); 2506 } 2507 2508 /** 2509 * See {@code LauncherBindingDelegate} 2510 */ 2511 @Override 2512 public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) { 2513 mModelCallbacks.bindDeepShortcutMap(deepShortcutMapCopy); 2514 } 2515 2516 @Override 2517 public void bindIncrementalDownloadProgressUpdated(AppInfo app) { 2518 mModelCallbacks.bindIncrementalDownloadProgressUpdated(app); 2519 } 2520 2521 /** 2522 * See {@code LauncherBindingDelegate} 2523 */ 2524 @Override 2525 public void bindItemsUpdated(Set<ItemInfo> updates) { 2526 mModelCallbacks.bindItemsUpdated(updates); 2527 } 2528 2529 /** 2530 * See {@code LauncherBindingDelegate} 2531 */ 2532 @Override 2533 public void bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher) { 2534 mModelCallbacks.bindWorkspaceComponentsRemoved(matcher); 2535 } 2536 2537 /** 2538 * See {@code LauncherBindingDelegate} 2539 */ 2540 @Override 2541 public void bindAllWidgets(@NonNull final List<WidgetsListBaseEntry> allWidgets) { 2542 mModelCallbacks.bindAllWidgets(allWidgets); 2543 } 2544 2545 @Override 2546 public void bindSmartspaceWidget() { 2547 mModelCallbacks.bindSmartspaceWidget(); 2548 } 2549 2550 @Override 2551 public void bindStringCache(StringCache cache) { 2552 mModelCallbacks.bindStringCache(cache); 2553 } 2554 2555 /** 2556 * @param packageUser if null, refreshes all widgets and shortcuts, otherwise only 2557 * refreshes the widgets and shortcuts associated with the given package/user 2558 */ 2559 public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) { 2560 mModel.refreshAndBindWidgetsAndShortcuts(packageUser); 2561 } 2562 2563 /** 2564 * $ adb shell dumpsys activity com.android.launcher3.Launcher [--all] 2565 */ 2566 @Override 2567 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 2568 super.dump(prefix, fd, writer, args); 2569 2570 if (args.length > 0 && TextUtils.equals(args[0], "--all")) { 2571 writer.println(prefix + "Workspace Items"); 2572 for (int i = 0; i < mWorkspace.getPageCount(); i++) { 2573 writer.println(prefix + " Homescreen " + i); 2574 2575 ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets(); 2576 for (int j = 0; j < layout.getChildCount(); j++) { 2577 Object tag = layout.getChildAt(j).getTag(); 2578 if (tag != null) { 2579 writer.println(prefix + " " + tag); 2580 } 2581 } 2582 } 2583 2584 writer.println(prefix + " Hotseat"); 2585 mHotseat.dump(prefix, writer); 2586 ViewGroup layout = mHotseat.getShortcutsAndWidgets(); 2587 for (int j = 0; j < layout.getChildCount(); j++) { 2588 Object tag = layout.getChildAt(j).getTag(); 2589 if (tag != null) { 2590 writer.println(prefix + " " + tag); 2591 } 2592 } 2593 } 2594 2595 writer.println(prefix + "Misc:"); 2596 dumpMisc(prefix + "\t", writer); 2597 writer.println(prefix + "\tmWorkspaceLoading=" + mModelCallbacks.getWorkspaceLoading()); 2598 writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs 2599 + " mPendingActivityResult=" + mPendingActivityResult); 2600 writer.println(prefix + "\tmRotationHelper: " + mRotationHelper); 2601 writer.println(prefix + "\tmAppWidgetHolder.isListening: " 2602 + mAppWidgetHolder.isListening()); 2603 2604 // b/349929393 2605 // The only way to reproduce this bug is to ensure that onLayout never gets called. This 2606 // theoretically is impossible, so these logs are being added to test if that actually is 2607 // what is happening. 2608 writer.println(prefix + "\tmWorkspace.mHasOnLayoutBeenCalled=" 2609 + mWorkspace.mHasOnLayoutBeenCalled); 2610 for (int i = 0; i < mWorkspace.getPageCount(); i++) { 2611 CellLayout cellLayout = (CellLayout) mWorkspace.getPageAt(i); 2612 writer.println(prefix + "\tcellLayout." + i + ".mHasOnLayoutBeenCalled=" 2613 + cellLayout.mHasOnLayoutBeenCalled); 2614 writer.println(prefix + "\tshortcutAndWidgetContainer." + i + ".mHasOnLayoutBeenCalled=" 2615 + cellLayout.getShortcutsAndWidgets().mHasOnLayoutBeenCalled); 2616 } 2617 2618 // Extra logging for general debugging 2619 mDragLayer.dump(prefix, writer); 2620 mStateManager.dump(prefix, writer); 2621 mPopupDataProvider.dump(prefix, writer); 2622 mWidgetPickerDataProvider.dump(prefix, writer); 2623 mDeviceProfile.dump(this, prefix, writer); 2624 mAppsView.getAppsStore().dump(prefix, writer); 2625 mAppsView.getPersonalAppList().dump(prefix, writer); 2626 if (mAppsView.shouldShowTabs()) { 2627 mAppsView.getWorkAppList().dump(prefix, writer); 2628 } 2629 2630 try { 2631 FileLog.flushAll(writer); 2632 } catch (Exception e) { 2633 // Ignore 2634 } 2635 2636 mModel.dumpState(prefix, fd, writer, args); 2637 mOverlayManager.dump(prefix, writer); 2638 ACTIVITY_TRACKER.dump(prefix, writer); 2639 MSDLPlayerWrapper.INSTANCE.get(getApplicationContext()).dump(prefix, writer); 2640 } 2641 2642 /** 2643 * Populates the list of shortcuts. Logic delegated to {@Link KeyboardShortcutsDelegate}. 2644 * 2645 * @param data The data list to populate with shortcuts. 2646 * @param menu The current menu, which may be null. 2647 * @param deviceId The id for the connected device the shortcuts should be provided for. 2648 */ 2649 @Override 2650 public void onProvideKeyboardShortcuts( 2651 List<KeyboardShortcutGroup> data, Menu menu, int deviceId) { 2652 mKeyboardShortcutsDelegate.onProvideKeyboardShortcuts(data, menu, deviceId); 2653 super.onProvideKeyboardShortcuts(data, menu, deviceId); 2654 } 2655 2656 /** 2657 * Logic delegated to {@Link KeyboardShortcutsDelegate}. 2658 * @param keyCode The value in event.getKeyCode(). 2659 * @param event Description of the key event. 2660 */ 2661 @Override 2662 public boolean onKeyShortcut(int keyCode, KeyEvent event) { 2663 Boolean result = mKeyboardShortcutsDelegate.onKeyShortcut(keyCode, event); 2664 return result != null ? result : super.onKeyShortcut(keyCode, event); 2665 } 2666 2667 /** 2668 * Logic delegated to {@Link KeyboardShortcutsDelegate}. 2669 * @param keyCode The value in event.getKeyCode(). 2670 * @param event Description of the key event. 2671 */ 2672 @Override 2673 public boolean onKeyDown(int keyCode, KeyEvent event) { 2674 Boolean result = mKeyboardShortcutsDelegate.onKeyDown(keyCode, event); 2675 return result != null ? result : super.onKeyDown(keyCode, event); 2676 } 2677 2678 /** 2679 * Logic delegated to {@Link KeyboardShortcutsDelegate}. 2680 * @param keyCode The value in event.getKeyCode(). 2681 * @param event Description of the key event. 2682 */ 2683 @Override 2684 public boolean onKeyUp(int keyCode, KeyEvent event) { 2685 Boolean result = mKeyboardShortcutsDelegate.onKeyUp(keyCode, event); 2686 return result != null ? result : super.onKeyUp(keyCode, event); 2687 } 2688 2689 /** 2690 * Shows the default options popup 2691 */ 2692 public void showDefaultOptions(float x, float y) { 2693 OptionsPopupView.show(this, getPopupTarget(x, y), OptionsPopupView.getOptions(this), 2694 false); 2695 } 2696 2697 @Override 2698 public boolean canUseMultipleShadesForPopup() { 2699 return getTopOpenViewWithType(this, TYPE_FOLDER) == null 2700 && getStateManager().getState() != LauncherState.ALL_APPS; 2701 } 2702 2703 @Override 2704 public void collectStateHandlers(List<StateHandler<LauncherState>> out) { 2705 out.add(getAllAppsController()); 2706 out.add(getWorkspace()); 2707 } 2708 2709 public TouchController[] createTouchControllers() { 2710 return new TouchController[] {getDragController(), new AllAppsSwipeController(this)}; 2711 } 2712 2713 public void onDragLayerHierarchyChanged() { 2714 updateDisallowBack(); 2715 } 2716 2717 protected void addBackAnimationCallback(BackPressHandler callback) { 2718 mBackPressedHandlers.add(callback); 2719 } 2720 2721 protected void removeBackAnimationCallback(BackPressHandler callback) { 2722 mBackPressedHandlers.remove(callback); 2723 } 2724 2725 private void updateDisallowBack() { 2726 if (BuildCompat.isAtLeastV() 2727 && Flags.enableDesktopWindowingMode() 2728 && !Flags.enableDesktopWindowingWallpaperActivity() 2729 && mDeviceProfile.isTablet) { 2730 // TODO(b/333533253): Clean up after desktop wallpaper activity flag is rolled out 2731 return; 2732 } 2733 LauncherRootView rv = getRootView(); 2734 if (rv != null) { 2735 boolean isSplitSelectionEnabled = isSplitSelectionActive(); 2736 boolean disableBack = getStateManager().getState() == NORMAL 2737 && AbstractFloatingView.getTopOpenView(this) == null 2738 && !isSplitSelectionEnabled; 2739 rv.setDisallowBackGesture(disableBack); 2740 } 2741 } 2742 2743 /** To be overridden by subclasses */ 2744 public boolean isSplitSelectionActive() { 2745 // Overridden 2746 return false; 2747 } 2748 2749 /** Call to dismiss the intermediary split selection state. */ 2750 public void dismissSplitSelection(StatsLogManager.LauncherEvent splitDismissEvent) { 2751 // Overridden; move this into ActivityContext if necessary for Taskbar 2752 } 2753 2754 /** 2755 * Callback for when launcher state transition completes after user swipes to home. 2756 * @param finalState The final state of the transition. 2757 */ 2758 public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) { 2759 // Overridden 2760 } 2761 2762 public void closeOpenViews() { 2763 closeOpenViews(true); 2764 } 2765 2766 protected void closeOpenViews(boolean animate) { 2767 AbstractFloatingView.closeAllOpenViews(this, animate); 2768 } 2769 2770 protected LauncherAccessibilityDelegate createAccessibilityDelegate() { 2771 return new LauncherAccessibilityDelegate(this); 2772 } 2773 2774 /** Enables/disabled the hotseat prediction icon long press edu for testing. */ 2775 @VisibleForTesting 2776 public void enableHotseatEdu(boolean enable) {} 2777 2778 2779 /** 2780 * Just a wrapper around the type cast to allow easier tracking of calls. 2781 */ 2782 public static <T extends Launcher> T cast(ActivityContext activityContext) { 2783 return (T) activityContext; 2784 } 2785 2786 public boolean supportsAdaptiveIconAnimation(View clickedView) { 2787 return false; 2788 } 2789 2790 /** 2791 * Animates Launcher elements during a transition to the All Apps page. 2792 * 2793 * @param progress Transition progress from 0 to 1; where 0 => home and 1 => all apps. 2794 */ 2795 public void onAllAppsTransition(float progress) { 2796 // No-Op 2797 } 2798 2799 /** 2800 * Animates Launcher elements during a transition to the Widgets pages. 2801 * 2802 * @param progress Transition progress from 0 to 1; where 0 => home and 1 => widgets. 2803 */ 2804 public void onWidgetsTransition(float progress) { 2805 float scale = Utilities.mapToRange(progress, 0f, 1f, 1f, 2806 mDeviceProfile.bottomSheetWorkspaceScale, EMPHASIZED); 2807 WORKSPACE_WIDGET_SCALE.set(getWorkspace(), scale); 2808 HOTSEAT_WIDGET_SCALE.set(getHotseat(), scale); 2809 } 2810 2811 private static class NonConfigInstance { 2812 public Configuration config; 2813 } 2814 2815 /** Pauses view updates that should not be run during the app launch animation. */ 2816 public void pauseExpensiveViewUpdates() { 2817 // Pause page indicator animations as they lead to layer trashing. 2818 getWorkspace().getPageIndicator().pauseAnimations(); 2819 2820 getWorkspace().mapOverItems((info, view) -> { 2821 if (view instanceof LauncherAppWidgetHostView) { 2822 ((LauncherAppWidgetHostView) view).beginDeferringUpdates(); 2823 } 2824 return false; // Return false to continue iterating through all the items. 2825 }); 2826 } 2827 2828 /** Resumes view updates at the end of the app launch animation. */ 2829 public void resumeExpensiveViewUpdates() { 2830 getWorkspace().getPageIndicator().skipAnimationsToEnd(); 2831 2832 getWorkspace().mapOverItems((info, view) -> { 2833 if (view instanceof LauncherAppWidgetHostView) { 2834 ((LauncherAppWidgetHostView) view).endDeferringUpdates(); 2835 } 2836 return false; // Return false to continue iterating through all the items. 2837 }); 2838 } 2839 2840 /** 2841 * Returns {@code true} if there are visible tasks with windowing mode set to 2842 * {@link android.app.WindowConfiguration#WINDOWING_MODE_FREEFORM} 2843 */ 2844 public boolean areDesktopTasksVisible() { 2845 return false; // Base launcher does not track desktop tasks 2846 } 2847 2848 // Getters and Setters 2849 2850 public boolean isWorkspaceLocked() { 2851 return isWorkspaceLoading() || mPendingRequestArgs != null; 2852 } 2853 2854 public boolean isWorkspaceLoading() { 2855 return mModelCallbacks.getWorkspaceLoading(); 2856 } 2857 2858 /** 2859 * Returns true if a touch interaction is in progress 2860 */ 2861 public boolean isTouchInProgress() { 2862 return mTouchInProgress; 2863 } 2864 2865 public boolean isDraggingEnabled() { 2866 // We prevent dragging when we are loading the workspace as it is possible to pick up a view 2867 // that is subsequently removed from the workspace in startBinding(). 2868 return !isWorkspaceLoading(); 2869 } 2870 2871 public boolean isNaturalScrollingEnabled() { 2872 return mIsNaturalScrollingEnabled; 2873 } 2874 2875 public void setWaitingForResult(PendingRequestArgs args) { 2876 mPendingRequestArgs = args; 2877 } 2878 2879 /** 2880 * Call this after onCreate to set or clear overlay. 2881 */ 2882 public void setLauncherOverlay(LauncherOverlayTouchProxy overlay) { 2883 mWorkspace.setLauncherOverlay(overlay); 2884 } 2885 2886 /** 2887 * Persistent callback which notifies when an activity launch is deferred because the activity 2888 * was not yet resumed. 2889 */ 2890 public void setOnDeferredActivityLaunchCallback(Runnable callback) { 2891 mOnDeferredActivityLaunchCallback = callback; 2892 } 2893 2894 /** 2895 * Sets the next pages to bind synchronously on next bind. 2896 * @param pages should not be null. 2897 */ 2898 public void setPagesToBindSynchronously(@NonNull IntSet pages) { 2899 mModelCallbacks.setPagesToBindSynchronously(pages); 2900 } 2901 2902 @Override 2903 public CellPosMapper getCellPosMapper() { 2904 return mCellPosMapper; 2905 } 2906 2907 public RotationHelper getRotationHelper() { 2908 return mRotationHelper; 2909 } 2910 2911 public ViewGroupFocusHelper getFocusHandler() { 2912 return mFocusHandler; 2913 } 2914 2915 @Override 2916 public StateManager<LauncherState, Launcher> getStateManager() { 2917 return mStateManager; 2918 } 2919 2920 @NonNull 2921 @Override 2922 public PopupDataProvider getPopupDataProvider() { 2923 return mPopupDataProvider; 2924 } 2925 2926 @NonNull 2927 @Override 2928 public WidgetPickerDataProvider getWidgetPickerDataProvider() { 2929 return mWidgetPickerDataProvider; 2930 } 2931 2932 @NonNull 2933 public LauncherOverlayManager getOverlayManager() { 2934 return mOverlayManager; 2935 } 2936 2937 public AllAppsTransitionController getAllAppsController() { 2938 return mAllAppsController; 2939 } 2940 2941 @Override 2942 public DragLayer getDragLayer() { 2943 return mDragLayer; 2944 } 2945 2946 @NonNull 2947 @Override 2948 public LauncherBindableItemsContainer getContent() { 2949 return mWorkspace; 2950 } 2951 2952 @Override 2953 public ActivityAllAppsContainerView<Launcher> getAppsView() { 2954 return mAppsView; 2955 } 2956 2957 public Workspace<?> getWorkspace() { 2958 return mWorkspace; 2959 } 2960 2961 public Hotseat getHotseat() { 2962 return mHotseat; 2963 } 2964 2965 public <T extends View> T getOverviewPanel() { 2966 return (T) mOverviewPanel; 2967 } 2968 2969 public DropTargetBar getDropTargetBar() { 2970 return mDropTargetBar; 2971 } 2972 2973 @Override 2974 public ScrimView getScrimView() { 2975 return mScrimView; 2976 } 2977 2978 public LauncherWidgetHolder getAppWidgetHolder() { 2979 return mAppWidgetHolder; 2980 } 2981 2982 public LauncherModel getModel() { 2983 return mModel; 2984 } 2985 2986 /** 2987 * Returns the ModelWriter writer, make sure to call the function every time you want to use it. 2988 */ 2989 public ModelWriter getModelWriter() { 2990 return mModelWriter; 2991 } 2992 2993 public SharedPreferences getSharedPrefs() { 2994 return mSharedPrefs; 2995 } 2996 2997 public int getOrientation() { 2998 return mOldConfig.orientation; 2999 } 3000 3001 /** 3002 * Returns the CellLayout of the specified container at the specified screen. 3003 * 3004 * @param screenId must be presenterPos and not modelPos. 3005 */ 3006 public CellLayout getCellLayout(int container, int screenId) { 3007 return (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) 3008 ? mHotseat : mWorkspace.getScreenWithId(screenId); 3009 } 3010 3011 @Override 3012 public StringCache getStringCache() { 3013 return mModelCallbacks.getStringCache(); 3014 } 3015 3016 /** 3017 * Returns target rectangle for anchoring a popup menu. 3018 */ 3019 protected RectF getPopupTarget(float x, float y) { 3020 float halfSize = getResources().getDimension(R.dimen.options_menu_thumb_size) / 2; 3021 if (x < 0 || y < 0) { 3022 x = mDragLayer.getWidth() / 2; 3023 y = mDragLayer.getHeight() / 2; 3024 } 3025 return new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize); 3026 } 3027 3028 public Stream<SystemShortcut.Factory> getSupportedShortcuts() { 3029 return Stream.of(APP_INFO, WIDGETS, INSTALL); 3030 } 3031 3032 /** 3033 * @see LauncherState#getOverviewScaleAndOffset(Launcher) 3034 */ 3035 public float[] getNormalOverviewScaleAndOffset() { 3036 return new float[] {NO_SCALE, NO_OFFSET}; 3037 } 3038 3039 /** 3040 * Handles an app pair launch; overridden in 3041 * {@link com.android.launcher3.uioverrides.QuickstepLauncher} 3042 */ 3043 public void launchAppPair(AppPairIcon appPairIcon) { 3044 // Overridden 3045 } 3046 3047 public boolean getIsFirstPagePinnedItemEnabled() { 3048 return mModelCallbacks.getIsFirstPagePinnedItemEnabled(); 3049 } 3050 3051 /** 3052 * Returns the animation coordinator for playing one-off animations 3053 */ 3054 public CannedAnimationCoordinator getAnimationCoordinator() { 3055 return mAnimationCoordinator; 3056 } 3057 3058 @Override 3059 public View.OnLongClickListener getAllAppsItemLongClickListener() { 3060 return ItemLongClickListener.INSTANCE_ALL_APPS; 3061 } 3062 3063 @Override 3064 public StatsLogManager getStatsLogManager() { 3065 return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId); 3066 } 3067 3068 @Override 3069 public ItemInflater<Launcher> getItemInflater() { 3070 return mItemInflater; 3071 } 3072 3073 /** 3074 * Returns the current popup for testing, if any. 3075 */ 3076 @VisibleForTesting 3077 @Nullable 3078 public ArrowPopup<?> getOptionsPopup() { 3079 return findViewById(R.id.popup_container); 3080 } 3081 3082 @Override 3083 public OnClickListener getItemOnClickListener() { 3084 return ItemClickHandler.INSTANCE; 3085 } 3086 3087 // End of Getters and Setters 3088 } 3089