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