• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3;
18 
19 import android.Manifest;
20 import android.animation.Animator;
21 import android.animation.AnimatorListenerAdapter;
22 import android.animation.AnimatorSet;
23 import android.animation.ObjectAnimator;
24 import android.animation.ValueAnimator;
25 import android.annotation.SuppressLint;
26 import android.annotation.TargetApi;
27 import android.app.ActivityOptions;
28 import android.app.AlertDialog;
29 import android.app.SearchManager;
30 import android.appwidget.AppWidgetHostView;
31 import android.appwidget.AppWidgetManager;
32 import android.content.ActivityNotFoundException;
33 import android.content.BroadcastReceiver;
34 import android.content.ComponentCallbacks2;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.ContextWrapper;
38 import android.content.DialogInterface;
39 import android.content.Intent;
40 import android.content.IntentFilter;
41 import android.content.IntentSender;
42 import android.content.SharedPreferences;
43 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
44 import android.content.pm.ActivityInfo;
45 import android.content.pm.PackageManager;
46 import android.database.sqlite.SQLiteDatabase;
47 import android.graphics.Point;
48 import android.graphics.Rect;
49 import android.graphics.drawable.Drawable;
50 import android.os.AsyncTask;
51 import android.os.Build;
52 import android.os.Bundle;
53 import android.os.Handler;
54 import android.os.Process;
55 import android.os.StrictMode;
56 import android.os.SystemClock;
57 import android.os.Trace;
58 import android.os.UserHandle;
59 import android.support.annotation.Nullable;
60 import android.text.Selection;
61 import android.text.SpannableStringBuilder;
62 import android.text.TextUtils;
63 import android.text.method.TextKeyListener;
64 import android.util.Log;
65 import android.view.Display;
66 import android.view.HapticFeedbackConstants;
67 import android.view.KeyEvent;
68 import android.view.KeyboardShortcutGroup;
69 import android.view.KeyboardShortcutInfo;
70 import android.view.LayoutInflater;
71 import android.view.Menu;
72 import android.view.MotionEvent;
73 import android.view.View;
74 import android.view.View.OnLongClickListener;
75 import android.view.ViewGroup;
76 import android.view.ViewTreeObserver;
77 import android.view.WindowManager;
78 import android.view.accessibility.AccessibilityEvent;
79 import android.view.accessibility.AccessibilityManager;
80 import android.view.animation.OvershootInterpolator;
81 import android.view.inputmethod.InputMethodManager;
82 import android.widget.Toast;
83 
84 import com.android.launcher3.DropTarget.DragObject;
85 import com.android.launcher3.LauncherSettings.Favorites;
86 import com.android.launcher3.Workspace.ItemOperator;
87 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
88 import com.android.launcher3.allapps.AllAppsContainerView;
89 import com.android.launcher3.allapps.AllAppsTransitionController;
90 import com.android.launcher3.anim.AnimationLayerSet;
91 import com.android.launcher3.compat.AppWidgetManagerCompat;
92 import com.android.launcher3.compat.LauncherAppsCompat;
93 import com.android.launcher3.compat.LauncherAppsCompatVO;
94 import com.android.launcher3.compat.UserManagerCompat;
95 import com.android.launcher3.config.FeatureFlags;
96 import com.android.launcher3.dragndrop.DragController;
97 import com.android.launcher3.dragndrop.DragLayer;
98 import com.android.launcher3.dragndrop.DragOptions;
99 import com.android.launcher3.dragndrop.DragView;
100 import com.android.launcher3.dragndrop.PinItemDragListener;
101 import com.android.launcher3.dynamicui.ExtractedColors;
102 import com.android.launcher3.dynamicui.WallpaperColorInfo;
103 import com.android.launcher3.folder.Folder;
104 import com.android.launcher3.folder.FolderIcon;
105 import com.android.launcher3.keyboard.CustomActionsPopup;
106 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
107 import com.android.launcher3.logging.FileLog;
108 import com.android.launcher3.logging.UserEventDispatcher;
109 import com.android.launcher3.model.ModelWriter;
110 import com.android.launcher3.model.PackageItemInfo;
111 import com.android.launcher3.model.WidgetItem;
112 import com.android.launcher3.notification.NotificationListener;
113 import com.android.launcher3.pageindicators.PageIndicator;
114 import com.android.launcher3.popup.PopupContainerWithArrow;
115 import com.android.launcher3.popup.PopupDataProvider;
116 import com.android.launcher3.shortcuts.DeepShortcutManager;
117 import com.android.launcher3.userevent.nano.LauncherLogProto;
118 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
119 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
120 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
121 import com.android.launcher3.util.ActivityResultInfo;
122 import com.android.launcher3.util.RunnableWithId;
123 import com.android.launcher3.util.ComponentKey;
124 import com.android.launcher3.util.ComponentKeyMapper;
125 import com.android.launcher3.util.ItemInfoMatcher;
126 import com.android.launcher3.util.MultiHashMap;
127 import com.android.launcher3.util.PackageManagerHelper;
128 import com.android.launcher3.util.PackageUserKey;
129 import com.android.launcher3.util.PendingRequestArgs;
130 import com.android.launcher3.util.SystemUiController;
131 import com.android.launcher3.util.TestingUtils;
132 import com.android.launcher3.util.Themes;
133 import com.android.launcher3.util.Thunk;
134 import com.android.launcher3.util.ViewOnDrawExecutor;
135 import com.android.launcher3.widget.PendingAddShortcutInfo;
136 import com.android.launcher3.widget.PendingAddWidgetInfo;
137 import com.android.launcher3.widget.WidgetAddFlowHandler;
138 import com.android.launcher3.widget.WidgetHostViewLoader;
139 import com.android.launcher3.widget.WidgetsContainerView;
140 
141 
142 import java.io.FileDescriptor;
143 import java.io.PrintWriter;
144 import java.util.ArrayList;
145 import java.util.Collection;
146 import java.util.HashMap;
147 import java.util.HashSet;
148 import java.util.List;
149 import java.util.Set;
150 import java.util.concurrent.Executor;
151 
152 import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS;
153 import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS;
154 
155 /**
156  * Default launcher application.
157  */
158 public class Launcher extends BaseActivity
159         implements LauncherExterns, View.OnClickListener, OnLongClickListener,
160                    LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
161                    AccessibilityManager.AccessibilityStateChangeListener,
162                    WallpaperColorInfo.OnThemeChangeListener {
163     public static final String TAG = "Launcher";
164     static final boolean LOGD = false;
165 
166     static final boolean DEBUG_WIDGETS = false;
167     static final boolean DEBUG_STRICT_MODE = false;
168     static final boolean DEBUG_RESUME_TIME = false;
169 
170     private static final int REQUEST_CREATE_SHORTCUT = 1;
171     private static final int REQUEST_CREATE_APPWIDGET = 5;
172 
173     private static final int REQUEST_PICK_APPWIDGET = 9;
174     private static final int REQUEST_PICK_WALLPAPER = 10;
175 
176     private static final int REQUEST_BIND_APPWIDGET = 11;
177     private static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
178     private static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
179 
180     private static final int REQUEST_PERMISSION_CALL_PHONE = 14;
181 
182     private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
183 
184     /**
185      * IntentStarter uses request codes starting with this. This must be greater than all activity
186      * request codes used internally.
187      */
188     protected static final int REQUEST_LAST = 100;
189 
190     private static final int SOFT_INPUT_MODE_DEFAULT =
191             WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
192     private static final int SOFT_INPUT_MODE_ALL_APPS =
193             WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
194 
195     // The Intent extra that defines whether to ignore the launch animation
196     static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
197             "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
198 
199     // Type: int
200     private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
201     // Type: int
202     private static final String RUNTIME_STATE = "launcher.state";
203     // Type: PendingRequestArgs
204     private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
205     // Type: ActivityResultInfo
206     private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
207 
208     static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
209 
210     /** The different states that Launcher can be in. */
211     enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED,
212         WIDGETS, WIDGETS_SPRING_LOADED }
213 
214     @Thunk State mState = State.WORKSPACE;
215     @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation;
216 
217     private boolean mIsSafeModeEnabled;
218 
219     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500;
220     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
221 
222     // How long to wait before the new-shortcut animation automatically pans the workspace
223     private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
224     private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
225     @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
226 
227     private final ExtractedColors mExtractedColors = new ExtractedColors();
228 
229     @Thunk Workspace mWorkspace;
230     private View mLauncherView;
231     @Thunk DragLayer mDragLayer;
232     private DragController mDragController;
233 
234     public View mWeightWatcher;
235 
236     private AppWidgetManagerCompat mAppWidgetManager;
237     private LauncherAppWidgetHost mAppWidgetHost;
238 
239     private final int[] mTmpAddItemCellCoordinates = new int[2];
240 
241     @Thunk Hotseat mHotseat;
242     private ViewGroup mOverviewPanel;
243 
244     private View mAllAppsButton;
245     private View mWidgetsButton;
246 
247     private DropTargetBar mDropTargetBar;
248 
249     // Main container view for the all apps screen.
250     @Thunk AllAppsContainerView mAppsView;
251     AllAppsTransitionController mAllAppsController;
252 
253     // Main container view and the model for the widget tray screen.
254     @Thunk WidgetsContainerView mWidgetsView;
255 
256     // We need to store the orientation Launcher was created with, due to a bug (b/64916689)
257     // that results in widgets being inflated in the wrong orientation.
258     private int mOrientation;
259 
260     // We set the state in both onCreate and then onNewIntent in some cases, which causes both
261     // scroll issues (because the workspace may not have been measured yet) and extra work.
262     // Instead, just save the state that we need to restore Launcher to, and commit it in onResume.
263     private State mOnResumeState = State.NONE;
264 
265     private SpannableStringBuilder mDefaultKeySsb = null;
266 
267     @Thunk boolean mWorkspaceLoading = true;
268 
269     private boolean mPaused = true;
270     private boolean mOnResumeNeedsLoad;
271 
272     private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
273     private final ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<>();
274     private ViewOnDrawExecutor mPendingExecutor;
275 
276     private LauncherModel mModel;
277     private ModelWriter mModelWriter;
278     private IconCache mIconCache;
279     private LauncherAccessibilityDelegate mAccessibilityDelegate;
280     private final Handler mHandler = new Handler();
281     private boolean mHasFocus = false;
282 
283     private ObjectAnimator mScrimAnimator;
284     private boolean mShouldFadeInScrim;
285 
286     private PopupDataProvider mPopupDataProvider;
287 
288     // Determines how long to wait after a rotation before restoring the screen orientation to
289     // match the sensor state.
290     private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
291 
292     private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<>();
293 
294     // We only want to get the SharedPreferences once since it does an FS stat each time we get
295     // it from the context.
296     private SharedPreferences mSharedPrefs;
297 
298     private boolean mMoveToDefaultScreenFromNewIntent;
299 
300     // This is set to the view that launched the activity that navigated the user away from
301     // launcher. Since there is no callback for when the activity has finished launching, enable
302     // the press state and keep this reference to reset the press state when we return to launcher.
303     private BubbleTextView mWaitingForResume;
304 
305     protected static final HashMap<String, CustomAppWidget> sCustomAppWidgets =
306         new HashMap<>();
307 
308     static {
309         if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) {
310             TestingUtils.addDummyWidget(sCustomAppWidgets);
311         }
312     }
313 
314     // Exiting spring loaded mode happens with a delay. This runnable object triggers the
315     // state transition. If another state transition happened during this delay,
316     // simply unregister this runnable.
317     private Runnable mExitSpringLoadedModeRunnable;
318 
319     @Thunk final Runnable mBuildLayersRunnable = new Runnable() {
320         public void run() {
321             if (mWorkspace != null) {
322                 mWorkspace.buildPageHardwareLayers();
323             }
324         }
325     };
326 
327     // Activity result which needs to be processed after workspace has loaded.
328     private ActivityResultInfo mPendingActivityResult;
329     /**
330      * Holds extra information required to handle a result from an external call, like
331      * {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)}
332      */
333     private PendingRequestArgs mPendingRequestArgs;
334 
335     private float mLastDispatchTouchEventX = 0.0f;
336 
337     public ViewGroupFocusHelper mFocusHandler;
338     private boolean mRotationEnabled = false;
339 
setOrientation()340     @Thunk void setOrientation() {
341         if (mRotationEnabled) {
342             unlockScreenOrientation(true);
343         } else {
344             setRequestedOrientation(
345                     ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
346         }
347     }
348 
349     private RotationPrefChangeHandler mRotationPrefChangeHandler;
350 
351     @Override
onCreate(Bundle savedInstanceState)352     protected void onCreate(Bundle savedInstanceState) {
353         if (DEBUG_STRICT_MODE) {
354             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
355                     .detectDiskReads()
356                     .detectDiskWrites()
357                     .detectNetwork()   // or .detectAll() for all detectable problems
358                     .penaltyLog()
359                     .build());
360             StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
361                     .detectLeakedSqlLiteObjects()
362                     .detectLeakedClosableObjects()
363                     .penaltyLog()
364                     .penaltyDeath()
365                     .build());
366         }
367         if (LauncherAppState.PROFILE_STARTUP) {
368             Trace.beginSection("Launcher-onCreate");
369         }
370 
371         if (mLauncherCallbacks != null) {
372             mLauncherCallbacks.preOnCreate();
373         }
374 
375         WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
376         wallpaperColorInfo.setOnThemeChangeListener(this);
377         overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
378 
379         super.onCreate(savedInstanceState);
380 
381         LauncherAppState app = LauncherAppState.getInstance(this);
382 
383         // Load configuration-specific DeviceProfile
384         mDeviceProfile = app.getInvariantDeviceProfile().getDeviceProfile(this);
385         if (isInMultiWindowModeCompat()) {
386             Display display = getWindowManager().getDefaultDisplay();
387             Point mwSize = new Point();
388             display.getSize(mwSize);
389             mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
390         }
391 
392         mOrientation = getResources().getConfiguration().orientation;
393         mSharedPrefs = Utilities.getPrefs(this);
394         mIsSafeModeEnabled = getPackageManager().isSafeMode();
395         mModel = app.setLauncher(this);
396         mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
397         mIconCache = app.getIconCache();
398         mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
399 
400         mDragController = new DragController(this);
401         mAllAppsController = new AllAppsTransitionController(this);
402         mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController);
403 
404         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
405 
406         mAppWidgetHost = new LauncherAppWidgetHost(this);
407         if (Utilities.ATLEAST_MARSHMALLOW) {
408             mAppWidgetHost.addProviderChangeListener(this);
409         }
410         mAppWidgetHost.startListening();
411 
412         // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
413         // this also ensures that any synchronous binding below doesn't re-trigger another
414         // LauncherModel load.
415         mPaused = false;
416 
417         mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
418 
419         setupViews();
420         mDeviceProfile.layout(this, false /* notifyListeners */);
421         loadExtractedColorsAndColorItems();
422 
423         mPopupDataProvider = new PopupDataProvider(this);
424 
425         ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
426                 .addAccessibilityStateChangeListener(this);
427 
428         lockAllApps();
429 
430         restoreState(savedInstanceState);
431 
432         if (LauncherAppState.PROFILE_STARTUP) {
433             Trace.endSection();
434         }
435 
436         // We only load the page synchronously if the user rotates (or triggers a
437         // configuration change) while launcher is in the foreground
438         int currentScreen = PagedView.INVALID_RESTORE_PAGE;
439         if (savedInstanceState != null) {
440             currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen);
441         }
442         if (!mModel.startLoader(currentScreen)) {
443             // If we are not binding synchronously, show a fade in animation when
444             // the first page bind completes.
445             mDragLayer.setAlpha(0);
446         } else {
447             // Pages bound synchronously.
448             mWorkspace.setCurrentPage(currentScreen);
449 
450             setWorkspaceLoading(true);
451         }
452 
453         // For handling default keys
454         mDefaultKeySsb = new SpannableStringBuilder();
455         Selection.setSelection(mDefaultKeySsb, 0);
456 
457         mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
458         // In case we are on a device with locked rotation, we should look at preferences to check
459         // if the user has specifically allowed rotation.
460         if (!mRotationEnabled) {
461             mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
462             mRotationPrefChangeHandler = new RotationPrefChangeHandler();
463             mSharedPrefs.registerOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
464         }
465 
466         if (PinItemDragListener.handleDragRequest(this, getIntent())) {
467             // Temporarily enable the rotation
468             mRotationEnabled = true;
469         }
470 
471         // On large interfaces, or on devices that a user has specifically enabled screen rotation,
472         // we want the screen to auto-rotate based on the current orientation
473         setOrientation();
474 
475         setContentView(mLauncherView);
476 
477         // Listen for broadcasts
478         IntentFilter filter = new IntentFilter();
479         filter.addAction(Intent.ACTION_SCREEN_OFF);
480         filter.addAction(Intent.ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
481         registerReceiver(mReceiver, filter);
482         mShouldFadeInScrim = true;
483 
484         getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
485                 Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
486 
487         if (mLauncherCallbacks != null) {
488             mLauncherCallbacks.onCreate(savedInstanceState);
489         }
490     }
491 
492     @Override
onThemeChanged()493     public void onThemeChanged() {
494         recreate();
495     }
496 
overrideTheme(boolean isDark, boolean supportsDarkText)497     protected void overrideTheme(boolean isDark, boolean supportsDarkText) {
498         if (isDark) {
499             setTheme(R.style.LauncherThemeDark);
500         } else if (supportsDarkText) {
501             setTheme(R.style.LauncherThemeDarkText);
502         }
503     }
504 
505     @Override
findViewById(int id)506     public <T extends View> T findViewById(int id) {
507         return mLauncherView.findViewById(id);
508     }
509 
510     @Override
onExtractedColorsChanged()511     public void onExtractedColorsChanged() {
512         loadExtractedColorsAndColorItems();
513         mExtractedColors.notifyChange();
514     }
515 
getExtractedColors()516     public ExtractedColors getExtractedColors() {
517         return mExtractedColors;
518     }
519 
520     @Override
onAppWidgetHostReset()521     public void onAppWidgetHostReset() {
522         if (mAppWidgetHost != null) {
523             mAppWidgetHost.startListening();
524         }
525     }
526 
loadExtractedColorsAndColorItems()527     private void loadExtractedColorsAndColorItems() {
528         // TODO: do this in pre-N as well, once the extraction part is complete.
529         if (Utilities.ATLEAST_NOUGAT) {
530             mExtractedColors.load(this);
531             mHotseat.updateColor(mExtractedColors, !mPaused);
532             mWorkspace.getPageIndicator().updateColor(mExtractedColors);
533         }
534     }
535 
536     private LauncherCallbacks mLauncherCallbacks;
537 
onPostCreate(Bundle savedInstanceState)538     public void onPostCreate(Bundle savedInstanceState) {
539         super.onPostCreate(savedInstanceState);
540         if (mLauncherCallbacks != null) {
541             mLauncherCallbacks.onPostCreate(savedInstanceState);
542         }
543     }
544 
onInsetsChanged(Rect insets)545     public void onInsetsChanged(Rect insets) {
546         mDeviceProfile.updateInsets(insets);
547         mDeviceProfile.layout(this, true /* notifyListeners */);
548     }
549 
550     /**
551      * Call this after onCreate to set or clear overlay.
552      */
setLauncherOverlay(LauncherOverlay overlay)553     public void setLauncherOverlay(LauncherOverlay overlay) {
554         if (overlay != null) {
555             overlay.setOverlayCallbacks(new LauncherOverlayCallbacksImpl());
556         }
557         mWorkspace.setLauncherOverlay(overlay);
558     }
559 
setLauncherCallbacks(LauncherCallbacks callbacks)560     public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
561         mLauncherCallbacks = callbacks;
562         return true;
563     }
564 
565     @Override
onLauncherProviderChanged()566     public void onLauncherProviderChanged() {
567         if (mLauncherCallbacks != null) {
568             mLauncherCallbacks.onLauncherProviderChange();
569         }
570     }
571 
572     /** To be overridden by subclasses to hint to Launcher that we have custom content */
hasCustomContentToLeft()573     protected boolean hasCustomContentToLeft() {
574         if (mLauncherCallbacks != null) {
575             return mLauncherCallbacks.hasCustomContentToLeft();
576         }
577         return false;
578     }
579 
580     /**
581      * To be overridden by subclasses to populate the custom content container and call
582      * {@link #addToCustomContentPage}. This will only be invoked if
583      * {@link #hasCustomContentToLeft()} is {@code true}.
584      */
populateCustomContentContainer()585     protected void populateCustomContentContainer() {
586         if (mLauncherCallbacks != null) {
587             mLauncherCallbacks.populateCustomContentContainer();
588         }
589     }
590 
591     /**
592      * Invoked by subclasses to signal a change to the {@link #addToCustomContentPage} value to
593      * ensure the custom content page is added or removed if necessary.
594      */
invalidateHasCustomContentToLeft()595     protected void invalidateHasCustomContentToLeft() {
596         if (mWorkspace == null || mWorkspace.getScreenOrder().isEmpty()) {
597             // Not bound yet, wait for bindScreens to be called.
598             return;
599         }
600 
601         if (!mWorkspace.hasCustomContent() && hasCustomContentToLeft()) {
602             // Create the custom content page and call the subclass to populate it.
603             mWorkspace.createCustomContentContainer();
604             populateCustomContentContainer();
605         } else if (mWorkspace.hasCustomContent() && !hasCustomContentToLeft()) {
606             mWorkspace.removeCustomContentPage();
607         }
608     }
609 
isDraggingEnabled()610     public boolean isDraggingEnabled() {
611         // We prevent dragging when we are loading the workspace as it is possible to pick up a view
612         // that is subsequently removed from the workspace in startBinding().
613         return !isWorkspaceLoading();
614     }
615 
getViewIdForItem(ItemInfo info)616     public int getViewIdForItem(ItemInfo info) {
617         // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
618         // This cast is safe as long as the id < 0x00FFFFFF
619         // Since we jail all the dynamically generated views, there should be no clashes
620         // with any other views.
621         return (int) info.id;
622     }
623 
getPopupDataProvider()624     public PopupDataProvider getPopupDataProvider() {
625         return mPopupDataProvider;
626     }
627 
628     /**
629      * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
630      * a configuration step, this allows the proper animations to run after other transitions.
631      */
completeAdd( int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info)632     private long completeAdd(
633             int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info) {
634         long screenId = info.screenId;
635         if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
636             // When the screen id represents an actual screen (as opposed to a rank) we make sure
637             // that the drop page actually exists.
638             screenId = ensurePendingDropLayoutExists(info.screenId);
639         }
640 
641         switch (requestCode) {
642             case REQUEST_CREATE_SHORTCUT:
643                 completeAddShortcut(intent, info.container, screenId, info.cellX, info.cellY, info);
644                 break;
645             case REQUEST_CREATE_APPWIDGET:
646                 completeAddAppWidget(appWidgetId, info, null, null);
647                 break;
648             case REQUEST_RECONFIGURE_APPWIDGET:
649                 completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
650                 break;
651             case REQUEST_BIND_PENDING_APPWIDGET: {
652                 int widgetId = appWidgetId;
653                 LauncherAppWidgetInfo widgetInfo =
654                         completeRestoreAppWidget(widgetId, LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
655                 if (widgetInfo != null) {
656                     // Since the view was just bound, also launch the configure activity if needed
657                     LauncherAppWidgetProviderInfo provider = mAppWidgetManager
658                             .getLauncherAppWidgetInfo(widgetId);
659                     if (provider != null) {
660                         new WidgetAddFlowHandler(provider)
661                                 .startConfigActivity(this, widgetInfo, REQUEST_RECONFIGURE_APPWIDGET);
662                     }
663                 }
664                 break;
665             }
666         }
667 
668         return screenId;
669     }
670 
handleActivityResult( final int requestCode, final int resultCode, final Intent data)671     private void handleActivityResult(
672             final int requestCode, final int resultCode, final Intent data) {
673         if (isWorkspaceLoading()) {
674             // process the result once the workspace has loaded.
675             mPendingActivityResult = new ActivityResultInfo(requestCode, resultCode, data);
676             return;
677         }
678         mPendingActivityResult = null;
679 
680         // Reset the startActivity waiting flag
681         final PendingRequestArgs requestArgs = mPendingRequestArgs;
682         setWaitingForResult(null);
683         if (requestArgs == null) {
684             return;
685         }
686 
687         final int pendingAddWidgetId = requestArgs.getWidgetId();
688 
689         Runnable exitSpringLoaded = new Runnable() {
690             @Override
691             public void run() {
692                 exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
693                         EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
694             }
695         };
696 
697         if (requestCode == REQUEST_BIND_APPWIDGET) {
698             // This is called only if the user did not previously have permissions to bind widgets
699             final int appWidgetId = data != null ?
700                     data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
701             if (resultCode == RESULT_CANCELED) {
702                 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId, requestArgs);
703                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
704                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
705             } else if (resultCode == RESULT_OK) {
706                 addAppWidgetImpl(
707                         appWidgetId, requestArgs, null,
708                         requestArgs.getWidgetHandler(),
709                         ON_ACTIVITY_RESULT_ANIMATION_DELAY);
710             }
711             return;
712         } else if (requestCode == REQUEST_PICK_WALLPAPER) {
713             if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) {
714                 // User could have free-scrolled between pages before picking a wallpaper; make sure
715                 // we move to the closest one now.
716                 mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen());
717                 showWorkspace(false);
718             }
719             return;
720         }
721 
722         boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
723                 requestCode == REQUEST_CREATE_APPWIDGET);
724 
725         // We have special handling for widgets
726         if (isWidgetDrop) {
727             final int appWidgetId;
728             int widgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
729                     : -1;
730             if (widgetId < 0) {
731                 appWidgetId = pendingAddWidgetId;
732             } else {
733                 appWidgetId = widgetId;
734             }
735 
736             final int result;
737             if (appWidgetId < 0 || resultCode == RESULT_CANCELED) {
738                 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " +
739                         "returned from the widget configuration activity.");
740                 result = RESULT_CANCELED;
741                 completeTwoStageWidgetDrop(result, appWidgetId, requestArgs);
742                 final Runnable onComplete = new Runnable() {
743                     @Override
744                     public void run() {
745                         exitSpringLoadedDragModeDelayed(false, 0, null);
746                     }
747                 };
748 
749                 mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
750                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
751             } else {
752                 if (requestArgs.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
753                     // When the screen id represents an actual screen (as opposed to a rank)
754                     // we make sure that the drop page actually exists.
755                     requestArgs.screenId =
756                             ensurePendingDropLayoutExists(requestArgs.screenId);
757                 }
758                 final CellLayout dropLayout =
759                         mWorkspace.getScreenWithId(requestArgs.screenId);
760 
761                 dropLayout.setDropPending(true);
762                 final Runnable onComplete = new Runnable() {
763                     @Override
764                     public void run() {
765                         completeTwoStageWidgetDrop(resultCode, appWidgetId, requestArgs);
766                         dropLayout.setDropPending(false);
767                     }
768                 };
769                 mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
770                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
771             }
772             return;
773         }
774 
775         if (requestCode == REQUEST_RECONFIGURE_APPWIDGET
776                 || requestCode == REQUEST_BIND_PENDING_APPWIDGET) {
777             if (resultCode == RESULT_OK) {
778                 // Update the widget view.
779                 completeAdd(requestCode, data, pendingAddWidgetId, requestArgs);
780             }
781             // Leave the widget in the pending state if the user canceled the configure.
782             return;
783         }
784 
785         if (requestCode == REQUEST_CREATE_SHORTCUT) {
786             // Handle custom shortcuts created using ACTION_CREATE_SHORTCUT.
787             if (resultCode == RESULT_OK && requestArgs.container != ItemInfo.NO_ID) {
788                 completeAdd(requestCode, data, -1, requestArgs);
789                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
790                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
791 
792             } else if (resultCode == RESULT_CANCELED) {
793                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
794                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
795             }
796         }
797         mDragLayer.clearAnimatedView();
798     }
799 
800     @Override
onActivityResult( final int requestCode, final int resultCode, final Intent data)801     public void onActivityResult(
802             final int requestCode, final int resultCode, final Intent data) {
803         handleActivityResult(requestCode, resultCode, data);
804         if (mLauncherCallbacks != null) {
805             mLauncherCallbacks.onActivityResult(requestCode, resultCode, data);
806         }
807     }
808 
809     /** @Override for MNC */
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)810     public void onRequestPermissionsResult(int requestCode, String[] permissions,
811             int[] grantResults) {
812         PendingRequestArgs pendingArgs = mPendingRequestArgs;
813         if (requestCode == REQUEST_PERMISSION_CALL_PHONE && pendingArgs != null
814                 && pendingArgs.getRequestCode() == REQUEST_PERMISSION_CALL_PHONE) {
815             setWaitingForResult(null);
816 
817             View v = null;
818             CellLayout layout = getCellLayout(pendingArgs.container, pendingArgs.screenId);
819             if (layout != null) {
820                 v = layout.getChildAt(pendingArgs.cellX, pendingArgs.cellY);
821             }
822             Intent intent = pendingArgs.getPendingIntent();
823 
824             if (grantResults.length > 0
825                     && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
826                 startActivitySafely(v, intent, null);
827             } else {
828                 // TODO: Show a snack bar with link to settings
829                 Toast.makeText(this, getString(R.string.msg_no_phone_permission,
830                         getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show();
831             }
832         }
833         if (mLauncherCallbacks != null) {
834             mLauncherCallbacks.onRequestPermissionsResult(requestCode, permissions,
835                     grantResults);
836         }
837     }
838 
839     /**
840      * Check to see if a given screen id exists. If not, create it at the end, return the new id.
841      *
842      * @param screenId the screen id to check
843      * @return the new screen, or screenId if it exists
844      */
ensurePendingDropLayoutExists(long screenId)845     private long ensurePendingDropLayoutExists(long screenId) {
846         CellLayout dropLayout = mWorkspace.getScreenWithId(screenId);
847         if (dropLayout == null) {
848             // it's possible that the add screen was removed because it was
849             // empty and a re-bind occurred
850             mWorkspace.addExtraEmptyScreen();
851             return mWorkspace.commitExtraEmptyScreen();
852         } else {
853             return screenId;
854         }
855     }
856 
completeTwoStageWidgetDrop( final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs)857     @Thunk void completeTwoStageWidgetDrop(
858             final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) {
859         CellLayout cellLayout = mWorkspace.getScreenWithId(requestArgs.screenId);
860         Runnable onCompleteRunnable = null;
861         int animationType = 0;
862 
863         AppWidgetHostView boundWidget = null;
864         if (resultCode == RESULT_OK) {
865             animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
866             final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
867                     requestArgs.getWidgetHandler().getProviderInfo(this));
868             boundWidget = layout;
869             onCompleteRunnable = new Runnable() {
870                 @Override
871                 public void run() {
872                     completeAddAppWidget(appWidgetId, requestArgs, layout, null);
873                     exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
874                             EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
875                 }
876             };
877         } else if (resultCode == RESULT_CANCELED) {
878             mAppWidgetHost.deleteAppWidgetId(appWidgetId);
879             animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
880         }
881         if (mDragLayer.getAnimatedView() != null) {
882             mWorkspace.animateWidgetDrop(requestArgs, cellLayout,
883                     (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
884                     animationType, boundWidget, true);
885         } else if (onCompleteRunnable != null) {
886             // The animated view may be null in the case of a rotation during widget configuration
887             onCompleteRunnable.run();
888         }
889     }
890 
891     @Override
onStop()892     protected void onStop() {
893         super.onStop();
894         FirstFrameAnimatorHelper.setIsVisible(false);
895 
896         if (mLauncherCallbacks != null) {
897             mLauncherCallbacks.onStop();
898         }
899 
900         if (Utilities.ATLEAST_NOUGAT_MR1) {
901             mAppWidgetHost.stopListening();
902         }
903 
904         NotificationListener.removeNotificationsChangedListener();
905     }
906 
907     @Override
onStart()908     protected void onStart() {
909         super.onStart();
910         FirstFrameAnimatorHelper.setIsVisible(true);
911 
912         if (mLauncherCallbacks != null) {
913             mLauncherCallbacks.onStart();
914         }
915 
916         if (Utilities.ATLEAST_NOUGAT_MR1) {
917             mAppWidgetHost.startListening();
918         }
919 
920         if (!isWorkspaceLoading()) {
921             NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
922         }
923 
924         if (mShouldFadeInScrim && mDragLayer.getBackground() != null) {
925             if (mScrimAnimator != null) {
926                 mScrimAnimator.cancel();
927             }
928             mDragLayer.getBackground().setAlpha(0);
929             mScrimAnimator = ObjectAnimator.ofInt(mDragLayer.getBackground(),
930                     LauncherAnimUtils.DRAWABLE_ALPHA, 0, 255);
931             mScrimAnimator.addListener(new AnimatorListenerAdapter() {
932                 @Override
933                 public void onAnimationEnd(Animator animation) {
934                     mScrimAnimator = null;
935                 }
936             });
937             mScrimAnimator.setDuration(600);
938             mScrimAnimator.setStartDelay(getWindow().getTransitionBackgroundFadeDuration());
939             mScrimAnimator.start();
940         }
941         mShouldFadeInScrim = false;
942     }
943 
944     @Override
onResume()945     protected void onResume() {
946         long startTime = 0;
947         if (DEBUG_RESUME_TIME) {
948             startTime = System.currentTimeMillis();
949             Log.v(TAG, "Launcher.onResume()");
950         }
951 
952         if (mLauncherCallbacks != null) {
953             mLauncherCallbacks.preOnResume();
954         }
955 
956         super.onResume();
957         getUserEventDispatcher().resetElapsedSessionMillis();
958 
959         // Restore the previous launcher state
960         if (mOnResumeState == State.WORKSPACE) {
961             showWorkspace(false);
962         } else if (mOnResumeState == State.APPS) {
963             boolean launchedFromApp = (mWaitingForResume != null);
964             // Don't update the predicted apps if the user is returning to launcher in the apps
965             // view after launching an app, as they may be depending on the UI to be static to
966             // switch to another app, otherwise, if it was
967             showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */);
968         } else if (mOnResumeState == State.WIDGETS) {
969             showWidgetsView(false, false);
970         }
971         if (mOnResumeState != State.APPS) {
972             tryAndUpdatePredictedApps();
973         }
974         mOnResumeState = State.NONE;
975 
976         mPaused = false;
977         if (mOnResumeNeedsLoad) {
978             setWorkspaceLoading(true);
979             mModel.startLoader(getCurrentWorkspaceScreen());
980             mOnResumeNeedsLoad = false;
981         }
982         if (mBindOnResumeCallbacks.size() > 0) {
983             // We might have postponed some bind calls until onResume (see waitUntilResume) --
984             // execute them here
985             long startTimeCallbacks = 0;
986             if (DEBUG_RESUME_TIME) {
987                 startTimeCallbacks = System.currentTimeMillis();
988             }
989 
990             for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
991                 mBindOnResumeCallbacks.get(i).run();
992             }
993             mBindOnResumeCallbacks.clear();
994             if (DEBUG_RESUME_TIME) {
995                 Log.d(TAG, "Time spent processing callbacks in onResume: " +
996                     (System.currentTimeMillis() - startTimeCallbacks));
997             }
998         }
999         if (mOnResumeCallbacks.size() > 0) {
1000             for (int i = 0; i < mOnResumeCallbacks.size(); i++) {
1001                 mOnResumeCallbacks.get(i).run();
1002             }
1003             mOnResumeCallbacks.clear();
1004         }
1005 
1006         // Reset the pressed state of icons that were locked in the press state while activities
1007         // were launching
1008         if (mWaitingForResume != null) {
1009             // Resets the previous workspace icon press state
1010             mWaitingForResume.setStayPressed(false);
1011         }
1012 
1013         // It is possible that widgets can receive updates while launcher is not in the foreground.
1014         // Consequently, the widgets will be inflated in the orientation of the foreground activity
1015         // (framework issue). On resuming, we ensure that any widgets are inflated for the current
1016         // orientation.
1017         if (!isWorkspaceLoading()) {
1018             getWorkspace().reinflateWidgetsIfNecessary();
1019         }
1020 
1021         if (DEBUG_RESUME_TIME) {
1022             Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime));
1023         }
1024 
1025         // We want to suppress callbacks about CustomContent being shown if we have just received
1026         // onNewIntent while the user was present within launcher. In that case, we post a call
1027         // to move the user to the main screen (which will occur after onResume). We don't want to
1028         // have onHide (from onPause), then onShow, then onHide again, which we get if we don't
1029         // suppress here.
1030         if (mWorkspace.getCustomContentCallbacks() != null
1031                 && !mMoveToDefaultScreenFromNewIntent) {
1032             // If we are resuming and the custom content is the current page, we call onShow().
1033             // It is also possible that onShow will instead be called slightly after first layout
1034             // if PagedView#setRestorePage was set to the custom content page in onCreate().
1035             if (mWorkspace.isOnOrMovingToCustomContent()) {
1036                 mWorkspace.getCustomContentCallbacks().onShow(true);
1037             }
1038         }
1039         mMoveToDefaultScreenFromNewIntent = false;
1040         updateInteraction(Workspace.State.NORMAL, mWorkspace.getState());
1041         mWorkspace.onResume();
1042 
1043         // Process any items that were added while Launcher was away.
1044         InstallShortcutReceiver.disableAndFlushInstallQueue(
1045                 InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
1046 
1047         // Refresh shortcuts if the permission changed.
1048         mModel.refreshShortcutsIfRequired();
1049 
1050         if (shouldShowDiscoveryBounce()) {
1051             mAllAppsController.showDiscoveryBounce();
1052         }
1053         if (mLauncherCallbacks != null) {
1054             mLauncherCallbacks.onResume();
1055         }
1056 
1057     }
1058 
1059     @Override
onPause()1060     protected void onPause() {
1061         // Ensure that items added to Launcher are queued until Launcher returns
1062         InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
1063 
1064         super.onPause();
1065         mPaused = true;
1066         mDragController.cancelDrag();
1067         mDragController.resetLastGestureUpTime();
1068 
1069         // We call onHide() aggressively. The custom content callbacks should be able to
1070         // debounce excess onHide calls.
1071         if (mWorkspace.getCustomContentCallbacks() != null) {
1072             mWorkspace.getCustomContentCallbacks().onHide();
1073         }
1074 
1075         if (mLauncherCallbacks != null) {
1076             mLauncherCallbacks.onPause();
1077         }
1078     }
1079 
1080     public interface CustomContentCallbacks {
1081         // Custom content is completely shown. {@code fromResume} indicates whether this was caused
1082         // by a onResume or by scrolling otherwise.
onShow(boolean fromResume)1083         void onShow(boolean fromResume);
1084 
1085         // Custom content is completely hidden
onHide()1086         void onHide();
1087 
1088         // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
onScrollProgressChanged(float progress)1089         void onScrollProgressChanged(float progress);
1090 
1091         // Indicates whether the user is allowed to scroll away from the custom content.
isScrollingAllowed()1092         boolean isScrollingAllowed();
1093     }
1094 
1095     public interface LauncherOverlay {
1096 
1097         /**
1098          * Touch interaction leading to overscroll has begun
1099          */
onScrollInteractionBegin()1100         void onScrollInteractionBegin();
1101 
1102         /**
1103          * Touch interaction related to overscroll has ended
1104          */
onScrollInteractionEnd()1105         void onScrollInteractionEnd();
1106 
1107         /**
1108          * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
1109          * screen (or in the case of RTL, the rightmost screen).
1110          */
onScrollChange(float progress, boolean rtl)1111         void onScrollChange(float progress, boolean rtl);
1112 
1113         /**
1114          * Called when the launcher is ready to use the overlay
1115          * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
1116          */
setOverlayCallbacks(LauncherOverlayCallbacks callbacks)1117         void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
1118     }
1119 
1120     public interface LauncherOverlayCallbacks {
onScrollChanged(float progress)1121         void onScrollChanged(float progress);
1122     }
1123 
1124     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
1125 
onScrollChanged(float progress)1126         public void onScrollChanged(float progress) {
1127             if (mWorkspace != null) {
1128                 mWorkspace.onOverlayScrollChanged(progress);
1129             }
1130         }
1131     }
1132 
hasSettings()1133     protected boolean hasSettings() {
1134         if (mLauncherCallbacks != null) {
1135             return mLauncherCallbacks.hasSettings();
1136         } else {
1137             // On O and above we there is always some setting present settings (add icon to
1138             // home screen or icon badging). On earlier APIs we will have the allow rotation
1139             // setting, on devices with a locked orientation,
1140             return Utilities.ATLEAST_OREO || !getResources().getBoolean(R.bool.allow_rotation);
1141         }
1142     }
1143 
addToCustomContentPage(View customContent, CustomContentCallbacks callbacks, String description)1144     public void addToCustomContentPage(View customContent,
1145             CustomContentCallbacks callbacks, String description) {
1146         mWorkspace.addToCustomContentPage(customContent, callbacks, description);
1147     }
1148 
1149     // The custom content needs to offset its content to account for the QSB
getTopOffsetForCustomContent()1150     public int getTopOffsetForCustomContent() {
1151         return mWorkspace.getPaddingTop();
1152     }
1153 
1154     @Override
onRetainNonConfigurationInstance()1155     public Object onRetainNonConfigurationInstance() {
1156         // Flag the loader to stop early before switching
1157         if (mModel.isCurrentCallbacks(this)) {
1158             mModel.stopLoader();
1159         }
1160         //TODO(hyunyoungs): stop the widgets loader when there is a rotation.
1161 
1162         return Boolean.TRUE;
1163     }
1164 
1165     // We can't hide the IME if it was forced open.  So don't bother
1166     @Override
onWindowFocusChanged(boolean hasFocus)1167     public void onWindowFocusChanged(boolean hasFocus) {
1168         super.onWindowFocusChanged(hasFocus);
1169         mHasFocus = hasFocus;
1170 
1171         if (mLauncherCallbacks != null) {
1172             mLauncherCallbacks.onWindowFocusChanged(hasFocus);
1173         }
1174     }
1175 
acceptFilter()1176     private boolean acceptFilter() {
1177         final InputMethodManager inputManager = (InputMethodManager)
1178                 getSystemService(Context.INPUT_METHOD_SERVICE);
1179         return !inputManager.isFullscreenMode();
1180     }
1181 
1182     @Override
onKeyDown(int keyCode, KeyEvent event)1183     public boolean onKeyDown(int keyCode, KeyEvent event) {
1184         final int uniChar = event.getUnicodeChar();
1185         final boolean handled = super.onKeyDown(keyCode, event);
1186         final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar);
1187         if (!handled && acceptFilter() && isKeyNotWhitespace) {
1188             boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
1189                     keyCode, event);
1190             if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
1191                 // something usable has been typed - start a search
1192                 // the typed text will be retrieved and cleared by
1193                 // showSearchDialog()
1194                 // If there are multiple keystrokes before the search dialog takes focus,
1195                 // onSearchRequested() will be called for every keystroke,
1196                 // but it is idempotent, so it's fine.
1197                 return onSearchRequested();
1198             }
1199         }
1200 
1201         // Eat the long press event so the keyboard doesn't come up.
1202         if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
1203             return true;
1204         }
1205 
1206         return handled;
1207     }
1208 
1209     @Override
onKeyUp(int keyCode, KeyEvent event)1210     public boolean onKeyUp(int keyCode, KeyEvent event) {
1211         if (keyCode == KeyEvent.KEYCODE_MENU) {
1212             // Ignore the menu key if we are currently dragging or are on the custom content screen
1213             if (!isOnCustomContent() && !mDragController.isDragging()) {
1214                 // Close any open floating view
1215                 AbstractFloatingView.closeAllOpenViews(this);
1216 
1217                 // Stop resizing any widgets
1218                 mWorkspace.exitWidgetResizeMode();
1219 
1220                 // Show the overview mode if we are on the workspace
1221                 if (mState == State.WORKSPACE && !mWorkspace.isInOverviewMode() &&
1222                         !mWorkspace.isSwitchingState()) {
1223                     mOverviewPanel.requestFocus();
1224                     showOverviewMode(true, true /* requestButtonFocus */);
1225                 }
1226             }
1227             return true;
1228         }
1229         return super.onKeyUp(keyCode, event);
1230     }
1231 
getTypedText()1232     private String getTypedText() {
1233         return mDefaultKeySsb.toString();
1234     }
1235 
1236     @Override
clearTypedText()1237     public void clearTypedText() {
1238         mDefaultKeySsb.clear();
1239         mDefaultKeySsb.clearSpans();
1240         Selection.setSelection(mDefaultKeySsb, 0);
1241     }
1242 
1243     /**
1244      * Restores the previous state, if it exists.
1245      *
1246      * @param savedState The previous state.
1247      */
restoreState(Bundle savedState)1248     private void restoreState(Bundle savedState) {
1249         if (savedState == null) {
1250             return;
1251         }
1252 
1253         int stateOrdinal = savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal());
1254         State[] stateValues = State.values();
1255         State state = (stateOrdinal >= 0 && stateOrdinal < stateValues.length)
1256                 ? stateValues[stateOrdinal] : State.WORKSPACE;
1257         if (state == State.APPS || state == State.WIDGETS) {
1258             mOnResumeState = state;
1259         }
1260 
1261         PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS);
1262         if (requestArgs != null) {
1263             setWaitingForResult(requestArgs);
1264         }
1265 
1266         mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT);
1267     }
1268 
1269     /**
1270      * Finds all the views we need and configure them properly.
1271      */
setupViews()1272     private void setupViews() {
1273         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
1274         mFocusHandler = mDragLayer.getFocusIndicatorHelper();
1275         mWorkspace = mDragLayer.findViewById(R.id.workspace);
1276         mWorkspace.initParentViews(mDragLayer);
1277 
1278         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
1279                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
1280                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
1281 
1282         // Setup the drag layer
1283         mDragLayer.setup(this, mDragController, mAllAppsController);
1284 
1285         // Setup the hotseat
1286         mHotseat = (Hotseat) findViewById(R.id.hotseat);
1287         if (mHotseat != null) {
1288             mHotseat.setOnLongClickListener(this);
1289         }
1290 
1291         // Setup the overview panel
1292         setupOverviewPanel();
1293 
1294         // Setup the workspace
1295         mWorkspace.setHapticFeedbackEnabled(false);
1296         mWorkspace.setOnLongClickListener(this);
1297         mWorkspace.setup(mDragController);
1298         // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
1299         // default state, otherwise we will update to the wrong offsets in RTL
1300         mWorkspace.lockWallpaperToDefaultPage();
1301         mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */);
1302         mDragController.addDragListener(mWorkspace);
1303 
1304         // Get the search/delete/uninstall bar
1305         mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
1306 
1307         // Setup Apps and Widgets
1308         mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
1309         mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
1310 
1311         // Setup the drag controller (drop targets have to be added in reverse order in priority)
1312         mDragController.setMoveTarget(mWorkspace);
1313         mDragController.addDropTarget(mWorkspace);
1314         mDropTargetBar.setup(mDragController);
1315 
1316         mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
1317 
1318         if (TestingUtils.MEMORY_DUMP_ENABLED) {
1319             TestingUtils.addWeightWatcher(this);
1320         }
1321     }
1322 
setupOverviewPanel()1323     private void setupOverviewPanel() {
1324         mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
1325 
1326         // Bind wallpaper button actions
1327         View wallpaperButton = findViewById(R.id.wallpaper_button);
1328         new OverviewButtonClickListener(ControlType.WALLPAPER_BUTTON) {
1329             @Override
1330             public void handleViewClick(View view) {
1331                 onClickWallpaperPicker(view);
1332             }
1333         }.attachTo(wallpaperButton);
1334 
1335         // Bind widget button actions
1336         mWidgetsButton = findViewById(R.id.widget_button);
1337         new OverviewButtonClickListener(ControlType.WIDGETS_BUTTON) {
1338             @Override
1339             public void handleViewClick(View view) {
1340                 onClickAddWidgetButton(view);
1341             }
1342         }.attachTo(mWidgetsButton);
1343 
1344         // Bind settings actions
1345         View settingsButton = findViewById(R.id.settings_button);
1346         boolean hasSettings = hasSettings();
1347         if (hasSettings) {
1348             new OverviewButtonClickListener(ControlType.SETTINGS_BUTTON) {
1349                 @Override
1350                 public void handleViewClick(View view) {
1351                     onClickSettingsButton(view);
1352                 }
1353             }.attachTo(settingsButton);
1354         } else {
1355             settingsButton.setVisibility(View.GONE);
1356         }
1357 
1358         mOverviewPanel.setAlpha(0f);
1359     }
1360 
1361     /**
1362      * Sets the all apps button. This method is called from {@link Hotseat}.
1363      * TODO: Get rid of this.
1364      */
setAllAppsButton(View allAppsButton)1365     public void setAllAppsButton(View allAppsButton) {
1366         mAllAppsButton = allAppsButton;
1367     }
1368 
getStartViewForAllAppsRevealAnimation()1369     public View getStartViewForAllAppsRevealAnimation() {
1370         return FeatureFlags.NO_ALL_APPS_ICON ? mWorkspace.getPageIndicator() : mAllAppsButton;
1371     }
1372 
getWidgetsButton()1373     public View getWidgetsButton() {
1374         return mWidgetsButton;
1375     }
1376 
1377     /**
1378      * Creates a view representing a shortcut.
1379      *
1380      * @param info The data structure describing the shortcut.
1381      */
createShortcut(ShortcutInfo info)1382     View createShortcut(ShortcutInfo info) {
1383         return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
1384     }
1385 
1386     /**
1387      * Creates a view representing a shortcut inflated from the specified resource.
1388      *
1389      * @param parent The group the shortcut belongs to.
1390      * @param info The data structure describing the shortcut.
1391      *
1392      * @return A View inflated from layoutResId.
1393      */
createShortcut(ViewGroup parent, ShortcutInfo info)1394     public View createShortcut(ViewGroup parent, ShortcutInfo info) {
1395         BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
1396                 .inflate(R.layout.app_icon, parent, false);
1397         favorite.applyFromShortcutInfo(info);
1398         favorite.setOnClickListener(this);
1399         favorite.setOnFocusChangeListener(mFocusHandler);
1400         return favorite;
1401     }
1402 
1403     /**
1404      * Add a shortcut to the workspace or to a Folder.
1405      *
1406      * @param data The intent describing the shortcut.
1407      */
completeAddShortcut(Intent data, long container, long screenId, int cellX, int cellY, PendingRequestArgs args)1408     private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
1409             int cellY, PendingRequestArgs args) {
1410         if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
1411                 || args.getPendingIntent().getComponent() == null) {
1412             return;
1413         }
1414 
1415         int[] cellXY = mTmpAddItemCellCoordinates;
1416         CellLayout layout = getCellLayout(container, screenId);
1417 
1418         ShortcutInfo info = null;
1419         if (Utilities.ATLEAST_OREO) {
1420             info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
1421                     this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
1422         }
1423 
1424         if (info == null) {
1425             // Legacy shortcuts are only supported for primary profile.
1426             info = Process.myUserHandle().equals(args.user)
1427                     ? InstallShortcutReceiver.fromShortcutIntent(this, data) : null;
1428 
1429             if (info == null) {
1430                 Log.e(TAG, "Unable to parse a valid custom shortcut result");
1431                 return;
1432             } else if (!new PackageManagerHelper(this).hasPermissionForActivity(
1433                     info.intent, args.getPendingIntent().getComponent().getPackageName())) {
1434                 // The app is trying to add a shortcut without sufficient permissions
1435                 Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0));
1436                 return;
1437             }
1438         }
1439 
1440         if (container < 0) {
1441             // Adding a shortcut to the Workspace.
1442             final View view = createShortcut(info);
1443             boolean foundCellSpan = false;
1444             // First we check if we already know the exact location where we want to add this item.
1445             if (cellX >= 0 && cellY >= 0) {
1446                 cellXY[0] = cellX;
1447                 cellXY[1] = cellY;
1448                 foundCellSpan = true;
1449 
1450                 // If appropriate, either create a folder or add to an existing folder
1451                 if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
1452                         true, null, null)) {
1453                     return;
1454                 }
1455                 DragObject dragObject = new DragObject();
1456                 dragObject.dragInfo = info;
1457                 if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
1458                         true)) {
1459                     return;
1460                 }
1461             } else {
1462                 foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
1463             }
1464 
1465             if (!foundCellSpan) {
1466                 mWorkspace.onNoCellFound(layout);
1467                 return;
1468             }
1469 
1470             getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
1471             mWorkspace.addInScreen(view, info);
1472         } else {
1473             // Adding a shortcut to a Folder.
1474             FolderIcon folderIcon = findFolderIcon(container);
1475             if (folderIcon != null) {
1476                 FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
1477                 folderInfo.add(info, args.rank, false);
1478             } else {
1479                 Log.e(TAG, "Could not find folder with id " + container + " to add shortcut.");
1480             }
1481         }
1482     }
1483 
findFolderIcon(final long folderIconId)1484     public FolderIcon findFolderIcon(final long folderIconId) {
1485         return (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
1486             @Override
1487             public boolean evaluate(ItemInfo info, View view) {
1488                 return info != null && info.id == folderIconId;
1489             }
1490         });
1491     }
1492 
1493     /**
1494      * Add a widget to the workspace.
1495      *
1496      * @param appWidgetId The app widget id
1497      */
1498     @Thunk void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
1499             AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
1500 
1501         if (appWidgetInfo == null) {
1502             appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
1503         }
1504 
1505         if (appWidgetInfo.isCustomWidget) {
1506             appWidgetId = LauncherAppWidgetInfo.CUSTOM_WIDGET_ID;
1507         }
1508 
1509         LauncherAppWidgetInfo launcherInfo;
1510         launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider);
1511         launcherInfo.spanX = itemInfo.spanX;
1512         launcherInfo.spanY = itemInfo.spanY;
1513         launcherInfo.minSpanX = itemInfo.minSpanX;
1514         launcherInfo.minSpanY = itemInfo.minSpanY;
1515         launcherInfo.user = appWidgetInfo.getUser();
1516 
1517         getModelWriter().addItemToDatabase(launcherInfo,
1518                 itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
1519 
1520         if (hostView == null) {
1521             // Perform actual inflation because we're live
1522             hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
1523         }
1524         hostView.setVisibility(View.VISIBLE);
1525         prepareAppWidget(hostView, launcherInfo);
1526         mWorkspace.addInScreen(hostView, launcherInfo);
1527     }
1528 
1529     private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) {
1530         hostView.setTag(item);
1531         item.onBindAppWidget(this, hostView);
1532         hostView.setFocusable(true);
1533         hostView.setOnFocusChangeListener(mFocusHandler);
1534     }
1535 
1536     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1537         @Override
1538         public void onReceive(Context context, Intent intent) {
1539             final String action = intent.getAction();
1540             if (Intent.ACTION_SCREEN_OFF.equals(action)) {
1541                 mDragLayer.clearResizeFrame();
1542 
1543                 // Reset AllApps to its initial state only if we are not in the middle of
1544                 // processing a multi-step drop
1545                 if (mAppsView != null && mWidgetsView != null && mPendingRequestArgs == null) {
1546                     if (!showWorkspace(false)) {
1547                         // If we are already on the workspace, then manually reset all apps
1548                         mAppsView.reset();
1549                     }
1550                 }
1551                 mShouldFadeInScrim = true;
1552             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
1553                 // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
1554                 // the user unlocked and the Launcher is not in the foreground.
1555                 mShouldFadeInScrim = false;
1556             }
1557         }
1558     };
1559 
1560     public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
1561         Runnable r = new Runnable() {
1562             @Override
1563             public void run() {
1564                 mWorkspace.updateIconBadges(updatedBadges);
1565                 mAppsView.updateIconBadges(updatedBadges);
1566 
1567                 PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
1568                 if (popup != null) {
1569                     popup.updateNotificationHeader(updatedBadges);
1570                 }
1571             }
1572         };
1573         if (!waitUntilResume(r)) {
1574             r.run();
1575         }
1576     }
1577 
1578     @Override
1579     public void onAttachedToWindow() {
1580         super.onAttachedToWindow();
1581 
1582         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
1583         if (mLauncherCallbacks != null) {
1584             mLauncherCallbacks.onAttachedToWindow();
1585         }
1586     }
1587 
1588     @Override
1589     public void onDetachedFromWindow() {
1590         super.onDetachedFromWindow();
1591 
1592         if (mLauncherCallbacks != null) {
1593             mLauncherCallbacks.onDetachedFromWindow();
1594         }
1595     }
1596 
1597     public void onWindowVisibilityChanged(int visibility) {
1598         // The following code used to be in onResume, but it turns out onResume is called when
1599         // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
1600         // is a more appropriate event to handle
1601         if (visibility == View.VISIBLE) {
1602             if (!mWorkspaceLoading) {
1603                 final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
1604                 // We want to let Launcher draw itself at least once before we force it to build
1605                 // layers on all the workspace pages, so that transitioning to Launcher from other
1606                 // apps is nice and speedy.
1607                 observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
1608                     private boolean mStarted = false;
1609                     public void onDraw() {
1610                         if (mStarted) return;
1611                         mStarted = true;
1612                         // We delay the layer building a bit in order to give
1613                         // other message processing a time to run.  In particular
1614                         // this avoids a delay in hiding the IME if it was
1615                         // currently shown, because doing that may involve
1616                         // some communication back with the app.
1617                         mWorkspace.postDelayed(mBuildLayersRunnable, 500);
1618                         final ViewTreeObserver.OnDrawListener listener = this;
1619                         mWorkspace.post(new Runnable() {
1620                             public void run() {
1621                                 if (mWorkspace != null &&
1622                                         mWorkspace.getViewTreeObserver() != null) {
1623                                     mWorkspace.getViewTreeObserver().
1624                                             removeOnDrawListener(listener);
1625                                 }
1626                             }
1627                         });
1628                     }
1629                 });
1630             }
1631             clearTypedText();
1632         }
1633     }
1634 
1635     public DragLayer getDragLayer() {
1636         return mDragLayer;
1637     }
1638 
1639     public AllAppsContainerView getAppsView() {
1640         return mAppsView;
1641     }
1642 
1643     public WidgetsContainerView getWidgetsView() {
1644         return mWidgetsView;
1645     }
1646 
1647     public Workspace getWorkspace() {
1648         return mWorkspace;
1649     }
1650 
1651     public Hotseat getHotseat() {
1652         return mHotseat;
1653     }
1654 
1655     public ViewGroup getOverviewPanel() {
1656         return mOverviewPanel;
1657     }
1658 
1659     public DropTargetBar getDropTargetBar() {
1660         return mDropTargetBar;
1661     }
1662 
1663     public LauncherAppWidgetHost getAppWidgetHost() {
1664         return mAppWidgetHost;
1665     }
1666 
1667     public LauncherModel getModel() {
1668         return mModel;
1669     }
1670 
1671     public ModelWriter getModelWriter() {
1672         return mModelWriter;
1673     }
1674 
1675     public SharedPreferences getSharedPrefs() {
1676         return mSharedPrefs;
1677     }
1678 
1679     public int getOrientation() { return mOrientation; }
1680 
1681     @Override
1682     protected void onNewIntent(Intent intent) {
1683         long startTime = 0;
1684         if (DEBUG_RESUME_TIME) {
1685             startTime = System.currentTimeMillis();
1686         }
1687         super.onNewIntent(intent);
1688 
1689         boolean alreadyOnHome = mHasFocus && ((intent.getFlags() &
1690                 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
1691                 != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
1692 
1693         // Check this condition before handling isActionMain, as this will get reset.
1694         boolean shouldMoveToDefaultScreen = alreadyOnHome &&
1695                 mState == State.WORKSPACE && AbstractFloatingView.getTopOpenView(this) == null;
1696 
1697         boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
1698         if (isActionMain) {
1699             if (mWorkspace == null) {
1700                 // Can be cases where mWorkspace is null, this prevents a NPE
1701                 return;
1702             }
1703 
1704             // Note: There should be at most one log per method call. This is enforced implicitly
1705             // by using if-else statements.
1706             UserEventDispatcher ued = getUserEventDispatcher();
1707 
1708             // TODO: Log this case.
1709             mWorkspace.exitWidgetResizeMode();
1710 
1711             AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(this);
1712             if (topOpenView instanceof PopupContainerWithArrow) {
1713                 ued.logActionCommand(Action.Command.HOME_INTENT,
1714                         topOpenView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
1715             } else if (topOpenView instanceof Folder) {
1716                 ued.logActionCommand(Action.Command.HOME_INTENT,
1717                             ((Folder) topOpenView).getFolderIcon(), ContainerType.FOLDER);
1718             } else if (alreadyOnHome) {
1719                 ued.logActionCommand(Action.Command.HOME_INTENT,
1720                         mWorkspace.getState().containerType, mWorkspace.getCurrentPage());
1721             }
1722 
1723             // In all these cases, only animate if we're already on home
1724             AbstractFloatingView.closeAllOpenViews(this, alreadyOnHome);
1725             exitSpringLoadedDragMode();
1726 
1727             // If we are already on home, then just animate back to the workspace,
1728             // otherwise, just wait until onResume to set the state back to Workspace
1729             if (alreadyOnHome) {
1730                 showWorkspace(true);
1731             } else {
1732                 mOnResumeState = State.WORKSPACE;
1733             }
1734 
1735             final View v = getWindow().peekDecorView();
1736             if (v != null && v.getWindowToken() != null) {
1737                 InputMethodManager imm = (InputMethodManager) getSystemService(
1738                         INPUT_METHOD_SERVICE);
1739                 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
1740             }
1741 
1742             // Reset the apps view
1743             if (!alreadyOnHome && mAppsView != null) {
1744                 mAppsView.reset();
1745             }
1746 
1747             // Reset the widgets view
1748             if (!alreadyOnHome && mWidgetsView != null) {
1749                 mWidgetsView.scrollToTop();
1750             }
1751 
1752             if (mLauncherCallbacks != null) {
1753                 mLauncherCallbacks.onHomeIntent();
1754             }
1755         }
1756         PinItemDragListener.handleDragRequest(this, intent);
1757 
1758         if (mLauncherCallbacks != null) {
1759             mLauncherCallbacks.onNewIntent(intent);
1760         }
1761 
1762         // Defer moving to the default screen until after we callback to the LauncherCallbacks
1763         // as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
1764         // animation.
1765         if (isActionMain) {
1766             boolean callbackAllowsMoveToDefaultScreen =
1767                 mLauncherCallbacks == null || mLauncherCallbacks
1768                     .shouldMoveToDefaultScreenOnHomeIntent();
1769             if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()
1770                     && callbackAllowsMoveToDefaultScreen) {
1771 
1772                 // We use this flag to suppress noisy callbacks above custom content state
1773                 // from onResume.
1774                 mMoveToDefaultScreenFromNewIntent = true;
1775                 mWorkspace.post(new Runnable() {
1776                     @Override
1777                     public void run() {
1778                         if (mWorkspace != null) {
1779                             mWorkspace.moveToDefaultScreen(true);
1780                         }
1781                     }
1782                 });
1783             }
1784         }
1785 
1786         if (DEBUG_RESUME_TIME) {
1787             Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime));
1788         }
1789     }
1790 
1791     @Override
1792     public void onRestoreInstanceState(Bundle state) {
1793         super.onRestoreInstanceState(state);
1794         for (int page: mSynchronouslyBoundPages) {
1795             mWorkspace.restoreInstanceStateForChild(page);
1796         }
1797     }
1798 
1799     @Override
1800     protected void onSaveInstanceState(Bundle outState) {
1801         if (mWorkspace.getChildCount() > 0) {
1802             outState.putInt(RUNTIME_STATE_CURRENT_SCREEN,
1803                     mWorkspace.getCurrentPageOffsetFromCustomContent());
1804 
1805         }
1806         super.onSaveInstanceState(outState);
1807 
1808         outState.putInt(RUNTIME_STATE, mState.ordinal());
1809         // We close any open folders and shortcut containers since they will not be re-opened,
1810         // and we need to make sure this state is reflected.
1811         AbstractFloatingView.closeAllOpenViews(this, false);
1812 
1813         if (mPendingRequestArgs != null) {
1814             outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
1815         }
1816         if (mPendingActivityResult != null) {
1817             outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult);
1818         }
1819 
1820         if (mLauncherCallbacks != null) {
1821             mLauncherCallbacks.onSaveInstanceState(outState);
1822         }
1823     }
1824 
1825     @Override
1826     public void onDestroy() {
1827         super.onDestroy();
1828 
1829         unregisterReceiver(mReceiver);
1830         mWorkspace.removeCallbacks(mBuildLayersRunnable);
1831         mWorkspace.removeFolderListeners();
1832 
1833         // Stop callbacks from LauncherModel
1834         // It's possible to receive onDestroy after a new Launcher activity has
1835         // been created. In this case, don't interfere with the new Launcher.
1836         if (mModel.isCurrentCallbacks(this)) {
1837             mModel.stopLoader();
1838             LauncherAppState.getInstance(this).setLauncher(null);
1839         }
1840 
1841         if (mRotationPrefChangeHandler != null) {
1842             mSharedPrefs.unregisterOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
1843         }
1844 
1845         try {
1846             mAppWidgetHost.stopListening();
1847         } catch (NullPointerException ex) {
1848             Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
1849         }
1850         mAppWidgetHost = null;
1851 
1852         TextKeyListener.getInstance().release();
1853 
1854         ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
1855                 .removeAccessibilityStateChangeListener(this);
1856 
1857         WallpaperColorInfo.getInstance(this).setOnThemeChangeListener(null);
1858 
1859         LauncherAnimUtils.onDestroyActivity();
1860 
1861         clearPendingBinds();
1862 
1863         if (mLauncherCallbacks != null) {
1864             mLauncherCallbacks.onDestroy();
1865         }
1866     }
1867 
1868     public LauncherAccessibilityDelegate getAccessibilityDelegate() {
1869         return mAccessibilityDelegate;
1870     }
1871 
1872     public DragController getDragController() {
1873         return mDragController;
1874     }
1875 
1876     @Override
1877     public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
1878         super.startActivityForResult(intent, requestCode, options);
1879     }
1880 
1881     @Override
1882     public void startIntentSenderForResult (IntentSender intent, int requestCode,
1883             Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
1884         try {
1885             super.startIntentSenderForResult(intent, requestCode,
1886                 fillInIntent, flagsMask, flagsValues, extraFlags, options);
1887         } catch (IntentSender.SendIntentException e) {
1888             throw new ActivityNotFoundException();
1889         }
1890     }
1891 
1892     /**
1893      * Indicates that we want global search for this activity by setting the globalSearch
1894      * argument for {@link #startSearch} to true.
1895      */
1896     @Override
1897     public void startSearch(String initialQuery, boolean selectInitialQuery,
1898             Bundle appSearchData, boolean globalSearch) {
1899 
1900         if (initialQuery == null) {
1901             // Use any text typed in the launcher as the initial query
1902             initialQuery = getTypedText();
1903         }
1904         if (appSearchData == null) {
1905             appSearchData = new Bundle();
1906             appSearchData.putString("source", "launcher-search");
1907         }
1908 
1909         if (mLauncherCallbacks == null ||
1910                 !mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData)) {
1911             // Starting search from the callbacks failed. Start the default global search.
1912             startGlobalSearch(initialQuery, selectInitialQuery, appSearchData, null);
1913         }
1914 
1915         // We need to show the workspace after starting the search
1916         showWorkspace(true);
1917     }
1918 
1919     /**
1920      * Starts the global search activity. This code is a copied from SearchManager
1921      */
1922     public void startGlobalSearch(String initialQuery,
1923             boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
1924         final SearchManager searchManager =
1925             (SearchManager) getSystemService(Context.SEARCH_SERVICE);
1926         ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
1927         if (globalSearchActivity == null) {
1928             Log.w(TAG, "No global search activity found.");
1929             return;
1930         }
1931         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
1932         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1933         intent.setComponent(globalSearchActivity);
1934         // Make sure that we have a Bundle to put source in
1935         if (appSearchData == null) {
1936             appSearchData = new Bundle();
1937         } else {
1938             appSearchData = new Bundle(appSearchData);
1939         }
1940         // Set source to package name of app that starts global search if not set already.
1941         if (!appSearchData.containsKey("source")) {
1942             appSearchData.putString("source", getPackageName());
1943         }
1944         intent.putExtra(SearchManager.APP_DATA, appSearchData);
1945         if (!TextUtils.isEmpty(initialQuery)) {
1946             intent.putExtra(SearchManager.QUERY, initialQuery);
1947         }
1948         if (selectInitialQuery) {
1949             intent.putExtra(SearchManager.EXTRA_SELECT_QUERY, selectInitialQuery);
1950         }
1951         intent.setSourceBounds(sourceBounds);
1952         try {
1953             startActivity(intent);
1954         } catch (ActivityNotFoundException ex) {
1955             Log.e(TAG, "Global search activity not found: " + globalSearchActivity);
1956         }
1957     }
1958 
1959     public boolean isOnCustomContent() {
1960         return mWorkspace.isOnOrMovingToCustomContent();
1961     }
1962 
1963     @Override
1964     public boolean onPrepareOptionsMenu(Menu menu) {
1965         super.onPrepareOptionsMenu(menu);
1966         if (mLauncherCallbacks != null) {
1967             return mLauncherCallbacks.onPrepareOptionsMenu(menu);
1968         }
1969         return false;
1970     }
1971 
1972     @Override
1973     public boolean onSearchRequested() {
1974         startSearch(null, false, null, true);
1975         // Use a custom animation for launching search
1976         return true;
1977     }
1978 
1979     public boolean isWorkspaceLocked() {
1980         return mWorkspaceLoading || mPendingRequestArgs != null;
1981     }
1982 
1983     public boolean isWorkspaceLoading() {
1984         return mWorkspaceLoading;
1985     }
1986 
1987     private void setWorkspaceLoading(boolean value) {
1988         boolean isLocked = isWorkspaceLocked();
1989         mWorkspaceLoading = value;
1990         if (isLocked != isWorkspaceLocked()) {
1991             onWorkspaceLockedChanged();
1992         }
1993     }
1994 
1995     public void setWaitingForResult(PendingRequestArgs args) {
1996         boolean isLocked = isWorkspaceLocked();
1997         mPendingRequestArgs = args;
1998         if (isLocked != isWorkspaceLocked()) {
1999             onWorkspaceLockedChanged();
2000         }
2001     }
2002 
2003     protected void onWorkspaceLockedChanged() {
2004         if (mLauncherCallbacks != null) {
2005             mLauncherCallbacks.onWorkspaceLockedChanged();
2006         }
2007     }
2008 
2009     void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
2010             WidgetAddFlowHandler addFlowHandler) {
2011         if (LOGD) {
2012             Log.d(TAG, "Adding widget from drop");
2013         }
2014         addAppWidgetImpl(appWidgetId, info, boundWidget, addFlowHandler, 0);
2015     }
2016 
2017     void addAppWidgetImpl(int appWidgetId, ItemInfo info,
2018             AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay) {
2019         if (!addFlowHandler.startConfigActivity(this, appWidgetId, info, REQUEST_CREATE_APPWIDGET)) {
2020             // If the configuration flow was not started, add the widget
2021 
2022             Runnable onComplete = new Runnable() {
2023                 @Override
2024                 public void run() {
2025                     // Exit spring loaded mode if necessary after adding the widget
2026                     exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT,
2027                             null);
2028                 }
2029             };
2030             completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this));
2031             mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
2032         }
2033     }
2034 
2035     protected void moveToCustomContentScreen(boolean animate) {
2036         // Close any folders that may be open.
2037         AbstractFloatingView.closeAllOpenViews(this, animate);
2038         mWorkspace.moveToCustomContentScreen(animate);
2039     }
2040 
2041     public void addPendingItem(PendingAddItemInfo info, long container, long screenId,
2042             int[] cell, int spanX, int spanY) {
2043         info.container = container;
2044         info.screenId = screenId;
2045         if (cell != null) {
2046             info.cellX = cell[0];
2047             info.cellY = cell[1];
2048         }
2049         info.spanX = spanX;
2050         info.spanY = spanY;
2051 
2052         switch (info.itemType) {
2053             case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
2054             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
2055                 addAppWidgetFromDrop((PendingAddWidgetInfo) info);
2056                 break;
2057             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
2058                 processShortcutFromDrop((PendingAddShortcutInfo) info);
2059                 break;
2060             default:
2061                 throw new IllegalStateException("Unknown item type: " + info.itemType);
2062             }
2063     }
2064 
2065     /**
2066      * Process a shortcut drop.
2067      */
2068     private void processShortcutFromDrop(PendingAddShortcutInfo info) {
2069         Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(info.componentName);
2070         setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_CREATE_SHORTCUT, intent, info));
2071         if (!info.activityInfo.startConfigActivity(this, REQUEST_CREATE_SHORTCUT)) {
2072             handleActivityResult(REQUEST_CREATE_SHORTCUT, RESULT_CANCELED, null);
2073         }
2074     }
2075 
2076     /**
2077      * Process a widget drop.
2078      */
2079     private void addAppWidgetFromDrop(PendingAddWidgetInfo info) {
2080         AppWidgetHostView hostView = info.boundWidget;
2081         int appWidgetId;
2082         WidgetAddFlowHandler addFlowHandler = info.getHandler();
2083         if (hostView != null) {
2084             // In the case where we've prebound the widget, we remove it from the DragLayer
2085             if (LOGD) {
2086                 Log.d(TAG, "Removing widget view from drag layer and setting boundWidget to null");
2087             }
2088             getDragLayer().removeView(hostView);
2089 
2090             appWidgetId = hostView.getAppWidgetId();
2091             addAppWidgetFromDropImpl(appWidgetId, info, hostView, addFlowHandler);
2092 
2093             // Clear the boundWidget so that it doesn't get destroyed.
2094             info.boundWidget = null;
2095         } else {
2096             // In this case, we either need to start an activity to get permission to bind
2097             // the widget, or we need to start an activity to configure the widget, or both.
2098             appWidgetId = getAppWidgetHost().allocateAppWidgetId();
2099             Bundle options = info.bindOptions;
2100 
2101             boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
2102                     appWidgetId, info.info, options);
2103             if (success) {
2104                 addAppWidgetFromDropImpl(appWidgetId, info, null, addFlowHandler);
2105             } else {
2106                 addFlowHandler.startBindFlow(this, appWidgetId, info, REQUEST_BIND_APPWIDGET);
2107             }
2108         }
2109     }
2110 
2111     FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
2112             int cellY) {
2113         final FolderInfo folderInfo = new FolderInfo();
2114         folderInfo.title = getText(R.string.folder_name);
2115 
2116         // Update the model
2117         getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
2118 
2119         // Create the view
2120         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo);
2121         mWorkspace.addInScreen(newFolder, folderInfo);
2122         // Force measure the new folder icon
2123         CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder);
2124         parent.getShortcutsAndWidgets().measureChild(newFolder);
2125         return newFolder;
2126     }
2127 
2128     /**
2129      * Unbinds the view for the specified item, and removes the item and all its children.
2130      *
2131      * @param v the view being removed.
2132      * @param itemInfo the {@link ItemInfo} for this view.
2133      * @param deleteFromDb whether or not to delete this item from the db.
2134      */
2135     public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) {
2136         if (itemInfo instanceof ShortcutInfo) {
2137             // Remove the shortcut from the folder before removing it from launcher
2138             View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
2139             if (folderIcon instanceof FolderIcon) {
2140                 ((FolderInfo) folderIcon.getTag()).remove((ShortcutInfo) itemInfo, true);
2141             } else {
2142                 mWorkspace.removeWorkspaceItem(v);
2143             }
2144             if (deleteFromDb) {
2145                 getModelWriter().deleteItemFromDatabase(itemInfo);
2146             }
2147         } else if (itemInfo instanceof FolderInfo) {
2148             final FolderInfo folderInfo = (FolderInfo) itemInfo;
2149             if (v instanceof FolderIcon) {
2150                 ((FolderIcon) v).removeListeners();
2151             }
2152             mWorkspace.removeWorkspaceItem(v);
2153             if (deleteFromDb) {
2154                 getModelWriter().deleteFolderAndContentsFromDatabase(folderInfo);
2155             }
2156         } else if (itemInfo instanceof LauncherAppWidgetInfo) {
2157             final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
2158             mWorkspace.removeWorkspaceItem(v);
2159             if (deleteFromDb) {
2160                 deleteWidgetInfo(widgetInfo);
2161             }
2162         } else {
2163             return false;
2164         }
2165         return true;
2166     }
2167 
2168     /**
2169      * Deletes the widget info and the widget id.
2170      */
2171     private void deleteWidgetInfo(final LauncherAppWidgetInfo widgetInfo) {
2172         final LauncherAppWidgetHost appWidgetHost = getAppWidgetHost();
2173         if (appWidgetHost != null && !widgetInfo.isCustomWidget() && widgetInfo.isWidgetIdAllocated()) {
2174             // Deleting an app widget ID is a void call but writes to disk before returning
2175             // to the caller...
2176             new AsyncTask<Void, Void, Void>() {
2177                 public Void doInBackground(Void ... args) {
2178                     appWidgetHost.deleteAppWidgetId(widgetInfo.appWidgetId);
2179                     return null;
2180                 }
2181             }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
2182         }
2183         getModelWriter().deleteItemFromDatabase(widgetInfo);
2184     }
2185 
2186     @Override
2187     public boolean dispatchKeyEvent(KeyEvent event) {
2188         return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
2189     }
2190 
2191     @Override
2192     public void onBackPressed() {
2193         if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) {
2194             return;
2195         }
2196 
2197         if (mDragController.isDragging()) {
2198             mDragController.cancelDrag();
2199             return;
2200         }
2201 
2202         // Note: There should be at most one log per method call. This is enforced implicitly
2203         // by using if-else statements.
2204         UserEventDispatcher ued = getUserEventDispatcher();
2205         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
2206         if (topView != null) {
2207             if (topView.getActiveTextView() != null) {
2208                 topView.getActiveTextView().dispatchBackKey();
2209             } else {
2210                 if (topView instanceof PopupContainerWithArrow) {
2211                     ued.logActionCommand(Action.Command.BACK,
2212                             topView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
2213                 } else if (topView instanceof Folder) {
2214                     ued.logActionCommand(Action.Command.BACK,
2215                             ((Folder) topView).getFolderIcon(), ContainerType.FOLDER);
2216                 }
2217                 topView.close(true);
2218             }
2219         } else if (isAppsViewVisible()) {
2220             ued.logActionCommand(Action.Command.BACK, ContainerType.ALLAPPS);
2221             showWorkspace(true);
2222         } else if (isWidgetsViewVisible())  {
2223             ued.logActionCommand(Action.Command.BACK, ContainerType.WIDGETS);
2224             showOverviewMode(true);
2225         } else if (mWorkspace.isInOverviewMode()) {
2226             ued.logActionCommand(Action.Command.BACK, ContainerType.OVERVIEW);
2227             showWorkspace(true);
2228         } else {
2229             // TODO: Log this case.
2230             mWorkspace.exitWidgetResizeMode();
2231 
2232             // Back button is a no-op here, but give at least some feedback for the button press
2233             mWorkspace.showOutlinesTemporarily();
2234         }
2235     }
2236 
2237     /**
2238      * Launches the intent referred by the clicked shortcut.
2239      *
2240      * @param v The view representing the clicked shortcut.
2241      */
2242     public void onClick(View v) {
2243         // Make sure that rogue clicks don't get through while allapps is launching, or after the
2244         // view has detached (it's possible for this to happen if the view is removed mid touch).
2245         if (v.getWindowToken() == null) {
2246             return;
2247         }
2248 
2249         if (!mWorkspace.isFinishedSwitchingState()) {
2250             return;
2251         }
2252 
2253         if (v instanceof Workspace) {
2254             if (mWorkspace.isInOverviewMode()) {
2255                 getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
2256                         LauncherLogProto.Action.Direction.NONE,
2257                         LauncherLogProto.ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
2258                 showWorkspace(true);
2259             }
2260             return;
2261         }
2262 
2263         if (v instanceof CellLayout) {
2264             if (mWorkspace.isInOverviewMode()) {
2265                 int page = mWorkspace.indexOfChild(v);
2266                 getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
2267                         LauncherLogProto.Action.Direction.NONE,
2268                         LauncherLogProto.ContainerType.OVERVIEW, page);
2269                 mWorkspace.snapToPageFromOverView(page);
2270                 showWorkspace(true);
2271             }
2272             return;
2273         }
2274 
2275         Object tag = v.getTag();
2276         if (tag instanceof ShortcutInfo) {
2277             onClickAppShortcut(v);
2278         } else if (tag instanceof FolderInfo) {
2279             if (v instanceof FolderIcon) {
2280                 onClickFolderIcon(v);
2281             }
2282         } else if ((v instanceof PageIndicator) ||
2283             (v == mAllAppsButton && mAllAppsButton != null)) {
2284             onClickAllAppsButton(v);
2285         } else if (tag instanceof AppInfo) {
2286             startAppShortcutOrInfoActivity(v);
2287         } else if (tag instanceof LauncherAppWidgetInfo) {
2288             if (v instanceof PendingAppWidgetHostView) {
2289                 onClickPendingWidget((PendingAppWidgetHostView) v);
2290             }
2291         }
2292     }
2293 
2294     @SuppressLint("ClickableViewAccessibility")
2295     public boolean onTouch(View v, MotionEvent event) {
2296         return false;
2297     }
2298 
2299     /**
2300      * Event handler for the app widget view which has not fully restored.
2301      */
2302     public void onClickPendingWidget(final PendingAppWidgetHostView v) {
2303         if (mIsSafeModeEnabled) {
2304             Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
2305             return;
2306         }
2307 
2308         final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
2309         if (v.isReadyForClickSetup()) {
2310             LauncherAppWidgetProviderInfo appWidgetInfo =
2311                     mAppWidgetManager.findProvider(info.providerName, info.user);
2312             if (appWidgetInfo == null) {
2313                 return;
2314             }
2315             WidgetAddFlowHandler addFlowHandler = new WidgetAddFlowHandler(appWidgetInfo);
2316 
2317             if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
2318                 if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
2319                     // This should not happen, as we make sure that an Id is allocated during bind.
2320                     return;
2321                 }
2322                 addFlowHandler.startBindFlow(this, info.appWidgetId, info,
2323                         REQUEST_BIND_PENDING_APPWIDGET);
2324             } else {
2325                 addFlowHandler.startConfigActivity(this, info, REQUEST_RECONFIGURE_APPWIDGET);
2326             }
2327         } else {
2328             final String packageName = info.providerName.getPackageName();
2329             onClickPendingAppItem(v, packageName, info.installProgress >= 0);
2330         }
2331     }
2332 
2333     /**
2334      * Event handler for the "grid" button or "caret" that appears on the home screen, which
2335      * enters all apps mode. In verticalBarLayout the caret can be seen when all apps is open, and
2336      * so in that case reverses the action.
2337      *
2338      * @param v The view that was clicked.
2339      */
2340     protected void onClickAllAppsButton(View v) {
2341         if (LOGD) Log.d(TAG, "onClickAllAppsButton");
2342         if (!isAppsViewVisible()) {
2343             getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
2344                     ControlType.ALL_APPS_BUTTON);
2345             showAppsView(true /* animated */, true /* updatePredictedApps */);
2346         } else {
2347             showWorkspace(true);
2348         }
2349     }
2350 
2351     private void onClickPendingAppItem(final View v, final String packageName,
2352             boolean downloadStarted) {
2353         if (downloadStarted) {
2354             // If the download has started, simply direct to the market app.
2355             startMarketIntentForPackage(v, packageName);
2356             return;
2357         }
2358         new AlertDialog.Builder(this)
2359             .setTitle(R.string.abandoned_promises_title)
2360             .setMessage(R.string.abandoned_promise_explanation)
2361             .setPositiveButton(R.string.abandoned_search, new DialogInterface.OnClickListener() {
2362                 @Override
2363                 public void onClick(DialogInterface dialogInterface, int i) {
2364                     startMarketIntentForPackage(v, packageName);
2365                 }
2366             })
2367             .setNeutralButton(R.string.abandoned_clean_this,
2368                 new DialogInterface.OnClickListener() {
2369                     public void onClick(DialogInterface dialog, int id) {
2370                         final UserHandle user = Process.myUserHandle();
2371                         mWorkspace.removeAbandonedPromise(packageName, user);
2372                     }
2373                 })
2374             .create().show();
2375     }
2376 
2377     private void startMarketIntentForPackage(View v, String packageName) {
2378         ItemInfo item = (ItemInfo) v.getTag();
2379         Intent intent = PackageManagerHelper.getMarketIntent(packageName);
2380         boolean success = startActivitySafely(v, intent, item);
2381         if (success && v instanceof BubbleTextView) {
2382             mWaitingForResume = (BubbleTextView) v;
2383             mWaitingForResume.setStayPressed(true);
2384         }
2385     }
2386 
2387     /**
2388      * Event handler for an app shortcut click.
2389      *
2390      * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
2391      */
2392     protected void onClickAppShortcut(final View v) {
2393         if (LOGD) Log.d(TAG, "onClickAppShortcut");
2394         Object tag = v.getTag();
2395         if (!(tag instanceof ShortcutInfo)) {
2396             throw new IllegalArgumentException("Input must be a Shortcut");
2397         }
2398 
2399         // Open shortcut
2400         final ShortcutInfo shortcut = (ShortcutInfo) tag;
2401 
2402         if (shortcut.isDisabled != 0) {
2403             if ((shortcut.isDisabled &
2404                     ~ShortcutInfo.FLAG_DISABLED_SUSPENDED &
2405                     ~ShortcutInfo.FLAG_DISABLED_QUIET_USER) == 0) {
2406                 // If the app is only disabled because of the above flags, launch activity anyway.
2407                 // Framework will tell the user why the app is suspended.
2408             } else {
2409                 if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
2410                     // Use a message specific to this shortcut, if it has one.
2411                     Toast.makeText(this, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
2412                     return;
2413                 }
2414                 // Otherwise just use a generic error message.
2415                 int error = R.string.activity_not_available;
2416                 if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
2417                     error = R.string.safemode_shortcut_error;
2418                 } else if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_BY_PUBLISHER) != 0 ||
2419                         (shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_LOCKED_USER) != 0) {
2420                     error = R.string.shortcut_not_available;
2421                 }
2422                 Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
2423                 return;
2424             }
2425         }
2426 
2427         // Check for abandoned promise
2428         if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
2429             String packageName = shortcut.intent.getComponent() != null ?
2430                     shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
2431             if (!TextUtils.isEmpty(packageName)) {
2432                 onClickPendingAppItem(v, packageName,
2433                         shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
2434                 return;
2435             }
2436         }
2437 
2438         // Start activities
2439         startAppShortcutOrInfoActivity(v);
2440     }
2441 
2442     private void startAppShortcutOrInfoActivity(View v) {
2443         ItemInfo item = (ItemInfo) v.getTag();
2444         Intent intent;
2445         if (item instanceof PromiseAppInfo) {
2446             PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
2447             intent = promiseAppInfo.getMarketIntent();
2448         } else {
2449             intent = item.getIntent();
2450         }
2451         if (intent == null) {
2452             throw new IllegalArgumentException("Input must have a valid intent");
2453         }
2454         boolean success = startActivitySafely(v, intent, item);
2455         getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
2456 
2457         if (success && v instanceof BubbleTextView) {
2458             mWaitingForResume = (BubbleTextView) v;
2459             mWaitingForResume.setStayPressed(true);
2460         }
2461     }
2462 
2463     /**
2464      * Event handler for a folder icon click.
2465      *
2466      * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
2467      */
2468     protected void onClickFolderIcon(View v) {
2469         if (LOGD) Log.d(TAG, "onClickFolder");
2470         if (!(v instanceof FolderIcon)){
2471             throw new IllegalArgumentException("Input must be a FolderIcon");
2472         }
2473 
2474         Folder folder = ((FolderIcon) v).getFolder();
2475         if (!folder.isOpen() && !folder.isDestroyed()) {
2476             // Open the requested folder
2477             folder.animateOpen();
2478         }
2479     }
2480 
2481     /**
2482      * Event handler for the (Add) Widgets button that appears after a long press
2483      * on the home screen.
2484      */
2485     public void onClickAddWidgetButton(View view) {
2486         if (LOGD) Log.d(TAG, "onClickAddWidgetButton");
2487         if (mIsSafeModeEnabled) {
2488             Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
2489         } else {
2490             showWidgetsView(true /* animated */, true /* resetPageToZero */);
2491         }
2492     }
2493 
2494     /**
2495      * Event handler for the wallpaper picker button that appears after a long press
2496      * on the home screen.
2497      */
2498     public void onClickWallpaperPicker(View v) {
2499         if (!Utilities.isWallpaperAllowed(this)) {
2500             Toast.makeText(this, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
2501             return;
2502         }
2503 
2504         int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
2505         float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
2506         setWaitingForResult(new PendingRequestArgs(new ItemInfo()));
2507         Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
2508                 .putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
2509 
2510         String pickerPackage = getString(R.string.wallpaper_picker_package);
2511         boolean hasTargetPackage = !TextUtils.isEmpty(pickerPackage);
2512         if (hasTargetPackage) {
2513             intent.setPackage(pickerPackage);
2514         }
2515 
2516         intent.setSourceBounds(getViewBounds(v));
2517         try {
2518             startActivityForResult(intent, REQUEST_PICK_WALLPAPER,
2519                     // If there is no target package, use the default intent chooser animation
2520                     hasTargetPackage ? getActivityLaunchOptions(v) : null);
2521         } catch (ActivityNotFoundException e) {
2522             setWaitingForResult(null);
2523             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
2524         }
2525     }
2526 
2527     /**
2528      * Event handler for a click on the settings button that appears after a long press
2529      * on the home screen.
2530      */
2531     public void onClickSettingsButton(View v) {
2532         if (LOGD) Log.d(TAG, "onClickSettingsButton");
2533         Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
2534                 .setPackage(getPackageName());
2535         intent.setSourceBounds(getViewBounds(v));
2536         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2537         startActivity(intent, getActivityLaunchOptions(v));
2538     }
2539 
2540     @Override
2541     public void onAccessibilityStateChanged(boolean enabled) {
2542         mDragLayer.onAccessibilityStateChanged(enabled);
2543     }
2544 
2545     public void onDragStarted() {
2546         if (isOnCustomContent()) {
2547             // Custom content screen doesn't participate in drag and drop. If on custom
2548             // content screen, move to default.
2549             moveWorkspaceToDefaultScreen();
2550         }
2551     }
2552 
2553     /**
2554      * Called when the user stops interacting with the launcher.
2555      * This implies that the user is now on the homescreen and is not doing housekeeping.
2556      */
2557     protected void onInteractionEnd() {
2558         if (mLauncherCallbacks != null) {
2559             mLauncherCallbacks.onInteractionEnd();
2560         }
2561     }
2562 
2563     /**
2564      * Called when the user starts interacting with the launcher.
2565      * The possible interactions are:
2566      *  - open all apps
2567      *  - reorder an app shortcut, or a widget
2568      *  - open the overview mode.
2569      * This is a good time to stop doing things that only make sense
2570      * when the user is on the homescreen and not doing housekeeping.
2571      */
2572     protected void onInteractionBegin() {
2573         if (mLauncherCallbacks != null) {
2574             mLauncherCallbacks.onInteractionBegin();
2575         }
2576     }
2577 
2578     /** Updates the interaction state. */
2579     public void updateInteraction(Workspace.State fromState, Workspace.State toState) {
2580         // Only update the interacting state if we are transitioning to/from a view with an
2581         // overlay
2582         boolean fromStateWithOverlay = fromState != Workspace.State.NORMAL;
2583         boolean toStateWithOverlay = toState != Workspace.State.NORMAL;
2584         if (toStateWithOverlay) {
2585             onInteractionBegin();
2586         } else if (fromStateWithOverlay) {
2587             onInteractionEnd();
2588         }
2589     }
2590 
2591     private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
2592         try {
2593             StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
2594             try {
2595                 // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
2596                 // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
2597                 // is enabled by default on NYC.
2598                 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
2599                         .penaltyLog().build());
2600 
2601                 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
2602                     String id = ((ShortcutInfo) info).getDeepShortcutId();
2603                     String packageName = intent.getPackage();
2604                     DeepShortcutManager.getInstance(this).startShortcut(
2605                             packageName, id, intent.getSourceBounds(), optsBundle, info.user);
2606                 } else {
2607                     // Could be launching some bookkeeping activity
2608                     startActivity(intent, optsBundle);
2609                 }
2610             } finally {
2611                 StrictMode.setVmPolicy(oldPolicy);
2612             }
2613         } catch (SecurityException e) {
2614             // Due to legacy reasons, direct call shortcuts require Launchers to have the
2615             // corresponding permission. Show the appropriate permission prompt if that
2616             // is the case.
2617             if (intent.getComponent() == null
2618                     && Intent.ACTION_CALL.equals(intent.getAction())
2619                     && checkSelfPermission(Manifest.permission.CALL_PHONE) !=
2620                     PackageManager.PERMISSION_GRANTED) {
2621 
2622                 setWaitingForResult(PendingRequestArgs
2623                         .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info));
2624                 requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
2625                         REQUEST_PERMISSION_CALL_PHONE);
2626             } else {
2627                 // No idea why this was thrown.
2628                 throw e;
2629             }
2630         }
2631     }
2632 
2633     @TargetApi(Build.VERSION_CODES.M)
2634     public Bundle getActivityLaunchOptions(View v) {
2635         if (Utilities.ATLEAST_MARSHMALLOW) {
2636             int left = 0, top = 0;
2637             int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
2638             if (v instanceof BubbleTextView) {
2639                 // Launch from center of icon, not entire view
2640                 Drawable icon = ((BubbleTextView) v).getIcon();
2641                 if (icon != null) {
2642                     Rect bounds = icon.getBounds();
2643                     left = (width - bounds.width()) / 2;
2644                     top = v.getPaddingTop();
2645                     width = bounds.width();
2646                     height = bounds.height();
2647                 }
2648             }
2649             return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height).toBundle();
2650         } else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
2651             // On L devices, we use the device default slide-up transition.
2652             // On L MR1 devices, we use a custom version of the slide-up transition which
2653             // doesn't have the delay present in the device default.
2654             return ActivityOptions.makeCustomAnimation(
2655                     this, R.anim.task_open_enter, R.anim.no_anim).toBundle();
2656         }
2657         return null;
2658     }
2659 
2660     public Rect getViewBounds(View v) {
2661         int[] pos = new int[2];
2662         v.getLocationOnScreen(pos);
2663         return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
2664     }
2665 
2666     public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
2667         if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
2668             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
2669             return false;
2670         }
2671         // Only launch using the new animation if the shortcut has not opted out (this is a
2672         // private contract between launcher and may be ignored in the future).
2673         boolean useLaunchAnimation = (v != null) &&
2674                 !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
2675         Bundle optsBundle = useLaunchAnimation ? getActivityLaunchOptions(v) : null;
2676 
2677         UserHandle user = item == null ? null : item.user;
2678 
2679         // Prepare intent
2680         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2681         if (v != null) {
2682             intent.setSourceBounds(getViewBounds(v));
2683         }
2684         try {
2685             if (Utilities.ATLEAST_MARSHMALLOW
2686                     && (item instanceof ShortcutInfo)
2687                     && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
2688                      || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
2689                     && !((ShortcutInfo) item).isPromise()) {
2690                 // Shortcuts need some special checks due to legacy reasons.
2691                 startShortcutIntentSafely(intent, optsBundle, item);
2692             } else if (user == null || user.equals(Process.myUserHandle())) {
2693                 // Could be launching some bookkeeping activity
2694                 startActivity(intent, optsBundle);
2695             } else {
2696                 LauncherAppsCompat.getInstance(this).startActivityForProfile(
2697                         intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
2698             }
2699             return true;
2700         } catch (ActivityNotFoundException|SecurityException e) {
2701             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
2702             Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
2703         }
2704         return false;
2705     }
2706 
2707     @Override
2708     public boolean dispatchTouchEvent(MotionEvent ev) {
2709         mLastDispatchTouchEventX = ev.getX();
2710         return super.dispatchTouchEvent(ev);
2711     }
2712 
2713     @Override
2714     public boolean onLongClick(View v) {
2715         if (!isDraggingEnabled()) return false;
2716         if (isWorkspaceLocked()) return false;
2717         if (mState != State.WORKSPACE) return false;
2718 
2719         boolean ignoreLongPressToOverview =
2720                 mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX);
2721 
2722         if (v instanceof Workspace) {
2723             if (!mWorkspace.isInOverviewMode()) {
2724                 if (!mWorkspace.isTouchActive() && !ignoreLongPressToOverview) {
2725                     getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
2726                             Action.Direction.NONE, ContainerType.WORKSPACE,
2727                             mWorkspace.getCurrentPage());
2728                     showOverviewMode(true);
2729                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
2730                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
2731                     return true;
2732                 } else {
2733                     return false;
2734                 }
2735             } else {
2736                 return false;
2737             }
2738         }
2739 
2740         CellLayout.CellInfo longClickCellInfo = null;
2741         View itemUnderLongClick = null;
2742         if (v.getTag() instanceof ItemInfo) {
2743             ItemInfo info = (ItemInfo) v.getTag();
2744             longClickCellInfo = new CellLayout.CellInfo(v, info);
2745             itemUnderLongClick = longClickCellInfo.cell;
2746             mPendingRequestArgs = null;
2747         }
2748 
2749         // The hotseat touch handling does not go through Workspace, and we always allow long press
2750         // on hotseat items.
2751         if (!mDragController.isDragging()) {
2752             if (itemUnderLongClick == null) {
2753                 // User long pressed on empty space
2754                 if (mWorkspace.isInOverviewMode()) {
2755                     mWorkspace.startReordering(v);
2756                     getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
2757                             Action.Direction.NONE, ContainerType.OVERVIEW);
2758                 } else {
2759                     if (ignoreLongPressToOverview) {
2760                         return false;
2761                     }
2762                     getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
2763                             Action.Direction.NONE, ContainerType.WORKSPACE,
2764                             mWorkspace.getCurrentPage());
2765                     showOverviewMode(true);
2766                 }
2767                 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
2768                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
2769             } else {
2770                 final boolean isAllAppsButton =
2771                         !FeatureFlags.NO_ALL_APPS_ICON && isHotseatLayout(v) &&
2772                                 mDeviceProfile.inv.isAllAppsButtonRank(mHotseat.getOrderInHotseat(
2773                                         longClickCellInfo.cellX, longClickCellInfo.cellY));
2774                 if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
2775                     // User long pressed on an item
2776                     mWorkspace.startDrag(longClickCellInfo, new DragOptions());
2777                 }
2778             }
2779         }
2780         return true;
2781     }
2782 
2783     boolean isHotseatLayout(View layout) {
2784         // TODO: Remove this method
2785         return mHotseat != null && layout != null &&
2786                 (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
2787     }
2788 
2789     /**
2790      * Returns the CellLayout of the specified container at the specified screen.
2791      */
2792     public CellLayout getCellLayout(long container, long screenId) {
2793         if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2794             if (mHotseat != null) {
2795                 return mHotseat.getLayout();
2796             } else {
2797                 return null;
2798             }
2799         } else {
2800             return mWorkspace.getScreenWithId(screenId);
2801         }
2802     }
2803 
2804     /**
2805      * For overridden classes.
2806      */
2807     public boolean isAllAppsVisible() {
2808         return isAppsViewVisible();
2809     }
2810 
2811     public boolean isAppsViewVisible() {
2812         return (mState == State.APPS) || (mOnResumeState == State.APPS);
2813     }
2814 
2815     public boolean isWidgetsViewVisible() {
2816         return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS);
2817     }
2818 
2819     @Override
2820     public void onTrimMemory(int level) {
2821         super.onTrimMemory(level);
2822         if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
2823             // The widget preview db can result in holding onto over
2824             // 3MB of memory for caching which isn't necessary.
2825             SQLiteDatabase.releaseMemory();
2826 
2827             // This clears all widget bitmaps from the widget tray
2828             // TODO(hyunyoungs)
2829         }
2830         if (mLauncherCallbacks != null) {
2831             mLauncherCallbacks.onTrimMemory(level);
2832         }
2833     }
2834 
2835     public boolean showWorkspace(boolean animated) {
2836         return showWorkspace(animated, null);
2837     }
2838 
2839     public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) {
2840         boolean changed = mState != State.WORKSPACE ||
2841                 mWorkspace.getState() != Workspace.State.NORMAL;
2842         if (changed || mAllAppsController.isTransitioning()) {
2843             mWorkspace.setVisibility(View.VISIBLE);
2844             mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
2845                     Workspace.State.NORMAL, animated, onCompleteRunnable);
2846 
2847             // Set focus to the AppsCustomize button
2848             if (mAllAppsButton != null) {
2849                 mAllAppsButton.requestFocus();
2850             }
2851         }
2852 
2853         // Change the state *after* we've called all the transition code
2854         setState(State.WORKSPACE);
2855 
2856         if (changed) {
2857             // Send an accessibility event to announce the context change
2858             getWindow().getDecorView()
2859                     .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
2860         }
2861         return changed;
2862     }
2863 
2864     /**
2865      * Shows the overview button.
2866      */
2867     public void showOverviewMode(boolean animated) {
2868         showOverviewMode(animated, false);
2869     }
2870 
2871     /**
2872      * Shows the overview button, and if {@param requestButtonFocus} is set, will force the focus
2873      * onto one of the overview panel buttons.
2874      */
2875     void showOverviewMode(boolean animated, boolean requestButtonFocus) {
2876         Runnable postAnimRunnable = null;
2877         if (requestButtonFocus) {
2878             postAnimRunnable = new Runnable() {
2879                 @Override
2880                 public void run() {
2881                     // Hitting the menu button when in touch mode does not trigger touch mode to
2882                     // be disabled, so if requested, force focus on one of the overview panel
2883                     // buttons.
2884                     mOverviewPanel.requestFocusFromTouch();
2885                 }
2886             };
2887         }
2888         mWorkspace.setVisibility(View.VISIBLE);
2889         mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
2890                 Workspace.State.OVERVIEW, animated, postAnimRunnable);
2891         setState(State.WORKSPACE);
2892 
2893         // If animated from long press, then don't allow any of the controller in the drag
2894         // layer to intercept any remaining touch.
2895         mWorkspace.requestDisallowInterceptTouchEvent(animated);
2896     }
2897 
2898     private void setState(State state) {
2899         this.mState = state;
2900         updateSoftInputMode();
2901     }
2902 
2903     private void updateSoftInputMode() {
2904         if (FeatureFlags.LAUNCHER3_UPDATE_SOFT_INPUT_MODE) {
2905             final int mode;
2906             if (isAppsViewVisible()) {
2907                 mode = SOFT_INPUT_MODE_ALL_APPS;
2908             } else {
2909                 mode = SOFT_INPUT_MODE_DEFAULT;
2910             }
2911             getWindow().setSoftInputMode(mode);
2912         }
2913     }
2914 
2915     /**
2916      * Shows the apps view.
2917      */
2918     public void showAppsView(boolean animated, boolean updatePredictedApps) {
2919         markAppsViewShown();
2920         if (updatePredictedApps) {
2921             tryAndUpdatePredictedApps();
2922         }
2923         showAppsOrWidgets(State.APPS, animated);
2924     }
2925 
2926     /**
2927      * Shows the widgets view.
2928      */
2929     void showWidgetsView(boolean animated, boolean resetPageToZero) {
2930         if (LOGD) Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero);
2931         if (resetPageToZero) {
2932             mWidgetsView.scrollToTop();
2933         }
2934         showAppsOrWidgets(State.WIDGETS, animated);
2935 
2936         mWidgetsView.post(new Runnable() {
2937             @Override
2938             public void run() {
2939                 mWidgetsView.requestFocus();
2940             }
2941         });
2942     }
2943 
2944     /**
2945      * Sets up the transition to show the apps/widgets view.
2946      *
2947      * @return whether the current from and to state allowed this operation
2948      */
2949     // TODO: calling method should use the return value so that when {@code false} is returned
2950     // the workspace transition doesn't fall into invalid state.
2951     private boolean showAppsOrWidgets(State toState, boolean animated) {
2952         if (!(mState == State.WORKSPACE ||
2953                 mState == State.APPS_SPRING_LOADED ||
2954                 mState == State.WIDGETS_SPRING_LOADED ||
2955                 (mState == State.APPS && mAllAppsController.isTransitioning()))) {
2956             return false;
2957         }
2958         if (toState != State.APPS && toState != State.WIDGETS) {
2959             return false;
2960         }
2961 
2962         // This is a safe and supported transition to bypass spring_loaded mode.
2963         if (mExitSpringLoadedModeRunnable != null) {
2964             mHandler.removeCallbacks(mExitSpringLoadedModeRunnable);
2965             mExitSpringLoadedModeRunnable = null;
2966         }
2967 
2968         if (toState == State.APPS) {
2969             mStateTransitionAnimation.startAnimationToAllApps(animated);
2970         } else {
2971             mStateTransitionAnimation.startAnimationToWidgets(animated);
2972         }
2973 
2974         // Change the state *after* we've called all the transition code
2975         setState(toState);
2976         AbstractFloatingView.closeAllOpenViews(this);
2977 
2978         // Send an accessibility event to announce the context change
2979         getWindow().getDecorView()
2980                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
2981         return true;
2982     }
2983 
2984     /**
2985      * Updates the workspace and interaction state on state change, and return the animation to this
2986      * new state.
2987      */
2988     public Animator startWorkspaceStateChangeAnimation(Workspace.State toState,
2989             boolean animated, AnimationLayerSet layerViews) {
2990         Workspace.State fromState = mWorkspace.getState();
2991         Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews);
2992         updateInteraction(fromState, toState);
2993         return anim;
2994     }
2995 
2996     public void enterSpringLoadedDragMode() {
2997         if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name()));
2998         if (isStateSpringLoaded()) {
2999             return;
3000         }
3001 
3002         mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
3003                 Workspace.State.SPRING_LOADED, true /* animated */,
3004                 null /* onCompleteRunnable */);
3005         setState(State.WORKSPACE_SPRING_LOADED);
3006     }
3007 
3008     public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
3009             final Runnable onCompleteRunnable) {
3010         if (!isStateSpringLoaded()) return;
3011 
3012         if (mExitSpringLoadedModeRunnable != null) {
3013             mHandler.removeCallbacks(mExitSpringLoadedModeRunnable);
3014         }
3015         mExitSpringLoadedModeRunnable = new Runnable() {
3016             @Override
3017             public void run() {
3018                 if (successfulDrop) {
3019                     // TODO(hyunyoungs): verify if this hack is still needed, if not, delete.
3020                     //
3021                     // Before we show workspace, hide all apps again because
3022                     // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
3023                     // clean up our state transition functions
3024                     mWidgetsView.setVisibility(View.GONE);
3025                     showWorkspace(true, onCompleteRunnable);
3026                 } else {
3027                     exitSpringLoadedDragMode();
3028                 }
3029                 mExitSpringLoadedModeRunnable = null;
3030             }
3031         };
3032         mHandler.postDelayed(mExitSpringLoadedModeRunnable, delay);
3033     }
3034 
3035     boolean isStateSpringLoaded() {
3036         return mState == State.WORKSPACE_SPRING_LOADED || mState == State.APPS_SPRING_LOADED
3037                 || mState == State.WIDGETS_SPRING_LOADED;
3038     }
3039 
3040     public void exitSpringLoadedDragMode() {
3041         if (mState == State.APPS_SPRING_LOADED) {
3042             showAppsView(true /* animated */, false /* updatePredictedApps */);
3043         } else if (mState == State.WIDGETS_SPRING_LOADED) {
3044             showWidgetsView(true, false);
3045         } else if (mState == State.WORKSPACE_SPRING_LOADED) {
3046             showWorkspace(true);
3047         }
3048     }
3049 
3050     /**
3051      * Updates the set of predicted apps if it hasn't been updated since the last time Launcher was
3052      * resumed.
3053      */
3054     public void tryAndUpdatePredictedApps() {
3055         if (mLauncherCallbacks != null) {
3056             List<ComponentKeyMapper<AppInfo>> apps = mLauncherCallbacks.getPredictedApps();
3057             if (apps != null) {
3058                 mAppsView.setPredictedApps(apps);
3059             }
3060         }
3061     }
3062 
3063     void lockAllApps() {
3064         // TODO
3065     }
3066 
3067     void unlockAllApps() {
3068         // TODO
3069     }
3070 
3071     @Override
3072     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
3073         final boolean result = super.dispatchPopulateAccessibilityEvent(event);
3074         final List<CharSequence> text = event.getText();
3075         text.clear();
3076         // Populate event with a fake title based on the current state.
3077         if (mState == State.APPS) {
3078             text.add(getString(R.string.all_apps_button_label));
3079         } else if (mState == State.WIDGETS) {
3080             text.add(getString(R.string.widget_button_text));
3081         } else if (mWorkspace != null) {
3082             text.add(mWorkspace.getCurrentPageDescription());
3083         } else {
3084             text.add(getString(R.string.all_apps_home_button_label));
3085         }
3086         return result;
3087     }
3088 
3089     /**
3090      * If the activity is currently paused, signal that we need to run the passed Runnable
3091      * in onResume.
3092      *
3093      * This needs to be called from incoming places where resources might have been loaded
3094      * while the activity is paused. That is because the Configuration (e.g., rotation)  might be
3095      * wrong when we're not running, and if the activity comes back to what the configuration was
3096      * when we were paused, activity is not restarted.
3097      *
3098      * Implementation of the method from LauncherModel.Callbacks.
3099      *
3100      * @return {@code true} if we are currently paused. The caller might be able to skip some work
3101      */
3102     @Thunk boolean waitUntilResume(Runnable run) {
3103         if (mPaused) {
3104             if (LOGD) Log.d(TAG, "Deferring update until onResume");
3105             if (run instanceof RunnableWithId) {
3106                 // Remove any runnables which have the same id
3107                 while (mBindOnResumeCallbacks.remove(run)) { }
3108             }
3109             mBindOnResumeCallbacks.add(run);
3110             return true;
3111         } else {
3112             return false;
3113         }
3114     }
3115 
3116     public void addOnResumeCallback(Runnable run) {
3117         mOnResumeCallbacks.add(run);
3118     }
3119 
3120     /**
3121      * If the activity is currently paused, signal that we need to re-run the loader
3122      * in onResume.
3123      *
3124      * This needs to be called from incoming places where resources might have been loaded
3125      * while we are paused.  That is becaues the Configuration might be wrong
3126      * when we're not running, and if it comes back to what it was when we
3127      * were paused, we are not restarted.
3128      *
3129      * Implementation of the method from LauncherModel.Callbacks.
3130      *
3131      * @return true if we are currently paused.  The caller might be able to
3132      * skip some work in that case since we will come back again.
3133      */
3134     @Override
3135     public boolean setLoadOnResume() {
3136         if (mPaused) {
3137             if (LOGD) Log.d(TAG, "setLoadOnResume");
3138             mOnResumeNeedsLoad = true;
3139             return true;
3140         } else {
3141             return false;
3142         }
3143     }
3144 
3145     /**
3146      * Implementation of the method from LauncherModel.Callbacks.
3147      */
3148     @Override
3149     public int getCurrentWorkspaceScreen() {
3150         if (mWorkspace != null) {
3151             return mWorkspace.getCurrentPage();
3152         } else {
3153             return 0;
3154         }
3155     }
3156 
3157     /**
3158      * Clear any pending bind callbacks. This is called when is loader is planning to
3159      * perform a full rebind from scratch.
3160      */
3161     @Override
3162     public void clearPendingBinds() {
3163         mBindOnResumeCallbacks.clear();
3164         if (mPendingExecutor != null) {
3165             mPendingExecutor.markCompleted();
3166             mPendingExecutor = null;
3167         }
3168     }
3169 
3170     /**
3171      * Refreshes the shortcuts shown on the workspace.
3172      *
3173      * Implementation of the method from LauncherModel.Callbacks.
3174      */
3175     public void startBinding() {
3176         if (LauncherAppState.PROFILE_STARTUP) {
3177             Trace.beginSection("Starting page bind");
3178         }
3179 
3180         AbstractFloatingView.closeAllOpenViews(this);
3181 
3182         setWorkspaceLoading(true);
3183 
3184         // Clear the workspace because it's going to be rebound
3185         mWorkspace.clearDropTargets();
3186         mWorkspace.removeAllWorkspaceScreens();
3187 
3188         if (mHotseat != null) {
3189             mHotseat.resetLayout();
3190         }
3191         if (LauncherAppState.PROFILE_STARTUP) {
3192             Trace.endSection();
3193         }
3194     }
3195 
3196     @Override
3197     public void bindScreens(ArrayList<Long> orderedScreenIds) {
3198         // Make sure the first screen is always at the start.
3199         if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
3200                 orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
3201             orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
3202             orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
3203             LauncherModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
3204         } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
3205             // If there are no screens, we need to have an empty screen
3206             mWorkspace.addExtraEmptyScreen();
3207         }
3208         bindAddScreens(orderedScreenIds);
3209 
3210         // Create the custom content page (this call updates mDefaultScreen which calls
3211         // setCurrentPage() so ensure that all pages are added before calling this).
3212         if (hasCustomContentToLeft()) {
3213             mWorkspace.createCustomContentContainer();
3214             populateCustomContentContainer();
3215         }
3216 
3217         // After we have added all the screens, if the wallpaper was locked to the default state,
3218         // then notify to indicate that it can be released and a proper wallpaper offset can be
3219         // computed before the next layout
3220         mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout();
3221     }
3222 
3223     private void bindAddScreens(ArrayList<Long> orderedScreenIds) {
3224         int count = orderedScreenIds.size();
3225         for (int i = 0; i < count; i++) {
3226             long screenId = orderedScreenIds.get(i);
3227             if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) {
3228                 // No need to bind the first screen, as its always bound.
3229                 mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
3230             }
3231         }
3232     }
3233 
3234     @Override
3235     public void bindAppsAdded(final ArrayList<Long> newScreens,
3236                               final ArrayList<ItemInfo> addNotAnimated,
3237                               final ArrayList<ItemInfo> addAnimated) {
3238         Runnable r = new Runnable() {
3239             public void run() {
3240                 bindAppsAdded(newScreens, addNotAnimated, addAnimated);
3241             }
3242         };
3243         if (waitUntilResume(r)) {
3244             return;
3245         }
3246 
3247         // Add the new screens
3248         if (newScreens != null) {
3249             bindAddScreens(newScreens);
3250         }
3251 
3252         // We add the items without animation on non-visible pages, and with
3253         // animations on the new page (which we will try and snap to).
3254         if (addNotAnimated != null && !addNotAnimated.isEmpty()) {
3255             bindItems(addNotAnimated, false);
3256         }
3257         if (addAnimated != null && !addAnimated.isEmpty()) {
3258             bindItems(addAnimated, true);
3259         }
3260 
3261         // Remove the extra empty screen
3262         mWorkspace.removeExtraEmptyScreen(false, false);
3263     }
3264 
3265     /**
3266      * Bind the items start-end from the list.
3267      *
3268      * Implementation of the method from LauncherModel.Callbacks.
3269      */
3270     @Override
3271     public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) {
3272         Runnable r = new Runnable() {
3273             public void run() {
3274                 bindItems(items, forceAnimateIcons);
3275             }
3276         };
3277         if (waitUntilResume(r)) {
3278             return;
3279         }
3280 
3281         // Get the list of added items and intersect them with the set of items here
3282         final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
3283         final Collection<Animator> bounceAnims = new ArrayList<>();
3284         final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
3285         Workspace workspace = mWorkspace;
3286         long newItemsScreenId = -1;
3287         int end = items.size();
3288         for (int i = 0; i < end; i++) {
3289             final ItemInfo item = items.get(i);
3290 
3291             // Short circuit if we are loading dock items for a configuration which has no dock
3292             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
3293                     mHotseat == null) {
3294                 continue;
3295             }
3296 
3297             final View view;
3298             switch (item.itemType) {
3299                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3300                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
3301                 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
3302                     ShortcutInfo info = (ShortcutInfo) item;
3303                     view = createShortcut(info);
3304                     break;
3305                 }
3306                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
3307                     view = FolderIcon.fromXml(R.layout.folder_icon, this,
3308                             (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
3309                             (FolderInfo) item);
3310                     break;
3311                 }
3312                 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
3313                     view = inflateAppWidget((LauncherAppWidgetInfo) item);
3314                     if (view == null) {
3315                         continue;
3316                     }
3317                     break;
3318                 }
3319                 default:
3320                     throw new RuntimeException("Invalid Item Type");
3321             }
3322 
3323              /*
3324              * Remove colliding items.
3325              */
3326             if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
3327                 CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
3328                 if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
3329                     View v = cl.getChildAt(item.cellX, item.cellY);
3330                     Object tag = v.getTag();
3331                     String desc = "Collision while binding workspace item: " + item
3332                             + ". Collides with " + tag;
3333                     if (FeatureFlags.IS_DOGFOOD_BUILD) {
3334                         throw (new RuntimeException(desc));
3335                     } else {
3336                         Log.d(TAG, desc);
3337                         getModelWriter().deleteItemFromDatabase(item);
3338                         continue;
3339                     }
3340                 }
3341             }
3342             workspace.addInScreenFromBind(view, item);
3343             if (animateIcons) {
3344                 // Animate all the applications up now
3345                 view.setAlpha(0f);
3346                 view.setScaleX(0f);
3347                 view.setScaleY(0f);
3348                 bounceAnims.add(createNewAppBounceAnimation(view, i));
3349                 newItemsScreenId = item.screenId;
3350             }
3351         }
3352 
3353         if (animateIcons) {
3354             // Animate to the correct page
3355             if (newItemsScreenId > -1) {
3356                 long currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
3357                 final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
3358                 final Runnable startBounceAnimRunnable = new Runnable() {
3359                     public void run() {
3360                         anim.playTogether(bounceAnims);
3361                         anim.start();
3362                     }
3363                 };
3364                 if (newItemsScreenId != currentScreenId) {
3365                     // We post the animation slightly delayed to prevent slowdowns
3366                     // when we are loading right after we return to launcher.
3367                     mWorkspace.postDelayed(new Runnable() {
3368                         public void run() {
3369                             if (mWorkspace != null) {
3370                                 mWorkspace.snapToPage(newScreenIndex);
3371                                 mWorkspace.postDelayed(startBounceAnimRunnable,
3372                                         NEW_APPS_ANIMATION_DELAY);
3373                             }
3374                         }
3375                     }, NEW_APPS_PAGE_MOVE_DELAY);
3376                 } else {
3377                     mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
3378                 }
3379             }
3380         }
3381         workspace.requestLayout();
3382     }
3383 
3384     /**
3385      * Add the views for a widget to the workspace.
3386      */
3387     public void bindAppWidget(LauncherAppWidgetInfo item) {
3388         View view = inflateAppWidget(item);
3389         if (view != null) {
3390             mWorkspace.addInScreen(view, item);
3391             mWorkspace.requestLayout();
3392         }
3393     }
3394 
3395     private View inflateAppWidget(LauncherAppWidgetInfo item) {
3396         if (mIsSafeModeEnabled) {
3397             PendingAppWidgetHostView view =
3398                     new PendingAppWidgetHostView(this, item, mIconCache, true);
3399             prepareAppWidget(view, item);
3400             return view;
3401         }
3402 
3403         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
3404         if (DEBUG_WIDGETS) {
3405             Log.d(TAG, "bindAppWidget: " + item);
3406         }
3407 
3408         final LauncherAppWidgetProviderInfo appWidgetInfo;
3409 
3410         if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
3411             // If the provider is not ready, bind as a pending widget.
3412             appWidgetInfo = null;
3413         } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
3414             // The widget id is not valid. Try to find the widget based on the provider info.
3415             appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
3416         } else {
3417             appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
3418         }
3419 
3420         // If the provider is ready, but the width is not yet restored, try to restore it.
3421         if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) &&
3422                 (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
3423             if (appWidgetInfo == null) {
3424                 if (DEBUG_WIDGETS) {
3425                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
3426                             + " belongs to component " + item.providerName
3427                             + ", as the provider is null");
3428                 }
3429                 getModelWriter().deleteItemFromDatabase(item);
3430                 return null;
3431             }
3432 
3433             // If we do not have a valid id, try to bind an id.
3434             if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
3435                 if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
3436                     // Id has not been allocated yet. Allocate a new id.
3437                     item.appWidgetId = mAppWidgetHost.allocateAppWidgetId();
3438                     item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
3439 
3440                     // Also try to bind the widget. If the bind fails, the user will be shown
3441                     // a click to setup UI, which will ask for the bind permission.
3442                     PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo);
3443                     pendingInfo.spanX = item.spanX;
3444                     pendingInfo.spanY = item.spanY;
3445                     pendingInfo.minSpanX = item.minSpanX;
3446                     pendingInfo.minSpanY = item.minSpanY;
3447                     Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
3448 
3449                     boolean isDirectConfig =
3450                             item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
3451                     if (isDirectConfig && item.bindOptions != null) {
3452                         Bundle newOptions = item.bindOptions.getExtras();
3453                         if (options != null) {
3454                             newOptions.putAll(options);
3455                         }
3456                         options = newOptions;
3457                     }
3458                     boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
3459                             item.appWidgetId, appWidgetInfo, options);
3460 
3461                     // We tried to bind once. If we were not able to bind, we would need to
3462                     // go through the permission dialog, which means we cannot skip the config
3463                     // activity.
3464                     item.bindOptions = null;
3465                     item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG;
3466 
3467                     // Bind succeeded
3468                     if (success) {
3469                         // If the widget has a configure activity, it is still needs to set it up,
3470                         // otherwise the widget is ready to go.
3471                         item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig
3472                                 ? LauncherAppWidgetInfo.RESTORE_COMPLETED
3473                                 : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
3474                     }
3475 
3476                     getModelWriter().updateItemInDatabase(item);
3477                 }
3478             } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
3479                     && (appWidgetInfo.configure == null)) {
3480                 // The widget was marked as UI not ready, but there is no configure activity to
3481                 // update the UI.
3482                 item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
3483                 getModelWriter().updateItemInDatabase(item);
3484             }
3485         }
3486 
3487         final AppWidgetHostView view;
3488         if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
3489             if (DEBUG_WIDGETS) {
3490                 Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component "
3491                         + appWidgetInfo.provider);
3492             }
3493 
3494             // Verify that we own the widget
3495             if (appWidgetInfo == null) {
3496                 FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
3497                 deleteWidgetInfo(item);
3498                 return null;
3499             }
3500 
3501             item.minSpanX = appWidgetInfo.minSpanX;
3502             item.minSpanY = appWidgetInfo.minSpanY;
3503             view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
3504         } else {
3505             view = new PendingAppWidgetHostView(this, item, mIconCache, false);
3506         }
3507         prepareAppWidget(view, item);
3508 
3509         if (DEBUG_WIDGETS) {
3510             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
3511                     + (SystemClock.uptimeMillis()-start) + "ms");
3512         }
3513         return view;
3514     }
3515 
3516     /**
3517      * Restores a pending widget.
3518      *
3519      * @param appWidgetId The app widget id
3520      */
3521     private LauncherAppWidgetInfo completeRestoreAppWidget(int appWidgetId, int finalRestoreFlag) {
3522         LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId);
3523         if ((view == null) || !(view instanceof PendingAppWidgetHostView)) {
3524             Log.e(TAG, "Widget update called, when the widget no longer exists.");
3525             return null;
3526         }
3527 
3528         LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
3529         info.restoreStatus = finalRestoreFlag;
3530         if (info.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
3531             info.pendingItemInfo = null;
3532         }
3533 
3534         mWorkspace.reinflateWidgetsIfNecessary();
3535         getModelWriter().updateItemInDatabase(info);
3536         return info;
3537     }
3538 
3539     public void onPageBoundSynchronously(int page) {
3540         mSynchronouslyBoundPages.add(page);
3541     }
3542 
3543     @Override
3544     public void executeOnNextDraw(ViewOnDrawExecutor executor) {
3545         if (mPendingExecutor != null) {
3546             mPendingExecutor.markCompleted();
3547         }
3548         mPendingExecutor = executor;
3549         executor.attachTo(this);
3550     }
3551 
3552     public void clearPendingExecutor(ViewOnDrawExecutor executor) {
3553         if (mPendingExecutor == executor) {
3554             mPendingExecutor = null;
3555         }
3556     }
3557 
3558     @Override
3559     public void finishFirstPageBind(final ViewOnDrawExecutor executor) {
3560         Runnable r = new Runnable() {
3561             public void run() {
3562                 finishFirstPageBind(executor);
3563             }
3564         };
3565         if (waitUntilResume(r)) {
3566             return;
3567         }
3568 
3569         Runnable onComplete = new Runnable() {
3570             @Override
3571             public void run() {
3572                 if (executor != null) {
3573                     executor.onLoadAnimationCompleted();
3574                 }
3575             }
3576         };
3577         if (mDragLayer.getAlpha() < 1) {
3578             mDragLayer.animate().alpha(1).withEndAction(onComplete).start();
3579         } else {
3580             onComplete.run();
3581         }
3582     }
3583 
3584     /**
3585      * Callback saying that there aren't any more items to bind.
3586      *
3587      * Implementation of the method from LauncherModel.Callbacks.
3588      */
3589     public void finishBindingItems() {
3590         Runnable r = new Runnable() {
3591             public void run() {
3592                 finishBindingItems();
3593             }
3594         };
3595         if (waitUntilResume(r)) {
3596             return;
3597         }
3598         if (LauncherAppState.PROFILE_STARTUP) {
3599             Trace.beginSection("Page bind completed");
3600         }
3601         mWorkspace.restoreInstanceStateForRemainingPages();
3602 
3603         setWorkspaceLoading(false);
3604 
3605         if (mPendingActivityResult != null) {
3606             handleActivityResult(mPendingActivityResult.requestCode,
3607                     mPendingActivityResult.resultCode, mPendingActivityResult.data);
3608             mPendingActivityResult = null;
3609         }
3610 
3611         InstallShortcutReceiver.disableAndFlushInstallQueue(
3612                 InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
3613 
3614         NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
3615 
3616         if (mLauncherCallbacks != null) {
3617             mLauncherCallbacks.finishBindingItems(false);
3618         }
3619         if (LauncherAppState.PROFILE_STARTUP) {
3620             Trace.endSection();
3621         }
3622     }
3623 
3624     private boolean canRunNewAppsAnimation() {
3625         long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
3626         return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
3627     }
3628 
3629     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
3630         ValueAnimator bounceAnim = LauncherAnimUtils.ofViewAlphaAndScale(v, 1, 1, 1);
3631         bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
3632         bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
3633         bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION));
3634         return bounceAnim;
3635     }
3636 
3637     public boolean useVerticalBarLayout() {
3638         return mDeviceProfile.isVerticalBarLayout();
3639     }
3640 
3641     public int getSearchBarHeight() {
3642         if (mLauncherCallbacks != null) {
3643             return mLauncherCallbacks.getSearchBarHeight();
3644         }
3645         return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL;
3646     }
3647 
3648     /**
3649      * Add the icons for all apps.
3650      *
3651      * Implementation of the method from LauncherModel.Callbacks.
3652      */
3653     public void bindAllApplications(final ArrayList<AppInfo> apps) {
3654         Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_APPS) {
3655             public void run() {
3656                 bindAllApplications(apps);
3657             }
3658         };
3659         if (waitUntilResume(r)) {
3660             return;
3661         }
3662 
3663         if (mAppsView != null) {
3664             Executor pendingExecutor = getPendingExecutor();
3665             if (pendingExecutor != null && mState != State.APPS) {
3666                 // Wait until the fade in animation has finished before setting all apps list.
3667                 pendingExecutor.execute(r);
3668                 return;
3669             }
3670 
3671             mAppsView.setApps(apps);
3672         }
3673         if (mLauncherCallbacks != null) {
3674             mLauncherCallbacks.bindAllApplications(apps);
3675         }
3676     }
3677 
3678     /**
3679      * Returns an Executor that will run after the launcher is first drawn (including after the
3680      * initial fade in animation). Returns null if the first draw has already occurred.
3681      */
3682     public @Nullable Executor getPendingExecutor() {
3683         return mPendingExecutor != null && mPendingExecutor.canQueue() ? mPendingExecutor : null;
3684     }
3685 
3686     /**
3687      * Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
3688      * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
3689      */
3690     @Override
3691     public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
3692         mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
3693     }
3694 
3695     /**
3696      * A package was updated.
3697      *
3698      * Implementation of the method from LauncherModel.Callbacks.
3699      */
3700     public void bindAppsAddedOrUpdated(final ArrayList<AppInfo> apps) {
3701         Runnable r = new Runnable() {
3702             public void run() {
3703                 bindAppsAddedOrUpdated(apps);
3704             }
3705         };
3706         if (waitUntilResume(r)) {
3707             return;
3708         }
3709 
3710         if (mAppsView != null) {
3711             mAppsView.addOrUpdateApps(apps);
3712         }
3713     }
3714 
3715     @Override
3716     public void bindPromiseAppProgressUpdated(final PromiseAppInfo app) {
3717         Runnable r = new Runnable() {
3718             public void run() {
3719                 bindPromiseAppProgressUpdated(app);
3720             }
3721         };
3722         if (waitUntilResume(r)) {
3723             return;
3724         }
3725 
3726         if (mAppsView != null) {
3727             mAppsView.updatePromiseAppProgress(app);
3728         }
3729     }
3730 
3731     @Override
3732     public void bindWidgetsRestored(final ArrayList<LauncherAppWidgetInfo> widgets) {
3733         Runnable r = new Runnable() {
3734             public void run() {
3735                 bindWidgetsRestored(widgets);
3736             }
3737         };
3738         if (waitUntilResume(r)) {
3739             return;
3740         }
3741         mWorkspace.widgetsRestored(widgets);
3742     }
3743 
3744     /**
3745      * Some shortcuts were updated in the background.
3746      * Implementation of the method from LauncherModel.Callbacks.
3747      *
3748      * @param updated list of shortcuts which have changed.
3749      */
3750     @Override
3751     public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated, final UserHandle user) {
3752         Runnable r = new Runnable() {
3753             public void run() {
3754                 bindShortcutsChanged(updated, user);
3755             }
3756         };
3757         if (waitUntilResume(r)) {
3758             return;
3759         }
3760 
3761         if (!updated.isEmpty()) {
3762             mWorkspace.updateShortcuts(updated);
3763         }
3764     }
3765 
3766     /**
3767      * Update the state of a package, typically related to install state.
3768      *
3769      * Implementation of the method from LauncherModel.Callbacks.
3770      */
3771     @Override
3772     public void bindRestoreItemsChange(final HashSet<ItemInfo> updates) {
3773         Runnable r = new Runnable() {
3774             public void run() {
3775                 bindRestoreItemsChange(updates);
3776             }
3777         };
3778         if (waitUntilResume(r)) {
3779             return;
3780         }
3781 
3782         mWorkspace.updateRestoreItems(updates);
3783     }
3784 
3785     /**
3786      * A package was uninstalled/updated.  We take both the super set of packageNames
3787      * in addition to specific applications to remove, the reason being that
3788      * this can be called when a package is updated as well.  In that scenario,
3789      * we only remove specific components from the workspace and hotseat, where as
3790      * package-removal should clear all items by package name.
3791      */
3792     @Override
3793     public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) {
3794         Runnable r = new Runnable() {
3795             public void run() {
3796                 bindWorkspaceComponentsRemoved(matcher);
3797             }
3798         };
3799         if (waitUntilResume(r)) {
3800             return;
3801         }
3802         mWorkspace.removeItemsByMatcher(matcher);
3803         mDragController.onAppsRemoved(matcher);
3804     }
3805 
3806     @Override
3807     public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
3808         Runnable r = new Runnable() {
3809             public void run() {
3810                 bindAppInfosRemoved(appInfos);
3811             }
3812         };
3813         if (waitUntilResume(r)) {
3814             return;
3815         }
3816 
3817         // Update AllApps
3818         if (mAppsView != null) {
3819             mAppsView.removeApps(appInfos);
3820             tryAndUpdatePredictedApps();
3821         }
3822     }
3823 
3824     @Override
3825     public void bindAllWidgets(final MultiHashMap<PackageItemInfo, WidgetItem> allWidgets) {
3826         Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) {
3827             @Override
3828             public void run() {
3829                 bindAllWidgets(allWidgets);
3830             }
3831         };
3832         if (waitUntilResume(r)) {
3833             return;
3834         }
3835 
3836         if (mWidgetsView != null && allWidgets != null) {
3837             Executor pendingExecutor = getPendingExecutor();
3838             if (pendingExecutor != null && mState != State.WIDGETS) {
3839                 pendingExecutor.execute(r);
3840                 return;
3841             }
3842             mWidgetsView.setWidgets(allWidgets);
3843         }
3844 
3845         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
3846         if (topView != null) {
3847             topView.onWidgetsBound();
3848         }
3849     }
3850 
3851     public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
3852         return mWidgetsView.getWidgetsForPackageUser(packageUserKey);
3853     }
3854 
3855     @Override
3856     public void notifyWidgetProvidersChanged() {
3857         if (mWorkspace.getState().shouldUpdateWidget) {
3858             refreshAndBindWidgetsForPackageUser(null);
3859         }
3860     }
3861 
3862     /**
3863      * @param packageUser if null, refreshes all widgets and shortcuts, otherwise only
3864      *                    refreshes the widgets and shortcuts associated with the given package/user
3865      */
3866     public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) {
3867         mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
3868     }
3869 
3870     public void lockScreenOrientation() {
3871         if (mRotationEnabled) {
3872             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
3873         }
3874     }
3875 
3876     public void unlockScreenOrientation(boolean immediate) {
3877         if (mRotationEnabled) {
3878             if (immediate) {
3879                 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
3880             } else {
3881                 mHandler.postDelayed(new Runnable() {
3882                     public void run() {
3883                         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
3884                     }
3885                 }, RESTORE_SCREEN_ORIENTATION_DELAY);
3886             }
3887         }
3888     }
3889 
3890     private void markAppsViewShown() {
3891         if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
3892             return;
3893         }
3894         mSharedPrefs.edit().putBoolean(APPS_VIEW_SHOWN, true).apply();
3895     }
3896 
3897     private boolean shouldShowDiscoveryBounce() {
3898         UserManagerCompat um = UserManagerCompat.getInstance(this);
3899         return mState == State.WORKSPACE && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false) && !um.isDemoUser();
3900     }
3901 
3902     protected void moveWorkspaceToDefaultScreen() {
3903         mWorkspace.moveToDefaultScreen(false);
3904     }
3905 
3906     /**
3907      * $ adb shell dumpsys activity com.android.launcher3.Launcher [--all]
3908      */
3909     @Override
3910     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
3911         super.dump(prefix, fd, writer, args);
3912 
3913         if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
3914             writer.println(prefix + "Workspace Items");
3915             for (int i = mWorkspace.numCustomPages(); i < mWorkspace.getPageCount(); i++) {
3916                 writer.println(prefix + "  Homescreen " + i);
3917 
3918                 ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets();
3919                 for (int j = 0; j < layout.getChildCount(); j++) {
3920                     Object tag = layout.getChildAt(j).getTag();
3921                     if (tag != null) {
3922                         writer.println(prefix + "    " + tag.toString());
3923                     }
3924                 }
3925             }
3926 
3927             writer.println(prefix + "  Hotseat");
3928             ViewGroup layout = mHotseat.getLayout().getShortcutsAndWidgets();
3929             for (int j = 0; j < layout.getChildCount(); j++) {
3930                 Object tag = layout.getChildAt(j).getTag();
3931                 if (tag != null) {
3932                     writer.println(prefix + "    " + tag.toString());
3933                 }
3934             }
3935 
3936             try {
3937                 FileLog.flushAll(writer);
3938             } catch (Exception e) {
3939                 // Ignore
3940             }
3941         }
3942 
3943         writer.println(prefix + "Misc:");
3944         writer.print(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading);
3945         writer.print(" mPendingRequestArgs=" + mPendingRequestArgs);
3946         writer.println(" mPendingActivityResult=" + mPendingActivityResult);
3947 
3948         mModel.dumpState(prefix, fd, writer, args);
3949 
3950         if (mLauncherCallbacks != null) {
3951             mLauncherCallbacks.dump(prefix, fd, writer, args);
3952         }
3953     }
3954 
3955     @Override
3956     @TargetApi(Build.VERSION_CODES.N)
3957     public void onProvideKeyboardShortcuts(
3958             List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
3959 
3960         ArrayList<KeyboardShortcutInfo> shortcutInfos = new ArrayList<>();
3961         if (mState == State.WORKSPACE) {
3962             shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label),
3963                     KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON));
3964         }
3965         View currentFocus = getCurrentFocus();
3966         if (new CustomActionsPopup(this, currentFocus).canShow()) {
3967             shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.custom_actions),
3968                     KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON));
3969         }
3970         if (currentFocus.getTag() instanceof ItemInfo
3971                 && DeepShortcutManager.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
3972             shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.action_deep_shortcut),
3973                     KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON));
3974         }
3975         if (!shortcutInfos.isEmpty()) {
3976             data.add(new KeyboardShortcutGroup(getString(R.string.home_screen), shortcutInfos));
3977         }
3978 
3979         super.onProvideKeyboardShortcuts(data, menu, deviceId);
3980     }
3981 
3982     @Override
3983     public boolean onKeyShortcut(int keyCode, KeyEvent event) {
3984         if (event.hasModifiers(KeyEvent.META_CTRL_ON)) {
3985             switch (keyCode) {
3986                 case KeyEvent.KEYCODE_A:
3987                     if (mState == State.WORKSPACE) {
3988                         showAppsView(true, true);
3989                         return true;
3990                     }
3991                     break;
3992                 case KeyEvent.KEYCODE_S: {
3993                     View focusedView = getCurrentFocus();
3994                     if (focusedView instanceof BubbleTextView
3995                             && focusedView.getTag() instanceof ItemInfo
3996                             && mAccessibilityDelegate.performAction(focusedView,
3997                                     (ItemInfo) focusedView.getTag(),
3998                                     LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
3999                         PopupContainerWithArrow.getOpen(this).requestFocus();
4000                         return true;
4001                     }
4002                     break;
4003                 }
4004                 case KeyEvent.KEYCODE_O:
4005                     if (new CustomActionsPopup(this, getCurrentFocus()).show()) {
4006                         return true;
4007                     }
4008                     break;
4009             }
4010         }
4011         return super.onKeyShortcut(keyCode, event);
4012     }
4013 
4014     public static CustomAppWidget getCustomAppWidget(String name) {
4015         return sCustomAppWidgets.get(name);
4016     }
4017 
4018     public static HashMap<String, CustomAppWidget> getCustomAppWidgets() {
4019         return sCustomAppWidgets;
4020     }
4021 
4022     public static Launcher getLauncher(Context context) {
4023         if (context instanceof Launcher) {
4024             return (Launcher) context;
4025         }
4026         return ((Launcher) ((ContextWrapper) context).getBaseContext());
4027     }
4028 
4029     private class RotationPrefChangeHandler implements OnSharedPreferenceChangeListener {
4030 
4031         @Override
4032         public void onSharedPreferenceChanged(
4033                 SharedPreferences sharedPreferences, String key) {
4034             if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) {
4035                 // Recreate the activity so that it initializes the rotation preference again.
4036                 recreate();
4037             }
4038         }
4039     }
4040 }
4041