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