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