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