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