Home | History | Annotate | Download | only in launcher3
      1 
      2 /*
      3  * Copyright (C) 2008 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.launcher3;
     19 
     20 import android.animation.Animator;
     21 import android.animation.AnimatorListenerAdapter;
     22 import android.animation.AnimatorSet;
     23 import android.animation.ObjectAnimator;
     24 import android.animation.PropertyValuesHolder;
     25 import android.animation.TimeInterpolator;
     26 import android.animation.ValueAnimator;
     27 import android.annotation.TargetApi;
     28 import android.app.Activity;
     29 import android.app.ActivityManager;
     30 import android.app.ActivityOptions;
     31 import android.app.AlertDialog;
     32 import android.app.SearchManager;
     33 import android.appwidget.AppWidgetHostView;
     34 import android.appwidget.AppWidgetManager;
     35 import android.appwidget.AppWidgetProviderInfo;
     36 import android.content.ActivityNotFoundException;
     37 import android.content.BroadcastReceiver;
     38 import android.content.ComponentCallbacks2;
     39 import android.content.ComponentName;
     40 import android.content.ContentResolver;
     41 import android.content.Context;
     42 import android.content.DialogInterface;
     43 import android.content.Intent;
     44 import android.content.IntentFilter;
     45 import android.content.SharedPreferences;
     46 import android.content.pm.ActivityInfo;
     47 import android.content.pm.ApplicationInfo;
     48 import android.content.pm.PackageManager;
     49 import android.content.pm.PackageManager.NameNotFoundException;
     50 import android.content.res.Configuration;
     51 import android.content.res.Resources;
     52 import android.database.ContentObserver;
     53 import android.graphics.Bitmap;
     54 import android.graphics.Canvas;
     55 import android.graphics.Color;
     56 import android.graphics.Point;
     57 import android.graphics.PorterDuff;
     58 import android.graphics.Rect;
     59 import android.graphics.drawable.Drawable;
     60 import android.net.Uri;
     61 import android.os.AsyncTask;
     62 import android.os.Build;
     63 import android.os.Bundle;
     64 import android.os.Environment;
     65 import android.os.Handler;
     66 import android.os.Message;
     67 import android.os.StrictMode;
     68 import android.os.SystemClock;
     69 import android.speech.RecognizerIntent;
     70 import android.text.Selection;
     71 import android.text.SpannableStringBuilder;
     72 import android.text.TextUtils;
     73 import android.text.method.TextKeyListener;
     74 import android.util.DisplayMetrics;
     75 import android.util.Log;
     76 import android.view.ContextThemeWrapper;
     77 import android.view.Display;
     78 import android.view.Gravity;
     79 import android.view.HapticFeedbackConstants;
     80 import android.view.KeyEvent;
     81 import android.view.LayoutInflater;
     82 import android.view.Menu;
     83 import android.view.MotionEvent;
     84 import android.view.Surface;
     85 import android.view.View;
     86 import android.view.View.OnClickListener;
     87 import android.view.View.OnLongClickListener;
     88 import android.view.ViewAnimationUtils;
     89 import android.view.ViewGroup;
     90 import android.view.ViewTreeObserver;
     91 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
     92 import android.view.Window;
     93 import android.view.WindowManager;
     94 import android.view.accessibility.AccessibilityEvent;
     95 import android.view.animation.AccelerateInterpolator;
     96 import android.view.animation.DecelerateInterpolator;
     97 import android.view.animation.Interpolator;
     98 import android.view.inputmethod.InputMethodManager;
     99 import android.widget.Advanceable;
    100 import android.widget.FrameLayout;
    101 import android.widget.ImageView;
    102 import android.widget.TextView;
    103 import android.widget.Toast;
    104 
    105 import com.android.launcher3.DropTarget.DragObject;
    106 import com.android.launcher3.PagedView.PageSwitchListener;
    107 import com.android.launcher3.compat.AppWidgetManagerCompat;
    108 import com.android.launcher3.compat.LauncherActivityInfoCompat;
    109 import com.android.launcher3.compat.LauncherAppsCompat;
    110 import com.android.launcher3.compat.PackageInstallerCompat;
    111 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
    112 import com.android.launcher3.compat.UserHandleCompat;
    113 import com.android.launcher3.compat.UserManagerCompat;
    114 
    115 import java.io.DataInputStream;
    116 import java.io.DataOutputStream;
    117 import java.io.File;
    118 import java.io.FileDescriptor;
    119 import java.io.FileNotFoundException;
    120 import java.io.FileOutputStream;
    121 import java.io.IOException;
    122 import java.io.PrintWriter;
    123 import java.lang.reflect.Field;
    124 import java.lang.reflect.InvocationTargetException;
    125 import java.lang.reflect.Method;
    126 import java.text.DateFormat;
    127 import java.util.ArrayList;
    128 import java.util.Collection;
    129 import java.util.Date;
    130 import java.util.HashMap;
    131 import java.util.List;
    132 import java.util.concurrent.atomic.AtomicInteger;
    133 
    134 /**
    135  * Default launcher application.
    136  */
    137 public class Launcher extends Activity
    138         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
    139                    View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
    140     static final String TAG = "Launcher";
    141     static final boolean LOGD = false;
    142 
    143     static final boolean PROFILE_STARTUP = false;
    144     static final boolean DEBUG_WIDGETS = false;
    145     static final boolean DEBUG_STRICT_MODE = false;
    146     static final boolean DEBUG_RESUME_TIME = false;
    147     static final boolean DEBUG_DUMP_LOG = false;
    148 
    149     static final boolean ENABLE_DEBUG_INTENTS = false; // allow DebugIntents to run
    150 
    151     private static final int REQUEST_CREATE_SHORTCUT = 1;
    152     private static final int REQUEST_CREATE_APPWIDGET = 5;
    153     private static final int REQUEST_PICK_SHORTCUT = 7;
    154     private static final int REQUEST_PICK_APPWIDGET = 9;
    155     private static final int REQUEST_PICK_WALLPAPER = 10;
    156 
    157     private static final int REQUEST_BIND_APPWIDGET = 11;
    158     private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
    159 
    160     /**
    161      * IntentStarter uses request codes starting with this. This must be greater than all activity
    162      * request codes used internally.
    163      */
    164     protected static final int REQUEST_LAST = 100;
    165 
    166     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
    167 
    168     static final int SCREEN_COUNT = 5;
    169     static final int DEFAULT_SCREEN = 2;
    170 
    171     private static final String PREFERENCES = "launcher.preferences";
    172     // To turn on these properties, type
    173     // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
    174     static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate";
    175     static final String DUMP_STATE_PROPERTY = "launcher_dump_state";
    176     static final String DISABLE_ALL_APPS_PROPERTY = "launcher_noallapps";
    177 
    178     // The Intent extra that defines whether to ignore the launch animation
    179     static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
    180             "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
    181 
    182     // Type: int
    183     private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
    184     // Type: int
    185     private static final String RUNTIME_STATE = "launcher.state";
    186     // Type: int
    187     private static final String RUNTIME_STATE_PENDING_ADD_CONTAINER = "launcher.add_container";
    188     // Type: int
    189     private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
    190     // Type: int
    191     private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x";
    192     // Type: int
    193     private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y";
    194     // Type: boolean
    195     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
    196     // Type: long
    197     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
    198     // Type: int
    199     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x";
    200     // Type: int
    201     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y";
    202     // Type: parcelable
    203     private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
    204     // Type: parcelable
    205     private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_ID = "launcher.add_widget_id";
    206     // Type: int[]
    207     private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids";
    208 
    209     static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
    210     static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
    211 
    212     static final String FIRST_LOAD_COMPLETE = "launcher.first_load_complete";
    213     static final String ACTION_FIRST_LOAD_COMPLETE =
    214             "com.android.launcher3.action.FIRST_LOAD_COMPLETE";
    215 
    216     private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
    217     private static final String TOOLBAR_SEARCH_ICON_METADATA_NAME =
    218             "com.android.launcher.toolbar_search_icon";
    219     private static final String TOOLBAR_VOICE_SEARCH_ICON_METADATA_NAME =
    220             "com.android.launcher.toolbar_voice_search_icon";
    221 
    222     public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem";
    223     public static final boolean SHOW_WEIGHT_WATCHER_DEFAULT = false;
    224 
    225     public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data";
    226 
    227     /** The different states that Launcher can be in. */
    228     private enum State { NONE, WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED };
    229     private State mState = State.WORKSPACE;
    230     private AnimatorSet mStateAnimation;
    231 
    232     private boolean mIsSafeModeEnabled;
    233 
    234     static final int APPWIDGET_HOST_ID = 1024;
    235     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
    236     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
    237     private static final int ACTIVITY_START_DELAY = 1000;
    238 
    239     private static final Object sLock = new Object();
    240     private static int sScreen = DEFAULT_SCREEN;
    241 
    242     private HashMap<Integer, Integer> mItemIdToViewId = new HashMap<Integer, Integer>();
    243     private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
    244 
    245     // How long to wait before the new-shortcut animation automatically pans the workspace
    246     private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
    247     private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
    248     private static int NEW_APPS_ANIMATION_DELAY = 500;
    249     private static final int SINGLE_FRAME_DELAY = 16;
    250 
    251     private final BroadcastReceiver mCloseSystemDialogsReceiver
    252             = new CloseSystemDialogsIntentReceiver();
    253     private final ContentObserver mWidgetObserver = new AppWidgetResetObserver();
    254 
    255     private LayoutInflater mInflater;
    256 
    257     private Workspace mWorkspace;
    258     private View mLauncherView;
    259     private View mPageIndicators;
    260     private DragLayer mDragLayer;
    261     private DragController mDragController;
    262     private View mWeightWatcher;
    263 
    264     private AppWidgetManagerCompat mAppWidgetManager;
    265     private LauncherAppWidgetHost mAppWidgetHost;
    266 
    267     private ItemInfo mPendingAddInfo = new ItemInfo();
    268     private AppWidgetProviderInfo mPendingAddWidgetInfo;
    269     private int mPendingAddWidgetId = -1;
    270 
    271     private int[] mTmpAddItemCellCoordinates = new int[2];
    272 
    273     private FolderInfo mFolderInfo;
    274 
    275     private Hotseat mHotseat;
    276     private ViewGroup mOverviewPanel;
    277 
    278     private View mAllAppsButton;
    279 
    280     private SearchDropTargetBar mSearchDropTargetBar;
    281     private AppsCustomizeTabHost mAppsCustomizeTabHost;
    282     private AppsCustomizePagedView mAppsCustomizeContent;
    283     private boolean mAutoAdvanceRunning = false;
    284     private View mQsb;
    285 
    286     private Bundle mSavedState;
    287     // We set the state in both onCreate and then onNewIntent in some cases, which causes both
    288     // scroll issues (because the workspace may not have been measured yet) and extra work.
    289     // Instead, just save the state that we need to restore Launcher to, and commit it in onResume.
    290     private State mOnResumeState = State.NONE;
    291 
    292     private SpannableStringBuilder mDefaultKeySsb = null;
    293 
    294     private boolean mWorkspaceLoading = true;
    295 
    296     private boolean mPaused = true;
    297     private boolean mRestoring;
    298     private boolean mWaitingForResult;
    299     private boolean mOnResumeNeedsLoad;
    300 
    301     private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
    302     private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
    303 
    304     private Bundle mSavedInstanceState;
    305 
    306     private LauncherModel mModel;
    307     private IconCache mIconCache;
    308     private boolean mUserPresent = true;
    309     private boolean mVisible = false;
    310     private boolean mHasFocus = false;
    311     private boolean mAttached = false;
    312 
    313     private static LocaleConfiguration sLocaleConfiguration = null;
    314 
    315     private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
    316 
    317     private View.OnTouchListener mHapticFeedbackTouchListener;
    318 
    319     // Related to the auto-advancing of widgets
    320     private final int ADVANCE_MSG = 1;
    321     private final int mAdvanceInterval = 20000;
    322     private final int mAdvanceStagger = 250;
    323     private long mAutoAdvanceSentTime;
    324     private long mAutoAdvanceTimeLeft = -1;
    325     private HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance =
    326         new HashMap<View, AppWidgetProviderInfo>();
    327 
    328     // Determines how long to wait after a rotation before restoring the screen orientation to
    329     // match the sensor state.
    330     private final int mRestoreScreenOrientationDelay = 500;
    331 
    332     // External icons saved in case of resource changes, orientation, etc.
    333     private static Drawable.ConstantState[] sGlobalSearchIcon = new Drawable.ConstantState[2];
    334     private static Drawable.ConstantState[] sVoiceSearchIcon = new Drawable.ConstantState[2];
    335 
    336     private Drawable mWorkspaceBackgroundDrawable;
    337 
    338     private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
    339     private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false;
    340 
    341     static final ArrayList<String> sDumpLogs = new ArrayList<String>();
    342     static Date sDateStamp = new Date();
    343     static DateFormat sDateFormat =
    344             DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
    345     static long sRunStart = System.currentTimeMillis();
    346     static final String CORRUPTION_EMAIL_SENT_KEY = "corruptionEmailSent";
    347 
    348     // We only want to get the SharedPreferences once since it does an FS stat each time we get
    349     // it from the context.
    350     private SharedPreferences mSharedPrefs;
    351 
    352     private static ArrayList<ComponentName> mIntentsOnWorkspaceFromUpgradePath = null;
    353 
    354     // Holds the page that we need to animate to, and the icon views that we need to animate up
    355     // when we scroll to that page on resume.
    356     private ImageView mFolderIconImageView;
    357     private Bitmap mFolderIconBitmap;
    358     private Canvas mFolderIconCanvas;
    359     private Rect mRectForFolderAnimation = new Rect();
    360 
    361     private BubbleTextView mWaitingForResume;
    362 
    363     private Runnable mBuildLayersRunnable = new Runnable() {
    364         public void run() {
    365             if (mWorkspace != null) {
    366                 mWorkspace.buildPageHardwareLayers();
    367             }
    368         }
    369     };
    370 
    371     private static PendingAddArguments sPendingAddItem;
    372 
    373     public static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY);
    374 
    375     private static class PendingAddArguments {
    376         int requestCode;
    377         Intent intent;
    378         long container;
    379         long screenId;
    380         int cellX;
    381         int cellY;
    382         int appWidgetId;
    383     }
    384 
    385     private Stats mStats;
    386 
    387     FocusIndicatorView mFocusHandler;
    388 
    389     static boolean isPropertyEnabled(String propertyName) {
    390         return Log.isLoggable(propertyName, Log.VERBOSE);
    391     }
    392 
    393     @Override
    394     protected void onCreate(Bundle savedInstanceState) {
    395         if (DEBUG_STRICT_MODE) {
    396             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    397                     .detectDiskReads()
    398                     .detectDiskWrites()
    399                     .detectNetwork()   // or .detectAll() for all detectable problems
    400                     .penaltyLog()
    401                     .build());
    402             StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    403                     .detectLeakedSqlLiteObjects()
    404                     .detectLeakedClosableObjects()
    405                     .penaltyLog()
    406                     .penaltyDeath()
    407                     .build());
    408         }
    409 
    410         super.onCreate(savedInstanceState);
    411 
    412         LauncherAppState.setApplicationContext(getApplicationContext());
    413         LauncherAppState app = LauncherAppState.getInstance();
    414         LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this);
    415         // Determine the dynamic grid properties
    416         Point smallestSize = new Point();
    417         Point largestSize = new Point();
    418         Point realSize = new Point();
    419         Display display = getWindowManager().getDefaultDisplay();
    420         display.getCurrentSizeRange(smallestSize, largestSize);
    421         display.getRealSize(realSize);
    422         DisplayMetrics dm = new DisplayMetrics();
    423         display.getMetrics(dm);
    424 
    425         // Lazy-initialize the dynamic grid
    426         DeviceProfile grid = app.initDynamicGrid(this,
    427                 Math.min(smallestSize.x, smallestSize.y),
    428                 Math.min(largestSize.x, largestSize.y),
    429                 realSize.x, realSize.y,
    430                 dm.widthPixels, dm.heightPixels);
    431 
    432         // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
    433         mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
    434                 Context.MODE_PRIVATE);
    435         mIsSafeModeEnabled = getPackageManager().isSafeMode();
    436         mModel = app.setLauncher(this);
    437         mIconCache = app.getIconCache();
    438         mIconCache.flushInvalidIcons(grid);
    439         mDragController = new DragController(this);
    440         mInflater = getLayoutInflater();
    441 
    442         mStats = new Stats(this);
    443 
    444         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
    445 
    446         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
    447         mAppWidgetHost.startListening();
    448 
    449         // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
    450         // this also ensures that any synchronous binding below doesn't re-trigger another
    451         // LauncherModel load.
    452         mPaused = false;
    453 
    454         if (PROFILE_STARTUP) {
    455             android.os.Debug.startMethodTracing(
    456                     Environment.getExternalStorageDirectory() + "/launcher");
    457         }
    458 
    459         checkForLocaleChange();
    460         setContentView(R.layout.launcher);
    461 
    462         setupViews();
    463         grid.layout(this);
    464 
    465         registerContentObservers();
    466 
    467         lockAllApps();
    468 
    469         mSavedState = savedInstanceState;
    470         restoreState(mSavedState);
    471 
    472         if (PROFILE_STARTUP) {
    473             android.os.Debug.stopMethodTracing();
    474         }
    475 
    476         if (!mRestoring) {
    477             if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
    478                 // If the user leaves launcher, then we should just load items asynchronously when
    479                 // they return.
    480                 mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
    481             } else {
    482                 // We only load the page synchronously if the user rotates (or triggers a
    483                 // configuration change) while launcher is in the foreground
    484                 mModel.startLoader(true, mWorkspace.getRestorePage());
    485             }
    486         }
    487 
    488         // For handling default keys
    489         mDefaultKeySsb = new SpannableStringBuilder();
    490         Selection.setSelection(mDefaultKeySsb, 0);
    491 
    492         IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    493         registerReceiver(mCloseSystemDialogsReceiver, filter);
    494 
    495         updateGlobalIcons();
    496 
    497         // On large interfaces, we want the screen to auto-rotate based on the current orientation
    498         unlockScreenOrientation(true);
    499 
    500         if (shouldShowIntroScreen()) {
    501             showIntroScreen();
    502         } else {
    503             showFirstRunActivity();
    504             showFirstRunClings();
    505         }
    506     }
    507 
    508     @Override
    509     public void onLauncherProviderChange() { }
    510 
    511     /** To be overriden by subclasses to hint to Launcher that we have custom content */
    512     protected boolean hasCustomContentToLeft() {
    513         return false;
    514     }
    515 
    516     /**
    517      * To be overridden by subclasses to populate the custom content container and call
    518      * {@link #addToCustomContentPage}. This will only be invoked if
    519      * {@link #hasCustomContentToLeft()} is {@code true}.
    520      */
    521     protected void populateCustomContentContainer() {
    522     }
    523 
    524     /**
    525      * Invoked by subclasses to signal a change to the {@link #addCustomContentToLeft} value to
    526      * ensure the custom content page is added or removed if necessary.
    527      */
    528     protected void invalidateHasCustomContentToLeft() {
    529         if (mWorkspace == null || mWorkspace.getScreenOrder().isEmpty()) {
    530             // Not bound yet, wait for bindScreens to be called.
    531             return;
    532         }
    533 
    534         if (!mWorkspace.hasCustomContent() && hasCustomContentToLeft()) {
    535             // Create the custom content page and call the subclass to populate it.
    536             mWorkspace.createCustomContentContainer();
    537             populateCustomContentContainer();
    538         } else if (mWorkspace.hasCustomContent() && !hasCustomContentToLeft()) {
    539             mWorkspace.removeCustomContentPage();
    540         }
    541     }
    542 
    543     private void updateGlobalIcons() {
    544         boolean searchVisible = false;
    545         boolean voiceVisible = false;
    546         // If we have a saved version of these external icons, we load them up immediately
    547         int coi = getCurrentOrientationIndexForGlobalIcons();
    548         if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null) {
    549             searchVisible = updateGlobalSearchIcon();
    550             voiceVisible = updateVoiceSearchIcon(searchVisible);
    551         }
    552         if (sGlobalSearchIcon[coi] != null) {
    553              updateGlobalSearchIcon(sGlobalSearchIcon[coi]);
    554              searchVisible = true;
    555         }
    556         if (sVoiceSearchIcon[coi] != null) {
    557             updateVoiceSearchIcon(sVoiceSearchIcon[coi]);
    558             voiceVisible = true;
    559         }
    560         if (mSearchDropTargetBar != null) {
    561             mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
    562         }
    563     }
    564 
    565     private void checkForLocaleChange() {
    566         if (sLocaleConfiguration == null) {
    567             new AsyncTask<Void, Void, LocaleConfiguration>() {
    568                 @Override
    569                 protected LocaleConfiguration doInBackground(Void... unused) {
    570                     LocaleConfiguration localeConfiguration = new LocaleConfiguration();
    571                     readConfiguration(Launcher.this, localeConfiguration);
    572                     return localeConfiguration;
    573                 }
    574 
    575                 @Override
    576                 protected void onPostExecute(LocaleConfiguration result) {
    577                     sLocaleConfiguration = result;
    578                     checkForLocaleChange();  // recursive, but now with a locale configuration
    579                 }
    580             }.execute();
    581             return;
    582         }
    583 
    584         final Configuration configuration = getResources().getConfiguration();
    585 
    586         final String previousLocale = sLocaleConfiguration.locale;
    587         final String locale = configuration.locale.toString();
    588 
    589         final int previousMcc = sLocaleConfiguration.mcc;
    590         final int mcc = configuration.mcc;
    591 
    592         final int previousMnc = sLocaleConfiguration.mnc;
    593         final int mnc = configuration.mnc;
    594 
    595         boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
    596 
    597         if (localeChanged) {
    598             sLocaleConfiguration.locale = locale;
    599             sLocaleConfiguration.mcc = mcc;
    600             sLocaleConfiguration.mnc = mnc;
    601 
    602             mIconCache.flush();
    603 
    604             final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
    605             new AsyncTask<Void, Void, Void>() {
    606                 public Void doInBackground(Void ... args) {
    607                     writeConfiguration(Launcher.this, localeConfiguration);
    608                     return null;
    609                 }
    610             }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
    611         }
    612     }
    613 
    614     private static class LocaleConfiguration {
    615         public String locale;
    616         public int mcc = -1;
    617         public int mnc = -1;
    618     }
    619 
    620     private static void readConfiguration(Context context, LocaleConfiguration configuration) {
    621         DataInputStream in = null;
    622         try {
    623             in = new DataInputStream(context.openFileInput(PREFERENCES));
    624             configuration.locale = in.readUTF();
    625             configuration.mcc = in.readInt();
    626             configuration.mnc = in.readInt();
    627         } catch (FileNotFoundException e) {
    628             // Ignore
    629         } catch (IOException e) {
    630             // Ignore
    631         } finally {
    632             if (in != null) {
    633                 try {
    634                     in.close();
    635                 } catch (IOException e) {
    636                     // Ignore
    637                 }
    638             }
    639         }
    640     }
    641 
    642     private static void writeConfiguration(Context context, LocaleConfiguration configuration) {
    643         DataOutputStream out = null;
    644         try {
    645             out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE));
    646             out.writeUTF(configuration.locale);
    647             out.writeInt(configuration.mcc);
    648             out.writeInt(configuration.mnc);
    649             out.flush();
    650         } catch (FileNotFoundException e) {
    651             // Ignore
    652         } catch (IOException e) {
    653             //noinspection ResultOfMethodCallIgnored
    654             context.getFileStreamPath(PREFERENCES).delete();
    655         } finally {
    656             if (out != null) {
    657                 try {
    658                     out.close();
    659                 } catch (IOException e) {
    660                     // Ignore
    661                 }
    662             }
    663         }
    664     }
    665 
    666     public Stats getStats() {
    667         return mStats;
    668     }
    669 
    670     public LayoutInflater getInflater() {
    671         return mInflater;
    672     }
    673 
    674     boolean isDraggingEnabled() {
    675         // We prevent dragging when we are loading the workspace as it is possible to pick up a view
    676         // that is subsequently removed from the workspace in startBinding().
    677         return !mModel.isLoadingWorkspace();
    678     }
    679 
    680     static int getScreen() {
    681         synchronized (sLock) {
    682             return sScreen;
    683         }
    684     }
    685 
    686     static void setScreen(int screen) {
    687         synchronized (sLock) {
    688             sScreen = screen;
    689         }
    690     }
    691 
    692     public static int generateViewId() {
    693         if (Build.VERSION.SDK_INT >= 17) {
    694             return View.generateViewId();
    695         } else {
    696             // View.generateViewId() is not available. The following fallback logic is a copy
    697             // of its implementation.
    698             for (;;) {
    699                 final int result = sNextGeneratedId.get();
    700                 // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
    701                 int newValue = result + 1;
    702                 if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
    703                 if (sNextGeneratedId.compareAndSet(result, newValue)) {
    704                     return result;
    705                 }
    706             }
    707         }
    708     }
    709 
    710     public int getViewIdForItem(ItemInfo info) {
    711         // This cast is safe given the > 2B range for int.
    712         int itemId = (int) info.id;
    713         if (mItemIdToViewId.containsKey(itemId)) {
    714             return mItemIdToViewId.get(itemId);
    715         }
    716         int viewId = generateViewId();
    717         mItemIdToViewId.put(itemId, viewId);
    718         return viewId;
    719     }
    720 
    721     /**
    722      * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
    723      * a configuration step, this allows the proper animations to run after other transitions.
    724      */
    725     private long completeAdd(PendingAddArguments args) {
    726         long screenId = args.screenId;
    727         if (args.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
    728             // When the screen id represents an actual screen (as opposed to a rank) we make sure
    729             // that the drop page actually exists.
    730             screenId = ensurePendingDropLayoutExists(args.screenId);
    731         }
    732 
    733         switch (args.requestCode) {
    734             case REQUEST_CREATE_SHORTCUT:
    735                 completeAddShortcut(args.intent, args.container, screenId, args.cellX,
    736                         args.cellY);
    737                 break;
    738             case REQUEST_CREATE_APPWIDGET:
    739                 completeAddAppWidget(args.appWidgetId, args.container, screenId, null, null);
    740                 break;
    741             case REQUEST_RECONFIGURE_APPWIDGET:
    742                 completeRestoreAppWidget(args.appWidgetId);
    743                 break;
    744         }
    745         // Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
    746         // if you turned the screen off and then back while in All Apps, Launcher would not
    747         // return to the workspace. Clearing mAddInfo.container here fixes this issue
    748         resetAddInfo();
    749         return screenId;
    750     }
    751 
    752     @Override
    753     protected void onActivityResult(
    754             final int requestCode, final int resultCode, final Intent data) {
    755         // Reset the startActivity waiting flag
    756         setWaitingForResult(false);
    757         final int pendingAddWidgetId = mPendingAddWidgetId;
    758         mPendingAddWidgetId = -1;
    759 
    760         Runnable exitSpringLoaded = new Runnable() {
    761             @Override
    762             public void run() {
    763                 exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
    764                         EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
    765             }
    766         };
    767 
    768         if (requestCode == REQUEST_BIND_APPWIDGET) {
    769             final int appWidgetId = data != null ?
    770                     data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
    771             if (resultCode == RESULT_CANCELED) {
    772                 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
    773                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
    774                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    775             } else if (resultCode == RESULT_OK) {
    776                 addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
    777                         mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
    778             }
    779             return;
    780         } else if (requestCode == REQUEST_PICK_WALLPAPER) {
    781             if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) {
    782                 mWorkspace.exitOverviewMode(false);
    783             }
    784             return;
    785         }
    786 
    787         boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
    788                 requestCode == REQUEST_CREATE_APPWIDGET);
    789 
    790         final boolean workspaceLocked = isWorkspaceLocked();
    791         // We have special handling for widgets
    792         if (isWidgetDrop) {
    793             final int appWidgetId;
    794             int widgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
    795                     : -1;
    796             if (widgetId < 0) {
    797                 appWidgetId = pendingAddWidgetId;
    798             } else {
    799                 appWidgetId = widgetId;
    800             }
    801 
    802             final int result;
    803             if (appWidgetId < 0 || resultCode == RESULT_CANCELED) {
    804                 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " +
    805                         "returned from the widget configuration activity.");
    806                 result = RESULT_CANCELED;
    807                 completeTwoStageWidgetDrop(result, appWidgetId);
    808                 final Runnable onComplete = new Runnable() {
    809                     @Override
    810                     public void run() {
    811                         exitSpringLoadedDragModeDelayed(false, 0, null);
    812                     }
    813                 };
    814                 if (workspaceLocked) {
    815                     // No need to remove the empty screen if we're mid-binding, as the
    816                     // the bind will not add the empty screen.
    817                     mWorkspace.postDelayed(onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
    818                 } else {
    819                     mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
    820                             ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    821                 }
    822             } else {
    823                 if (!workspaceLocked) {
    824                     if (mPendingAddInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
    825                         // When the screen id represents an actual screen (as opposed to a rank)
    826                         // we make sure that the drop page actually exists.
    827                         mPendingAddInfo.screenId =
    828                                 ensurePendingDropLayoutExists(mPendingAddInfo.screenId);
    829                     }
    830                     final CellLayout dropLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
    831 
    832                     dropLayout.setDropPending(true);
    833                     final Runnable onComplete = new Runnable() {
    834                         @Override
    835                         public void run() {
    836                             completeTwoStageWidgetDrop(resultCode, appWidgetId);
    837                             dropLayout.setDropPending(false);
    838                         }
    839                     };
    840                     mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
    841                             ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    842                 } else {
    843                     PendingAddArguments args = preparePendingAddArgs(requestCode, data, appWidgetId,
    844                             mPendingAddInfo);
    845                     sPendingAddItem = args;
    846                 }
    847             }
    848             return;
    849         }
    850 
    851         if (requestCode == REQUEST_RECONFIGURE_APPWIDGET) {
    852             if (resultCode == RESULT_OK) {
    853                 // Update the widget view.
    854                 PendingAddArguments args = preparePendingAddArgs(requestCode, data,
    855                         pendingAddWidgetId, mPendingAddInfo);
    856                 if (workspaceLocked) {
    857                     sPendingAddItem = args;
    858                 } else {
    859                     completeAdd(args);
    860                 }
    861             }
    862             // Leave the widget in the pending state if the user canceled the configure.
    863             return;
    864         }
    865 
    866         // The pattern used here is that a user PICKs a specific application,
    867         // which, depending on the target, might need to CREATE the actual target.
    868 
    869         // For example, the user would PICK_SHORTCUT for "Music playlist", and we
    870         // launch over to the Music app to actually CREATE_SHORTCUT.
    871         if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
    872             final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1,
    873                     mPendingAddInfo);
    874             if (isWorkspaceLocked()) {
    875                 sPendingAddItem = args;
    876             } else {
    877                 completeAdd(args);
    878                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
    879                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    880             }
    881         } else if (resultCode == RESULT_CANCELED) {
    882             mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
    883                     ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    884         }
    885         mDragLayer.clearAnimatedView();
    886     }
    887 
    888     private PendingAddArguments preparePendingAddArgs(int requestCode, Intent data, int
    889             appWidgetId, ItemInfo info) {
    890         PendingAddArguments args = new PendingAddArguments();
    891         args.requestCode = requestCode;
    892         args.intent = data;
    893         args.container = info.container;
    894         args.screenId = info.screenId;
    895         args.cellX = info.cellX;
    896         args.cellY = info.cellY;
    897         args.appWidgetId = appWidgetId;
    898         return args;
    899     }
    900 
    901     /**
    902      * Check to see if a given screen id exists. If not, create it at the end, return the new id.
    903      *
    904      * @param screenId the screen id to check
    905      * @return the new screen, or screenId if it exists
    906      */
    907     private long ensurePendingDropLayoutExists(long screenId) {
    908         CellLayout dropLayout =
    909                 (CellLayout) mWorkspace.getScreenWithId(screenId);
    910         if (dropLayout == null) {
    911             // it's possible that the add screen was removed because it was
    912             // empty and a re-bind occurred
    913             mWorkspace.addExtraEmptyScreen();
    914             return mWorkspace.commitExtraEmptyScreen();
    915         } else {
    916             return screenId;
    917         }
    918     }
    919 
    920     private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
    921         CellLayout cellLayout =
    922                 (CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
    923         Runnable onCompleteRunnable = null;
    924         int animationType = 0;
    925 
    926         AppWidgetHostView boundWidget = null;
    927         if (resultCode == RESULT_OK) {
    928             animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
    929             final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
    930                     mPendingAddWidgetInfo);
    931             boundWidget = layout;
    932             onCompleteRunnable = new Runnable() {
    933                 @Override
    934                 public void run() {
    935                     completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
    936                             mPendingAddInfo.screenId, layout, null);
    937                     exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
    938                             EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
    939                 }
    940             };
    941         } else if (resultCode == RESULT_CANCELED) {
    942             mAppWidgetHost.deleteAppWidgetId(appWidgetId);
    943             animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
    944         }
    945         if (mDragLayer.getAnimatedView() != null) {
    946             mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout,
    947                     (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
    948                     animationType, boundWidget, true);
    949         } else if (onCompleteRunnable != null) {
    950             // The animated view may be null in the case of a rotation during widget configuration
    951             onCompleteRunnable.run();
    952         }
    953     }
    954 
    955     @Override
    956     protected void onStop() {
    957         super.onStop();
    958         FirstFrameAnimatorHelper.setIsVisible(false);
    959     }
    960 
    961     @Override
    962     protected void onStart() {
    963         super.onStart();
    964         FirstFrameAnimatorHelper.setIsVisible(true);
    965     }
    966 
    967     @Override
    968     protected void onResume() {
    969         long startTime = 0;
    970         if (DEBUG_RESUME_TIME) {
    971             startTime = System.currentTimeMillis();
    972             Log.v(TAG, "Launcher.onResume()");
    973         }
    974         super.onResume();
    975 
    976         // Restore the previous launcher state
    977         if (mOnResumeState == State.WORKSPACE) {
    978             showWorkspace(false);
    979         } else if (mOnResumeState == State.APPS_CUSTOMIZE) {
    980             showAllApps(false, mAppsCustomizeContent.getContentType(), false);
    981         }
    982         mOnResumeState = State.NONE;
    983 
    984         // Background was set to gradient in onPause(), restore to black if in all apps.
    985         setWorkspaceBackground(mState == State.WORKSPACE);
    986 
    987         mPaused = false;
    988         if (mRestoring || mOnResumeNeedsLoad) {
    989             setWorkspaceLoading(true);
    990             mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
    991             mRestoring = false;
    992             mOnResumeNeedsLoad = false;
    993         }
    994         if (mBindOnResumeCallbacks.size() > 0) {
    995             // We might have postponed some bind calls until onResume (see waitUntilResume) --
    996             // execute them here
    997             long startTimeCallbacks = 0;
    998             if (DEBUG_RESUME_TIME) {
    999                 startTimeCallbacks = System.currentTimeMillis();
   1000             }
   1001 
   1002             if (mAppsCustomizeContent != null) {
   1003                 mAppsCustomizeContent.setBulkBind(true);
   1004             }
   1005             for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
   1006                 mBindOnResumeCallbacks.get(i).run();
   1007             }
   1008             if (mAppsCustomizeContent != null) {
   1009                 mAppsCustomizeContent.setBulkBind(false);
   1010             }
   1011             mBindOnResumeCallbacks.clear();
   1012             if (DEBUG_RESUME_TIME) {
   1013                 Log.d(TAG, "Time spent processing callbacks in onResume: " +
   1014                     (System.currentTimeMillis() - startTimeCallbacks));
   1015             }
   1016         }
   1017         if (mOnResumeCallbacks.size() > 0) {
   1018             for (int i = 0; i < mOnResumeCallbacks.size(); i++) {
   1019                 mOnResumeCallbacks.get(i).run();
   1020             }
   1021             mOnResumeCallbacks.clear();
   1022         }
   1023 
   1024         // Reset the pressed state of icons that were locked in the press state while activities
   1025         // were launching
   1026         if (mWaitingForResume != null) {
   1027             // Resets the previous workspace icon press state
   1028             mWaitingForResume.setStayPressed(false);
   1029         }
   1030 
   1031         // It is possible that widgets can receive updates while launcher is not in the foreground.
   1032         // Consequently, the widgets will be inflated in the orientation of the foreground activity
   1033         // (framework issue). On resuming, we ensure that any widgets are inflated for the current
   1034         // orientation.
   1035         getWorkspace().reinflateWidgetsIfNecessary();
   1036 
   1037         // Process any items that were added while Launcher was away.
   1038         InstallShortcutReceiver.disableAndFlushInstallQueue(this);
   1039 
   1040         // Update the voice search button proxy
   1041         updateVoiceButtonProxyVisible(false);
   1042 
   1043         // Again, as with the above scenario, it's possible that one or more of the global icons
   1044         // were updated in the wrong orientation.
   1045         updateGlobalIcons();
   1046         if (DEBUG_RESUME_TIME) {
   1047             Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime));
   1048         }
   1049 
   1050         if (mWorkspace.getCustomContentCallbacks() != null) {
   1051             // If we are resuming and the custom content is the current page, we call onShow().
   1052             // It is also poassible that onShow will instead be called slightly after first layout
   1053             // if PagedView#setRestorePage was set to the custom content page in onCreate().
   1054             if (mWorkspace.isOnOrMovingToCustomContent()) {
   1055                 mWorkspace.getCustomContentCallbacks().onShow(true);
   1056             }
   1057         }
   1058         mWorkspace.updateInteractionForState();
   1059         mWorkspace.onResume();
   1060 
   1061         PackageInstallerCompat.getInstance(this).onResume();
   1062     }
   1063 
   1064     @Override
   1065     protected void onPause() {
   1066         // Ensure that items added to Launcher are queued until Launcher returns
   1067         InstallShortcutReceiver.enableInstallQueue();
   1068         PackageInstallerCompat.getInstance(this).onPause();
   1069 
   1070         super.onPause();
   1071         mPaused = true;
   1072         mDragController.cancelDrag();
   1073         mDragController.resetLastGestureUpTime();
   1074 
   1075         // We call onHide() aggressively. The custom content callbacks should be able to
   1076         // debounce excess onHide calls.
   1077         if (mWorkspace.getCustomContentCallbacks() != null) {
   1078             mWorkspace.getCustomContentCallbacks().onHide();
   1079         }
   1080     }
   1081 
   1082     QSBScroller mQsbScroller = new QSBScroller() {
   1083         int scrollY = 0;
   1084 
   1085         @Override
   1086         public void setScrollY(int scroll) {
   1087             scrollY = scroll;
   1088 
   1089             if (mWorkspace.isOnOrMovingToCustomContent()) {
   1090                 mSearchDropTargetBar.setTranslationY(- scrollY);
   1091                 getQsbBar().setTranslationY(-scrollY);
   1092             }
   1093         }
   1094     };
   1095 
   1096     public void resetQSBScroll() {
   1097         mSearchDropTargetBar.animate().translationY(0).start();
   1098         getQsbBar().animate().translationY(0).start();
   1099     }
   1100 
   1101     public interface CustomContentCallbacks {
   1102         // Custom content is completely shown. {@code fromResume} indicates whether this was caused
   1103         // by a onResume or by scrolling otherwise.
   1104         public void onShow(boolean fromResume);
   1105 
   1106         // Custom content is completely hidden
   1107         public void onHide();
   1108 
   1109         // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
   1110         public void onScrollProgressChanged(float progress);
   1111 
   1112         // Indicates whether the user is allowed to scroll away from the custom content.
   1113         boolean isScrollingAllowed();
   1114     }
   1115 
   1116     protected boolean hasSettings() {
   1117         return false;
   1118     }
   1119 
   1120     public interface QSBScroller {
   1121         public void setScrollY(int scrollY);
   1122     }
   1123 
   1124     public QSBScroller addToCustomContentPage(View customContent,
   1125             CustomContentCallbacks callbacks, String description) {
   1126         mWorkspace.addToCustomContentPage(customContent, callbacks, description);
   1127         return mQsbScroller;
   1128     }
   1129 
   1130     // The custom content needs to offset its content to account for the QSB
   1131     public int getTopOffsetForCustomContent() {
   1132         return mWorkspace.getPaddingTop();
   1133     }
   1134 
   1135     @Override
   1136     public Object onRetainNonConfigurationInstance() {
   1137         // Flag the loader to stop early before switching
   1138         if (mModel.isCurrentCallbacks(this)) {
   1139             mModel.stopLoader();
   1140         }
   1141         if (mAppsCustomizeContent != null) {
   1142             mAppsCustomizeContent.surrender();
   1143         }
   1144         return Boolean.TRUE;
   1145     }
   1146 
   1147     // We can't hide the IME if it was forced open.  So don't bother
   1148     @Override
   1149     public void onWindowFocusChanged(boolean hasFocus) {
   1150         super.onWindowFocusChanged(hasFocus);
   1151         mHasFocus = hasFocus;
   1152     }
   1153 
   1154     private boolean acceptFilter() {
   1155         final InputMethodManager inputManager = (InputMethodManager)
   1156                 getSystemService(Context.INPUT_METHOD_SERVICE);
   1157         return !inputManager.isFullscreenMode();
   1158     }
   1159 
   1160     @Override
   1161     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1162         final int uniChar = event.getUnicodeChar();
   1163         final boolean handled = super.onKeyDown(keyCode, event);
   1164         final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar);
   1165         if (!handled && acceptFilter() && isKeyNotWhitespace) {
   1166             boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
   1167                     keyCode, event);
   1168             if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
   1169                 // something usable has been typed - start a search
   1170                 // the typed text will be retrieved and cleared by
   1171                 // showSearchDialog()
   1172                 // If there are multiple keystrokes before the search dialog takes focus,
   1173                 // onSearchRequested() will be called for every keystroke,
   1174                 // but it is idempotent, so it's fine.
   1175                 return onSearchRequested();
   1176             }
   1177         }
   1178 
   1179         // Eat the long press event so the keyboard doesn't come up.
   1180         if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
   1181             return true;
   1182         }
   1183 
   1184         return handled;
   1185     }
   1186 
   1187     private String getTypedText() {
   1188         return mDefaultKeySsb.toString();
   1189     }
   1190 
   1191     private void clearTypedText() {
   1192         mDefaultKeySsb.clear();
   1193         mDefaultKeySsb.clearSpans();
   1194         Selection.setSelection(mDefaultKeySsb, 0);
   1195     }
   1196 
   1197     /**
   1198      * Given the integer (ordinal) value of a State enum instance, convert it to a variable of type
   1199      * State
   1200      */
   1201     private static State intToState(int stateOrdinal) {
   1202         State state = State.WORKSPACE;
   1203         final State[] stateValues = State.values();
   1204         for (int i = 0; i < stateValues.length; i++) {
   1205             if (stateValues[i].ordinal() == stateOrdinal) {
   1206                 state = stateValues[i];
   1207                 break;
   1208             }
   1209         }
   1210         return state;
   1211     }
   1212 
   1213     /**
   1214      * Restores the previous state, if it exists.
   1215      *
   1216      * @param savedState The previous state.
   1217      */
   1218     @SuppressWarnings("unchecked")
   1219     private void restoreState(Bundle savedState) {
   1220         if (savedState == null) {
   1221             return;
   1222         }
   1223 
   1224         State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()));
   1225         if (state == State.APPS_CUSTOMIZE) {
   1226             mOnResumeState = State.APPS_CUSTOMIZE;
   1227         }
   1228 
   1229         int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN,
   1230                 PagedView.INVALID_RESTORE_PAGE);
   1231         if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
   1232             mWorkspace.setRestorePage(currentScreen);
   1233         }
   1234 
   1235         final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
   1236         final long pendingAddScreen = savedState.getLong(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
   1237 
   1238         if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
   1239             mPendingAddInfo.container = pendingAddContainer;
   1240             mPendingAddInfo.screenId = pendingAddScreen;
   1241             mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
   1242             mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
   1243             mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
   1244             mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
   1245             mPendingAddWidgetInfo = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
   1246             mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID);
   1247             setWaitingForResult(true);
   1248             mRestoring = true;
   1249         }
   1250 
   1251         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
   1252         if (renameFolder) {
   1253             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
   1254             mFolderInfo = mModel.getFolderById(this, sFolders, id);
   1255             mRestoring = true;
   1256         }
   1257 
   1258         // Restore the AppsCustomize tab
   1259         if (mAppsCustomizeTabHost != null) {
   1260             String curTab = savedState.getString("apps_customize_currentTab");
   1261             if (curTab != null) {
   1262                 mAppsCustomizeTabHost.setContentTypeImmediate(
   1263                         mAppsCustomizeTabHost.getContentTypeForTabTag(curTab));
   1264                 mAppsCustomizeContent.loadAssociatedPages(
   1265                         mAppsCustomizeContent.getCurrentPage());
   1266             }
   1267 
   1268             int currentIndex = savedState.getInt("apps_customize_currentIndex");
   1269             mAppsCustomizeContent.restorePageForIndex(currentIndex);
   1270         }
   1271         mItemIdToViewId = (HashMap<Integer, Integer>)
   1272                 savedState.getSerializable(RUNTIME_STATE_VIEW_IDS);
   1273     }
   1274 
   1275     /**
   1276      * Finds all the views we need and configure them properly.
   1277      */
   1278     private void setupViews() {
   1279         final DragController dragController = mDragController;
   1280 
   1281         mLauncherView = findViewById(R.id.launcher);
   1282         mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
   1283         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
   1284         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
   1285         mWorkspace.setPageSwitchListener(this);
   1286         mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
   1287 
   1288         mLauncherView.setSystemUiVisibility(
   1289                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
   1290         mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg);
   1291 
   1292         // Setup the drag layer
   1293         mDragLayer.setup(this, dragController);
   1294 
   1295         // Setup the hotseat
   1296         mHotseat = (Hotseat) findViewById(R.id.hotseat);
   1297         if (mHotseat != null) {
   1298             mHotseat.setup(this);
   1299             mHotseat.setOnLongClickListener(this);
   1300         }
   1301 
   1302         mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
   1303         View widgetButton = findViewById(R.id.widget_button);
   1304         widgetButton.setOnClickListener(new OnClickListener() {
   1305             @Override
   1306             public void onClick(View arg0) {
   1307                 if (!mWorkspace.isSwitchingState()) {
   1308                     onClickAddWidgetButton(arg0);
   1309                 }
   1310             }
   1311         });
   1312         widgetButton.setOnTouchListener(getHapticFeedbackTouchListener());
   1313 
   1314         View wallpaperButton = findViewById(R.id.wallpaper_button);
   1315         wallpaperButton.setOnClickListener(new OnClickListener() {
   1316             @Override
   1317             public void onClick(View arg0) {
   1318                 if (!mWorkspace.isSwitchingState()) {
   1319                     onClickWallpaperPicker(arg0);
   1320                 }
   1321             }
   1322         });
   1323         wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
   1324 
   1325         View settingsButton = findViewById(R.id.settings_button);
   1326         if (hasSettings()) {
   1327             settingsButton.setOnClickListener(new OnClickListener() {
   1328                 @Override
   1329                 public void onClick(View arg0) {
   1330                     if (!mWorkspace.isSwitchingState()) {
   1331                         onClickSettingsButton(arg0);
   1332                     }
   1333                 }
   1334             });
   1335             settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
   1336         } else {
   1337             settingsButton.setVisibility(View.GONE);
   1338             FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) widgetButton.getLayoutParams();
   1339             lp.gravity = Gravity.END | Gravity.TOP;
   1340             widgetButton.requestLayout();
   1341         }
   1342 
   1343         mOverviewPanel.setAlpha(0f);
   1344 
   1345         // Setup the workspace
   1346         mWorkspace.setHapticFeedbackEnabled(false);
   1347         mWorkspace.setOnLongClickListener(this);
   1348         mWorkspace.setup(dragController);
   1349         dragController.addDragListener(mWorkspace);
   1350 
   1351         // Get the search/delete bar
   1352         mSearchDropTargetBar = (SearchDropTargetBar)
   1353                 mDragLayer.findViewById(R.id.search_drop_target_bar);
   1354 
   1355         // Setup AppsCustomize
   1356         mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane);
   1357         mAppsCustomizeContent = (AppsCustomizePagedView)
   1358                 mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
   1359         mAppsCustomizeContent.setup(this, dragController);
   1360 
   1361         // Setup the drag controller (drop targets have to be added in reverse order in priority)
   1362         dragController.setDragScoller(mWorkspace);
   1363         dragController.setScrollView(mDragLayer);
   1364         dragController.setMoveTarget(mWorkspace);
   1365         dragController.addDropTarget(mWorkspace);
   1366         if (mSearchDropTargetBar != null) {
   1367             mSearchDropTargetBar.setup(this, dragController);
   1368         }
   1369 
   1370         if (getResources().getBoolean(R.bool.debug_memory_enabled)) {
   1371             Log.v(TAG, "adding WeightWatcher");
   1372             mWeightWatcher = new WeightWatcher(this);
   1373             mWeightWatcher.setAlpha(0.5f);
   1374             ((FrameLayout) mLauncherView).addView(mWeightWatcher,
   1375                     new FrameLayout.LayoutParams(
   1376                             FrameLayout.LayoutParams.MATCH_PARENT,
   1377                             FrameLayout.LayoutParams.WRAP_CONTENT,
   1378                             Gravity.BOTTOM)
   1379             );
   1380 
   1381             boolean show = shouldShowWeightWatcher();
   1382             mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
   1383         }
   1384     }
   1385 
   1386     /**
   1387      * Sets the all apps button. This method is called from {@link Hotseat}.
   1388      */
   1389     public void setAllAppsButton(View allAppsButton) {
   1390         mAllAppsButton = allAppsButton;
   1391     }
   1392 
   1393     public View getAllAppsButton() {
   1394         return mAllAppsButton;
   1395     }
   1396 
   1397     /**
   1398      * Creates a view representing a shortcut.
   1399      *
   1400      * @param info The data structure describing the shortcut.
   1401      *
   1402      * @return A View inflated from R.layout.application.
   1403      */
   1404     View createShortcut(ShortcutInfo info) {
   1405         return createShortcut(R.layout.application,
   1406                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
   1407     }
   1408 
   1409     /**
   1410      * Creates a view representing a shortcut inflated from the specified resource.
   1411      *
   1412      * @param layoutResId The id of the XML layout used to create the shortcut.
   1413      * @param parent The group the shortcut belongs to.
   1414      * @param info The data structure describing the shortcut.
   1415      *
   1416      * @return A View inflated from layoutResId.
   1417      */
   1418     View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
   1419         BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
   1420         favorite.applyFromShortcutInfo(info, mIconCache, true);
   1421         favorite.setOnClickListener(this);
   1422         favorite.setOnFocusChangeListener(mFocusHandler);
   1423         return favorite;
   1424     }
   1425 
   1426     /**
   1427      * Add a shortcut to the workspace.
   1428      *
   1429      * @param data The intent describing the shortcut.
   1430      * @param cellInfo The position on screen where to create the shortcut.
   1431      */
   1432     private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
   1433             int cellY) {
   1434         int[] cellXY = mTmpAddItemCellCoordinates;
   1435         int[] touchXY = mPendingAddInfo.dropPos;
   1436         CellLayout layout = getCellLayout(container, screenId);
   1437 
   1438         boolean foundCellSpan = false;
   1439 
   1440         ShortcutInfo info = mModel.infoFromShortcutIntent(this, data, null);
   1441         if (info == null) {
   1442             return;
   1443         }
   1444         final View view = createShortcut(info);
   1445 
   1446         // First we check if we already know the exact location where we want to add this item.
   1447         if (cellX >= 0 && cellY >= 0) {
   1448             cellXY[0] = cellX;
   1449             cellXY[1] = cellY;
   1450             foundCellSpan = true;
   1451 
   1452             // If appropriate, either create a folder or add to an existing folder
   1453             if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
   1454                     true, null,null)) {
   1455                 return;
   1456             }
   1457             DragObject dragObject = new DragObject();
   1458             dragObject.dragInfo = info;
   1459             if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
   1460                     true)) {
   1461                 return;
   1462             }
   1463         } else if (touchXY != null) {
   1464             // when dragging and dropping, just find the closest free spot
   1465             int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY);
   1466             foundCellSpan = (result != null);
   1467         } else {
   1468             foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
   1469         }
   1470 
   1471         if (!foundCellSpan) {
   1472             showOutOfSpaceMessage(isHotseatLayout(layout));
   1473             return;
   1474         }
   1475 
   1476         LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1], false);
   1477 
   1478         if (!mRestoring) {
   1479             mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
   1480                     isWorkspaceLocked());
   1481         }
   1482     }
   1483 
   1484     static int[] getSpanForWidget(Context context, ComponentName component, int minWidth,
   1485             int minHeight) {
   1486         Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null);
   1487         // We want to account for the extra amount of padding that we are adding to the widget
   1488         // to ensure that it gets the full amount of space that it has requested
   1489         int requiredWidth = minWidth + padding.left + padding.right;
   1490         int requiredHeight = minHeight + padding.top + padding.bottom;
   1491         return CellLayout.rectToCell(requiredWidth, requiredHeight, null);
   1492     }
   1493 
   1494     static int[] getSpanForWidget(Context context, AppWidgetProviderInfo info) {
   1495         return getSpanForWidget(context, info.provider, info.minWidth, info.minHeight);
   1496     }
   1497 
   1498     static int[] getMinSpanForWidget(Context context, AppWidgetProviderInfo info) {
   1499         return getSpanForWidget(context, info.provider, info.minResizeWidth, info.minResizeHeight);
   1500     }
   1501 
   1502     static int[] getSpanForWidget(Context context, PendingAddWidgetInfo info) {
   1503         return getSpanForWidget(context, info.componentName, info.minWidth, info.minHeight);
   1504     }
   1505 
   1506     static int[] getMinSpanForWidget(Context context, PendingAddWidgetInfo info) {
   1507         return getSpanForWidget(context, info.componentName, info.minResizeWidth,
   1508                 info.minResizeHeight);
   1509     }
   1510 
   1511     /**
   1512      * Add a widget to the workspace.
   1513      *
   1514      * @param appWidgetId The app widget id
   1515      * @param cellInfo The position on screen where to create the widget.
   1516      */
   1517     private void completeAddAppWidget(final int appWidgetId, long container, long screenId,
   1518             AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) {
   1519         if (appWidgetInfo == null) {
   1520             appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   1521         }
   1522 
   1523         // Calculate the grid spans needed to fit this widget
   1524         CellLayout layout = getCellLayout(container, screenId);
   1525 
   1526         int[] minSpanXY = getMinSpanForWidget(this, appWidgetInfo);
   1527         int[] spanXY = getSpanForWidget(this, appWidgetInfo);
   1528 
   1529         // Try finding open space on Launcher screen
   1530         // We have saved the position to which the widget was dragged-- this really only matters
   1531         // if we are placing widgets on a "spring-loaded" screen
   1532         int[] cellXY = mTmpAddItemCellCoordinates;
   1533         int[] touchXY = mPendingAddInfo.dropPos;
   1534         int[] finalSpan = new int[2];
   1535         boolean foundCellSpan = false;
   1536         if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) {
   1537             cellXY[0] = mPendingAddInfo.cellX;
   1538             cellXY[1] = mPendingAddInfo.cellY;
   1539             spanXY[0] = mPendingAddInfo.spanX;
   1540             spanXY[1] = mPendingAddInfo.spanY;
   1541             foundCellSpan = true;
   1542         } else if (touchXY != null) {
   1543             // when dragging and dropping, just find the closest free spot
   1544             int[] result = layout.findNearestVacantArea(
   1545                     touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0],
   1546                     spanXY[1], cellXY, finalSpan);
   1547             spanXY[0] = finalSpan[0];
   1548             spanXY[1] = finalSpan[1];
   1549             foundCellSpan = (result != null);
   1550         } else {
   1551             foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]);
   1552         }
   1553 
   1554         if (!foundCellSpan) {
   1555             if (appWidgetId != -1) {
   1556                 // Deleting an app widget ID is a void call but writes to disk before returning
   1557                 // to the caller...
   1558                 new AsyncTask<Void, Void, Void>() {
   1559                     public Void doInBackground(Void ... args) {
   1560                         mAppWidgetHost.deleteAppWidgetId(appWidgetId);
   1561                         return null;
   1562                     }
   1563                 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
   1564             }
   1565             showOutOfSpaceMessage(isHotseatLayout(layout));
   1566             return;
   1567         }
   1568 
   1569         // Build Launcher-specific widget info and save to database
   1570         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId,
   1571                 appWidgetInfo.provider);
   1572         launcherInfo.spanX = spanXY[0];
   1573         launcherInfo.spanY = spanXY[1];
   1574         launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
   1575         launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
   1576         launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
   1577 
   1578         LauncherModel.addItemToDatabase(this, launcherInfo,
   1579                 container, screenId, cellXY[0], cellXY[1], false);
   1580 
   1581         if (!mRestoring) {
   1582             if (hostView == null) {
   1583                 // Perform actual inflation because we're live
   1584                 launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   1585                 launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
   1586             } else {
   1587                 // The AppWidgetHostView has already been inflated and instantiated
   1588                 launcherInfo.hostView = hostView;
   1589             }
   1590 
   1591             launcherInfo.hostView.setTag(launcherInfo);
   1592             launcherInfo.hostView.setVisibility(View.VISIBLE);
   1593             launcherInfo.notifyWidgetSizeChanged(this);
   1594 
   1595             mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, cellXY[0], cellXY[1],
   1596                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
   1597 
   1598             addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
   1599         }
   1600         resetAddInfo();
   1601     }
   1602 
   1603     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
   1604         @Override
   1605         public void onReceive(Context context, Intent intent) {
   1606             final String action = intent.getAction();
   1607             if (Intent.ACTION_SCREEN_OFF.equals(action)) {
   1608                 mUserPresent = false;
   1609                 mDragLayer.clearAllResizeFrames();
   1610                 updateRunning();
   1611 
   1612                 // Reset AllApps to its initial state only if we are not in the middle of
   1613                 // processing a multi-step drop
   1614                 if (mAppsCustomizeTabHost != null && mPendingAddInfo.container == ItemInfo.NO_ID) {
   1615                     showWorkspace(false);
   1616                 }
   1617             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
   1618                 mUserPresent = true;
   1619                 updateRunning();
   1620             } else if (ENABLE_DEBUG_INTENTS && DebugIntents.DELETE_DATABASE.equals(action)) {
   1621                 mModel.resetLoadedState(false, true);
   1622                 mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
   1623                         LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
   1624             } else if (ENABLE_DEBUG_INTENTS && DebugIntents.MIGRATE_DATABASE.equals(action)) {
   1625                 mModel.resetLoadedState(false, true);
   1626                 mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
   1627                         LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
   1628                                 | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
   1629             } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
   1630                     || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
   1631                 getModel().forceReload();
   1632             }
   1633         }
   1634     };
   1635 
   1636     @Override
   1637     public void onAttachedToWindow() {
   1638         super.onAttachedToWindow();
   1639 
   1640         // Listen for broadcasts related to user-presence
   1641         final IntentFilter filter = new IntentFilter();
   1642         filter.addAction(Intent.ACTION_SCREEN_OFF);
   1643         filter.addAction(Intent.ACTION_USER_PRESENT);
   1644         // For handling managed profiles
   1645         filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
   1646         filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
   1647         if (ENABLE_DEBUG_INTENTS) {
   1648             filter.addAction(DebugIntents.DELETE_DATABASE);
   1649             filter.addAction(DebugIntents.MIGRATE_DATABASE);
   1650         }
   1651         registerReceiver(mReceiver, filter);
   1652         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
   1653         setupTransparentSystemBarsForLmp();
   1654         mAttached = true;
   1655         mVisible = true;
   1656     }
   1657 
   1658     /**
   1659      * Sets up transparent navigation and status bars in LMP.
   1660      * This method is a no-op for other platform versions.
   1661      */
   1662     @TargetApi(19)
   1663     private void setupTransparentSystemBarsForLmp() {
   1664         // TODO(sansid): use the APIs directly when compiling against L sdk.
   1665         // Currently we use reflection to access the flags and the API to set the transparency
   1666         // on the System bars.
   1667         if (Utilities.isLmpOrAbove()) {
   1668             try {
   1669                 getWindow().getAttributes().systemUiVisibility |=
   1670                         (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
   1671                                 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
   1672                                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
   1673                 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
   1674                         | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
   1675                 Field drawsSysBackgroundsField = WindowManager.LayoutParams.class.getField(
   1676                         "FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS");
   1677                 getWindow().addFlags(drawsSysBackgroundsField.getInt(null));
   1678 
   1679                 Method setStatusBarColorMethod =
   1680                         Window.class.getDeclaredMethod("setStatusBarColor", int.class);
   1681                 Method setNavigationBarColorMethod =
   1682                         Window.class.getDeclaredMethod("setNavigationBarColor", int.class);
   1683                 setStatusBarColorMethod.invoke(getWindow(), Color.TRANSPARENT);
   1684                 setNavigationBarColorMethod.invoke(getWindow(), Color.TRANSPARENT);
   1685             } catch (NoSuchFieldException e) {
   1686                 Log.w(TAG, "NoSuchFieldException while setting up transparent bars");
   1687             } catch (NoSuchMethodException ex) {
   1688                 Log.w(TAG, "NoSuchMethodException while setting up transparent bars");
   1689             } catch (IllegalAccessException e) {
   1690                 Log.w(TAG, "IllegalAccessException while setting up transparent bars");
   1691             } catch (IllegalArgumentException e) {
   1692                 Log.w(TAG, "IllegalArgumentException while setting up transparent bars");
   1693             } catch (InvocationTargetException e) {
   1694                 Log.w(TAG, "InvocationTargetException while setting up transparent bars");
   1695             } finally {}
   1696         }
   1697     }
   1698 
   1699     @Override
   1700     public void onDetachedFromWindow() {
   1701         super.onDetachedFromWindow();
   1702         mVisible = false;
   1703 
   1704         if (mAttached) {
   1705             unregisterReceiver(mReceiver);
   1706             mAttached = false;
   1707         }
   1708         updateRunning();
   1709     }
   1710 
   1711     public void onWindowVisibilityChanged(int visibility) {
   1712         mVisible = visibility == View.VISIBLE;
   1713         updateRunning();
   1714         // The following code used to be in onResume, but it turns out onResume is called when
   1715         // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
   1716         // is a more appropriate event to handle
   1717         if (mVisible) {
   1718             mAppsCustomizeTabHost.onWindowVisible();
   1719             if (!mWorkspaceLoading) {
   1720                 final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
   1721                 // We want to let Launcher draw itself at least once before we force it to build
   1722                 // layers on all the workspace pages, so that transitioning to Launcher from other
   1723                 // apps is nice and speedy.
   1724                 observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
   1725                     private boolean mStarted = false;
   1726                     public void onDraw() {
   1727                         if (mStarted) return;
   1728                         mStarted = true;
   1729                         // We delay the layer building a bit in order to give
   1730                         // other message processing a time to run.  In particular
   1731                         // this avoids a delay in hiding the IME if it was
   1732                         // currently shown, because doing that may involve
   1733                         // some communication back with the app.
   1734                         mWorkspace.postDelayed(mBuildLayersRunnable, 500);
   1735                         final ViewTreeObserver.OnDrawListener listener = this;
   1736                         mWorkspace.post(new Runnable() {
   1737                                 public void run() {
   1738                                     if (mWorkspace != null &&
   1739                                             mWorkspace.getViewTreeObserver() != null) {
   1740                                         mWorkspace.getViewTreeObserver().
   1741                                                 removeOnDrawListener(listener);
   1742                                     }
   1743                                 }
   1744                             });
   1745                         return;
   1746                     }
   1747                 });
   1748             }
   1749             clearTypedText();
   1750         }
   1751     }
   1752 
   1753     private void sendAdvanceMessage(long delay) {
   1754         mHandler.removeMessages(ADVANCE_MSG);
   1755         Message msg = mHandler.obtainMessage(ADVANCE_MSG);
   1756         mHandler.sendMessageDelayed(msg, delay);
   1757         mAutoAdvanceSentTime = System.currentTimeMillis();
   1758     }
   1759 
   1760     private void updateRunning() {
   1761         boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty();
   1762         if (autoAdvanceRunning != mAutoAdvanceRunning) {
   1763             mAutoAdvanceRunning = autoAdvanceRunning;
   1764             if (autoAdvanceRunning) {
   1765                 long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft;
   1766                 sendAdvanceMessage(delay);
   1767             } else {
   1768                 if (!mWidgetsToAdvance.isEmpty()) {
   1769                     mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval -
   1770                             (System.currentTimeMillis() - mAutoAdvanceSentTime));
   1771                 }
   1772                 mHandler.removeMessages(ADVANCE_MSG);
   1773                 mHandler.removeMessages(0); // Remove messages sent using postDelayed()
   1774             }
   1775         }
   1776     }
   1777 
   1778     private final Handler mHandler = new Handler() {
   1779         @Override
   1780         public void handleMessage(Message msg) {
   1781             if (msg.what == ADVANCE_MSG) {
   1782                 int i = 0;
   1783                 for (View key: mWidgetsToAdvance.keySet()) {
   1784                     final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId);
   1785                     final int delay = mAdvanceStagger * i;
   1786                     if (v instanceof Advanceable) {
   1787                        postDelayed(new Runnable() {
   1788                            public void run() {
   1789                                ((Advanceable) v).advance();
   1790                            }
   1791                        }, delay);
   1792                     }
   1793                     i++;
   1794                 }
   1795                 sendAdvanceMessage(mAdvanceInterval);
   1796             }
   1797         }
   1798     };
   1799 
   1800     void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) {
   1801         if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return;
   1802         View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId);
   1803         if (v instanceof Advanceable) {
   1804             mWidgetsToAdvance.put(hostView, appWidgetInfo);
   1805             ((Advanceable) v).fyiWillBeAdvancedByHostKThx();
   1806             updateRunning();
   1807         }
   1808     }
   1809 
   1810     void removeWidgetToAutoAdvance(View hostView) {
   1811         if (mWidgetsToAdvance.containsKey(hostView)) {
   1812             mWidgetsToAdvance.remove(hostView);
   1813             updateRunning();
   1814         }
   1815     }
   1816 
   1817     public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
   1818         removeWidgetToAutoAdvance(launcherInfo.hostView);
   1819         launcherInfo.hostView = null;
   1820     }
   1821 
   1822     void showOutOfSpaceMessage(boolean isHotseatLayout) {
   1823         int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
   1824         Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
   1825     }
   1826 
   1827     public DragLayer getDragLayer() {
   1828         return mDragLayer;
   1829     }
   1830 
   1831     public Workspace getWorkspace() {
   1832         return mWorkspace;
   1833     }
   1834 
   1835     public Hotseat getHotseat() {
   1836         return mHotseat;
   1837     }
   1838 
   1839     public ViewGroup getOverviewPanel() {
   1840         return mOverviewPanel;
   1841     }
   1842 
   1843     public SearchDropTargetBar getSearchBar() {
   1844         return mSearchDropTargetBar;
   1845     }
   1846 
   1847     public LauncherAppWidgetHost getAppWidgetHost() {
   1848         return mAppWidgetHost;
   1849     }
   1850 
   1851     public LauncherModel getModel() {
   1852         return mModel;
   1853     }
   1854 
   1855     protected SharedPreferences getSharedPrefs() {
   1856         return mSharedPrefs;
   1857     }
   1858 
   1859     public void closeSystemDialogs() {
   1860         getWindow().closeAllPanels();
   1861 
   1862         // Whatever we were doing is hereby canceled.
   1863         setWaitingForResult(false);
   1864     }
   1865 
   1866     @Override
   1867     protected void onNewIntent(Intent intent) {
   1868         long startTime = 0;
   1869         if (DEBUG_RESUME_TIME) {
   1870             startTime = System.currentTimeMillis();
   1871         }
   1872         super.onNewIntent(intent);
   1873 
   1874         // Close the menu
   1875         if (Intent.ACTION_MAIN.equals(intent.getAction())) {
   1876             // also will cancel mWaitingForResult.
   1877             closeSystemDialogs();
   1878 
   1879             final boolean alreadyOnHome = mHasFocus && ((intent.getFlags() &
   1880                     Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
   1881                     != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
   1882 
   1883             if (mWorkspace == null) {
   1884                 // Can be cases where mWorkspace is null, this prevents a NPE
   1885                 return;
   1886             }
   1887             Folder openFolder = mWorkspace.getOpenFolder();
   1888             // In all these cases, only animate if we're already on home
   1889             mWorkspace.exitWidgetResizeMode();
   1890             if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
   1891                     openFolder == null && shouldMoveToDefaultScreenOnHomeIntent()) {
   1892                 mWorkspace.moveToDefaultScreen(true);
   1893             }
   1894 
   1895             closeFolder();
   1896             exitSpringLoadedDragMode();
   1897 
   1898             // If we are already on home, then just animate back to the workspace,
   1899             // otherwise, just wait until onResume to set the state back to Workspace
   1900             if (alreadyOnHome) {
   1901                 showWorkspace(true);
   1902             } else {
   1903                 mOnResumeState = State.WORKSPACE;
   1904             }
   1905 
   1906             final View v = getWindow().peekDecorView();
   1907             if (v != null && v.getWindowToken() != null) {
   1908                 InputMethodManager imm = (InputMethodManager)getSystemService(
   1909                         INPUT_METHOD_SERVICE);
   1910                 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
   1911             }
   1912 
   1913             // Reset the apps customize page
   1914             if (!alreadyOnHome && mAppsCustomizeTabHost != null) {
   1915                 mAppsCustomizeTabHost.reset();
   1916             }
   1917 
   1918             onHomeIntent();
   1919         }
   1920 
   1921         if (DEBUG_RESUME_TIME) {
   1922             Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime));
   1923         }
   1924     }
   1925 
   1926     /**
   1927      * Override point for subclasses to prevent movement to the default screen when the home
   1928      * button is pressed. Used (for example) in GEL, to prevent movement during a search.
   1929      */
   1930     protected boolean shouldMoveToDefaultScreenOnHomeIntent() {
   1931         return true;
   1932     }
   1933 
   1934     /**
   1935      * Override point for subclasses to provide custom behaviour for when a home intent is fired.
   1936      */
   1937     protected void onHomeIntent() {
   1938         // Do nothing
   1939     }
   1940 
   1941     @Override
   1942     public void onRestoreInstanceState(Bundle state) {
   1943         super.onRestoreInstanceState(state);
   1944         for (int page: mSynchronouslyBoundPages) {
   1945             mWorkspace.restoreInstanceStateForChild(page);
   1946         }
   1947     }
   1948 
   1949     @Override
   1950     protected void onSaveInstanceState(Bundle outState) {
   1951         if (mWorkspace.getChildCount() > 0) {
   1952             outState.putInt(RUNTIME_STATE_CURRENT_SCREEN,
   1953                     mWorkspace.getCurrentPageOffsetFromCustomContent());
   1954         }
   1955         super.onSaveInstanceState(outState);
   1956 
   1957         outState.putInt(RUNTIME_STATE, mState.ordinal());
   1958         // We close any open folder since it will not be re-opened, and we need to make sure
   1959         // this state is reflected.
   1960         closeFolder();
   1961 
   1962         if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
   1963                 mWaitingForResult) {
   1964             outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container);
   1965             outState.putLong(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screenId);
   1966             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX);
   1967             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
   1968             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
   1969             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY);
   1970             outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
   1971             outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
   1972         }
   1973 
   1974         if (mFolderInfo != null && mWaitingForResult) {
   1975             outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
   1976             outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
   1977         }
   1978 
   1979         // Save the current AppsCustomize tab
   1980         if (mAppsCustomizeTabHost != null) {
   1981             AppsCustomizePagedView.ContentType type = mAppsCustomizeContent.getContentType();
   1982             String currentTabTag = mAppsCustomizeTabHost.getTabTagForContentType(type);
   1983             if (currentTabTag != null) {
   1984                 outState.putString("apps_customize_currentTab", currentTabTag);
   1985             }
   1986             int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex();
   1987             outState.putInt("apps_customize_currentIndex", currentIndex);
   1988         }
   1989         outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId);
   1990     }
   1991 
   1992     @Override
   1993     public void onDestroy() {
   1994         super.onDestroy();
   1995 
   1996         // Remove all pending runnables
   1997         mHandler.removeMessages(ADVANCE_MSG);
   1998         mHandler.removeMessages(0);
   1999         mWorkspace.removeCallbacks(mBuildLayersRunnable);
   2000 
   2001         // Stop callbacks from LauncherModel
   2002         LauncherAppState app = (LauncherAppState.getInstance());
   2003 
   2004         // It's possible to receive onDestroy after a new Launcher activity has
   2005         // been created. In this case, don't interfere with the new Launcher.
   2006         if (mModel.isCurrentCallbacks(this)) {
   2007             mModel.stopLoader();
   2008             app.setLauncher(null);
   2009         }
   2010 
   2011         try {
   2012             mAppWidgetHost.stopListening();
   2013         } catch (NullPointerException ex) {
   2014             Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
   2015         }
   2016         mAppWidgetHost = null;
   2017 
   2018         mWidgetsToAdvance.clear();
   2019 
   2020         TextKeyListener.getInstance().release();
   2021 
   2022         // Disconnect any of the callbacks and drawables associated with ItemInfos on the workspace
   2023         // to prevent leaking Launcher activities on orientation change.
   2024         if (mModel != null) {
   2025             mModel.unbindItemInfosAndClearQueuedBindRunnables();
   2026         }
   2027 
   2028         getContentResolver().unregisterContentObserver(mWidgetObserver);
   2029         unregisterReceiver(mCloseSystemDialogsReceiver);
   2030 
   2031         mDragLayer.clearAllResizeFrames();
   2032         ((ViewGroup) mWorkspace.getParent()).removeAllViews();
   2033         mWorkspace.removeAllWorkspaceScreens();
   2034         mWorkspace = null;
   2035         mDragController = null;
   2036 
   2037         PackageInstallerCompat.getInstance(this).onStop();
   2038         LauncherAnimUtils.onDestroyActivity();
   2039     }
   2040 
   2041     public DragController getDragController() {
   2042         return mDragController;
   2043     }
   2044 
   2045     @Override
   2046     public void startActivityForResult(Intent intent, int requestCode) {
   2047         if (requestCode >= 0) {
   2048             setWaitingForResult(true);
   2049         }
   2050         super.startActivityForResult(intent, requestCode);
   2051     }
   2052 
   2053     /**
   2054      * Indicates that we want global search for this activity by setting the globalSearch
   2055      * argument for {@link #startSearch} to true.
   2056      */
   2057     @Override
   2058     public void startSearch(String initialQuery, boolean selectInitialQuery,
   2059             Bundle appSearchData, boolean globalSearch) {
   2060 
   2061         showWorkspace(true);
   2062 
   2063         if (initialQuery == null) {
   2064             // Use any text typed in the launcher as the initial query
   2065             initialQuery = getTypedText();
   2066         }
   2067         if (appSearchData == null) {
   2068             appSearchData = new Bundle();
   2069             appSearchData.putString("source", "launcher-search");
   2070         }
   2071         Rect sourceBounds = new Rect();
   2072         if (mSearchDropTargetBar != null) {
   2073             sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
   2074         }
   2075 
   2076         boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery,
   2077                 appSearchData, sourceBounds);
   2078         if (clearTextImmediately) {
   2079             clearTypedText();
   2080         }
   2081     }
   2082 
   2083     /**
   2084      * Start a text search.
   2085      *
   2086      * @return {@code true} if the search will start immediately, so any further keypresses
   2087      * will be handled directly by the search UI. {@code false} if {@link Launcher} should continue
   2088      * to buffer keypresses.
   2089      */
   2090     public boolean startSearch(String initialQuery,
   2091             boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
   2092         startGlobalSearch(initialQuery, selectInitialQuery,
   2093                 appSearchData, sourceBounds);
   2094         return false;
   2095     }
   2096 
   2097     /**
   2098      * Starts the global search activity. This code is a copied from SearchManager
   2099      */
   2100     private void startGlobalSearch(String initialQuery,
   2101             boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
   2102         final SearchManager searchManager =
   2103             (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   2104         ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
   2105         if (globalSearchActivity == null) {
   2106             Log.w(TAG, "No global search activity found.");
   2107             return;
   2108         }
   2109         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
   2110         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2111         intent.setComponent(globalSearchActivity);
   2112         // Make sure that we have a Bundle to put source in
   2113         if (appSearchData == null) {
   2114             appSearchData = new Bundle();
   2115         } else {
   2116             appSearchData = new Bundle(appSearchData);
   2117         }
   2118         // Set source to package name of app that starts global search, if not set already.
   2119         if (!appSearchData.containsKey("source")) {
   2120             appSearchData.putString("source", getPackageName());
   2121         }
   2122         intent.putExtra(SearchManager.APP_DATA, appSearchData);
   2123         if (!TextUtils.isEmpty(initialQuery)) {
   2124             intent.putExtra(SearchManager.QUERY, initialQuery);
   2125         }
   2126         if (selectInitialQuery) {
   2127             intent.putExtra(SearchManager.EXTRA_SELECT_QUERY, selectInitialQuery);
   2128         }
   2129         intent.setSourceBounds(sourceBounds);
   2130         try {
   2131             startActivity(intent);
   2132         } catch (ActivityNotFoundException ex) {
   2133             Log.e(TAG, "Global search activity not found: " + globalSearchActivity);
   2134         }
   2135     }
   2136 
   2137     public boolean isOnCustomContent() {
   2138         return mWorkspace.isOnOrMovingToCustomContent();
   2139     }
   2140 
   2141     @Override
   2142     public boolean onPrepareOptionsMenu(Menu menu) {
   2143         super.onPrepareOptionsMenu(menu);
   2144         if (!isOnCustomContent()) {
   2145             // Close any open folders
   2146             closeFolder();
   2147             // Stop resizing any widgets
   2148             mWorkspace.exitWidgetResizeMode();
   2149             if (!mWorkspace.isInOverviewMode()) {
   2150                 // Show the overview mode
   2151                 showOverviewMode(true);
   2152             } else {
   2153                 showWorkspace(true);
   2154             }
   2155         }
   2156         return false;
   2157     }
   2158 
   2159     @Override
   2160     public boolean onSearchRequested() {
   2161         startSearch(null, false, null, true);
   2162         // Use a custom animation for launching search
   2163         return true;
   2164     }
   2165 
   2166     public boolean isWorkspaceLocked() {
   2167         return mWorkspaceLoading || mWaitingForResult;
   2168     }
   2169 
   2170     public boolean isWorkspaceLoading() {
   2171         return mWorkspaceLoading;
   2172     }
   2173 
   2174     private void setWorkspaceLoading(boolean value) {
   2175         boolean isLocked = isWorkspaceLocked();
   2176         mWorkspaceLoading = value;
   2177         if (isLocked != isWorkspaceLocked()) {
   2178             onWorkspaceLockedChanged();
   2179         }
   2180     }
   2181 
   2182     private void setWaitingForResult(boolean value) {
   2183         boolean isLocked = isWorkspaceLocked();
   2184         mWaitingForResult = value;
   2185         if (isLocked != isWorkspaceLocked()) {
   2186             onWorkspaceLockedChanged();
   2187         }
   2188     }
   2189 
   2190     protected void onWorkspaceLockedChanged() { }
   2191 
   2192     private void resetAddInfo() {
   2193         mPendingAddInfo.container = ItemInfo.NO_ID;
   2194         mPendingAddInfo.screenId = -1;
   2195         mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
   2196         mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
   2197         mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
   2198         mPendingAddInfo.dropPos = null;
   2199     }
   2200 
   2201     void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
   2202             final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo) {
   2203         addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
   2204     }
   2205 
   2206     void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
   2207             final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo, int
   2208             delay) {
   2209         if (appWidgetInfo.configure != null) {
   2210             mPendingAddWidgetInfo = appWidgetInfo;
   2211             mPendingAddWidgetId = appWidgetId;
   2212 
   2213             // Launch over to configure widget, if needed
   2214             mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
   2215                     mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
   2216 
   2217         } else {
   2218             // Otherwise just add it
   2219             Runnable onComplete = new Runnable() {
   2220                 @Override
   2221                 public void run() {
   2222                     // Exit spring loaded mode if necessary after adding the widget
   2223                     exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT,
   2224                             null);
   2225                 }
   2226             };
   2227             completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
   2228                     appWidgetInfo);
   2229             mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
   2230         }
   2231     }
   2232 
   2233     protected void moveToCustomContentScreen(boolean animate) {
   2234         // Close any folders that may be open.
   2235         closeFolder();
   2236         mWorkspace.moveToCustomContentScreen(animate);
   2237     }
   2238     /**
   2239      * Process a shortcut drop.
   2240      *
   2241      * @param componentName The name of the component
   2242      * @param screenId The ID of the screen where it should be added
   2243      * @param cell The cell it should be added to, optional
   2244      * @param position The location on the screen where it was dropped, optional
   2245      */
   2246     void processShortcutFromDrop(ComponentName componentName, long container, long screenId,
   2247             int[] cell, int[] loc) {
   2248         resetAddInfo();
   2249         mPendingAddInfo.container = container;
   2250         mPendingAddInfo.screenId = screenId;
   2251         mPendingAddInfo.dropPos = loc;
   2252 
   2253         if (cell != null) {
   2254             mPendingAddInfo.cellX = cell[0];
   2255             mPendingAddInfo.cellY = cell[1];
   2256         }
   2257 
   2258         Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
   2259         createShortcutIntent.setComponent(componentName);
   2260         processShortcut(createShortcutIntent);
   2261     }
   2262 
   2263     /**
   2264      * Process a widget drop.
   2265      *
   2266      * @param info The PendingAppWidgetInfo of the widget being added.
   2267      * @param screenId The ID of the screen where it should be added
   2268      * @param cell The cell it should be added to, optional
   2269      * @param position The location on the screen where it was dropped, optional
   2270      */
   2271     void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId,
   2272             int[] cell, int[] span, int[] loc) {
   2273         resetAddInfo();
   2274         mPendingAddInfo.container = info.container = container;
   2275         mPendingAddInfo.screenId = info.screenId = screenId;
   2276         mPendingAddInfo.dropPos = loc;
   2277         mPendingAddInfo.minSpanX = info.minSpanX;
   2278         mPendingAddInfo.minSpanY = info.minSpanY;
   2279 
   2280         if (cell != null) {
   2281             mPendingAddInfo.cellX = cell[0];
   2282             mPendingAddInfo.cellY = cell[1];
   2283         }
   2284         if (span != null) {
   2285             mPendingAddInfo.spanX = span[0];
   2286             mPendingAddInfo.spanY = span[1];
   2287         }
   2288 
   2289         AppWidgetHostView hostView = info.boundWidget;
   2290         int appWidgetId;
   2291         if (hostView != null) {
   2292             appWidgetId = hostView.getAppWidgetId();
   2293             addAppWidgetImpl(appWidgetId, info, hostView, info.info);
   2294         } else {
   2295             // In this case, we either need to start an activity to get permission to bind
   2296             // the widget, or we need to start an activity to configure the widget, or both.
   2297             appWidgetId = getAppWidgetHost().allocateAppWidgetId();
   2298             Bundle options = info.bindOptions;
   2299 
   2300             boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
   2301                     appWidgetId, info.info, options);
   2302             if (success) {
   2303                 addAppWidgetImpl(appWidgetId, info, null, info.info);
   2304             } else {
   2305                 mPendingAddWidgetInfo = info.info;
   2306                 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
   2307                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   2308                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
   2309                 mAppWidgetManager.getUser(mPendingAddWidgetInfo)
   2310                     .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
   2311                 // TODO: we need to make sure that this accounts for the options bundle.
   2312                 // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
   2313                 startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
   2314             }
   2315         }
   2316     }
   2317 
   2318     void processShortcut(Intent intent) {
   2319         Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
   2320     }
   2321 
   2322     void processWallpaper(Intent intent) {
   2323         startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
   2324     }
   2325 
   2326     FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
   2327             int cellY) {
   2328         final FolderInfo folderInfo = new FolderInfo();
   2329         folderInfo.title = getText(R.string.folder_name);
   2330 
   2331         // Update the model
   2332         LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId, cellX, cellY,
   2333                 false);
   2334         sFolders.put(folderInfo.id, folderInfo);
   2335 
   2336         // Create the view
   2337         FolderIcon newFolder =
   2338             FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
   2339         mWorkspace.addInScreen(newFolder, container, screenId, cellX, cellY, 1, 1,
   2340                 isWorkspaceLocked());
   2341         // Force measure the new folder icon
   2342         CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder);
   2343         parent.getShortcutsAndWidgets().measureChild(newFolder);
   2344         return newFolder;
   2345     }
   2346 
   2347     void removeFolder(FolderInfo folder) {
   2348         sFolders.remove(folder.id);
   2349     }
   2350 
   2351     protected ComponentName getWallpaperPickerComponent() {
   2352         return new ComponentName(getPackageName(), LauncherWallpaperPickerActivity.class.getName());
   2353     }
   2354 
   2355     /**
   2356      * Registers various content observers. The current implementation registers
   2357      * only a favorites observer to keep track of the favorites applications.
   2358      */
   2359     private void registerContentObservers() {
   2360         ContentResolver resolver = getContentResolver();
   2361         resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
   2362                 true, mWidgetObserver);
   2363     }
   2364 
   2365     @Override
   2366     public boolean dispatchKeyEvent(KeyEvent event) {
   2367         if (event.getAction() == KeyEvent.ACTION_DOWN) {
   2368             switch (event.getKeyCode()) {
   2369                 case KeyEvent.KEYCODE_HOME:
   2370                     return true;
   2371                 case KeyEvent.KEYCODE_VOLUME_DOWN:
   2372                     if (isPropertyEnabled(DUMP_STATE_PROPERTY)) {
   2373                         dumpState();
   2374                         return true;
   2375                     }
   2376                     break;
   2377             }
   2378         } else if (event.getAction() == KeyEvent.ACTION_UP) {
   2379             switch (event.getKeyCode()) {
   2380                 case KeyEvent.KEYCODE_HOME:
   2381                     return true;
   2382             }
   2383         }
   2384 
   2385         return super.dispatchKeyEvent(event);
   2386     }
   2387 
   2388     @Override
   2389     public void onBackPressed() {
   2390         if (isAllAppsVisible()) {
   2391             if (mAppsCustomizeContent.getContentType() ==
   2392                     AppsCustomizePagedView.ContentType.Applications) {
   2393                 showWorkspace(true);
   2394             } else {
   2395                 showOverviewMode(true);
   2396             }
   2397         } else if (mWorkspace.isInOverviewMode()) {
   2398             mWorkspace.exitOverviewMode(true);
   2399         } else if (mWorkspace.getOpenFolder() != null) {
   2400             Folder openFolder = mWorkspace.getOpenFolder();
   2401             if (openFolder.isEditingName()) {
   2402                 openFolder.dismissEditingName();
   2403             } else {
   2404                 closeFolder();
   2405             }
   2406         } else {
   2407             mWorkspace.exitWidgetResizeMode();
   2408 
   2409             // Back button is a no-op here, but give at least some feedback for the button press
   2410             mWorkspace.showOutlinesTemporarily();
   2411         }
   2412     }
   2413 
   2414     /**
   2415      * Re-listen when widgets are reset.
   2416      */
   2417     private void onAppWidgetReset() {
   2418         if (mAppWidgetHost != null) {
   2419             mAppWidgetHost.startListening();
   2420         }
   2421     }
   2422 
   2423     /**
   2424      * Launches the intent referred by the clicked shortcut.
   2425      *
   2426      * @param v The view representing the clicked shortcut.
   2427      */
   2428     public void onClick(View v) {
   2429         // Make sure that rogue clicks don't get through while allapps is launching, or after the
   2430         // view has detached (it's possible for this to happen if the view is removed mid touch).
   2431         if (v.getWindowToken() == null) {
   2432             return;
   2433         }
   2434 
   2435         if (!mWorkspace.isFinishedSwitchingState()) {
   2436             return;
   2437         }
   2438 
   2439         if (v instanceof Workspace) {
   2440             if (mWorkspace.isInOverviewMode()) {
   2441                 mWorkspace.exitOverviewMode(true);
   2442             }
   2443             return;
   2444         }
   2445 
   2446         if (v instanceof CellLayout) {
   2447             if (mWorkspace.isInOverviewMode()) {
   2448                 mWorkspace.exitOverviewMode(mWorkspace.indexOfChild(v), true);
   2449             }
   2450         }
   2451 
   2452         Object tag = v.getTag();
   2453         if (tag instanceof ShortcutInfo) {
   2454             onClickAppShortcut(v);
   2455         } else if (tag instanceof FolderInfo) {
   2456             if (v instanceof FolderIcon) {
   2457                 onClickFolderIcon(v);
   2458             }
   2459         } else if (v == mAllAppsButton) {
   2460             onClickAllAppsButton(v);
   2461         } else if (tag instanceof AppInfo) {
   2462             startAppShortcutOrInfoActivity(v);
   2463         } else if (tag instanceof LauncherAppWidgetInfo) {
   2464             if (v instanceof PendingAppWidgetHostView) {
   2465                 onClickPendingWidget((PendingAppWidgetHostView) v);
   2466             }
   2467         }
   2468     }
   2469 
   2470     public void onClickPagedViewIcon(View v) {
   2471         startAppShortcutOrInfoActivity(v);
   2472     }
   2473 
   2474     public boolean onTouch(View v, MotionEvent event) {
   2475         return false;
   2476     }
   2477 
   2478     /**
   2479      * Event handler for the app widget view which has not fully restored.
   2480      */
   2481     public void onClickPendingWidget(final PendingAppWidgetHostView v) {
   2482         final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
   2483         if (v.isReadyForClickSetup()) {
   2484             int widgetId = info.appWidgetId;
   2485             AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
   2486             if (appWidgetInfo != null) {
   2487                 mPendingAddWidgetInfo = appWidgetInfo;
   2488                 mPendingAddInfo.copyFrom(info);
   2489                 mPendingAddWidgetId = widgetId;
   2490 
   2491                 AppWidgetManagerCompat.getInstance(this).startConfigActivity(appWidgetInfo,
   2492                         info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
   2493             }
   2494         } else if (info.installProgress < 0) {
   2495             // The install has not been queued
   2496             final String packageName = info.providerName.getPackageName();
   2497             showBrokenAppInstallDialog(packageName,
   2498                 new DialogInterface.OnClickListener() {
   2499                     public void onClick(DialogInterface dialog, int id) {
   2500                         startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
   2501                     }
   2502                 });
   2503         } else {
   2504             // Download has started.
   2505             final String packageName = info.providerName.getPackageName();
   2506             startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
   2507         }
   2508     }
   2509 
   2510     /**
   2511      * Event handler for the search button
   2512      *
   2513      * @param v The view that was clicked.
   2514      */
   2515     public void onClickSearchButton(View v) {
   2516         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2517 
   2518         onSearchRequested();
   2519     }
   2520 
   2521     /**
   2522      * Event handler for the voice button
   2523      *
   2524      * @param v The view that was clicked.
   2525      */
   2526     public void onClickVoiceButton(View v) {
   2527         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2528 
   2529         startVoice();
   2530     }
   2531 
   2532     public void startVoice() {
   2533         try {
   2534             final SearchManager searchManager =
   2535                     (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   2536             ComponentName activityName = searchManager.getGlobalSearchActivity();
   2537             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   2538             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2539             if (activityName != null) {
   2540                 intent.setPackage(activityName.getPackageName());
   2541             }
   2542             startActivity(null, intent, "onClickVoiceButton");
   2543         } catch (ActivityNotFoundException e) {
   2544             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   2545             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2546             startActivitySafely(null, intent, "onClickVoiceButton");
   2547         }
   2548     }
   2549 
   2550     /**
   2551      * Event handler for the "grid" button that appears on the home screen, which
   2552      * enters all apps mode.
   2553      *
   2554      * @param v The view that was clicked.
   2555      */
   2556     protected void onClickAllAppsButton(View v) {
   2557         if (LOGD) Log.d(TAG, "onClickAllAppsButton");
   2558         if (isAllAppsVisible()) {
   2559             showWorkspace(true);
   2560         } else {
   2561             showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
   2562         }
   2563     }
   2564 
   2565     private void showBrokenAppInstallDialog(final String packageName,
   2566             DialogInterface.OnClickListener onSearchClickListener) {
   2567         new AlertDialog.Builder(new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault))
   2568             .setTitle(R.string.abandoned_promises_title)
   2569             .setMessage(R.string.abandoned_promise_explanation)
   2570             .setPositiveButton(R.string.abandoned_search, onSearchClickListener)
   2571             .setNeutralButton(R.string.abandoned_clean_this,
   2572                 new DialogInterface.OnClickListener() {
   2573                     public void onClick(DialogInterface dialog, int id) {
   2574                         final UserHandleCompat user = UserHandleCompat.myUserHandle();
   2575                         mWorkspace.removeAbandonedPromise(packageName, user);
   2576                     }
   2577                 })
   2578             .create().show();
   2579         return;
   2580     }
   2581 
   2582     /**
   2583      * Event handler for an app shortcut click.
   2584      *
   2585      * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
   2586      */
   2587     protected void onClickAppShortcut(final View v) {
   2588         if (LOGD) Log.d(TAG, "onClickAppShortcut");
   2589         Object tag = v.getTag();
   2590         if (!(tag instanceof ShortcutInfo)) {
   2591             throw new IllegalArgumentException("Input must be a Shortcut");
   2592         }
   2593 
   2594         // Open shortcut
   2595         final ShortcutInfo shortcut = (ShortcutInfo) tag;
   2596         final Intent intent = shortcut.intent;
   2597 
   2598         // Check for special shortcuts
   2599         if (intent.getComponent() != null) {
   2600             final String shortcutClass = intent.getComponent().getClassName();
   2601 
   2602             if (shortcutClass.equals(MemoryDumpActivity.class.getName())) {
   2603                 MemoryDumpActivity.startDump(this);
   2604                 return;
   2605             } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) {
   2606                 toggleShowWeightWatcher();
   2607                 return;
   2608             }
   2609         }
   2610 
   2611         // Check for abandoned promise
   2612         if ((v instanceof BubbleTextView)
   2613                 && shortcut.isPromise()
   2614                 && !shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE)) {
   2615             showBrokenAppInstallDialog(
   2616                     shortcut.getTargetComponent().getPackageName(),
   2617                     new DialogInterface.OnClickListener() {
   2618                         public void onClick(DialogInterface dialog, int id) {
   2619                             startAppShortcutOrInfoActivity(v);
   2620                         }
   2621                     });
   2622             return;
   2623         }
   2624 
   2625         // Start activities
   2626         startAppShortcutOrInfoActivity(v);
   2627     }
   2628 
   2629     private void startAppShortcutOrInfoActivity(View v) {
   2630         Object tag = v.getTag();
   2631         final ShortcutInfo shortcut;
   2632         final Intent intent;
   2633         if (tag instanceof ShortcutInfo) {
   2634             shortcut = (ShortcutInfo) tag;
   2635             intent = shortcut.intent;
   2636             int[] pos = new int[2];
   2637             v.getLocationOnScreen(pos);
   2638             intent.setSourceBounds(new Rect(pos[0], pos[1],
   2639                     pos[0] + v.getWidth(), pos[1] + v.getHeight()));
   2640 
   2641         } else if (tag instanceof AppInfo) {
   2642             shortcut = null;
   2643             intent = ((AppInfo) tag).intent;
   2644         } else {
   2645             throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
   2646         }
   2647 
   2648         boolean success = startActivitySafely(v, intent, tag);
   2649         mStats.recordLaunch(intent, shortcut);
   2650 
   2651         if (success && v instanceof BubbleTextView) {
   2652             mWaitingForResume = (BubbleTextView) v;
   2653             mWaitingForResume.setStayPressed(true);
   2654         }
   2655     }
   2656 
   2657     /**
   2658      * Event handler for a folder icon click.
   2659      *
   2660      * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
   2661      */
   2662     protected void onClickFolderIcon(View v) {
   2663         if (LOGD) Log.d(TAG, "onClickFolder");
   2664         if (!(v instanceof FolderIcon)){
   2665             throw new IllegalArgumentException("Input must be a FolderIcon");
   2666         }
   2667 
   2668         FolderIcon folderIcon = (FolderIcon) v;
   2669         final FolderInfo info = folderIcon.getFolderInfo();
   2670         Folder openFolder = mWorkspace.getFolderForTag(info);
   2671 
   2672         // If the folder info reports that the associated folder is open, then verify that
   2673         // it is actually opened. There have been a few instances where this gets out of sync.
   2674         if (info.opened && openFolder == null) {
   2675             Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
   2676                     + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
   2677             info.opened = false;
   2678         }
   2679 
   2680         if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
   2681             // Close any open folder
   2682             closeFolder();
   2683             // Open the requested folder
   2684             openFolder(folderIcon);
   2685         } else {
   2686             // Find the open folder...
   2687             int folderScreen;
   2688             if (openFolder != null) {
   2689                 folderScreen = mWorkspace.getPageForView(openFolder);
   2690                 // .. and close it
   2691                 closeFolder(openFolder);
   2692                 if (folderScreen != mWorkspace.getCurrentPage()) {
   2693                     // Close any folder open on the current screen
   2694                     closeFolder();
   2695                     // Pull the folder onto this screen
   2696                     openFolder(folderIcon);
   2697                 }
   2698             }
   2699         }
   2700     }
   2701 
   2702     /**
   2703      * Event handler for the (Add) Widgets button that appears after a long press
   2704      * on the home screen.
   2705      */
   2706     protected void onClickAddWidgetButton(View view) {
   2707         if (LOGD) Log.d(TAG, "onClickAddWidgetButton");
   2708         showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
   2709     }
   2710 
   2711     /**
   2712      * Event handler for the wallpaper picker button that appears after a long press
   2713      * on the home screen.
   2714      */
   2715     protected void onClickWallpaperPicker(View v) {
   2716         if (LOGD) Log.d(TAG, "onClickWallpaperPicker");
   2717         final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
   2718         pickWallpaper.setComponent(getWallpaperPickerComponent());
   2719         startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER);
   2720     }
   2721 
   2722     /**
   2723      * Event handler for a click on the settings button that appears after a long press
   2724      * on the home screen.
   2725      */
   2726     protected void onClickSettingsButton(View v) {
   2727         if (LOGD) Log.d(TAG, "onClickSettingsButton");
   2728     }
   2729 
   2730     public void onTouchDownAllAppsButton(View v) {
   2731         // Provide the same haptic feedback that the system offers for virtual keys.
   2732         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2733     }
   2734 
   2735     public void performHapticFeedbackOnTouchDown(View v) {
   2736         // Provide the same haptic feedback that the system offers for virtual keys.
   2737         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2738     }
   2739 
   2740     public View.OnTouchListener getHapticFeedbackTouchListener() {
   2741         if (mHapticFeedbackTouchListener == null) {
   2742             mHapticFeedbackTouchListener = new View.OnTouchListener() {
   2743                 @Override
   2744                 public boolean onTouch(View v, MotionEvent event) {
   2745                     if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
   2746                         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2747                     }
   2748                     return false;
   2749                 }
   2750             };
   2751         }
   2752         return mHapticFeedbackTouchListener;
   2753     }
   2754 
   2755     public void onDragStarted(View view) {}
   2756 
   2757     /**
   2758      * Called when the user stops interacting with the launcher.
   2759      * This implies that the user is now on the homescreen and is not doing housekeeping.
   2760      */
   2761     protected void onInteractionEnd() {}
   2762 
   2763     /**
   2764      * Called when the user starts interacting with the launcher.
   2765      * The possible interactions are:
   2766      *  - open all apps
   2767      *  - reorder an app shortcut, or a widget
   2768      *  - open the overview mode.
   2769      * This is a good time to stop doing things that only make sense
   2770      * when the user is on the homescreen and not doing housekeeping.
   2771      */
   2772     protected void onInteractionBegin() {}
   2773 
   2774     void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) {
   2775         String packageName = componentName.getPackageName();
   2776         try {
   2777             LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
   2778             UserManagerCompat userManager = UserManagerCompat.getInstance(this);
   2779             launcherApps.showAppDetailsForProfile(componentName, user);
   2780         } catch (SecurityException e) {
   2781             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2782             Log.e(TAG, "Launcher does not have permission to launch settings");
   2783         } catch (ActivityNotFoundException e) {
   2784             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2785             Log.e(TAG, "Unable to launch settings");
   2786         }
   2787     }
   2788 
   2789     // returns true if the activity was started
   2790     boolean startApplicationUninstallActivity(ComponentName componentName, int flags,
   2791             UserHandleCompat user) {
   2792         if ((flags & AppInfo.DOWNLOADED_FLAG) == 0) {
   2793             // System applications cannot be installed. For now, show a toast explaining that.
   2794             // We may give them the option of disabling apps this way.
   2795             int messageId = R.string.uninstall_system_app_text;
   2796             Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
   2797             return false;
   2798         } else {
   2799             String packageName = componentName.getPackageName();
   2800             String className = componentName.getClassName();
   2801             Intent intent = new Intent(
   2802                     Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
   2803             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
   2804                     Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   2805             if (user != null) {
   2806                 user.addToIntent(intent, Intent.EXTRA_USER);
   2807             }
   2808             startActivity(intent);
   2809             return true;
   2810         }
   2811     }
   2812 
   2813     boolean startActivity(View v, Intent intent, Object tag) {
   2814         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2815         try {
   2816             // Only launch using the new animation if the shortcut has not opted out (this is a
   2817             // private contract between launcher and may be ignored in the future).
   2818             boolean useLaunchAnimation = (v != null) &&
   2819                     !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
   2820             LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
   2821             UserManagerCompat userManager = UserManagerCompat.getInstance(this);
   2822 
   2823             UserHandleCompat user = null;
   2824             if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
   2825                 long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
   2826                 user = userManager.getUserForSerialNumber(serialNumber);
   2827             }
   2828 
   2829             Bundle optsBundle = null;
   2830             if (useLaunchAnimation) {
   2831                 ActivityOptions opts = Utilities.isLmpOrAbove() ?
   2832                         ActivityOptions.makeCustomAnimation(this, R.anim.task_open_enter, R.anim.no_anim) :
   2833                         ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
   2834                 optsBundle = opts.toBundle();
   2835             }
   2836 
   2837             if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
   2838                 // Could be launching some bookkeeping activity
   2839                 startActivity(intent, optsBundle);
   2840             } else {
   2841                 // TODO Component can be null when shortcuts are supported for secondary user
   2842                 launcherApps.startActivityForProfile(intent.getComponent(), user,
   2843                         intent.getSourceBounds(), optsBundle);
   2844             }
   2845             return true;
   2846         } catch (SecurityException e) {
   2847             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2848             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   2849                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   2850                     "or use the exported attribute for this activity. "
   2851                     + "tag="+ tag + " intent=" + intent, e);
   2852         }
   2853         return false;
   2854     }
   2855 
   2856     boolean startActivitySafely(View v, Intent intent, Object tag) {
   2857         boolean success = false;
   2858         if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
   2859             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
   2860             return false;
   2861         }
   2862         try {
   2863             success = startActivity(v, intent, tag);
   2864         } catch (ActivityNotFoundException e) {
   2865             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2866             Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
   2867         }
   2868         return success;
   2869     }
   2870 
   2871     /**
   2872      * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
   2873      * in the DragLayer in the exact absolute location of the original FolderIcon.
   2874      */
   2875     private void copyFolderIconToImage(FolderIcon fi) {
   2876         final int width = fi.getMeasuredWidth();
   2877         final int height = fi.getMeasuredHeight();
   2878 
   2879         // Lazy load ImageView, Bitmap and Canvas
   2880         if (mFolderIconImageView == null) {
   2881             mFolderIconImageView = new ImageView(this);
   2882         }
   2883         if (mFolderIconBitmap == null || mFolderIconBitmap.getWidth() != width ||
   2884                 mFolderIconBitmap.getHeight() != height) {
   2885             mFolderIconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
   2886             mFolderIconCanvas = new Canvas(mFolderIconBitmap);
   2887         }
   2888 
   2889         DragLayer.LayoutParams lp;
   2890         if (mFolderIconImageView.getLayoutParams() instanceof DragLayer.LayoutParams) {
   2891             lp = (DragLayer.LayoutParams) mFolderIconImageView.getLayoutParams();
   2892         } else {
   2893             lp = new DragLayer.LayoutParams(width, height);
   2894         }
   2895 
   2896         // The layout from which the folder is being opened may be scaled, adjust the starting
   2897         // view size by this scale factor.
   2898         float scale = mDragLayer.getDescendantRectRelativeToSelf(fi, mRectForFolderAnimation);
   2899         lp.customPosition = true;
   2900         lp.x = mRectForFolderAnimation.left;
   2901         lp.y = mRectForFolderAnimation.top;
   2902         lp.width = (int) (scale * width);
   2903         lp.height = (int) (scale * height);
   2904 
   2905         mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
   2906         fi.draw(mFolderIconCanvas);
   2907         mFolderIconImageView.setImageBitmap(mFolderIconBitmap);
   2908         if (fi.getFolder() != null) {
   2909             mFolderIconImageView.setPivotX(fi.getFolder().getPivotXForIconAnimation());
   2910             mFolderIconImageView.setPivotY(fi.getFolder().getPivotYForIconAnimation());
   2911         }
   2912         // Just in case this image view is still in the drag layer from a previous animation,
   2913         // we remove it and re-add it.
   2914         if (mDragLayer.indexOfChild(mFolderIconImageView) != -1) {
   2915             mDragLayer.removeView(mFolderIconImageView);
   2916         }
   2917         mDragLayer.addView(mFolderIconImageView, lp);
   2918         if (fi.getFolder() != null) {
   2919             fi.getFolder().bringToFront();
   2920         }
   2921     }
   2922 
   2923     private void growAndFadeOutFolderIcon(FolderIcon fi) {
   2924         if (fi == null) return;
   2925         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
   2926         PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f);
   2927         PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f);
   2928 
   2929         FolderInfo info = (FolderInfo) fi.getTag();
   2930         if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   2931             CellLayout cl = (CellLayout) fi.getParent().getParent();
   2932             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) fi.getLayoutParams();
   2933             cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
   2934         }
   2935 
   2936         // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
   2937         copyFolderIconToImage(fi);
   2938         fi.setVisibility(View.INVISIBLE);
   2939 
   2940         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
   2941                 scaleX, scaleY);
   2942         if (Utilities.isLmpOrAbove()) {
   2943             oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
   2944         }
   2945         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
   2946         oa.start();
   2947     }
   2948 
   2949     private void shrinkAndFadeInFolderIcon(final FolderIcon fi) {
   2950         if (fi == null) return;
   2951         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
   2952         PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
   2953         PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
   2954 
   2955         final CellLayout cl = (CellLayout) fi.getParent().getParent();
   2956 
   2957         // We remove and re-draw the FolderIcon in-case it has changed
   2958         mDragLayer.removeView(mFolderIconImageView);
   2959         copyFolderIconToImage(fi);
   2960         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
   2961                 scaleX, scaleY);
   2962         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
   2963         oa.addListener(new AnimatorListenerAdapter() {
   2964             @Override
   2965             public void onAnimationEnd(Animator animation) {
   2966                 if (cl != null) {
   2967                     cl.clearFolderLeaveBehind();
   2968                     // Remove the ImageView copy of the FolderIcon and make the original visible.
   2969                     mDragLayer.removeView(mFolderIconImageView);
   2970                     fi.setVisibility(View.VISIBLE);
   2971                 }
   2972             }
   2973         });
   2974         oa.start();
   2975     }
   2976 
   2977     /**
   2978      * Opens the user folder described by the specified tag. The opening of the folder
   2979      * is animated relative to the specified View. If the View is null, no animation
   2980      * is played.
   2981      *
   2982      * @param folderInfo The FolderInfo describing the folder to open.
   2983      */
   2984     public void openFolder(FolderIcon folderIcon) {
   2985         Folder folder = folderIcon.getFolder();
   2986         FolderInfo info = folder.mInfo;
   2987 
   2988         info.opened = true;
   2989 
   2990         // Just verify that the folder hasn't already been added to the DragLayer.
   2991         // There was a one-off crash where the folder had a parent already.
   2992         if (folder.getParent() == null) {
   2993             mDragLayer.addView(folder);
   2994             mDragController.addDropTarget((DropTarget) folder);
   2995         } else {
   2996             Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
   2997                     folder.getParent() + ").");
   2998         }
   2999         folder.animateOpen();
   3000         growAndFadeOutFolderIcon(folderIcon);
   3001 
   3002         // Notify the accessibility manager that this folder "window" has appeared and occluded
   3003         // the workspace items
   3004         folder.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3005         getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
   3006     }
   3007 
   3008     public void closeFolder() {
   3009         Folder folder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
   3010         if (folder != null) {
   3011             if (folder.isEditingName()) {
   3012                 folder.dismissEditingName();
   3013             }
   3014             closeFolder(folder);
   3015         }
   3016     }
   3017 
   3018     void closeFolder(Folder folder) {
   3019         folder.getInfo().opened = false;
   3020 
   3021         ViewGroup parent = (ViewGroup) folder.getParent().getParent();
   3022         if (parent != null) {
   3023             FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
   3024             shrinkAndFadeInFolderIcon(fi);
   3025         }
   3026         folder.animateClosed();
   3027 
   3028         // Notify the accessibility manager that this folder "window" has disappeard and no
   3029         // longer occludeds the workspace items
   3030         getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3031     }
   3032 
   3033     public boolean onLongClick(View v) {
   3034         if (!isDraggingEnabled()) return false;
   3035         if (isWorkspaceLocked()) return false;
   3036         if (mState != State.WORKSPACE) return false;
   3037 
   3038         if (v instanceof Workspace) {
   3039             if (!mWorkspace.isInOverviewMode()) {
   3040                 if (mWorkspace.enterOverviewMode()) {
   3041                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   3042                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   3043                     return true;
   3044                 } else {
   3045                     return false;
   3046                 }
   3047             } else {
   3048                 return false;
   3049             }
   3050         }
   3051 
   3052         CellLayout.CellInfo longClickCellInfo = null;
   3053         View itemUnderLongClick = null;
   3054         if (v.getTag() instanceof ItemInfo) {
   3055             ItemInfo info = (ItemInfo) v.getTag();
   3056             longClickCellInfo = new CellLayout.CellInfo(v, info);;
   3057             itemUnderLongClick = longClickCellInfo.cell;
   3058             resetAddInfo();
   3059         }
   3060 
   3061         // The hotseat touch handling does not go through Workspace, and we always allow long press
   3062         // on hotseat items.
   3063         final boolean inHotseat = isHotseatLayout(v);
   3064         boolean allowLongPress = inHotseat || mWorkspace.allowLongPress();
   3065         if (allowLongPress && !mDragController.isDragging()) {
   3066             if (itemUnderLongClick == null) {
   3067                 // User long pressed on empty space
   3068                 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   3069                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   3070                 if (mWorkspace.isInOverviewMode()) {
   3071                     mWorkspace.startReordering(v);
   3072                 } else {
   3073                     mWorkspace.enterOverviewMode();
   3074                 }
   3075             } else {
   3076                 final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank(
   3077                         mHotseat.getOrderInHotseat(
   3078                                 longClickCellInfo.cellX,
   3079                                 longClickCellInfo.cellY));
   3080                 if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
   3081                     // User long pressed on an item
   3082                     mWorkspace.startDrag(longClickCellInfo);
   3083                 }
   3084             }
   3085         }
   3086         return true;
   3087     }
   3088 
   3089     boolean isHotseatLayout(View layout) {
   3090         return mHotseat != null && layout != null &&
   3091                 (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
   3092     }
   3093 
   3094     /**
   3095      * Returns the CellLayout of the specified container at the specified screen.
   3096      */
   3097     CellLayout getCellLayout(long container, long screenId) {
   3098         if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   3099             if (mHotseat != null) {
   3100                 return mHotseat.getLayout();
   3101             } else {
   3102                 return null;
   3103             }
   3104         } else {
   3105             return (CellLayout) mWorkspace.getScreenWithId(screenId);
   3106         }
   3107     }
   3108 
   3109     public boolean isAllAppsVisible() {
   3110         return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
   3111     }
   3112 
   3113     private void setWorkspaceBackground(boolean workspace) {
   3114         mLauncherView.setBackground(workspace ?
   3115                 mWorkspaceBackgroundDrawable : null);
   3116     }
   3117 
   3118     protected void changeWallpaperVisiblity(boolean visible) {
   3119         int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
   3120         int curflags = getWindow().getAttributes().flags
   3121                 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
   3122         if (wpflags != curflags) {
   3123             getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
   3124         }
   3125         setWorkspaceBackground(visible);
   3126     }
   3127 
   3128     private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
   3129         if (v instanceof LauncherTransitionable) {
   3130             ((LauncherTransitionable) v).onLauncherTransitionPrepare(this, animated, toWorkspace);
   3131         }
   3132     }
   3133 
   3134     private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
   3135         if (v instanceof LauncherTransitionable) {
   3136             ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace);
   3137         }
   3138 
   3139         // Update the workspace transition step as well
   3140         dispatchOnLauncherTransitionStep(v, 0f);
   3141     }
   3142 
   3143     private void dispatchOnLauncherTransitionStep(View v, float t) {
   3144         if (v instanceof LauncherTransitionable) {
   3145             ((LauncherTransitionable) v).onLauncherTransitionStep(this, t);
   3146         }
   3147     }
   3148 
   3149     private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
   3150         if (v instanceof LauncherTransitionable) {
   3151             ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace);
   3152         }
   3153 
   3154         // Update the workspace transition step as well
   3155         dispatchOnLauncherTransitionStep(v, 1f);
   3156     }
   3157 
   3158     /**
   3159      * Things to test when changing the following seven functions.
   3160      *   - Home from workspace
   3161      *          - from center screen
   3162      *          - from other screens
   3163      *   - Home from all apps
   3164      *          - from center screen
   3165      *          - from other screens
   3166      *   - Back from all apps
   3167      *          - from center screen
   3168      *          - from other screens
   3169      *   - Launch app from workspace and quit
   3170      *          - with back
   3171      *          - with home
   3172      *   - Launch app from all apps and quit
   3173      *          - with back
   3174      *          - with home
   3175      *   - Go to a screen that's not the default, then all
   3176      *     apps, and launch and app, and go back
   3177      *          - with back
   3178      *          -with home
   3179      *   - On workspace, long press power and go back
   3180      *          - with back
   3181      *          - with home
   3182      *   - On all apps, long press power and go back
   3183      *          - with back
   3184      *          - with home
   3185      *   - On workspace, power off
   3186      *   - On all apps, power off
   3187      *   - Launch an app and turn off the screen while in that app
   3188      *          - Go back with home key
   3189      *          - Go back with back key  TODO: make this not go to workspace
   3190      *          - From all apps
   3191      *          - From workspace
   3192      *   - Enter and exit car mode (becuase it causes an extra configuration changed)
   3193      *          - From all apps
   3194      *          - From the center workspace
   3195      *          - From another workspace
   3196      */
   3197 
   3198     /**
   3199      * Zoom the camera out from the workspace to reveal 'toView'.
   3200      * Assumes that the view to show is anchored at either the very top or very bottom
   3201      * of the screen.
   3202      */
   3203     private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) {
   3204         AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType();
   3205         showAppsCustomizeHelper(animated, springLoaded, contentType);
   3206     }
   3207 
   3208     private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded,
   3209                                          final AppsCustomizePagedView.ContentType contentType) {
   3210         if (mStateAnimation != null) {
   3211             mStateAnimation.setDuration(0);
   3212             mStateAnimation.cancel();
   3213             mStateAnimation = null;
   3214         }
   3215 
   3216         boolean material = Utilities.isLmpOrAbove();
   3217 
   3218         final Resources res = getResources();
   3219 
   3220         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
   3221         final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
   3222         final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
   3223         final int itemsAlphaStagger =
   3224                 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
   3225 
   3226         final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
   3227         final View fromView = mWorkspace;
   3228         final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
   3229 
   3230         final ArrayList<View> layerViews = new ArrayList<View>();
   3231 
   3232         Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ?
   3233                 Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN;
   3234         Animator workspaceAnim =
   3235                 mWorkspace.getChangeStateAnimation(workspaceState, animated, layerViews);
   3236         if (!LauncherAppState.isDisableAllApps()
   3237                 || contentType == AppsCustomizePagedView.ContentType.Widgets) {
   3238             // Set the content type for the all apps/widgets space
   3239             mAppsCustomizeTabHost.setContentTypeImmediate(contentType);
   3240         }
   3241 
   3242         // If for some reason our views aren't initialized, don't animate
   3243         boolean initialized = getAllAppsButton() != null;
   3244 
   3245         if (animated && initialized) {
   3246             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
   3247             final AppsCustomizePagedView content = (AppsCustomizePagedView)
   3248                     toView.findViewById(R.id.apps_customize_pane_content);
   3249 
   3250             final View page = content.getPageAt(content.getCurrentPage());
   3251             final View revealView = toView.findViewById(R.id.fake_page);
   3252 
   3253             final float initialPanelAlpha = 1f;
   3254 
   3255             final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets;
   3256             if (isWidgetTray) {
   3257                 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
   3258             } else {
   3259                 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
   3260             }
   3261 
   3262             // Hide the real page background, and swap in the fake one
   3263             content.setPageBackgroundsVisible(false);
   3264             revealView.setVisibility(View.VISIBLE);
   3265             // We need to hide this view as the animation start will be posted.
   3266             revealView.setAlpha(0);
   3267 
   3268             int width = revealView.getMeasuredWidth();
   3269             int height = revealView.getMeasuredHeight();
   3270             float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
   3271 
   3272             revealView.setTranslationY(0);
   3273             revealView.setTranslationX(0);
   3274 
   3275             // Get the y delta between the center of the page and the center of the all apps button
   3276             int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
   3277                     getAllAppsButton(), null);
   3278 
   3279             float alpha = 0;
   3280             float xDrift = 0;
   3281             float yDrift = 0;
   3282             if (material) {
   3283                 alpha = isWidgetTray ? 0.3f : 1f;
   3284                 yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
   3285                 xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
   3286             } else {
   3287                 yDrift = 2 * height / 3;
   3288                 xDrift = 0;
   3289             }
   3290             final float initAlpha = alpha;
   3291 
   3292             revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3293             layerViews.add(revealView);
   3294             PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f);
   3295             PropertyValuesHolder panelDriftY =
   3296                     PropertyValuesHolder.ofFloat("translationY", yDrift, 0);
   3297             PropertyValuesHolder panelDriftX =
   3298                     PropertyValuesHolder.ofFloat("translationX", xDrift, 0);
   3299 
   3300             ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
   3301                     panelAlpha, panelDriftY, panelDriftX);
   3302 
   3303             panelAlphaAndDrift.setDuration(revealDuration);
   3304             panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
   3305 
   3306             mStateAnimation.play(panelAlphaAndDrift);
   3307 
   3308             if (page != null) {
   3309                 page.setVisibility(View.VISIBLE);
   3310                 page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3311                 layerViews.add(page);
   3312 
   3313                 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0);
   3314                 page.setTranslationY(yDrift);
   3315                 pageDrift.setDuration(revealDuration);
   3316                 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
   3317                 pageDrift.setStartDelay(itemsAlphaStagger);
   3318                 mStateAnimation.play(pageDrift);
   3319 
   3320                 page.setAlpha(0f);
   3321                 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, "alpha", 0f, 1f);
   3322                 itemsAlpha.setDuration(revealDuration);
   3323                 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
   3324                 itemsAlpha.setStartDelay(itemsAlphaStagger);
   3325                 mStateAnimation.play(itemsAlpha);
   3326             }
   3327 
   3328             View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator);
   3329             pageIndicators.setAlpha(0.01f);
   3330             ObjectAnimator indicatorsAlpha =
   3331                     ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f);
   3332             indicatorsAlpha.setDuration(revealDuration);
   3333             mStateAnimation.play(indicatorsAlpha);
   3334 
   3335             if (material) {
   3336                 final View allApps = getAllAppsButton();
   3337                 int allAppsButtonSize = LauncherAppState.getInstance().
   3338                         getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
   3339                 float startRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
   3340                 Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2,
   3341                                 height / 2, startRadius, revealRadius);
   3342                 reveal.setDuration(revealDuration);
   3343                 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
   3344 
   3345                 reveal.addListener(new AnimatorListenerAdapter() {
   3346                     public void onAnimationStart(Animator animation) {
   3347                         if (!isWidgetTray) {
   3348                             allApps.setVisibility(View.INVISIBLE);
   3349                         }
   3350                     }
   3351                     public void onAnimationEnd(Animator animation) {
   3352                         if (!isWidgetTray) {
   3353                             allApps.setVisibility(View.VISIBLE);
   3354                         }
   3355                     }
   3356                 });
   3357                 mStateAnimation.play(reveal);
   3358             }
   3359 
   3360             mStateAnimation.addListener(new AnimatorListenerAdapter() {
   3361                 @Override
   3362                 public void onAnimationEnd(Animator animation) {
   3363                     dispatchOnLauncherTransitionEnd(fromView, animated, false);
   3364                     dispatchOnLauncherTransitionEnd(toView, animated, false);
   3365 
   3366                     revealView.setVisibility(View.INVISIBLE);
   3367                     revealView.setLayerType(View.LAYER_TYPE_NONE, null);
   3368                     if (page != null) {
   3369                         page.setLayerType(View.LAYER_TYPE_NONE, null);
   3370                     }
   3371                     content.setPageBackgroundsVisible(true);
   3372 
   3373                     // Hide the search bar
   3374                     if (mSearchDropTargetBar != null) {
   3375                         mSearchDropTargetBar.hideSearchBar(false);
   3376                     }
   3377                 }
   3378 
   3379             });
   3380 
   3381             if (workspaceAnim != null) {
   3382                 mStateAnimation.play(workspaceAnim);
   3383             }
   3384 
   3385             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
   3386             dispatchOnLauncherTransitionPrepare(toView, animated, false);
   3387             final AnimatorSet stateAnimation = mStateAnimation;
   3388             final Runnable startAnimRunnable = new Runnable() {
   3389                 public void run() {
   3390                     // Check that mStateAnimation hasn't changed while
   3391                     // we waited for a layout/draw pass
   3392                     if (mStateAnimation != stateAnimation)
   3393                         return;
   3394                     dispatchOnLauncherTransitionStart(fromView, animated, false);
   3395                     dispatchOnLauncherTransitionStart(toView, animated, false);
   3396 
   3397                     revealView.setAlpha(initAlpha);
   3398                     if (Utilities.isLmpOrAbove()) {
   3399                         for (int i = 0; i < layerViews.size(); i++) {
   3400                             View v = layerViews.get(i);
   3401                             if (v != null) {
   3402                                 boolean attached = true;
   3403                                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
   3404                                     attached = v.isAttachedToWindow();
   3405                                 }
   3406                                 if (attached) v.buildLayer();
   3407                             }
   3408                         }
   3409                     }
   3410                     mStateAnimation.start();
   3411                 }
   3412             };
   3413             toView.bringToFront();
   3414             toView.setVisibility(View.VISIBLE);
   3415             toView.post(startAnimRunnable);
   3416         } else {
   3417             toView.setTranslationX(0.0f);
   3418             toView.setTranslationY(0.0f);
   3419             toView.setScaleX(1.0f);
   3420             toView.setScaleY(1.0f);
   3421             toView.setVisibility(View.VISIBLE);
   3422             toView.bringToFront();
   3423 
   3424             if (!springLoaded && !LauncherAppState.getInstance().isScreenLarge()) {
   3425                 // Hide the search bar
   3426                 if (mSearchDropTargetBar != null) {
   3427                     mSearchDropTargetBar.hideSearchBar(false);
   3428                 }
   3429             }
   3430             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
   3431             dispatchOnLauncherTransitionStart(fromView, animated, false);
   3432             dispatchOnLauncherTransitionEnd(fromView, animated, false);
   3433             dispatchOnLauncherTransitionPrepare(toView, animated, false);
   3434             dispatchOnLauncherTransitionStart(toView, animated, false);
   3435             dispatchOnLauncherTransitionEnd(toView, animated, false);
   3436         }
   3437     }
   3438 
   3439     /**
   3440      * Zoom the camera back into the workspace, hiding 'fromView'.
   3441      * This is the opposite of showAppsCustomizeHelper.
   3442      * @param animated If true, the transition will be animated.
   3443      */
   3444     private void hideAppsCustomizeHelper(Workspace.State toState, final boolean animated,
   3445             final boolean springLoaded, final Runnable onCompleteRunnable) {
   3446 
   3447         if (mStateAnimation != null) {
   3448             mStateAnimation.setDuration(0);
   3449             mStateAnimation.cancel();
   3450             mStateAnimation = null;
   3451         }
   3452 
   3453         boolean material = Utilities.isLmpOrAbove();
   3454         Resources res = getResources();
   3455 
   3456         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
   3457         final int fadeOutDuration = res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
   3458         final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime);
   3459         final int itemsAlphaStagger =
   3460                 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
   3461 
   3462         final float scaleFactor = (float)
   3463                 res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
   3464         final View fromView = mAppsCustomizeTabHost;
   3465         final View toView = mWorkspace;
   3466         Animator workspaceAnim = null;
   3467         final ArrayList<View> layerViews = new ArrayList<View>();
   3468 
   3469         if (toState == Workspace.State.NORMAL) {
   3470             workspaceAnim = mWorkspace.getChangeStateAnimation(
   3471                     toState, animated, layerViews);
   3472         } else if (toState == Workspace.State.SPRING_LOADED ||
   3473                 toState == Workspace.State.OVERVIEW) {
   3474             workspaceAnim = mWorkspace.getChangeStateAnimation(
   3475                     toState, animated, layerViews);
   3476         }
   3477 
   3478         // If for some reason our views aren't initialized, don't animate
   3479         boolean initialized = getAllAppsButton() != null;
   3480 
   3481         if (animated && initialized) {
   3482             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
   3483             if (workspaceAnim != null) {
   3484                 mStateAnimation.play(workspaceAnim);
   3485             }
   3486 
   3487             final AppsCustomizePagedView content = (AppsCustomizePagedView)
   3488                     fromView.findViewById(R.id.apps_customize_pane_content);
   3489 
   3490             final View page = content.getPageAt(content.getNextPage());
   3491 
   3492             // We need to hide side pages of the Apps / Widget tray to avoid some ugly edge cases
   3493             int count = content.getChildCount();
   3494             for (int i = 0; i < count; i++) {
   3495                 View child = content.getChildAt(i);
   3496                 if (child != page) {
   3497                     child.setVisibility(View.INVISIBLE);
   3498                 }
   3499             }
   3500             final View revealView = fromView.findViewById(R.id.fake_page);
   3501 
   3502             // hideAppsCustomizeHelper is called in some cases when it is already hidden
   3503             // don't perform all these no-op animations. In particularly, this was causing
   3504             // the all-apps button to pop in and out.
   3505             if (fromView.getVisibility() == View.VISIBLE) {
   3506                 AppsCustomizePagedView.ContentType contentType = content.getContentType();
   3507                 final boolean isWidgetTray =
   3508                         contentType == AppsCustomizePagedView.ContentType.Widgets;
   3509 
   3510                 if (isWidgetTray) {
   3511                     revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
   3512                 } else {
   3513                     revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
   3514                 }
   3515 
   3516                 int width = revealView.getMeasuredWidth();
   3517                 int height = revealView.getMeasuredHeight();
   3518                 float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
   3519 
   3520                 // Hide the real page background, and swap in the fake one
   3521                 revealView.setVisibility(View.VISIBLE);
   3522                 content.setPageBackgroundsVisible(false);
   3523 
   3524                 final View allAppsButton = getAllAppsButton();
   3525                 revealView.setTranslationY(0);
   3526                 int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
   3527                         allAppsButton, null);
   3528 
   3529                 float xDrift = 0;
   3530                 float yDrift = 0;
   3531                 if (material) {
   3532                     yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
   3533                     xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
   3534                 } else {
   3535                     yDrift = 5 * height / 4;
   3536                     xDrift = 0;
   3537                 }
   3538 
   3539                 revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3540                 TimeInterpolator decelerateInterpolator = material ?
   3541                         new LogDecelerateInterpolator(100, 0) :
   3542                         new LogDecelerateInterpolator(30, 0);
   3543 
   3544                 // The vertical motion of the apps panel should be delayed by one frame
   3545                 // from the conceal animation in order to give the right feel. We correpsondingly
   3546                 // shorten the duration so that the slide and conceal end at the same time.
   3547                 ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
   3548                         0, yDrift);
   3549                 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
   3550                 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
   3551                 panelDriftY.setInterpolator(decelerateInterpolator);
   3552                 mStateAnimation.play(panelDriftY);
   3553 
   3554                 ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
   3555                         0, xDrift);
   3556                 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
   3557                 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
   3558                 panelDriftX.setInterpolator(decelerateInterpolator);
   3559                 mStateAnimation.play(panelDriftX);
   3560 
   3561                 if (isWidgetTray || !material) {
   3562                     float finalAlpha = material ? 0.4f : 0f;
   3563                     revealView.setAlpha(1f);
   3564                     ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
   3565                             1f, finalAlpha);
   3566                     panelAlpha.setDuration(revealDuration);
   3567                     panelAlpha.setInterpolator(material ? decelerateInterpolator :
   3568                         new AccelerateInterpolator(1.5f));
   3569                     mStateAnimation.play(panelAlpha);
   3570                 }
   3571 
   3572                 if (page != null) {
   3573                     page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3574 
   3575                     ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY",
   3576                             0, yDrift);
   3577                     page.setTranslationY(0);
   3578                     pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
   3579                     pageDrift.setInterpolator(decelerateInterpolator);
   3580                     pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
   3581                     mStateAnimation.play(pageDrift);
   3582 
   3583                     page.setAlpha(1f);
   3584                     ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(page, "alpha", 1f, 0f);
   3585                     itemsAlpha.setDuration(100);
   3586                     itemsAlpha.setInterpolator(decelerateInterpolator);
   3587                     mStateAnimation.play(itemsAlpha);
   3588                 }
   3589 
   3590                 View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator);
   3591                 pageIndicators.setAlpha(1f);
   3592                 ObjectAnimator indicatorsAlpha =
   3593                         LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f);
   3594                 indicatorsAlpha.setDuration(revealDuration);
   3595                 indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
   3596                 mStateAnimation.play(indicatorsAlpha);
   3597 
   3598                 width = revealView.getMeasuredWidth();
   3599 
   3600                 if (material) {
   3601                     if (!isWidgetTray) {
   3602                         allAppsButton.setVisibility(View.INVISIBLE);
   3603                     }
   3604                     int allAppsButtonSize = LauncherAppState.getInstance().
   3605                             getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
   3606                     float finalRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
   3607                     Animator reveal =
   3608                             LauncherAnimUtils.createCircularReveal(revealView, width / 2,
   3609                                     height / 2, revealRadius, finalRadius);
   3610                     reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
   3611                     reveal.setDuration(revealDuration);
   3612                     reveal.setStartDelay(itemsAlphaStagger);
   3613 
   3614                     reveal.addListener(new AnimatorListenerAdapter() {
   3615                         public void onAnimationEnd(Animator animation) {
   3616                             revealView.setVisibility(View.INVISIBLE);
   3617                             if (!isWidgetTray) {
   3618                                 allAppsButton.setVisibility(View.VISIBLE);
   3619                             }
   3620                         }
   3621                     });
   3622 
   3623                     mStateAnimation.play(reveal);
   3624                 }
   3625 
   3626                 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
   3627                 dispatchOnLauncherTransitionPrepare(toView, animated, true);
   3628                 mAppsCustomizeContent.stopScrolling();
   3629             }
   3630 
   3631             mStateAnimation.addListener(new AnimatorListenerAdapter() {
   3632                 @Override
   3633                 public void onAnimationEnd(Animator animation) {
   3634                     fromView.setVisibility(View.GONE);
   3635                     dispatchOnLauncherTransitionEnd(fromView, animated, true);
   3636                     dispatchOnLauncherTransitionEnd(toView, animated, true);
   3637                     if (onCompleteRunnable != null) {
   3638                         onCompleteRunnable.run();
   3639                     }
   3640 
   3641                     revealView.setLayerType(View.LAYER_TYPE_NONE, null);
   3642                     if (page != null) {
   3643                         page.setLayerType(View.LAYER_TYPE_NONE, null);
   3644                     }
   3645                     content.setPageBackgroundsVisible(true);
   3646                     // Unhide side pages
   3647                     int count = content.getChildCount();
   3648                     for (int i = 0; i < count; i++) {
   3649                         View child = content.getChildAt(i);
   3650                         child.setVisibility(View.VISIBLE);
   3651                     }
   3652 
   3653                     // Reset page transforms
   3654                     if (page != null) {
   3655                         page.setTranslationX(0);
   3656                         page.setTranslationY(0);
   3657                         page.setAlpha(1);
   3658                     }
   3659                     content.setCurrentPage(content.getNextPage());
   3660 
   3661                     mAppsCustomizeContent.updateCurrentPageScroll();
   3662                 }
   3663             });
   3664 
   3665             final AnimatorSet stateAnimation = mStateAnimation;
   3666             final Runnable startAnimRunnable = new Runnable() {
   3667                 public void run() {
   3668                     // Check that mStateAnimation hasn't changed while
   3669                     // we waited for a layout/draw pass
   3670                     if (mStateAnimation != stateAnimation)
   3671                         return;
   3672                     dispatchOnLauncherTransitionStart(fromView, animated, false);
   3673                     dispatchOnLauncherTransitionStart(toView, animated, false);
   3674 
   3675                     if (Utilities.isLmpOrAbove()) {
   3676                         for (int i = 0; i < layerViews.size(); i++) {
   3677                             View v = layerViews.get(i);
   3678                             if (v != null) {
   3679                                 boolean attached = true;
   3680                                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
   3681                                     attached = v.isAttachedToWindow();
   3682                                 }
   3683                                 if (attached) v.buildLayer();
   3684                             }
   3685                         }
   3686                     }
   3687                     mStateAnimation.start();
   3688                 }
   3689             };
   3690             fromView.post(startAnimRunnable);
   3691         } else {
   3692             fromView.setVisibility(View.GONE);
   3693             dispatchOnLauncherTransitionPrepare(fromView, animated, true);
   3694             dispatchOnLauncherTransitionStart(fromView, animated, true);
   3695             dispatchOnLauncherTransitionEnd(fromView, animated, true);
   3696             dispatchOnLauncherTransitionPrepare(toView, animated, true);
   3697             dispatchOnLauncherTransitionStart(toView, animated, true);
   3698             dispatchOnLauncherTransitionEnd(toView, animated, true);
   3699         }
   3700     }
   3701 
   3702     @Override
   3703     public void onTrimMemory(int level) {
   3704         super.onTrimMemory(level);
   3705         if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
   3706             mAppsCustomizeTabHost.onTrimMemory();
   3707         }
   3708     }
   3709 
   3710     protected void showWorkspace(boolean animated) {
   3711         showWorkspace(animated, null);
   3712     }
   3713 
   3714     protected void showWorkspace() {
   3715         showWorkspace(true);
   3716     }
   3717 
   3718     void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
   3719         if (mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL) {
   3720             boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
   3721             mWorkspace.setVisibility(View.VISIBLE);
   3722             hideAppsCustomizeHelper(Workspace.State.NORMAL, animated, false, onCompleteRunnable);
   3723 
   3724             // Show the search bar (only animate if we were showing the drop target bar in spring
   3725             // loaded mode)
   3726             if (mSearchDropTargetBar != null) {
   3727                 mSearchDropTargetBar.showSearchBar(animated && wasInSpringLoadedMode);
   3728             }
   3729 
   3730             // Set focus to the AppsCustomize button
   3731             if (mAllAppsButton != null) {
   3732                 mAllAppsButton.requestFocus();
   3733             }
   3734         }
   3735 
   3736         // Change the state *after* we've called all the transition code
   3737         mState = State.WORKSPACE;
   3738 
   3739         // Resume the auto-advance of widgets
   3740         mUserPresent = true;
   3741         updateRunning();
   3742 
   3743         // Send an accessibility event to announce the context change
   3744         getWindow().getDecorView()
   3745                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3746 
   3747         onWorkspaceShown(animated);
   3748     }
   3749 
   3750     void showOverviewMode(boolean animated) {
   3751         mWorkspace.setVisibility(View.VISIBLE);
   3752         hideAppsCustomizeHelper(Workspace.State.OVERVIEW, animated, false, null);
   3753         mState = State.WORKSPACE;
   3754         onWorkspaceShown(animated);
   3755     }
   3756 
   3757     public void onWorkspaceShown(boolean animated) {
   3758     }
   3759 
   3760     void showAllApps(boolean animated, AppsCustomizePagedView.ContentType contentType,
   3761                      boolean resetPageToZero) {
   3762         if (mState != State.WORKSPACE) return;
   3763 
   3764         if (resetPageToZero) {
   3765             mAppsCustomizeTabHost.reset();
   3766         }
   3767         showAppsCustomizeHelper(animated, false, contentType);
   3768         mAppsCustomizeTabHost.post(new Runnable() {
   3769             @Override
   3770             public void run() {
   3771                 // We post this in-case the all apps view isn't yet constructed.
   3772                 mAppsCustomizeTabHost.requestFocus();
   3773             }
   3774         });
   3775 
   3776         // Change the state *after* we've called all the transition code
   3777         mState = State.APPS_CUSTOMIZE;
   3778 
   3779         // Pause the auto-advance of widgets until we are out of AllApps
   3780         mUserPresent = false;
   3781         updateRunning();
   3782         closeFolder();
   3783 
   3784         // Send an accessibility event to announce the context change
   3785         getWindow().getDecorView()
   3786                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3787     }
   3788 
   3789     void enterSpringLoadedDragMode() {
   3790         if (isAllAppsVisible()) {
   3791             hideAppsCustomizeHelper(Workspace.State.SPRING_LOADED, true, true, null);
   3792             mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
   3793         }
   3794     }
   3795 
   3796     void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
   3797             final Runnable onCompleteRunnable) {
   3798         if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
   3799 
   3800         mHandler.postDelayed(new Runnable() {
   3801             @Override
   3802             public void run() {
   3803                 if (successfulDrop) {
   3804                     // Before we show workspace, hide all apps again because
   3805                     // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
   3806                     // clean up our state transition functions
   3807                     mAppsCustomizeTabHost.setVisibility(View.GONE);
   3808                     showWorkspace(true, onCompleteRunnable);
   3809                 } else {
   3810                     exitSpringLoadedDragMode();
   3811                 }
   3812             }
   3813         }, delay);
   3814     }
   3815 
   3816     void exitSpringLoadedDragMode() {
   3817         if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
   3818             final boolean animated = true;
   3819             final boolean springLoaded = true;
   3820             showAppsCustomizeHelper(animated, springLoaded);
   3821             mState = State.APPS_CUSTOMIZE;
   3822         }
   3823         // Otherwise, we are not in spring loaded mode, so don't do anything.
   3824     }
   3825 
   3826     void lockAllApps() {
   3827         // TODO
   3828     }
   3829 
   3830     void unlockAllApps() {
   3831         // TODO
   3832     }
   3833 
   3834     /**
   3835      * Hides the hotseat area.
   3836      */
   3837     void hideHotseat(boolean animated) {
   3838         if (!LauncherAppState.getInstance().isScreenLarge()) {
   3839             if (animated) {
   3840                 if (mHotseat.getAlpha() != 0f) {
   3841                     int duration = 0;
   3842                     if (mSearchDropTargetBar != null) {
   3843                         duration = mSearchDropTargetBar.getTransitionOutDuration();
   3844                     }
   3845                     mHotseat.animate().alpha(0f).setDuration(duration);
   3846                 }
   3847             } else {
   3848                 mHotseat.setAlpha(0f);
   3849             }
   3850         }
   3851     }
   3852 
   3853     /**
   3854      * Add an item from all apps or customize onto the given workspace screen.
   3855      * If layout is null, add to the current screen.
   3856      */
   3857     void addExternalItemToScreen(ItemInfo itemInfo, final CellLayout layout) {
   3858         if (!mWorkspace.addExternalItemToScreen(itemInfo, layout)) {
   3859             showOutOfSpaceMessage(isHotseatLayout(layout));
   3860         }
   3861     }
   3862 
   3863     /** Maps the current orientation to an index for referencing orientation correct global icons */
   3864     private int getCurrentOrientationIndexForGlobalIcons() {
   3865         // default - 0, landscape - 1
   3866         switch (getResources().getConfiguration().orientation) {
   3867         case Configuration.ORIENTATION_LANDSCAPE:
   3868             return 1;
   3869         default:
   3870             return 0;
   3871         }
   3872     }
   3873 
   3874     private Drawable getExternalPackageToolbarIcon(ComponentName activityName, String resourceName) {
   3875         try {
   3876             PackageManager packageManager = getPackageManager();
   3877             // Look for the toolbar icon specified in the activity meta-data
   3878             Bundle metaData = packageManager.getActivityInfo(
   3879                     activityName, PackageManager.GET_META_DATA).metaData;
   3880             if (metaData != null) {
   3881                 int iconResId = metaData.getInt(resourceName);
   3882                 if (iconResId != 0) {
   3883                     Resources res = packageManager.getResourcesForActivity(activityName);
   3884                     return res.getDrawable(iconResId);
   3885                 }
   3886             }
   3887         } catch (NameNotFoundException e) {
   3888             // This can happen if the activity defines an invalid drawable
   3889             Log.w(TAG, "Failed to load toolbar icon; " + activityName.flattenToShortString() +
   3890                     " not found", e);
   3891         } catch (Resources.NotFoundException nfe) {
   3892             // This can happen if the activity defines an invalid drawable
   3893             Log.w(TAG, "Failed to load toolbar icon from " + activityName.flattenToShortString(),
   3894                     nfe);
   3895         }
   3896         return null;
   3897     }
   3898 
   3899     // if successful in getting icon, return it; otherwise, set button to use default drawable
   3900     private Drawable.ConstantState updateTextButtonWithIconFromExternalActivity(
   3901             int buttonId, ComponentName activityName, int fallbackDrawableId,
   3902             String toolbarResourceName) {
   3903         Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName);
   3904         Resources r = getResources();
   3905         int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
   3906         int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
   3907 
   3908         TextView button = (TextView) findViewById(buttonId);
   3909         // If we were unable to find the icon via the meta-data, use a generic one
   3910         if (toolbarIcon == null) {
   3911             toolbarIcon = r.getDrawable(fallbackDrawableId);
   3912             toolbarIcon.setBounds(0, 0, w, h);
   3913             if (button != null) {
   3914                 button.setCompoundDrawables(toolbarIcon, null, null, null);
   3915             }
   3916             return null;
   3917         } else {
   3918             toolbarIcon.setBounds(0, 0, w, h);
   3919             if (button != null) {
   3920                 button.setCompoundDrawables(toolbarIcon, null, null, null);
   3921             }
   3922             return toolbarIcon.getConstantState();
   3923         }
   3924     }
   3925 
   3926     // if successful in getting icon, return it; otherwise, set button to use default drawable
   3927     private Drawable.ConstantState updateButtonWithIconFromExternalActivity(
   3928             int buttonId, ComponentName activityName, int fallbackDrawableId,
   3929             String toolbarResourceName) {
   3930         ImageView button = (ImageView) findViewById(buttonId);
   3931         Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName);
   3932 
   3933         if (button != null) {
   3934             // If we were unable to find the icon via the meta-data, use a
   3935             // generic one
   3936             if (toolbarIcon == null) {
   3937                 button.setImageResource(fallbackDrawableId);
   3938             } else {
   3939                 button.setImageDrawable(toolbarIcon);
   3940             }
   3941         }
   3942 
   3943         return toolbarIcon != null ? toolbarIcon.getConstantState() : null;
   3944 
   3945     }
   3946 
   3947     private void updateTextButtonWithDrawable(int buttonId, Drawable d) {
   3948         TextView button = (TextView) findViewById(buttonId);
   3949         button.setCompoundDrawables(d, null, null, null);
   3950     }
   3951 
   3952     private void updateButtonWithDrawable(int buttonId, Drawable.ConstantState d) {
   3953         ImageView button = (ImageView) findViewById(buttonId);
   3954         button.setImageDrawable(d.newDrawable(getResources()));
   3955     }
   3956 
   3957     private void invalidatePressedFocusedStates(View container, View button) {
   3958         if (container instanceof HolographicLinearLayout) {
   3959             HolographicLinearLayout layout = (HolographicLinearLayout) container;
   3960             layout.invalidatePressedFocusedStates();
   3961         } else if (button instanceof HolographicImageView) {
   3962             HolographicImageView view = (HolographicImageView) button;
   3963             view.invalidatePressedFocusedStates();
   3964         }
   3965     }
   3966 
   3967     public View getQsbBar() {
   3968         if (mQsb == null) {
   3969             mQsb = mInflater.inflate(R.layout.qsb, mSearchDropTargetBar, false);
   3970             mSearchDropTargetBar.addView(mQsb);
   3971         }
   3972         return mQsb;
   3973     }
   3974 
   3975     protected boolean updateGlobalSearchIcon() {
   3976         final View searchButtonContainer = findViewById(R.id.search_button_container);
   3977         final ImageView searchButton = (ImageView) findViewById(R.id.search_button);
   3978         final View voiceButtonContainer = findViewById(R.id.voice_button_container);
   3979         final View voiceButton = findViewById(R.id.voice_button);
   3980 
   3981         final SearchManager searchManager =
   3982                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   3983         ComponentName activityName = searchManager.getGlobalSearchActivity();
   3984         if (activityName != null) {
   3985             int coi = getCurrentOrientationIndexForGlobalIcons();
   3986             sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   3987                     R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo,
   3988                     TOOLBAR_SEARCH_ICON_METADATA_NAME);
   3989             if (sGlobalSearchIcon[coi] == null) {
   3990                 sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   3991                         R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo,
   3992                         TOOLBAR_ICON_METADATA_NAME);
   3993             }
   3994 
   3995             if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.VISIBLE);
   3996             searchButton.setVisibility(View.VISIBLE);
   3997             invalidatePressedFocusedStates(searchButtonContainer, searchButton);
   3998             return true;
   3999         } else {
   4000             // We disable both search and voice search when there is no global search provider
   4001             if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.GONE);
   4002             if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE);
   4003             if (searchButton != null) searchButton.setVisibility(View.GONE);
   4004             if (voiceButton != null) voiceButton.setVisibility(View.GONE);
   4005             updateVoiceButtonProxyVisible(false);
   4006             return false;
   4007         }
   4008     }
   4009 
   4010     protected void updateGlobalSearchIcon(Drawable.ConstantState d) {
   4011         final View searchButtonContainer = findViewById(R.id.search_button_container);
   4012         final View searchButton = (ImageView) findViewById(R.id.search_button);
   4013         updateButtonWithDrawable(R.id.search_button, d);
   4014         invalidatePressedFocusedStates(searchButtonContainer, searchButton);
   4015     }
   4016 
   4017     protected boolean updateVoiceSearchIcon(boolean searchVisible) {
   4018         final View voiceButtonContainer = findViewById(R.id.voice_button_container);
   4019         final View voiceButton = findViewById(R.id.voice_button);
   4020 
   4021         // We only show/update the voice search icon if the search icon is enabled as well
   4022         final SearchManager searchManager =
   4023                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   4024         ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
   4025 
   4026         ComponentName activityName = null;
   4027         if (globalSearchActivity != null) {
   4028             // Check if the global search activity handles voice search
   4029             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   4030             intent.setPackage(globalSearchActivity.getPackageName());
   4031             activityName = intent.resolveActivity(getPackageManager());
   4032         }
   4033 
   4034         if (activityName == null) {
   4035             // Fallback: check if an activity other than the global search activity
   4036             // resolves this
   4037             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   4038             activityName = intent.resolveActivity(getPackageManager());
   4039         }
   4040         if (searchVisible && activityName != null) {
   4041             int coi = getCurrentOrientationIndexForGlobalIcons();
   4042             sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   4043                     R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo,
   4044                     TOOLBAR_VOICE_SEARCH_ICON_METADATA_NAME);
   4045             if (sVoiceSearchIcon[coi] == null) {
   4046                 sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   4047                         R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo,
   4048                         TOOLBAR_ICON_METADATA_NAME);
   4049             }
   4050             if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.VISIBLE);
   4051             voiceButton.setVisibility(View.VISIBLE);
   4052             updateVoiceButtonProxyVisible(false);
   4053             invalidatePressedFocusedStates(voiceButtonContainer, voiceButton);
   4054             return true;
   4055         } else {
   4056             if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE);
   4057             if (voiceButton != null) voiceButton.setVisibility(View.GONE);
   4058             updateVoiceButtonProxyVisible(false);
   4059             return false;
   4060         }
   4061     }
   4062 
   4063     protected void updateVoiceSearchIcon(Drawable.ConstantState d) {
   4064         final View voiceButtonContainer = findViewById(R.id.voice_button_container);
   4065         final View voiceButton = findViewById(R.id.voice_button);
   4066         updateButtonWithDrawable(R.id.voice_button, d);
   4067         invalidatePressedFocusedStates(voiceButtonContainer, voiceButton);
   4068     }
   4069 
   4070     public void updateVoiceButtonProxyVisible(boolean forceDisableVoiceButtonProxy) {
   4071         final View voiceButtonProxy = findViewById(R.id.voice_button_proxy);
   4072         if (voiceButtonProxy != null) {
   4073             boolean visible = !forceDisableVoiceButtonProxy &&
   4074                     mWorkspace.shouldVoiceButtonProxyBeVisible();
   4075             voiceButtonProxy.setVisibility(visible ? View.VISIBLE : View.GONE);
   4076             voiceButtonProxy.bringToFront();
   4077         }
   4078     }
   4079 
   4080     /**
   4081      * This is an overrid eot disable the voice button proxy.  If disabled is true, then the voice button proxy
   4082      * will be hidden regardless of what shouldVoiceButtonProxyBeVisible() returns.
   4083      */
   4084     public void disableVoiceButtonProxy(boolean disabled) {
   4085         updateVoiceButtonProxyVisible(disabled);
   4086     }
   4087 
   4088     @Override
   4089     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
   4090         final boolean result = super.dispatchPopulateAccessibilityEvent(event);
   4091         final List<CharSequence> text = event.getText();
   4092         text.clear();
   4093         // Populate event with a fake title based on the current state.
   4094         if (mState == State.APPS_CUSTOMIZE) {
   4095             text.add(mAppsCustomizeTabHost.getContentTag());
   4096         } else {
   4097             text.add(getString(R.string.all_apps_home_button_label));
   4098         }
   4099         return result;
   4100     }
   4101 
   4102     /**
   4103      * Receives notifications when system dialogs are to be closed.
   4104      */
   4105     private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
   4106         @Override
   4107         public void onReceive(Context context, Intent intent) {
   4108             closeSystemDialogs();
   4109         }
   4110     }
   4111 
   4112     /**
   4113      * Receives notifications whenever the appwidgets are reset.
   4114      */
   4115     private class AppWidgetResetObserver extends ContentObserver {
   4116         public AppWidgetResetObserver() {
   4117             super(new Handler());
   4118         }
   4119 
   4120         @Override
   4121         public void onChange(boolean selfChange) {
   4122             onAppWidgetReset();
   4123         }
   4124     }
   4125 
   4126     /**
   4127      * If the activity is currently paused, signal that we need to run the passed Runnable
   4128      * in onResume.
   4129      *
   4130      * This needs to be called from incoming places where resources might have been loaded
   4131      * while we are paused.  That is becaues the Configuration might be wrong
   4132      * when we're not running, and if it comes back to what it was when we
   4133      * were paused, we are not restarted.
   4134      *
   4135      * Implementation of the method from LauncherModel.Callbacks.
   4136      *
   4137      * @return true if we are currently paused.  The caller might be able to
   4138      * skip some work in that case since we will come back again.
   4139      */
   4140     private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) {
   4141         if (mPaused) {
   4142             Log.i(TAG, "Deferring update until onResume");
   4143             if (deletePreviousRunnables) {
   4144                 while (mBindOnResumeCallbacks.remove(run)) {
   4145                 }
   4146             }
   4147             mBindOnResumeCallbacks.add(run);
   4148             return true;
   4149         } else {
   4150             return false;
   4151         }
   4152     }
   4153 
   4154     private boolean waitUntilResume(Runnable run) {
   4155         return waitUntilResume(run, false);
   4156     }
   4157 
   4158     public void addOnResumeCallback(Runnable run) {
   4159         mOnResumeCallbacks.add(run);
   4160     }
   4161 
   4162     /**
   4163      * If the activity is currently paused, signal that we need to re-run the loader
   4164      * in onResume.
   4165      *
   4166      * This needs to be called from incoming places where resources might have been loaded
   4167      * while we are paused.  That is becaues the Configuration might be wrong
   4168      * when we're not running, and if it comes back to what it was when we
   4169      * were paused, we are not restarted.
   4170      *
   4171      * Implementation of the method from LauncherModel.Callbacks.
   4172      *
   4173      * @return true if we are currently paused.  The caller might be able to
   4174      * skip some work in that case since we will come back again.
   4175      */
   4176     public boolean setLoadOnResume() {
   4177         if (mPaused) {
   4178             Log.i(TAG, "setLoadOnResume");
   4179             mOnResumeNeedsLoad = true;
   4180             return true;
   4181         } else {
   4182             return false;
   4183         }
   4184     }
   4185 
   4186     /**
   4187      * Implementation of the method from LauncherModel.Callbacks.
   4188      */
   4189     public int getCurrentWorkspaceScreen() {
   4190         if (mWorkspace != null) {
   4191             return mWorkspace.getCurrentPage();
   4192         } else {
   4193             return SCREEN_COUNT / 2;
   4194         }
   4195     }
   4196 
   4197     /**
   4198      * Refreshes the shortcuts shown on the workspace.
   4199      *
   4200      * Implementation of the method from LauncherModel.Callbacks.
   4201      */
   4202     public void startBinding() {
   4203         setWorkspaceLoading(true);
   4204 
   4205         // If we're starting binding all over again, clear any bind calls we'd postponed in
   4206         // the past (see waitUntilResume) -- we don't need them since we're starting binding
   4207         // from scratch again
   4208         mBindOnResumeCallbacks.clear();
   4209 
   4210         // Clear the workspace because it's going to be rebound
   4211         mWorkspace.clearDropTargets();
   4212         mWorkspace.removeAllWorkspaceScreens();
   4213 
   4214         mWidgetsToAdvance.clear();
   4215         if (mHotseat != null) {
   4216             mHotseat.resetLayout();
   4217         }
   4218     }
   4219 
   4220     @Override
   4221     public void bindScreens(ArrayList<Long> orderedScreenIds) {
   4222         bindAddScreens(orderedScreenIds);
   4223 
   4224         // If there are no screens, we need to have an empty screen
   4225         if (orderedScreenIds.size() == 0) {
   4226             mWorkspace.addExtraEmptyScreen();
   4227         }
   4228 
   4229         // Create the custom content page (this call updates mDefaultScreen which calls
   4230         // setCurrentPage() so ensure that all pages are added before calling this).
   4231         if (hasCustomContentToLeft()) {
   4232             mWorkspace.createCustomContentContainer();
   4233             populateCustomContentContainer();
   4234         }
   4235     }
   4236 
   4237     @Override
   4238     public void bindAddScreens(ArrayList<Long> orderedScreenIds) {
   4239         // Log to disk
   4240         Launcher.addDumpLog(TAG, "11683562 - bindAddScreens()", true);
   4241         Launcher.addDumpLog(TAG, "11683562 -   orderedScreenIds: " +
   4242                 TextUtils.join(", ", orderedScreenIds), true);
   4243         int count = orderedScreenIds.size();
   4244         for (int i = 0; i < count; i++) {
   4245             mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i));
   4246         }
   4247     }
   4248 
   4249     private boolean shouldShowWeightWatcher() {
   4250         String spKey = LauncherAppState.getSharedPreferencesKey();
   4251         SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
   4252         boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, SHOW_WEIGHT_WATCHER_DEFAULT);
   4253 
   4254         return show;
   4255     }
   4256 
   4257     private void toggleShowWeightWatcher() {
   4258         String spKey = LauncherAppState.getSharedPreferencesKey();
   4259         SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
   4260         boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, true);
   4261 
   4262         show = !show;
   4263 
   4264         SharedPreferences.Editor editor = sp.edit();
   4265         editor.putBoolean(SHOW_WEIGHT_WATCHER, show);
   4266         editor.commit();
   4267 
   4268         if (mWeightWatcher != null) {
   4269             mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
   4270         }
   4271     }
   4272 
   4273     public void bindAppsAdded(final ArrayList<Long> newScreens,
   4274                               final ArrayList<ItemInfo> addNotAnimated,
   4275                               final ArrayList<ItemInfo> addAnimated,
   4276                               final ArrayList<AppInfo> addedApps) {
   4277         Runnable r = new Runnable() {
   4278             public void run() {
   4279                 bindAppsAdded(newScreens, addNotAnimated, addAnimated, addedApps);
   4280             }
   4281         };
   4282         if (waitUntilResume(r)) {
   4283             return;
   4284         }
   4285 
   4286         // Add the new screens
   4287         if (newScreens != null) {
   4288             bindAddScreens(newScreens);
   4289         }
   4290 
   4291         // We add the items without animation on non-visible pages, and with
   4292         // animations on the new page (which we will try and snap to).
   4293         if (addNotAnimated != null && !addNotAnimated.isEmpty()) {
   4294             bindItems(addNotAnimated, 0,
   4295                     addNotAnimated.size(), false);
   4296         }
   4297         if (addAnimated != null && !addAnimated.isEmpty()) {
   4298             bindItems(addAnimated, 0,
   4299                     addAnimated.size(), true);
   4300         }
   4301 
   4302         // Remove the extra empty screen
   4303         mWorkspace.removeExtraEmptyScreen(false, false);
   4304 
   4305         if (!LauncherAppState.isDisableAllApps() &&
   4306                 addedApps != null && mAppsCustomizeContent != null) {
   4307             mAppsCustomizeContent.addApps(addedApps);
   4308         }
   4309     }
   4310 
   4311     /**
   4312      * Bind the items start-end from the list.
   4313      *
   4314      * Implementation of the method from LauncherModel.Callbacks.
   4315      */
   4316     public void bindItems(final ArrayList<ItemInfo> shortcuts, final int start, final int end,
   4317                           final boolean forceAnimateIcons) {
   4318         Runnable r = new Runnable() {
   4319             public void run() {
   4320                 bindItems(shortcuts, start, end, forceAnimateIcons);
   4321             }
   4322         };
   4323         if (waitUntilResume(r)) {
   4324             return;
   4325         }
   4326 
   4327         // Get the list of added shortcuts and intersect them with the set of shortcuts here
   4328         final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
   4329         final Collection<Animator> bounceAnims = new ArrayList<Animator>();
   4330         final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
   4331         Workspace workspace = mWorkspace;
   4332         long newShortcutsScreenId = -1;
   4333         for (int i = start; i < end; i++) {
   4334             final ItemInfo item = shortcuts.get(i);
   4335 
   4336             // Short circuit if we are loading dock items for a configuration which has no dock
   4337             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
   4338                     mHotseat == null) {
   4339                 continue;
   4340             }
   4341 
   4342             switch (item.itemType) {
   4343                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   4344                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   4345                     ShortcutInfo info = (ShortcutInfo) item;
   4346                     View shortcut = createShortcut(info);
   4347 
   4348                     /*
   4349                      * TODO: FIX collision case
   4350                      */
   4351                     if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
   4352                         CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
   4353                         if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
   4354                             View v = cl.getChildAt(item.cellX, item.cellY);
   4355                             Object tag = v.getTag();
   4356                             String desc = "Collision while binding workspace item: " + item
   4357                                     + ". Collides with " + tag;
   4358                             if (LauncherAppState.isDogfoodBuild()) {
   4359                                 throw (new RuntimeException(desc));
   4360                             } else {
   4361                                 Log.d(TAG, desc);
   4362                             }
   4363                         }
   4364                     }
   4365 
   4366                     workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX,
   4367                             item.cellY, 1, 1);
   4368                     if (animateIcons) {
   4369                         // Animate all the applications up now
   4370                         shortcut.setAlpha(0f);
   4371                         shortcut.setScaleX(0f);
   4372                         shortcut.setScaleY(0f);
   4373                         bounceAnims.add(createNewAppBounceAnimation(shortcut, i));
   4374                         newShortcutsScreenId = item.screenId;
   4375                     }
   4376                     break;
   4377                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
   4378                     FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
   4379                             (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
   4380                             (FolderInfo) item, mIconCache);
   4381                     workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX,
   4382                             item.cellY, 1, 1);
   4383                     break;
   4384                 default:
   4385                     throw new RuntimeException("Invalid Item Type");
   4386             }
   4387         }
   4388 
   4389         if (animateIcons) {
   4390             // Animate to the correct page
   4391             if (newShortcutsScreenId > -1) {
   4392                 long currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
   4393                 final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newShortcutsScreenId);
   4394                 final Runnable startBounceAnimRunnable = new Runnable() {
   4395                     public void run() {
   4396                         anim.playTogether(bounceAnims);
   4397                         anim.start();
   4398                     }
   4399                 };
   4400                 if (newShortcutsScreenId != currentScreenId) {
   4401                     // We post the animation slightly delayed to prevent slowdowns
   4402                     // when we are loading right after we return to launcher.
   4403                     mWorkspace.postDelayed(new Runnable() {
   4404                         public void run() {
   4405                             if (mWorkspace != null) {
   4406                                 mWorkspace.snapToPage(newScreenIndex);
   4407                                 mWorkspace.postDelayed(startBounceAnimRunnable,
   4408                                         NEW_APPS_ANIMATION_DELAY);
   4409                             }
   4410                         }
   4411                     }, NEW_APPS_PAGE_MOVE_DELAY);
   4412                 } else {
   4413                     mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
   4414                 }
   4415             }
   4416         }
   4417         workspace.requestLayout();
   4418     }
   4419 
   4420     /**
   4421      * Implementation of the method from LauncherModel.Callbacks.
   4422      */
   4423     public void bindFolders(final HashMap<Long, FolderInfo> folders) {
   4424         Runnable r = new Runnable() {
   4425             public void run() {
   4426                 bindFolders(folders);
   4427             }
   4428         };
   4429         if (waitUntilResume(r)) {
   4430             return;
   4431         }
   4432         sFolders.clear();
   4433         sFolders.putAll(folders);
   4434     }
   4435 
   4436     /**
   4437      * Add the views for a widget to the workspace.
   4438      *
   4439      * Implementation of the method from LauncherModel.Callbacks.
   4440      */
   4441     public void bindAppWidget(final LauncherAppWidgetInfo item) {
   4442         Runnable r = new Runnable() {
   4443             public void run() {
   4444                 bindAppWidget(item);
   4445             }
   4446         };
   4447         if (waitUntilResume(r)) {
   4448             return;
   4449         }
   4450 
   4451         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
   4452         if (DEBUG_WIDGETS) {
   4453             Log.d(TAG, "bindAppWidget: " + item);
   4454         }
   4455         final Workspace workspace = mWorkspace;
   4456 
   4457         AppWidgetProviderInfo appWidgetInfo;
   4458         if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) &&
   4459                 ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) {
   4460 
   4461             appWidgetInfo = mModel.findAppWidgetProviderInfoWithComponent(this, item.providerName);
   4462             if (appWidgetInfo == null) {
   4463                 if (DEBUG_WIDGETS) {
   4464                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
   4465                             + " belongs to component " + item.providerName
   4466                             + ", as the povider is null");
   4467                 }
   4468                 LauncherModel.deleteItemFromDatabase(this, item);
   4469                 return;
   4470             }
   4471             // Note: This assumes that the id remap broadcast is received before this step.
   4472             // If that is not the case, the id remap will be ignored and user may see the
   4473             // click to setup view.
   4474             PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null, null);
   4475             pendingInfo.spanX = item.spanX;
   4476             pendingInfo.spanY = item.spanY;
   4477             pendingInfo.minSpanX = item.minSpanX;
   4478             pendingInfo.minSpanY = item.minSpanY;
   4479             Bundle options =
   4480                     AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
   4481 
   4482             int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
   4483             boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
   4484                     newWidgetId, appWidgetInfo, options);
   4485 
   4486             // TODO consider showing a permission dialog when the widget is clicked.
   4487             if (!success) {
   4488                 mAppWidgetHost.deleteAppWidgetId(newWidgetId);
   4489                 if (DEBUG_WIDGETS) {
   4490                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
   4491                             + " belongs to component " + item.providerName
   4492                             + ", as the launcher is unable to bing a new widget id");
   4493                 }
   4494                 LauncherModel.deleteItemFromDatabase(this, item);
   4495                 return;
   4496             }
   4497 
   4498             item.appWidgetId = newWidgetId;
   4499 
   4500             // If the widget has a configure activity, it is still needs to set it up, otherwise
   4501             // the widget is ready to go.
   4502             item.restoreStatus = (appWidgetInfo.configure == null)
   4503                     ? LauncherAppWidgetInfo.RESTORE_COMPLETED
   4504                     : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
   4505 
   4506             LauncherModel.updateItemInDatabase(this, item);
   4507         }
   4508 
   4509         if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
   4510             final int appWidgetId = item.appWidgetId;
   4511             appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   4512             if (DEBUG_WIDGETS) {
   4513                 Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
   4514             }
   4515 
   4516             item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   4517         } else {
   4518             appWidgetInfo = null;
   4519             PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item);
   4520             view.updateIcon(mIconCache);
   4521             item.hostView = view;
   4522             item.hostView.updateAppWidget(null);
   4523             item.hostView.setOnClickListener(this);
   4524         }
   4525 
   4526         item.hostView.setTag(item);
   4527         item.onBindAppWidget(this);
   4528 
   4529         workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX,
   4530                 item.cellY, item.spanX, item.spanY, false);
   4531         addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
   4532 
   4533         workspace.requestLayout();
   4534 
   4535         if (DEBUG_WIDGETS) {
   4536             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
   4537                     + (SystemClock.uptimeMillis()-start) + "ms");
   4538         }
   4539     }
   4540 
   4541     /**
   4542      * Restores a pending widget.
   4543      *
   4544      * @param appWidgetId The app widget id
   4545      * @param cellInfo The position on screen where to create the widget.
   4546      */
   4547     private void completeRestoreAppWidget(final int appWidgetId) {
   4548         LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId);
   4549         if ((view == null) || !(view instanceof PendingAppWidgetHostView)) {
   4550             Log.e(TAG, "Widget update called, when the widget no longer exists.");
   4551             return;
   4552         }
   4553 
   4554         LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
   4555         info.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
   4556 
   4557         mWorkspace.reinflateWidgetsIfNecessary();
   4558         LauncherModel.updateItemInDatabase(this, info);
   4559     }
   4560 
   4561     public void onPageBoundSynchronously(int page) {
   4562         mSynchronouslyBoundPages.add(page);
   4563     }
   4564 
   4565     /**
   4566      * Callback saying that there aren't any more items to bind.
   4567      *
   4568      * Implementation of the method from LauncherModel.Callbacks.
   4569      */
   4570     public void finishBindingItems(final boolean upgradePath) {
   4571         Runnable r = new Runnable() {
   4572             public void run() {
   4573                 finishBindingItems(upgradePath);
   4574             }
   4575         };
   4576         if (waitUntilResume(r)) {
   4577             return;
   4578         }
   4579         if (mSavedState != null) {
   4580             if (!mWorkspace.hasFocus()) {
   4581                 mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
   4582             }
   4583             mSavedState = null;
   4584         }
   4585 
   4586         mWorkspace.restoreInstanceStateForRemainingPages();
   4587 
   4588         setWorkspaceLoading(false);
   4589         sendLoadingCompleteBroadcastIfNecessary();
   4590 
   4591         // If we received the result of any pending adds while the loader was running (e.g. the
   4592         // widget configuration forced an orientation change), process them now.
   4593         if (sPendingAddItem != null) {
   4594             final long screenId = completeAdd(sPendingAddItem);
   4595 
   4596             // TODO: this moves the user to the page where the pending item was added. Ideally,
   4597             // the screen would be guaranteed to exist after bind, and the page would be set through
   4598             // the workspace restore process.
   4599             mWorkspace.post(new Runnable() {
   4600                 @Override
   4601                 public void run() {
   4602                     mWorkspace.snapToScreenId(screenId);
   4603                 }
   4604             });
   4605             sPendingAddItem = null;
   4606         }
   4607 
   4608         if (upgradePath) {
   4609             mWorkspace.getUniqueComponents(true, null);
   4610             mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null);
   4611         }
   4612         PackageInstallerCompat.getInstance(this).onFinishBind();
   4613         mModel.recheckRestoredItems(this);
   4614     }
   4615 
   4616     private void sendLoadingCompleteBroadcastIfNecessary() {
   4617         if (!mSharedPrefs.getBoolean(FIRST_LOAD_COMPLETE, false)) {
   4618             String permission =
   4619                     getResources().getString(R.string.receive_first_load_broadcast_permission);
   4620             Intent intent = new Intent(ACTION_FIRST_LOAD_COMPLETE);
   4621             sendBroadcast(intent, permission);
   4622             SharedPreferences.Editor editor = mSharedPrefs.edit();
   4623             editor.putBoolean(FIRST_LOAD_COMPLETE, true);
   4624             editor.apply();
   4625         }
   4626     }
   4627 
   4628     public boolean isAllAppsButtonRank(int rank) {
   4629         if (mHotseat != null) {
   4630             return mHotseat.isAllAppsButtonRank(rank);
   4631         }
   4632         return false;
   4633     }
   4634 
   4635     private boolean canRunNewAppsAnimation() {
   4636         long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
   4637         return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
   4638     }
   4639 
   4640     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
   4641         ValueAnimator bounceAnim = LauncherAnimUtils.ofPropertyValuesHolder(v,
   4642                 PropertyValuesHolder.ofFloat("alpha", 1f),
   4643                 PropertyValuesHolder.ofFloat("scaleX", 1f),
   4644                 PropertyValuesHolder.ofFloat("scaleY", 1f));
   4645         bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
   4646         bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
   4647         bounceAnim.setInterpolator(new SmoothPagedView.OvershootInterpolator());
   4648         return bounceAnim;
   4649     }
   4650 
   4651     public boolean useVerticalBarLayout() {
   4652         return LauncherAppState.getInstance().getDynamicGrid().
   4653                 getDeviceProfile().isVerticalBarLayout();
   4654     }
   4655 
   4656     protected Rect getSearchBarBounds() {
   4657         return LauncherAppState.getInstance().getDynamicGrid().
   4658                 getDeviceProfile().getSearchBarBounds();
   4659     }
   4660 
   4661     @Override
   4662     public void bindSearchablesChanged() {
   4663         boolean searchVisible = updateGlobalSearchIcon();
   4664         boolean voiceVisible = updateVoiceSearchIcon(searchVisible);
   4665         if (mSearchDropTargetBar != null) {
   4666             mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
   4667         }
   4668     }
   4669 
   4670     /**
   4671      * Add the icons for all apps.
   4672      *
   4673      * Implementation of the method from LauncherModel.Callbacks.
   4674      */
   4675     public void bindAllApplications(final ArrayList<AppInfo> apps) {
   4676         if (LauncherAppState.isDisableAllApps()) {
   4677             if (mIntentsOnWorkspaceFromUpgradePath != null) {
   4678                 if (LauncherModel.UPGRADE_USE_MORE_APPS_FOLDER) {
   4679                     getHotseat().addAllAppsFolder(mIconCache, apps,
   4680                             mIntentsOnWorkspaceFromUpgradePath, Launcher.this, mWorkspace);
   4681                 }
   4682                 mIntentsOnWorkspaceFromUpgradePath = null;
   4683             }
   4684             if (mAppsCustomizeContent != null) {
   4685                 mAppsCustomizeContent.onPackagesUpdated(
   4686                         LauncherModel.getSortedWidgetsAndShortcuts(this));
   4687             }
   4688         } else {
   4689             if (mAppsCustomizeContent != null) {
   4690                 mAppsCustomizeContent.setApps(apps);
   4691                 mAppsCustomizeContent.onPackagesUpdated(
   4692                         LauncherModel.getSortedWidgetsAndShortcuts(this));
   4693             }
   4694         }
   4695     }
   4696 
   4697     /**
   4698      * A package was updated.
   4699      *
   4700      * Implementation of the method from LauncherModel.Callbacks.
   4701      */
   4702     public void bindAppsUpdated(final ArrayList<AppInfo> apps) {
   4703         Runnable r = new Runnable() {
   4704             public void run() {
   4705                 bindAppsUpdated(apps);
   4706             }
   4707         };
   4708         if (waitUntilResume(r)) {
   4709             return;
   4710         }
   4711 
   4712         if (mWorkspace != null) {
   4713             mWorkspace.updateShortcutsAndWidgets(apps);
   4714         }
   4715 
   4716         if (!LauncherAppState.isDisableAllApps() &&
   4717                 mAppsCustomizeContent != null) {
   4718             mAppsCustomizeContent.updateApps(apps);
   4719         }
   4720     }
   4721 
   4722     /**
   4723      * Packages were restored
   4724      */
   4725     public void bindAppsRestored(final ArrayList<AppInfo> apps) {
   4726         Runnable r = new Runnable() {
   4727             public void run() {
   4728                 bindAppsRestored(apps);
   4729             }
   4730         };
   4731         if (waitUntilResume(r)) {
   4732             return;
   4733         }
   4734 
   4735         if (mWorkspace != null) {
   4736             mWorkspace.updateShortcutsAndWidgets(apps);
   4737         }
   4738     }
   4739 
   4740     /**
   4741      * Update the state of a package, typically related to install state.
   4742      *
   4743      * Implementation of the method from LauncherModel.Callbacks.
   4744      */
   4745     @Override
   4746     public void updatePackageState(ArrayList<PackageInstallInfo> installInfo) {
   4747         if (mWorkspace != null) {
   4748             mWorkspace.updatePackageState(installInfo);
   4749         }
   4750     }
   4751 
   4752     /**
   4753      * Update the label and icon of all the icons in a package
   4754      *
   4755      * Implementation of the method from LauncherModel.Callbacks.
   4756      */
   4757     @Override
   4758     public void updatePackageBadge(String packageName) {
   4759         if (mWorkspace != null) {
   4760             mWorkspace.updatePackageBadge(packageName, UserHandleCompat.myUserHandle());
   4761         }
   4762     }
   4763 
   4764     /**
   4765      * A package was uninstalled.  We take both the super set of packageNames
   4766      * in addition to specific applications to remove, the reason being that
   4767      * this can be called when a package is updated as well.  In that scenario,
   4768      * we only remove specific components from the workspace, where as
   4769      * package-removal should clear all items by package name.
   4770      *
   4771      * Implementation of the method from LauncherModel.Callbacks.
   4772      */
   4773     public void bindComponentsRemoved(final ArrayList<String> packageNames,
   4774             final ArrayList<AppInfo> appInfos, final UserHandleCompat user) {
   4775         Runnable r = new Runnable() {
   4776             public void run() {
   4777                 bindComponentsRemoved(packageNames, appInfos, user);
   4778             }
   4779         };
   4780         if (waitUntilResume(r)) {
   4781             return;
   4782         }
   4783 
   4784         if (!packageNames.isEmpty()) {
   4785             mWorkspace.removeItemsByPackageName(packageNames, user);
   4786         }
   4787         if (!appInfos.isEmpty()) {
   4788             mWorkspace.removeItemsByApplicationInfo(appInfos, user);
   4789         }
   4790 
   4791         // Notify the drag controller
   4792         mDragController.onAppsRemoved(packageNames, appInfos);
   4793 
   4794         // Update AllApps
   4795         if (!LauncherAppState.isDisableAllApps() &&
   4796                 mAppsCustomizeContent != null) {
   4797             mAppsCustomizeContent.removeApps(appInfos);
   4798         }
   4799     }
   4800 
   4801     /**
   4802      * A number of packages were updated.
   4803      */
   4804     private ArrayList<Object> mWidgetsAndShortcuts;
   4805     private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
   4806             public void run() {
   4807                 bindPackagesUpdated(mWidgetsAndShortcuts);
   4808                 mWidgetsAndShortcuts = null;
   4809             }
   4810         };
   4811     public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) {
   4812         if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
   4813             mWidgetsAndShortcuts = widgetsAndShortcuts;
   4814             return;
   4815         }
   4816 
   4817         // Update the widgets pane
   4818         if (mAppsCustomizeContent != null) {
   4819             mAppsCustomizeContent.onPackagesUpdated(widgetsAndShortcuts);
   4820         }
   4821     }
   4822 
   4823     private int mapConfigurationOriActivityInfoOri(int configOri) {
   4824         final Display d = getWindowManager().getDefaultDisplay();
   4825         int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
   4826         switch (d.getRotation()) {
   4827         case Surface.ROTATION_0:
   4828         case Surface.ROTATION_180:
   4829             // We are currently in the same basic orientation as the natural orientation
   4830             naturalOri = configOri;
   4831             break;
   4832         case Surface.ROTATION_90:
   4833         case Surface.ROTATION_270:
   4834             // We are currently in the other basic orientation to the natural orientation
   4835             naturalOri = (configOri == Configuration.ORIENTATION_LANDSCAPE) ?
   4836                     Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
   4837             break;
   4838         }
   4839 
   4840         int[] oriMap = {
   4841                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
   4842                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
   4843                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
   4844                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
   4845         };
   4846         // Since the map starts at portrait, we need to offset if this device's natural orientation
   4847         // is landscape.
   4848         int indexOffset = 0;
   4849         if (naturalOri == Configuration.ORIENTATION_LANDSCAPE) {
   4850             indexOffset = 1;
   4851         }
   4852         return oriMap[(d.getRotation() + indexOffset) % 4];
   4853     }
   4854 
   4855     public boolean isRotationEnabled() {
   4856         boolean enableRotation = sForceEnableRotation ||
   4857                 getResources().getBoolean(R.bool.allow_rotation);
   4858         return enableRotation;
   4859     }
   4860     public void lockScreenOrientation() {
   4861         if (isRotationEnabled()) {
   4862             setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
   4863                     .getConfiguration().orientation));
   4864         }
   4865     }
   4866     public void unlockScreenOrientation(boolean immediate) {
   4867         if (isRotationEnabled()) {
   4868             if (immediate) {
   4869                 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   4870             } else {
   4871                 mHandler.postDelayed(new Runnable() {
   4872                     public void run() {
   4873                         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   4874                     }
   4875                 }, mRestoreScreenOrientationDelay);
   4876             }
   4877         }
   4878     }
   4879 
   4880     /**
   4881      * Called when the SearchBar hint should be changed.
   4882      *
   4883      * @param hint the hint to be displayed in the search bar.
   4884      */
   4885     protected void onSearchBarHintChanged(String hint) {
   4886 
   4887     }
   4888 
   4889     protected boolean isLauncherPreinstalled() {
   4890         PackageManager pm = getPackageManager();
   4891         try {
   4892             ApplicationInfo ai = pm.getApplicationInfo(getComponentName().getPackageName(), 0);
   4893             if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
   4894                 return true;
   4895             } else {
   4896                 return false;
   4897             }
   4898         } catch (NameNotFoundException e) {
   4899             e.printStackTrace();
   4900             return false;
   4901         }
   4902     }
   4903 
   4904     /**
   4905      * This method indicates whether or not we should suggest default wallpaper dimensions
   4906      * when our wallpaper cropper was not yet used to set a wallpaper.
   4907      */
   4908     protected boolean overrideWallpaperDimensions() {
   4909         return true;
   4910     }
   4911 
   4912     protected boolean shouldClingFocusHotseatApp() {
   4913         return false;
   4914     }
   4915     protected String getFirstRunClingSearchBarHint() {
   4916         return "";
   4917     }
   4918     protected String getFirstRunCustomContentHint() {
   4919         return "";
   4920     }
   4921     protected int getFirstRunFocusedHotseatAppDrawableId() {
   4922         return -1;
   4923     }
   4924     protected ComponentName getFirstRunFocusedHotseatAppComponentName() {
   4925         return null;
   4926     }
   4927     protected int getFirstRunFocusedHotseatAppRank() {
   4928         return -1;
   4929     }
   4930     protected String getFirstRunFocusedHotseatAppBubbleTitle() {
   4931         return "";
   4932     }
   4933     protected String getFirstRunFocusedHotseatAppBubbleDescription() {
   4934         return "";
   4935     }
   4936 
   4937     /**
   4938      * To be overridden by subclasses to indicate that there is an activity to launch
   4939      * before showing the standard launcher experience.
   4940      */
   4941     protected boolean hasFirstRunActivity() {
   4942         return false;
   4943     }
   4944 
   4945     /**
   4946      * To be overridden by subclasses to launch any first run activity
   4947      */
   4948     protected Intent getFirstRunActivity() {
   4949         return null;
   4950     }
   4951 
   4952     private boolean shouldRunFirstRunActivity() {
   4953         return !ActivityManager.isRunningInTestHarness() &&
   4954                 !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
   4955     }
   4956 
   4957     protected boolean hasRunFirstRunActivity() {
   4958         return mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
   4959     }
   4960 
   4961     public boolean showFirstRunActivity() {
   4962         if (shouldRunFirstRunActivity() &&
   4963                 hasFirstRunActivity()) {
   4964             Intent firstRunIntent = getFirstRunActivity();
   4965             if (firstRunIntent != null) {
   4966                 startActivity(firstRunIntent);
   4967                 markFirstRunActivityShown();
   4968                 return true;
   4969             }
   4970         }
   4971         return false;
   4972     }
   4973 
   4974     private void markFirstRunActivityShown() {
   4975         SharedPreferences.Editor editor = mSharedPrefs.edit();
   4976         editor.putBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, true);
   4977         editor.apply();
   4978     }
   4979 
   4980     /**
   4981      * To be overridden by subclasses to indicate that there is an in-activity full-screen intro
   4982      * screen that must be displayed and dismissed.
   4983      */
   4984     protected boolean hasDismissableIntroScreen() {
   4985         return false;
   4986     }
   4987 
   4988     /**
   4989      * Full screen intro screen to be shown and dismissed before the launcher can be used.
   4990      */
   4991     protected View getIntroScreen() {
   4992         return null;
   4993     }
   4994 
   4995     /**
   4996      * To be overriden by subclasses to indicate whether the in-activity intro screen has been
   4997      * dismissed. This method is ignored if #hasDismissableIntroScreen returns false.
   4998      */
   4999     private boolean shouldShowIntroScreen() {
   5000         return hasDismissableIntroScreen() &&
   5001                 !mSharedPrefs.getBoolean(INTRO_SCREEN_DISMISSED, false);
   5002     }
   5003 
   5004     protected void showIntroScreen() {
   5005         View introScreen = getIntroScreen();
   5006         changeWallpaperVisiblity(false);
   5007         if (introScreen != null) {
   5008             mDragLayer.showOverlayView(introScreen);
   5009         }
   5010     }
   5011 
   5012     public void dismissIntroScreen() {
   5013         markIntroScreenDismissed();
   5014         if (showFirstRunActivity()) {
   5015             // We delay hiding the intro view until the first run activity is showing. This
   5016             // avoids a blip.
   5017             mWorkspace.postDelayed(new Runnable() {
   5018                 @Override
   5019                 public void run() {
   5020                     mDragLayer.dismissOverlayView();
   5021                     showFirstRunClings();
   5022                 }
   5023             }, ACTIVITY_START_DELAY);
   5024         } else {
   5025             mDragLayer.dismissOverlayView();
   5026             showFirstRunClings();
   5027         }
   5028         changeWallpaperVisiblity(true);
   5029     }
   5030 
   5031     private void markIntroScreenDismissed() {
   5032         SharedPreferences.Editor editor = mSharedPrefs.edit();
   5033         editor.putBoolean(INTRO_SCREEN_DISMISSED, true);
   5034         editor.apply();
   5035     }
   5036 
   5037     private void showFirstRunClings() {
   5038         // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
   5039         // on the device, then we always show the first run cling experience (or if there is no
   5040         // launcher2). Otherwise, we prompt the user upon started for migration
   5041         LauncherClings launcherClings = new LauncherClings(this);
   5042         if (launcherClings.shouldShowFirstRunOrMigrationClings()) {
   5043             if (mModel.canMigrateFromOldLauncherDb(this)) {
   5044                 launcherClings.showMigrationCling();
   5045             } else {
   5046                 launcherClings.showLongPressCling(true);
   5047             }
   5048         }
   5049     }
   5050 
   5051     void showWorkspaceSearchAndHotseat() {
   5052         if (mWorkspace != null) mWorkspace.setAlpha(1f);
   5053         if (mHotseat != null) mHotseat.setAlpha(1f);
   5054         if (mPageIndicators != null) mPageIndicators.setAlpha(1f);
   5055         if (mSearchDropTargetBar != null) mSearchDropTargetBar.showSearchBar(false);
   5056     }
   5057 
   5058     void hideWorkspaceSearchAndHotseat() {
   5059         if (mWorkspace != null) mWorkspace.setAlpha(0f);
   5060         if (mHotseat != null) mHotseat.setAlpha(0f);
   5061         if (mPageIndicators != null) mPageIndicators.setAlpha(0f);
   5062         if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false);
   5063     }
   5064 
   5065     public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
   5066         // Called from search suggestion, not supported in other profiles.
   5067         final UserHandleCompat myUser = UserHandleCompat.myUserHandle();
   5068         LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
   5069         LauncherActivityInfoCompat activityInfo = launcherApps.resolveActivity(appLaunchIntent,
   5070                 myUser);
   5071         if (activityInfo == null) {
   5072             return null;
   5073         }
   5074         return new AppInfo(this, activityInfo, myUser, mIconCache, null);
   5075     }
   5076 
   5077     public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
   5078             Bitmap icon) {
   5079         // Called from search suggestion, not supported in other profiles.
   5080         return createShortcutDragInfo(shortcutIntent, caption, icon,
   5081                 UserHandleCompat.myUserHandle());
   5082     }
   5083 
   5084     public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
   5085             Bitmap icon, UserHandleCompat user) {
   5086         UserManagerCompat userManager = UserManagerCompat.getInstance(this);
   5087         CharSequence contentDescription = userManager.getBadgedLabelForUser(caption, user);
   5088         return new ShortcutInfo(shortcutIntent, caption, contentDescription, icon, user);
   5089     }
   5090 
   5091     protected void moveWorkspaceToDefaultScreen() {
   5092         mWorkspace.moveToDefaultScreen(false);
   5093     }
   5094 
   5095     public void startDrag(View dragView, ItemInfo dragInfo, DragSource source) {
   5096         dragView.setTag(dragInfo);
   5097         mWorkspace.onExternalDragStartedWithItem(dragView);
   5098         mWorkspace.beginExternalDragShared(dragView, source);
   5099     }
   5100 
   5101     @Override
   5102     public void onPageSwitch(View newPage, int newPageIndex) {
   5103     }
   5104 
   5105     /**
   5106      * Prints out out state for debugging.
   5107      */
   5108     public void dumpState() {
   5109         Log.d(TAG, "BEGIN launcher3 dump state for launcher " + this);
   5110         Log.d(TAG, "mSavedState=" + mSavedState);
   5111         Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
   5112         Log.d(TAG, "mRestoring=" + mRestoring);
   5113         Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
   5114         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
   5115         Log.d(TAG, "sFolders.size=" + sFolders.size());
   5116         mModel.dumpState();
   5117 
   5118         if (mAppsCustomizeContent != null) {
   5119             mAppsCustomizeContent.dumpState();
   5120         }
   5121         Log.d(TAG, "END launcher3 dump state");
   5122     }
   5123 
   5124     @Override
   5125     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
   5126         super.dump(prefix, fd, writer, args);
   5127         synchronized (sDumpLogs) {
   5128             writer.println(" ");
   5129             writer.println("Debug logs: ");
   5130             for (int i = 0; i < sDumpLogs.size(); i++) {
   5131                 writer.println("  " + sDumpLogs.get(i));
   5132             }
   5133         }
   5134     }
   5135 
   5136     public static void dumpDebugLogsToConsole() {
   5137         if (DEBUG_DUMP_LOG) {
   5138             synchronized (sDumpLogs) {
   5139                 Log.d(TAG, "");
   5140                 Log.d(TAG, "*********************");
   5141                 Log.d(TAG, "Launcher debug logs: ");
   5142                 for (int i = 0; i < sDumpLogs.size(); i++) {
   5143                     Log.d(TAG, "  " + sDumpLogs.get(i));
   5144                 }
   5145                 Log.d(TAG, "*********************");
   5146                 Log.d(TAG, "");
   5147             }
   5148         }
   5149     }
   5150 
   5151     public static void addDumpLog(String tag, String log, boolean debugLog) {
   5152         addDumpLog(tag, log, null, debugLog);
   5153     }
   5154 
   5155     public static void addDumpLog(String tag, String log, Exception e, boolean debugLog) {
   5156         if (debugLog) {
   5157             if (e != null) {
   5158                 Log.d(tag, log, e);
   5159             } else {
   5160                 Log.d(tag, log);
   5161             }
   5162         }
   5163         if (DEBUG_DUMP_LOG) {
   5164             sDateStamp.setTime(System.currentTimeMillis());
   5165             synchronized (sDumpLogs) {
   5166                 sDumpLogs.add(sDateFormat.format(sDateStamp) + ": " + tag + ", " + log
   5167                     + (e == null ? "" : (", Exception: " + e)));
   5168             }
   5169         }
   5170     }
   5171 
   5172     public void dumpLogsToLocalData() {
   5173         if (DEBUG_DUMP_LOG) {
   5174             new AsyncTask<Void, Void, Void>() {
   5175                 public Void doInBackground(Void ... args) {
   5176                     boolean success = false;
   5177                     sDateStamp.setTime(sRunStart);
   5178                     String FILENAME = sDateStamp.getMonth() + "-"
   5179                             + sDateStamp.getDay() + "_"
   5180                             + sDateStamp.getHours() + "-"
   5181                             + sDateStamp.getMinutes() + "_"
   5182                             + sDateStamp.getSeconds() + ".txt";
   5183 
   5184                     FileOutputStream fos = null;
   5185                     File outFile = null;
   5186                     try {
   5187                         outFile = new File(getFilesDir(), FILENAME);
   5188                         outFile.createNewFile();
   5189                         fos = new FileOutputStream(outFile);
   5190                     } catch (Exception e) {
   5191                         e.printStackTrace();
   5192                     }
   5193                     if (fos != null) {
   5194                         PrintWriter writer = new PrintWriter(fos);
   5195 
   5196                         writer.println(" ");
   5197                         writer.println("Debug logs: ");
   5198                         synchronized (sDumpLogs) {
   5199                             for (int i = 0; i < sDumpLogs.size(); i++) {
   5200                                 writer.println("  " + sDumpLogs.get(i));
   5201                             }
   5202                         }
   5203                         writer.close();
   5204                     }
   5205                     try {
   5206                         if (fos != null) {
   5207                             fos.close();
   5208                             success = true;
   5209                         }
   5210                     } catch (IOException e) {
   5211                         e.printStackTrace();
   5212                     }
   5213                     return null;
   5214                 }
   5215             }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
   5216         }
   5217     }
   5218 }
   5219 
   5220 interface LauncherTransitionable {
   5221     View getContent();
   5222     void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);
   5223     void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
   5224     void onLauncherTransitionStep(Launcher l, float t);
   5225     void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
   5226 }
   5227 
   5228 interface DebugIntents {
   5229     static final String DELETE_DATABASE = "com.android.launcher3.action.DELETE_DATABASE";
   5230     static final String MIGRATE_DATABASE = "com.android.launcher3.action.MIGRATE_DATABASE";
   5231 }
   5232