Home | History | Annotate | Download | only in launcher3
      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.getTag() instanceof ItemInfo
   4065                 && DeepShortcutManager.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
   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 }
   4136