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