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