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