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