OSDN Git Service

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