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