Home | History | Annotate | Download | only in launcher2
      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.launcher2;
     19 
     20 import android.accounts.Account;
     21 import android.accounts.AccountManager;
     22 import android.animation.Animator;
     23 import android.animation.AnimatorListenerAdapter;
     24 import android.animation.AnimatorSet;
     25 import android.animation.ObjectAnimator;
     26 import android.animation.PropertyValuesHolder;
     27 import android.animation.ValueAnimator;
     28 import android.animation.ValueAnimator.AnimatorUpdateListener;
     29 import android.app.Activity;
     30 import android.app.ActivityManager;
     31 import android.app.ActivityOptions;
     32 import android.app.SearchManager;
     33 import android.appwidget.AppWidgetHostView;
     34 import android.appwidget.AppWidgetManager;
     35 import android.appwidget.AppWidgetProviderInfo;
     36 import android.content.ActivityNotFoundException;
     37 import android.content.BroadcastReceiver;
     38 import android.content.ComponentCallbacks2;
     39 import android.content.ComponentName;
     40 import android.content.ContentResolver;
     41 import android.content.Context;
     42 import android.content.Intent;
     43 import android.content.IntentFilter;
     44 import android.content.SharedPreferences;
     45 import android.content.pm.ActivityInfo;
     46 import android.content.pm.PackageManager;
     47 import android.content.pm.PackageManager.NameNotFoundException;
     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.PorterDuff;
     54 import android.graphics.Rect;
     55 import android.graphics.drawable.Drawable;
     56 import android.net.Uri;
     57 import android.os.AsyncTask;
     58 import android.os.Bundle;
     59 import android.os.Environment;
     60 import android.os.Handler;
     61 import android.os.Message;
     62 import android.os.StrictMode;
     63 import android.os.SystemClock;
     64 import android.os.SystemProperties;
     65 import android.provider.Settings;
     66 import android.speech.RecognizerIntent;
     67 import android.text.Selection;
     68 import android.text.SpannableStringBuilder;
     69 import android.text.method.TextKeyListener;
     70 import android.util.Log;
     71 import android.view.Display;
     72 import android.view.HapticFeedbackConstants;
     73 import android.view.KeyEvent;
     74 import android.view.LayoutInflater;
     75 import android.view.Menu;
     76 import android.view.MenuItem;
     77 import android.view.MotionEvent;
     78 import android.view.Surface;
     79 import android.view.View;
     80 import android.view.View.OnLongClickListener;
     81 import android.view.ViewGroup;
     82 import android.view.ViewTreeObserver;
     83 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
     84 import android.view.WindowManager;
     85 import android.view.accessibility.AccessibilityEvent;
     86 import android.view.animation.AccelerateDecelerateInterpolator;
     87 import android.view.animation.AccelerateInterpolator;
     88 import android.view.animation.DecelerateInterpolator;
     89 import android.view.inputmethod.InputMethodManager;
     90 import android.widget.Advanceable;
     91 import android.widget.ImageView;
     92 import android.widget.TextView;
     93 import android.widget.Toast;
     94 
     95 import com.android.common.Search;
     96 import com.android.launcher.R;
     97 import com.android.launcher2.DropTarget.DragObject;
     98 
     99 import java.io.DataInputStream;
    100 import java.io.DataOutputStream;
    101 import java.io.FileDescriptor;
    102 import java.io.FileNotFoundException;
    103 import java.io.IOException;
    104 import java.io.PrintWriter;
    105 import java.util.ArrayList;
    106 import java.util.Collection;
    107 import java.util.Collections;
    108 import java.util.Comparator;
    109 import java.util.HashMap;
    110 import java.util.HashSet;
    111 import java.util.List;
    112 import java.util.Set;
    113 
    114 /**
    115  * Default launcher application.
    116  */
    117 public final class Launcher extends Activity
    118         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
    119                    AllAppsView.Watcher, View.OnTouchListener {
    120     static final String TAG = "Launcher";
    121     static final boolean LOGD = false;
    122 
    123     static final boolean PROFILE_STARTUP = false;
    124     static final boolean DEBUG_WIDGETS = false;
    125     static final boolean DEBUG_STRICT_MODE = false;
    126 
    127     private static final int MENU_GROUP_WALLPAPER = 1;
    128     private static final int MENU_WALLPAPER_SETTINGS = Menu.FIRST + 1;
    129     private static final int MENU_MANAGE_APPS = MENU_WALLPAPER_SETTINGS + 1;
    130     private static final int MENU_SYSTEM_SETTINGS = MENU_MANAGE_APPS + 1;
    131     private static final int MENU_HELP = MENU_SYSTEM_SETTINGS + 1;
    132 
    133     private static final int REQUEST_CREATE_SHORTCUT = 1;
    134     private static final int REQUEST_CREATE_APPWIDGET = 5;
    135     private static final int REQUEST_PICK_APPLICATION = 6;
    136     private static final int REQUEST_PICK_SHORTCUT = 7;
    137     private static final int REQUEST_PICK_APPWIDGET = 9;
    138     private static final int REQUEST_PICK_WALLPAPER = 10;
    139 
    140     private static final int REQUEST_BIND_APPWIDGET = 11;
    141 
    142     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
    143 
    144     static final int SCREEN_COUNT = 5;
    145     static final int DEFAULT_SCREEN = 2;
    146 
    147     private static final String PREFERENCES = "launcher.preferences";
    148     static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher.force_enable_rotation";
    149 
    150     // The Intent extra that defines whether to ignore the launch animation
    151     static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
    152             "com.android.launcher.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
    153 
    154     // Type: int
    155     private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
    156     // Type: int
    157     private static final String RUNTIME_STATE = "launcher.state";
    158     // Type: int
    159     private static final String RUNTIME_STATE_PENDING_ADD_CONTAINER = "launcher.add_container";
    160     // Type: int
    161     private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
    162     // Type: int
    163     private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x";
    164     // Type: int
    165     private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y";
    166     // Type: boolean
    167     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
    168     // Type: long
    169     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
    170     // Type: int
    171     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x";
    172     // Type: int
    173     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y";
    174     // Type: parcelable
    175     private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
    176 
    177     private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
    178     private static final String TOOLBAR_SEARCH_ICON_METADATA_NAME =
    179             "com.android.launcher.toolbar_search_icon";
    180     private static final String TOOLBAR_VOICE_SEARCH_ICON_METADATA_NAME =
    181             "com.android.launcher.toolbar_voice_search_icon";
    182 
    183     /** The different states that Launcher can be in. */
    184     private enum State { NONE, WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED };
    185     private State mState = State.WORKSPACE;
    186     private AnimatorSet mStateAnimation;
    187     private AnimatorSet mDividerAnimator;
    188 
    189     static final int APPWIDGET_HOST_ID = 1024;
    190     private static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
    191     private static final int EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT = 600;
    192     private static final int SHOW_CLING_DURATION = 550;
    193     private static final int DISMISS_CLING_DURATION = 250;
    194 
    195     private static final Object sLock = new Object();
    196     private static int sScreen = DEFAULT_SCREEN;
    197 
    198     // How long to wait before the new-shortcut animation automatically pans the workspace
    199     private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 10;
    200 
    201     private final BroadcastReceiver mCloseSystemDialogsReceiver
    202             = new CloseSystemDialogsIntentReceiver();
    203     private final ContentObserver mWidgetObserver = new AppWidgetResetObserver();
    204 
    205     private LayoutInflater mInflater;
    206 
    207     private Workspace mWorkspace;
    208     private View mQsbDivider;
    209     private View mDockDivider;
    210     private DragLayer mDragLayer;
    211     private DragController mDragController;
    212 
    213     private AppWidgetManager mAppWidgetManager;
    214     private LauncherAppWidgetHost mAppWidgetHost;
    215 
    216     private ItemInfo mPendingAddInfo = new ItemInfo();
    217     private AppWidgetProviderInfo mPendingAddWidgetInfo;
    218 
    219     private int[] mTmpAddItemCellCoordinates = new int[2];
    220 
    221     private FolderInfo mFolderInfo;
    222 
    223     private Hotseat mHotseat;
    224     private View mAllAppsButton;
    225 
    226     private SearchDropTargetBar mSearchDropTargetBar;
    227     private AppsCustomizeTabHost mAppsCustomizeTabHost;
    228     private AppsCustomizePagedView mAppsCustomizeContent;
    229     private boolean mAutoAdvanceRunning = false;
    230 
    231     private Bundle mSavedState;
    232     // We set the state in both onCreate and then onNewIntent in some cases, which causes both
    233     // scroll issues (because the workspace may not have been measured yet) and extra work.
    234     // Instead, just save the state that we need to restore Launcher to, and commit it in onResume.
    235     private State mOnResumeState = State.NONE;
    236 
    237     private SpannableStringBuilder mDefaultKeySsb = null;
    238 
    239     private boolean mWorkspaceLoading = true;
    240 
    241     private boolean mPaused = true;
    242     private boolean mRestoring;
    243     private boolean mWaitingForResult;
    244     private boolean mOnResumeNeedsLoad;
    245 
    246     // Keep track of whether the user has left launcher
    247     private static boolean sPausedFromUserAction = false;
    248 
    249     private Bundle mSavedInstanceState;
    250 
    251     private LauncherModel mModel;
    252     private IconCache mIconCache;
    253     private boolean mUserPresent = true;
    254     private boolean mVisible = false;
    255     private boolean mAttached = false;
    256 
    257     private static LocaleConfiguration sLocaleConfiguration = null;
    258 
    259     private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
    260 
    261     private Intent mAppMarketIntent = null;
    262 
    263     // Related to the auto-advancing of widgets
    264     private final int ADVANCE_MSG = 1;
    265     private final int mAdvanceInterval = 20000;
    266     private final int mAdvanceStagger = 250;
    267     private long mAutoAdvanceSentTime;
    268     private long mAutoAdvanceTimeLeft = -1;
    269     private HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance =
    270         new HashMap<View, AppWidgetProviderInfo>();
    271 
    272     // Determines how long to wait after a rotation before restoring the screen orientation to
    273     // match the sensor state.
    274     private final int mRestoreScreenOrientationDelay = 500;
    275 
    276     // External icons saved in case of resource changes, orientation, etc.
    277     private static Drawable.ConstantState[] sGlobalSearchIcon = new Drawable.ConstantState[2];
    278     private static Drawable.ConstantState[] sVoiceSearchIcon = new Drawable.ConstantState[2];
    279     private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2];
    280 
    281     private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
    282 
    283     static final ArrayList<String> sDumpLogs = new ArrayList<String>();
    284 
    285     // We only want to get the SharedPreferences once since it does an FS stat each time we get
    286     // it from the context.
    287     private SharedPreferences mSharedPrefs;
    288 
    289     // Holds the page that we need to animate to, and the icon views that we need to animate up
    290     // when we scroll to that page on resume.
    291     private int mNewShortcutAnimatePage = -1;
    292     private ArrayList<View> mNewShortcutAnimateViews = new ArrayList<View>();
    293     private ImageView mFolderIconImageView;
    294     private Bitmap mFolderIconBitmap;
    295     private Canvas mFolderIconCanvas;
    296     private Rect mRectForFolderAnimation = new Rect();
    297 
    298     private BubbleTextView mWaitingForResume;
    299 
    300     private Runnable mBuildLayersRunnable = new Runnable() {
    301         public void run() {
    302             if (mWorkspace != null) {
    303                 mWorkspace.buildPageHardwareLayers();
    304             }
    305         }
    306     };
    307 
    308     private static ArrayList<PendingAddArguments> sPendingAddList
    309             = new ArrayList<PendingAddArguments>();
    310 
    311     private static class PendingAddArguments {
    312         int requestCode;
    313         Intent intent;
    314         long container;
    315         int screen;
    316         int cellX;
    317         int cellY;
    318     }
    319 
    320     @Override
    321     protected void onCreate(Bundle savedInstanceState) {
    322         if (DEBUG_STRICT_MODE) {
    323             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    324                     .detectDiskReads()
    325                     .detectDiskWrites()
    326                     .detectNetwork()   // or .detectAll() for all detectable problems
    327                     .penaltyLog()
    328                     .build());
    329             StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    330                     .detectLeakedSqlLiteObjects()
    331                     .detectLeakedClosableObjects()
    332                     .penaltyLog()
    333                     .penaltyDeath()
    334                     .build());
    335         }
    336 
    337         super.onCreate(savedInstanceState);
    338         LauncherApplication app = ((LauncherApplication)getApplication());
    339         mSharedPrefs = getSharedPreferences(LauncherApplication.getSharedPreferencesKey(),
    340                 Context.MODE_PRIVATE);
    341         mModel = app.setLauncher(this);
    342         mIconCache = app.getIconCache();
    343         mDragController = new DragController(this);
    344         mInflater = getLayoutInflater();
    345 
    346         mAppWidgetManager = AppWidgetManager.getInstance(this);
    347         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
    348         mAppWidgetHost.startListening();
    349 
    350         // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
    351         // this also ensures that any synchronous binding below doesn't re-trigger another
    352         // LauncherModel load.
    353         mPaused = false;
    354 
    355         if (PROFILE_STARTUP) {
    356             android.os.Debug.startMethodTracing(
    357                     Environment.getExternalStorageDirectory() + "/launcher");
    358         }
    359 
    360         checkForLocaleChange();
    361         setContentView(R.layout.launcher);
    362         setupViews();
    363         showFirstRunWorkspaceCling();
    364 
    365         registerContentObservers();
    366 
    367         lockAllApps();
    368 
    369         mSavedState = savedInstanceState;
    370         restoreState(mSavedState);
    371 
    372         // Update customization drawer _after_ restoring the states
    373         if (mAppsCustomizeContent != null) {
    374             mAppsCustomizeContent.onPackagesUpdated(true);
    375         }
    376 
    377         if (PROFILE_STARTUP) {
    378             android.os.Debug.stopMethodTracing();
    379         }
    380 
    381         if (!mRestoring) {
    382             if (sPausedFromUserAction) {
    383                 // If the user leaves launcher, then we should just load items asynchronously when
    384                 // they return.
    385                 mModel.startLoader(true, -1);
    386             } else {
    387                 // We only load the page synchronously if the user rotates (or triggers a
    388                 // configuration change) while launcher is in the foreground
    389                 mModel.startLoader(true, mWorkspace.getCurrentPage());
    390             }
    391         }
    392 
    393         if (!mModel.isAllAppsLoaded()) {
    394             ViewGroup appsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent();
    395             mInflater.inflate(R.layout.apps_customize_progressbar, appsCustomizeContentParent);
    396         }
    397 
    398         // For handling default keys
    399         mDefaultKeySsb = new SpannableStringBuilder();
    400         Selection.setSelection(mDefaultKeySsb, 0);
    401 
    402         IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    403         registerReceiver(mCloseSystemDialogsReceiver, filter);
    404 
    405         updateGlobalIcons();
    406 
    407         // On large interfaces, we want the screen to auto-rotate based on the current orientation
    408         unlockScreenOrientation(true);
    409     }
    410 
    411     protected void onUserLeaveHint() {
    412         super.onUserLeaveHint();
    413         sPausedFromUserAction = true;
    414     }
    415 
    416     private void updateGlobalIcons() {
    417         boolean searchVisible = false;
    418         boolean voiceVisible = false;
    419         // If we have a saved version of these external icons, we load them up immediately
    420         int coi = getCurrentOrientationIndexForGlobalIcons();
    421         if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null ||
    422                 sAppMarketIcon[coi] == null) {
    423             updateAppMarketIcon();
    424             searchVisible = updateGlobalSearchIcon();
    425             voiceVisible = updateVoiceSearchIcon(searchVisible);
    426         }
    427         if (sGlobalSearchIcon[coi] != null) {
    428              updateGlobalSearchIcon(sGlobalSearchIcon[coi]);
    429              searchVisible = true;
    430         }
    431         if (sVoiceSearchIcon[coi] != null) {
    432             updateVoiceSearchIcon(sVoiceSearchIcon[coi]);
    433             voiceVisible = true;
    434         }
    435         if (sAppMarketIcon[coi] != null) {
    436             updateAppMarketIcon(sAppMarketIcon[coi]);
    437         }
    438         mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
    439     }
    440 
    441     private void checkForLocaleChange() {
    442         if (sLocaleConfiguration == null) {
    443             new AsyncTask<Void, Void, LocaleConfiguration>() {
    444                 @Override
    445                 protected LocaleConfiguration doInBackground(Void... unused) {
    446                     LocaleConfiguration localeConfiguration = new LocaleConfiguration();
    447                     readConfiguration(Launcher.this, localeConfiguration);
    448                     return localeConfiguration;
    449                 }
    450 
    451                 @Override
    452                 protected void onPostExecute(LocaleConfiguration result) {
    453                     sLocaleConfiguration = result;
    454                     checkForLocaleChange();  // recursive, but now with a locale configuration
    455                 }
    456             }.execute();
    457             return;
    458         }
    459 
    460         final Configuration configuration = getResources().getConfiguration();
    461 
    462         final String previousLocale = sLocaleConfiguration.locale;
    463         final String locale = configuration.locale.toString();
    464 
    465         final int previousMcc = sLocaleConfiguration.mcc;
    466         final int mcc = configuration.mcc;
    467 
    468         final int previousMnc = sLocaleConfiguration.mnc;
    469         final int mnc = configuration.mnc;
    470 
    471         boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
    472 
    473         if (localeChanged) {
    474             sLocaleConfiguration.locale = locale;
    475             sLocaleConfiguration.mcc = mcc;
    476             sLocaleConfiguration.mnc = mnc;
    477 
    478             mIconCache.flush();
    479 
    480             final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
    481             new Thread("WriteLocaleConfiguration") {
    482                 @Override
    483                 public void run() {
    484                     writeConfiguration(Launcher.this, localeConfiguration);
    485                 }
    486             }.start();
    487         }
    488     }
    489 
    490     private static class LocaleConfiguration {
    491         public String locale;
    492         public int mcc = -1;
    493         public int mnc = -1;
    494     }
    495 
    496     private static void readConfiguration(Context context, LocaleConfiguration configuration) {
    497         DataInputStream in = null;
    498         try {
    499             in = new DataInputStream(context.openFileInput(PREFERENCES));
    500             configuration.locale = in.readUTF();
    501             configuration.mcc = in.readInt();
    502             configuration.mnc = in.readInt();
    503         } catch (FileNotFoundException e) {
    504             // Ignore
    505         } catch (IOException e) {
    506             // Ignore
    507         } finally {
    508             if (in != null) {
    509                 try {
    510                     in.close();
    511                 } catch (IOException e) {
    512                     // Ignore
    513                 }
    514             }
    515         }
    516     }
    517 
    518     private static void writeConfiguration(Context context, LocaleConfiguration configuration) {
    519         DataOutputStream out = null;
    520         try {
    521             out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE));
    522             out.writeUTF(configuration.locale);
    523             out.writeInt(configuration.mcc);
    524             out.writeInt(configuration.mnc);
    525             out.flush();
    526         } catch (FileNotFoundException e) {
    527             // Ignore
    528         } catch (IOException e) {
    529             //noinspection ResultOfMethodCallIgnored
    530             context.getFileStreamPath(PREFERENCES).delete();
    531         } finally {
    532             if (out != null) {
    533                 try {
    534                     out.close();
    535                 } catch (IOException e) {
    536                     // Ignore
    537                 }
    538             }
    539         }
    540     }
    541 
    542     public DragLayer getDragLayer() {
    543         return mDragLayer;
    544     }
    545 
    546     boolean isDraggingEnabled() {
    547         // We prevent dragging when we are loading the workspace as it is possible to pick up a view
    548         // that is subsequently removed from the workspace in startBinding().
    549         return !mModel.isLoadingWorkspace();
    550     }
    551 
    552     static int getScreen() {
    553         synchronized (sLock) {
    554             return sScreen;
    555         }
    556     }
    557 
    558     static void setScreen(int screen) {
    559         synchronized (sLock) {
    560             sScreen = screen;
    561         }
    562     }
    563 
    564     /**
    565      * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
    566      * a configuration step, this allows the proper animations to run after other transitions.
    567      */
    568     private boolean completeAdd(PendingAddArguments args) {
    569         boolean result = false;
    570         switch (args.requestCode) {
    571             case REQUEST_PICK_APPLICATION:
    572                 completeAddApplication(args.intent, args.container, args.screen, args.cellX,
    573                         args.cellY);
    574                 break;
    575             case REQUEST_PICK_SHORTCUT:
    576                 processShortcut(args.intent);
    577                 break;
    578             case REQUEST_CREATE_SHORTCUT:
    579                 completeAddShortcut(args.intent, args.container, args.screen, args.cellX,
    580                         args.cellY);
    581                 result = true;
    582                 break;
    583             case REQUEST_CREATE_APPWIDGET:
    584                 int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    585                 completeAddAppWidget(appWidgetId, args.container, args.screen, null, null);
    586                 result = true;
    587                 break;
    588             case REQUEST_PICK_WALLPAPER:
    589                 // We just wanted the activity result here so we can clear mWaitingForResult
    590                 break;
    591         }
    592         // Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
    593         // if you turned the screen off and then back while in All Apps, Launcher would not
    594         // return to the workspace. Clearing mAddInfo.container here fixes this issue
    595         resetAddInfo();
    596         return result;
    597     }
    598 
    599     @Override
    600     protected void onActivityResult(
    601             final int requestCode, final int resultCode, final Intent data) {
    602         if (requestCode == REQUEST_BIND_APPWIDGET) {
    603             int appWidgetId = data != null ?
    604                     data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
    605             if (resultCode == RESULT_CANCELED) {
    606                 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
    607             } else if (resultCode == RESULT_OK) {
    608                 addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo);
    609             }
    610             return;
    611         }
    612         boolean delayExitSpringLoadedMode = false;
    613         boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
    614                 requestCode == REQUEST_CREATE_APPWIDGET);
    615         mWaitingForResult = false;
    616 
    617         // We have special handling for widgets
    618         if (isWidgetDrop) {
    619             int appWidgetId = data != null ?
    620                     data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
    621             if (appWidgetId < 0) {
    622                 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \\" +
    623                         "widget configuration activity.");
    624                 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
    625             } else {
    626                 completeTwoStageWidgetDrop(resultCode, appWidgetId);
    627             }
    628             return;
    629         }
    630 
    631         // The pattern used here is that a user PICKs a specific application,
    632         // which, depending on the target, might need to CREATE the actual target.
    633 
    634         // For example, the user would PICK_SHORTCUT for "Music playlist", and we
    635         // launch over to the Music app to actually CREATE_SHORTCUT.
    636         if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
    637             final PendingAddArguments args = new PendingAddArguments();
    638             args.requestCode = requestCode;
    639             args.intent = data;
    640             args.container = mPendingAddInfo.container;
    641             args.screen = mPendingAddInfo.screen;
    642             args.cellX = mPendingAddInfo.cellX;
    643             args.cellY = mPendingAddInfo.cellY;
    644             if (isWorkspaceLocked()) {
    645                 sPendingAddList.add(args);
    646             } else {
    647                 delayExitSpringLoadedMode = completeAdd(args);
    648             }
    649         }
    650         mDragLayer.clearAnimatedView();
    651         // Exit spring loaded mode if necessary after cancelling the configuration of a widget
    652         exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode,
    653                 null);
    654     }
    655 
    656     private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
    657         CellLayout cellLayout =
    658                 (CellLayout) mWorkspace.getChildAt(mPendingAddInfo.screen);
    659         Runnable onCompleteRunnable = null;
    660         int animationType = 0;
    661 
    662         AppWidgetHostView boundWidget = null;
    663         if (resultCode == RESULT_OK) {
    664             animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
    665             final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
    666                     mPendingAddWidgetInfo);
    667             boundWidget = layout;
    668             onCompleteRunnable = new Runnable() {
    669                 @Override
    670                 public void run() {
    671                     completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
    672                             mPendingAddInfo.screen, layout, null);
    673                     exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
    674                             null);
    675                 }
    676             };
    677         } else if (resultCode == RESULT_CANCELED) {
    678             animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
    679             onCompleteRunnable = new Runnable() {
    680                 @Override
    681                 public void run() {
    682                     exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
    683                             null);
    684                 }
    685             };
    686         }
    687         if (mDragLayer.getAnimatedView() != null) {
    688             mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout,
    689                     (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
    690                     animationType, boundWidget, true);
    691         } else {
    692             // The animated view may be null in the case of a rotation during widget configuration
    693             onCompleteRunnable.run();
    694         }
    695     }
    696 
    697     @Override
    698     protected void onResume() {
    699         super.onResume();
    700 
    701         // Restore the previous launcher state
    702         if (mOnResumeState == State.WORKSPACE) {
    703             showWorkspace(false);
    704         } else if (mOnResumeState == State.APPS_CUSTOMIZE) {
    705             showAllApps(false);
    706         }
    707         mOnResumeState = State.NONE;
    708 
    709         // Process any items that were added while Launcher was away
    710         InstallShortcutReceiver.flushInstallQueue(this);
    711 
    712         mPaused = false;
    713         sPausedFromUserAction = false;
    714         if (mRestoring || mOnResumeNeedsLoad) {
    715             mWorkspaceLoading = true;
    716             mModel.startLoader(true, -1);
    717             mRestoring = false;
    718             mOnResumeNeedsLoad = false;
    719         }
    720 
    721         // Reset the pressed state of icons that were locked in the press state while activities
    722         // were launching
    723         if (mWaitingForResume != null) {
    724             // Resets the previous workspace icon press state
    725             mWaitingForResume.setStayPressed(false);
    726         }
    727         if (mAppsCustomizeContent != null) {
    728             // Resets the previous all apps icon press state
    729             mAppsCustomizeContent.resetDrawableState();
    730         }
    731         // It is possible that widgets can receive updates while launcher is not in the foreground.
    732         // Consequently, the widgets will be inflated in the orientation of the foreground activity
    733         // (framework issue). On resuming, we ensure that any widgets are inflated for the current
    734         // orientation.
    735         getWorkspace().reinflateWidgetsIfNecessary();
    736 
    737         // Again, as with the above scenario, it's possible that one or more of the global icons
    738         // were updated in the wrong orientation.
    739         updateGlobalIcons();
    740     }
    741 
    742     @Override
    743     protected void onPause() {
    744         // NOTE: We want all transitions from launcher to act as if the wallpaper were enabled
    745         // to be consistent.  So re-enable the flag here, and we will re-disable it as necessary
    746         // when Launcher resumes and we are still in AllApps.
    747         updateWallpaperVisibility(true);
    748 
    749         super.onPause();
    750         mPaused = true;
    751         mDragController.cancelDrag();
    752         mDragController.resetLastGestureUpTime();
    753     }
    754 
    755     @Override
    756     public Object onRetainNonConfigurationInstance() {
    757         // Flag the loader to stop early before switching
    758         mModel.stopLoader();
    759         if (mAppsCustomizeContent != null) {
    760             mAppsCustomizeContent.surrender();
    761         }
    762         return Boolean.TRUE;
    763     }
    764 
    765     // We can't hide the IME if it was forced open.  So don't bother
    766     /*
    767     @Override
    768     public void onWindowFocusChanged(boolean hasFocus) {
    769         super.onWindowFocusChanged(hasFocus);
    770 
    771         if (hasFocus) {
    772             final InputMethodManager inputManager = (InputMethodManager)
    773                     getSystemService(Context.INPUT_METHOD_SERVICE);
    774             WindowManager.LayoutParams lp = getWindow().getAttributes();
    775             inputManager.hideSoftInputFromWindow(lp.token, 0, new android.os.ResultReceiver(new
    776                         android.os.Handler()) {
    777                         protected void onReceiveResult(int resultCode, Bundle resultData) {
    778                             Log.d(TAG, "ResultReceiver got resultCode=" + resultCode);
    779                         }
    780                     });
    781             Log.d(TAG, "called hideSoftInputFromWindow from onWindowFocusChanged");
    782         }
    783     }
    784     */
    785 
    786     private boolean acceptFilter() {
    787         final InputMethodManager inputManager = (InputMethodManager)
    788                 getSystemService(Context.INPUT_METHOD_SERVICE);
    789         return !inputManager.isFullscreenMode();
    790     }
    791 
    792     @Override
    793     public boolean onKeyDown(int keyCode, KeyEvent event) {
    794         final int uniChar = event.getUnicodeChar();
    795         final boolean handled = super.onKeyDown(keyCode, event);
    796         final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar);
    797         if (!handled && acceptFilter() && isKeyNotWhitespace) {
    798             boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
    799                     keyCode, event);
    800             if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
    801                 // something usable has been typed - start a search
    802                 // the typed text will be retrieved and cleared by
    803                 // showSearchDialog()
    804                 // If there are multiple keystrokes before the search dialog takes focus,
    805                 // onSearchRequested() will be called for every keystroke,
    806                 // but it is idempotent, so it's fine.
    807                 return onSearchRequested();
    808             }
    809         }
    810 
    811         // Eat the long press event so the keyboard doesn't come up.
    812         if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
    813             return true;
    814         }
    815 
    816         return handled;
    817     }
    818 
    819     private String getTypedText() {
    820         return mDefaultKeySsb.toString();
    821     }
    822 
    823     private void clearTypedText() {
    824         mDefaultKeySsb.clear();
    825         mDefaultKeySsb.clearSpans();
    826         Selection.setSelection(mDefaultKeySsb, 0);
    827     }
    828 
    829     /**
    830      * Given the integer (ordinal) value of a State enum instance, convert it to a variable of type
    831      * State
    832      */
    833     private static State intToState(int stateOrdinal) {
    834         State state = State.WORKSPACE;
    835         final State[] stateValues = State.values();
    836         for (int i = 0; i < stateValues.length; i++) {
    837             if (stateValues[i].ordinal() == stateOrdinal) {
    838                 state = stateValues[i];
    839                 break;
    840             }
    841         }
    842         return state;
    843     }
    844 
    845     /**
    846      * Restores the previous state, if it exists.
    847      *
    848      * @param savedState The previous state.
    849      */
    850     private void restoreState(Bundle savedState) {
    851         if (savedState == null) {
    852             return;
    853         }
    854 
    855         State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()));
    856         if (state == State.APPS_CUSTOMIZE) {
    857             mOnResumeState = State.APPS_CUSTOMIZE;
    858         }
    859 
    860         int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
    861         if (currentScreen > -1) {
    862             mWorkspace.setCurrentPage(currentScreen);
    863         }
    864 
    865         final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
    866         final int pendingAddScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
    867 
    868         if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
    869             mPendingAddInfo.container = pendingAddContainer;
    870             mPendingAddInfo.screen = pendingAddScreen;
    871             mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
    872             mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
    873             mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
    874             mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
    875             mPendingAddWidgetInfo = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
    876             mWaitingForResult = true;
    877             mRestoring = true;
    878         }
    879 
    880 
    881         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
    882         if (renameFolder) {
    883             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
    884             mFolderInfo = mModel.getFolderById(this, sFolders, id);
    885             mRestoring = true;
    886         }
    887 
    888 
    889         // Restore the AppsCustomize tab
    890         if (mAppsCustomizeTabHost != null) {
    891             String curTab = savedState.getString("apps_customize_currentTab");
    892             if (curTab != null) {
    893                 mAppsCustomizeTabHost.setContentTypeImmediate(
    894                         mAppsCustomizeTabHost.getContentTypeForTabTag(curTab));
    895                 mAppsCustomizeContent.loadAssociatedPages(
    896                         mAppsCustomizeContent.getCurrentPage());
    897             }
    898 
    899             int currentIndex = savedState.getInt("apps_customize_currentIndex");
    900             mAppsCustomizeContent.restorePageForIndex(currentIndex);
    901         }
    902     }
    903 
    904     /**
    905      * Finds all the views we need and configure them properly.
    906      */
    907     private void setupViews() {
    908         final DragController dragController = mDragController;
    909 
    910         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
    911         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
    912         mQsbDivider = (ImageView) findViewById(R.id.qsb_divider);
    913         mDockDivider = (ImageView) findViewById(R.id.dock_divider);
    914 
    915         // Setup the drag layer
    916         mDragLayer.setup(this, dragController);
    917 
    918         // Setup the hotseat
    919         mHotseat = (Hotseat) findViewById(R.id.hotseat);
    920         if (mHotseat != null) {
    921             mHotseat.setup(this);
    922         }
    923 
    924         // Setup the workspace
    925         mWorkspace.setHapticFeedbackEnabled(false);
    926         mWorkspace.setOnLongClickListener(this);
    927         mWorkspace.setup(dragController);
    928         dragController.addDragListener(mWorkspace);
    929 
    930         // Get the search/delete bar
    931         mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.qsb_bar);
    932 
    933         // Setup AppsCustomize
    934         mAppsCustomizeTabHost = (AppsCustomizeTabHost)
    935                 findViewById(R.id.apps_customize_pane);
    936         mAppsCustomizeContent = (AppsCustomizePagedView)
    937                 mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
    938         mAppsCustomizeTabHost.setup(this);
    939         mAppsCustomizeContent.setup(this, dragController);
    940 
    941         // Get the all apps button
    942         mAllAppsButton = findViewById(R.id.all_apps_button);
    943         if (mAllAppsButton != null) {
    944             mAllAppsButton.setOnTouchListener(new View.OnTouchListener() {
    945                 @Override
    946                 public boolean onTouch(View v, MotionEvent event) {
    947                     if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
    948                         onTouchDownAllAppsButton(v);
    949                     }
    950                     return false;
    951                 }
    952             });
    953         }
    954         // Setup the drag controller (drop targets have to be added in reverse order in priority)
    955         dragController.setDragScoller(mWorkspace);
    956         dragController.setScrollView(mDragLayer);
    957         dragController.setMoveTarget(mWorkspace);
    958         dragController.addDropTarget(mWorkspace);
    959         if (mSearchDropTargetBar != null) {
    960             mSearchDropTargetBar.setup(this, dragController);
    961         }
    962     }
    963 
    964     /**
    965      * Creates a view representing a shortcut.
    966      *
    967      * @param info The data structure describing the shortcut.
    968      *
    969      * @return A View inflated from R.layout.application.
    970      */
    971     View createShortcut(ShortcutInfo info) {
    972         return createShortcut(R.layout.application,
    973                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
    974     }
    975 
    976     /**
    977      * Creates a view representing a shortcut inflated from the specified resource.
    978      *
    979      * @param layoutResId The id of the XML layout used to create the shortcut.
    980      * @param parent The group the shortcut belongs to.
    981      * @param info The data structure describing the shortcut.
    982      *
    983      * @return A View inflated from layoutResId.
    984      */
    985     View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
    986         BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
    987         favorite.applyFromShortcutInfo(info, mIconCache);
    988         favorite.setOnClickListener(this);
    989         return favorite;
    990     }
    991 
    992     /**
    993      * Add an application shortcut to the workspace.
    994      *
    995      * @param data The intent describing the application.
    996      * @param cellInfo The position on screen where to create the shortcut.
    997      */
    998     void completeAddApplication(Intent data, long container, int screen, int cellX, int cellY) {
    999         final int[] cellXY = mTmpAddItemCellCoordinates;
   1000         final CellLayout layout = getCellLayout(container, screen);
   1001 
   1002         // First we check if we already know the exact location where we want to add this item.
   1003         if (cellX >= 0 && cellY >= 0) {
   1004             cellXY[0] = cellX;
   1005             cellXY[1] = cellY;
   1006         } else if (!layout.findCellForSpan(cellXY, 1, 1)) {
   1007             showOutOfSpaceMessage(isHotseatLayout(layout));
   1008             return;
   1009         }
   1010 
   1011         final ShortcutInfo info = mModel.getShortcutInfo(getPackageManager(), data, this);
   1012 
   1013         if (info != null) {
   1014             info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
   1015                     Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
   1016             info.container = ItemInfo.NO_ID;
   1017             mWorkspace.addApplicationShortcut(info, layout, container, screen, cellXY[0], cellXY[1],
   1018                     isWorkspaceLocked(), cellX, cellY);
   1019         } else {
   1020             Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
   1021         }
   1022     }
   1023 
   1024     /**
   1025      * Add a shortcut to the workspace.
   1026      *
   1027      * @param data The intent describing the shortcut.
   1028      * @param cellInfo The position on screen where to create the shortcut.
   1029      */
   1030     private void completeAddShortcut(Intent data, long container, int screen, int cellX,
   1031             int cellY) {
   1032         int[] cellXY = mTmpAddItemCellCoordinates;
   1033         int[] touchXY = mPendingAddInfo.dropPos;
   1034         CellLayout layout = getCellLayout(container, screen);
   1035 
   1036         boolean foundCellSpan = false;
   1037 
   1038         ShortcutInfo info = mModel.infoFromShortcutIntent(this, data, null);
   1039         if (info == null) {
   1040             return;
   1041         }
   1042         final View view = createShortcut(info);
   1043 
   1044         // First we check if we already know the exact location where we want to add this item.
   1045         if (cellX >= 0 && cellY >= 0) {
   1046             cellXY[0] = cellX;
   1047             cellXY[1] = cellY;
   1048             foundCellSpan = true;
   1049 
   1050             // If appropriate, either create a folder or add to an existing folder
   1051             if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
   1052                     true, null,null)) {
   1053                 return;
   1054             }
   1055             DragObject dragObject = new DragObject();
   1056             dragObject.dragInfo = info;
   1057             if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
   1058                     true)) {
   1059                 return;
   1060             }
   1061         } else if (touchXY != null) {
   1062             // when dragging and dropping, just find the closest free spot
   1063             int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY);
   1064             foundCellSpan = (result != null);
   1065         } else {
   1066             foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
   1067         }
   1068 
   1069         if (!foundCellSpan) {
   1070             showOutOfSpaceMessage(isHotseatLayout(layout));
   1071             return;
   1072         }
   1073 
   1074         LauncherModel.addItemToDatabase(this, info, container, screen, cellXY[0], cellXY[1], false);
   1075 
   1076         if (!mRestoring) {
   1077             mWorkspace.addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1,
   1078                     isWorkspaceLocked());
   1079         }
   1080     }
   1081 
   1082     static int[] getSpanForWidget(Context context, ComponentName component, int minWidth,
   1083             int minHeight) {
   1084         Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null);
   1085         // We want to account for the extra amount of padding that we are adding to the widget
   1086         // to ensure that it gets the full amount of space that it has requested
   1087         int requiredWidth = minWidth + padding.left + padding.right;
   1088         int requiredHeight = minHeight + padding.top + padding.bottom;
   1089         return CellLayout.rectToCell(context.getResources(), requiredWidth, requiredHeight, null);
   1090     }
   1091 
   1092     static int[] getSpanForWidget(Context context, AppWidgetProviderInfo info) {
   1093         return getSpanForWidget(context, info.provider, info.minWidth, info.minHeight);
   1094     }
   1095 
   1096     static int[] getMinSpanForWidget(Context context, AppWidgetProviderInfo info) {
   1097         return getSpanForWidget(context, info.provider, info.minResizeWidth, info.minResizeHeight);
   1098     }
   1099 
   1100     static int[] getSpanForWidget(Context context, PendingAddWidgetInfo info) {
   1101         return getSpanForWidget(context, info.componentName, info.minWidth, info.minHeight);
   1102     }
   1103 
   1104     static int[] getMinSpanForWidget(Context context, PendingAddWidgetInfo info) {
   1105         return getSpanForWidget(context, info.componentName, info.minResizeWidth,
   1106                 info.minResizeHeight);
   1107     }
   1108 
   1109     /**
   1110      * Add a widget to the workspace.
   1111      *
   1112      * @param appWidgetId The app widget id
   1113      * @param cellInfo The position on screen where to create the widget.
   1114      */
   1115     private void completeAddAppWidget(final int appWidgetId, long container, int screen,
   1116             AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) {
   1117         if (appWidgetInfo == null) {
   1118             appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   1119         }
   1120 
   1121         // Calculate the grid spans needed to fit this widget
   1122         CellLayout layout = getCellLayout(container, screen);
   1123 
   1124         int[] minSpanXY = getMinSpanForWidget(this, appWidgetInfo);
   1125         int[] spanXY = getSpanForWidget(this, appWidgetInfo);
   1126 
   1127         // Try finding open space on Launcher screen
   1128         // We have saved the position to which the widget was dragged-- this really only matters
   1129         // if we are placing widgets on a "spring-loaded" screen
   1130         int[] cellXY = mTmpAddItemCellCoordinates;
   1131         int[] touchXY = mPendingAddInfo.dropPos;
   1132         int[] finalSpan = new int[2];
   1133         boolean foundCellSpan = false;
   1134         if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) {
   1135             cellXY[0] = mPendingAddInfo.cellX;
   1136             cellXY[1] = mPendingAddInfo.cellY;
   1137             spanXY[0] = mPendingAddInfo.spanX;
   1138             spanXY[1] = mPendingAddInfo.spanY;
   1139             foundCellSpan = true;
   1140         } else if (touchXY != null) {
   1141             // when dragging and dropping, just find the closest free spot
   1142             int[] result = layout.findNearestVacantArea(
   1143                     touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0],
   1144                     spanXY[1], cellXY, finalSpan);
   1145             spanXY[0] = finalSpan[0];
   1146             spanXY[1] = finalSpan[1];
   1147             foundCellSpan = (result != null);
   1148         } else {
   1149             foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]);
   1150         }
   1151 
   1152         if (!foundCellSpan) {
   1153             if (appWidgetId != -1) {
   1154                 // Deleting an app widget ID is a void call but writes to disk before returning
   1155                 // to the caller...
   1156                 new Thread("deleteAppWidgetId") {
   1157                     public void run() {
   1158                         mAppWidgetHost.deleteAppWidgetId(appWidgetId);
   1159                     }
   1160                 }.start();
   1161             }
   1162             showOutOfSpaceMessage(isHotseatLayout(layout));
   1163             return;
   1164         }
   1165 
   1166         // Build Launcher-specific widget info and save to database
   1167         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId,
   1168                 appWidgetInfo.provider);
   1169         launcherInfo.spanX = spanXY[0];
   1170         launcherInfo.spanY = spanXY[1];
   1171         launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
   1172         launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
   1173 
   1174         LauncherModel.addItemToDatabase(this, launcherInfo,
   1175                 container, screen, cellXY[0], cellXY[1], false);
   1176 
   1177         if (!mRestoring) {
   1178             if (hostView == null) {
   1179                 // Perform actual inflation because we're live
   1180                 launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   1181                 launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
   1182             } else {
   1183                 // The AppWidgetHostView has already been inflated and instantiated
   1184                 launcherInfo.hostView = hostView;
   1185             }
   1186 
   1187             launcherInfo.hostView.setTag(launcherInfo);
   1188             launcherInfo.hostView.setVisibility(View.VISIBLE);
   1189             launcherInfo.notifyWidgetSizeChanged(this);
   1190             mWorkspace.addInScreen(launcherInfo.hostView, container, screen, cellXY[0], cellXY[1],
   1191                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
   1192 
   1193             addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
   1194         }
   1195         resetAddInfo();
   1196     }
   1197 
   1198     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
   1199         @Override
   1200         public void onReceive(Context context, Intent intent) {
   1201             final String action = intent.getAction();
   1202             if (Intent.ACTION_SCREEN_OFF.equals(action)) {
   1203                 mUserPresent = false;
   1204                 mDragLayer.clearAllResizeFrames();
   1205                 updateRunning();
   1206 
   1207                 // Reset AllApps to its initial state only if we are not in the middle of
   1208                 // processing a multi-step drop
   1209                 if (mAppsCustomizeTabHost != null && mPendingAddInfo.container == ItemInfo.NO_ID) {
   1210                     mAppsCustomizeTabHost.reset();
   1211                     showWorkspace(false);
   1212                 }
   1213             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
   1214                 mUserPresent = true;
   1215                 updateRunning();
   1216             }
   1217         }
   1218     };
   1219 
   1220     @Override
   1221     public void onAttachedToWindow() {
   1222         super.onAttachedToWindow();
   1223 
   1224         // Listen for broadcasts related to user-presence
   1225         final IntentFilter filter = new IntentFilter();
   1226         filter.addAction(Intent.ACTION_SCREEN_OFF);
   1227         filter.addAction(Intent.ACTION_USER_PRESENT);
   1228         registerReceiver(mReceiver, filter);
   1229 
   1230         mAttached = true;
   1231         mVisible = true;
   1232     }
   1233 
   1234     @Override
   1235     public void onDetachedFromWindow() {
   1236         super.onDetachedFromWindow();
   1237         mVisible = false;
   1238 
   1239         if (mAttached) {
   1240             unregisterReceiver(mReceiver);
   1241             mAttached = false;
   1242         }
   1243         updateRunning();
   1244     }
   1245 
   1246     public void onWindowVisibilityChanged(int visibility) {
   1247         mVisible = visibility == View.VISIBLE;
   1248         updateRunning();
   1249         // The following code used to be in onResume, but it turns out onResume is called when
   1250         // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
   1251         // is a more appropriate event to handle
   1252         if (mVisible) {
   1253             mAppsCustomizeTabHost.onWindowVisible();
   1254             if (!mWorkspaceLoading) {
   1255                 final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
   1256                 // We want to let Launcher draw itself at least once before we force it to build
   1257                 // layers on all the workspace pages, so that transitioning to Launcher from other
   1258                 // apps is nice and speedy. Usually the first call to preDraw doesn't correspond to
   1259                 // a true draw so we wait until the second preDraw call to be safe
   1260                 observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
   1261                     public boolean onPreDraw() {
   1262                         // We delay the layer building a bit in order to give
   1263                         // other message processing a time to run.  In particular
   1264                         // this avoids a delay in hiding the IME if it was
   1265                         // currently shown, because doing that may involve
   1266                         // some communication back with the app.
   1267                         mWorkspace.postDelayed(mBuildLayersRunnable, 500);
   1268 
   1269                         observer.removeOnPreDrawListener(this);
   1270                         return true;
   1271                     }
   1272                 });
   1273             }
   1274             // When Launcher comes back to foreground, a different Activity might be responsible for
   1275             // the app market intent, so refresh the icon
   1276             updateAppMarketIcon();
   1277             clearTypedText();
   1278         }
   1279     }
   1280 
   1281     private void sendAdvanceMessage(long delay) {
   1282         mHandler.removeMessages(ADVANCE_MSG);
   1283         Message msg = mHandler.obtainMessage(ADVANCE_MSG);
   1284         mHandler.sendMessageDelayed(msg, delay);
   1285         mAutoAdvanceSentTime = System.currentTimeMillis();
   1286     }
   1287 
   1288     private void updateRunning() {
   1289         boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty();
   1290         if (autoAdvanceRunning != mAutoAdvanceRunning) {
   1291             mAutoAdvanceRunning = autoAdvanceRunning;
   1292             if (autoAdvanceRunning) {
   1293                 long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft;
   1294                 sendAdvanceMessage(delay);
   1295             } else {
   1296                 if (!mWidgetsToAdvance.isEmpty()) {
   1297                     mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval -
   1298                             (System.currentTimeMillis() - mAutoAdvanceSentTime));
   1299                 }
   1300                 mHandler.removeMessages(ADVANCE_MSG);
   1301                 mHandler.removeMessages(0); // Remove messages sent using postDelayed()
   1302             }
   1303         }
   1304     }
   1305 
   1306     private final Handler mHandler = new Handler() {
   1307         @Override
   1308         public void handleMessage(Message msg) {
   1309             if (msg.what == ADVANCE_MSG) {
   1310                 int i = 0;
   1311                 for (View key: mWidgetsToAdvance.keySet()) {
   1312                     final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId);
   1313                     final int delay = mAdvanceStagger * i;
   1314                     if (v instanceof Advanceable) {
   1315                        postDelayed(new Runnable() {
   1316                            public void run() {
   1317                                ((Advanceable) v).advance();
   1318                            }
   1319                        }, delay);
   1320                     }
   1321                     i++;
   1322                 }
   1323                 sendAdvanceMessage(mAdvanceInterval);
   1324             }
   1325         }
   1326     };
   1327 
   1328     void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) {
   1329         if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return;
   1330         View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId);
   1331         if (v instanceof Advanceable) {
   1332             mWidgetsToAdvance.put(hostView, appWidgetInfo);
   1333             ((Advanceable) v).fyiWillBeAdvancedByHostKThx();
   1334             updateRunning();
   1335         }
   1336     }
   1337 
   1338     void removeWidgetToAutoAdvance(View hostView) {
   1339         if (mWidgetsToAdvance.containsKey(hostView)) {
   1340             mWidgetsToAdvance.remove(hostView);
   1341             updateRunning();
   1342         }
   1343     }
   1344 
   1345     public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
   1346         removeWidgetToAutoAdvance(launcherInfo.hostView);
   1347         launcherInfo.hostView = null;
   1348     }
   1349 
   1350     void showOutOfSpaceMessage(boolean isHotseatLayout) {
   1351         int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
   1352         Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
   1353     }
   1354 
   1355     public LauncherAppWidgetHost getAppWidgetHost() {
   1356         return mAppWidgetHost;
   1357     }
   1358 
   1359     public LauncherModel getModel() {
   1360         return mModel;
   1361     }
   1362 
   1363     void closeSystemDialogs() {
   1364         getWindow().closeAllPanels();
   1365 
   1366         // Whatever we were doing is hereby canceled.
   1367         mWaitingForResult = false;
   1368     }
   1369 
   1370     @Override
   1371     protected void onNewIntent(Intent intent) {
   1372         super.onNewIntent(intent);
   1373 
   1374         // Close the menu
   1375         if (Intent.ACTION_MAIN.equals(intent.getAction())) {
   1376             // also will cancel mWaitingForResult.
   1377             closeSystemDialogs();
   1378 
   1379             boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
   1380                         != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
   1381 
   1382             Folder openFolder = mWorkspace.getOpenFolder();
   1383             // In all these cases, only animate if we're already on home
   1384             mWorkspace.exitWidgetResizeMode();
   1385             if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
   1386                     openFolder == null) {
   1387                 mWorkspace.moveToDefaultScreen(true);
   1388             }
   1389 
   1390             closeFolder();
   1391             exitSpringLoadedDragMode();
   1392 
   1393             // If we are already on home, then just animate back to the workspace, otherwise, just
   1394             // wait until onResume to set the state back to Workspace
   1395             if (alreadyOnHome) {
   1396                 showWorkspace(true);
   1397             } else {
   1398                 mOnResumeState = State.WORKSPACE;
   1399             }
   1400 
   1401             final View v = getWindow().peekDecorView();
   1402             if (v != null && v.getWindowToken() != null) {
   1403                 InputMethodManager imm = (InputMethodManager)getSystemService(
   1404                         INPUT_METHOD_SERVICE);
   1405                 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
   1406             }
   1407 
   1408             // Reset AllApps to its initial state
   1409             if (!alreadyOnHome && mAppsCustomizeTabHost != null) {
   1410                 mAppsCustomizeTabHost.reset();
   1411             }
   1412         }
   1413     }
   1414 
   1415     @Override
   1416     public void onRestoreInstanceState(Bundle state) {
   1417         super.onRestoreInstanceState(state);
   1418         for (int page: mSynchronouslyBoundPages) {
   1419             mWorkspace.restoreInstanceStateForChild(page);
   1420         }
   1421     }
   1422 
   1423     @Override
   1424     protected void onSaveInstanceState(Bundle outState) {
   1425         outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage());
   1426         super.onSaveInstanceState(outState);
   1427 
   1428         outState.putInt(RUNTIME_STATE, mState.ordinal());
   1429         // We close any open folder since it will not be re-opened, and we need to make sure
   1430         // this state is reflected.
   1431         closeFolder();
   1432 
   1433         if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screen > -1 &&
   1434                 mWaitingForResult) {
   1435             outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container);
   1436             outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screen);
   1437             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX);
   1438             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
   1439             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
   1440             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY);
   1441             outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
   1442         }
   1443 
   1444         if (mFolderInfo != null && mWaitingForResult) {
   1445             outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
   1446             outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
   1447         }
   1448 
   1449         // Save the current AppsCustomize tab
   1450         if (mAppsCustomizeTabHost != null) {
   1451             String currentTabTag = mAppsCustomizeTabHost.getCurrentTabTag();
   1452             if (currentTabTag != null) {
   1453                 outState.putString("apps_customize_currentTab", currentTabTag);
   1454             }
   1455             int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex();
   1456             outState.putInt("apps_customize_currentIndex", currentIndex);
   1457         }
   1458     }
   1459 
   1460     @Override
   1461     public void onDestroy() {
   1462         super.onDestroy();
   1463 
   1464         // Remove all pending runnables
   1465         mHandler.removeMessages(ADVANCE_MSG);
   1466         mHandler.removeMessages(0);
   1467         mWorkspace.removeCallbacks(mBuildLayersRunnable);
   1468 
   1469         // Stop callbacks from LauncherModel
   1470         LauncherApplication app = ((LauncherApplication) getApplication());
   1471         mModel.stopLoader();
   1472         app.setLauncher(null);
   1473 
   1474         try {
   1475             mAppWidgetHost.stopListening();
   1476         } catch (NullPointerException ex) {
   1477             Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
   1478         }
   1479         mAppWidgetHost = null;
   1480 
   1481         mWidgetsToAdvance.clear();
   1482 
   1483         TextKeyListener.getInstance().release();
   1484 
   1485 
   1486         unbindWorkspaceAndHotseatItems();
   1487 
   1488         getContentResolver().unregisterContentObserver(mWidgetObserver);
   1489         unregisterReceiver(mCloseSystemDialogsReceiver);
   1490 
   1491         mDragLayer.clearAllResizeFrames();
   1492         ((ViewGroup) mWorkspace.getParent()).removeAllViews();
   1493         mWorkspace.removeAllViews();
   1494         mWorkspace = null;
   1495         mDragController = null;
   1496 
   1497         ValueAnimator.clearAllAnimations();
   1498     }
   1499 
   1500     public DragController getDragController() {
   1501         return mDragController;
   1502     }
   1503 
   1504     @Override
   1505     public void startActivityForResult(Intent intent, int requestCode) {
   1506         if (requestCode >= 0) mWaitingForResult = true;
   1507         super.startActivityForResult(intent, requestCode);
   1508     }
   1509 
   1510     /**
   1511      * Indicates that we want global search for this activity by setting the globalSearch
   1512      * argument for {@link #startSearch} to true.
   1513      */
   1514     @Override
   1515     public void startSearch(String initialQuery, boolean selectInitialQuery,
   1516             Bundle appSearchData, boolean globalSearch) {
   1517 
   1518         showWorkspace(true);
   1519 
   1520         if (initialQuery == null) {
   1521             // Use any text typed in the launcher as the initial query
   1522             initialQuery = getTypedText();
   1523         }
   1524         if (appSearchData == null) {
   1525             appSearchData = new Bundle();
   1526             appSearchData.putString(Search.SOURCE, "launcher-search");
   1527         }
   1528         Rect sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
   1529 
   1530         final SearchManager searchManager =
   1531                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   1532         searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
   1533             appSearchData, globalSearch, sourceBounds);
   1534     }
   1535 
   1536     @Override
   1537     public boolean onCreateOptionsMenu(Menu menu) {
   1538         if (isWorkspaceLocked()) {
   1539             return false;
   1540         }
   1541 
   1542         super.onCreateOptionsMenu(menu);
   1543 
   1544         Intent manageApps = new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS);
   1545         manageApps.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1546                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   1547         Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
   1548         settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1549                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
   1550         String helpUrl = getString(R.string.help_url);
   1551         Intent help = new Intent(Intent.ACTION_VIEW, Uri.parse(helpUrl));
   1552         help.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1553                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   1554 
   1555         menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
   1556             .setIcon(android.R.drawable.ic_menu_gallery)
   1557             .setAlphabeticShortcut('W');
   1558         menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)
   1559             .setIcon(android.R.drawable.ic_menu_manage)
   1560             .setIntent(manageApps)
   1561             .setAlphabeticShortcut('M');
   1562         menu.add(0, MENU_SYSTEM_SETTINGS, 0, R.string.menu_settings)
   1563             .setIcon(android.R.drawable.ic_menu_preferences)
   1564             .setIntent(settings)
   1565             .setAlphabeticShortcut('P');
   1566         if (!helpUrl.isEmpty()) {
   1567             menu.add(0, MENU_HELP, 0, R.string.menu_help)
   1568                 .setIcon(android.R.drawable.ic_menu_help)
   1569                 .setIntent(help)
   1570                 .setAlphabeticShortcut('H');
   1571         }
   1572         return true;
   1573     }
   1574 
   1575     @Override
   1576     public boolean onPrepareOptionsMenu(Menu menu) {
   1577         super.onPrepareOptionsMenu(menu);
   1578 
   1579         if (mAppsCustomizeTabHost.isTransitioning()) {
   1580             return false;
   1581         }
   1582         boolean allAppsVisible = (mAppsCustomizeTabHost.getVisibility() == View.VISIBLE);
   1583         menu.setGroupVisible(MENU_GROUP_WALLPAPER, !allAppsVisible);
   1584 
   1585         return true;
   1586     }
   1587 
   1588     @Override
   1589     public boolean onOptionsItemSelected(MenuItem item) {
   1590         switch (item.getItemId()) {
   1591         case MENU_WALLPAPER_SETTINGS:
   1592             startWallpaper();
   1593             return true;
   1594         }
   1595 
   1596         return super.onOptionsItemSelected(item);
   1597     }
   1598 
   1599     @Override
   1600     public boolean onSearchRequested() {
   1601         startSearch(null, false, null, true);
   1602         // Use a custom animation for launching search
   1603         overridePendingTransition(R.anim.fade_in_fast, R.anim.fade_out_fast);
   1604         return true;
   1605     }
   1606 
   1607     public boolean isWorkspaceLocked() {
   1608         return mWorkspaceLoading || mWaitingForResult;
   1609     }
   1610 
   1611     private void resetAddInfo() {
   1612         mPendingAddInfo.container = ItemInfo.NO_ID;
   1613         mPendingAddInfo.screen = -1;
   1614         mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
   1615         mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
   1616         mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
   1617         mPendingAddInfo.dropPos = null;
   1618     }
   1619 
   1620     void addAppWidgetImpl(final int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
   1621             AppWidgetProviderInfo appWidgetInfo) {
   1622         if (appWidgetInfo.configure != null) {
   1623             mPendingAddWidgetInfo = appWidgetInfo;
   1624 
   1625             // Launch over to configure widget, if needed
   1626             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
   1627             intent.setComponent(appWidgetInfo.configure);
   1628             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   1629             startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
   1630         } else {
   1631             // Otherwise just add it
   1632             completeAddAppWidget(appWidgetId, info.container, info.screen, boundWidget,
   1633                     appWidgetInfo);
   1634             // Exit spring loaded mode if necessary after adding the widget
   1635             exitSpringLoadedDragModeDelayed(true, false, null);
   1636         }
   1637     }
   1638 
   1639     /**
   1640      * Process a shortcut drop.
   1641      *
   1642      * @param componentName The name of the component
   1643      * @param screen The screen where it should be added
   1644      * @param cell The cell it should be added to, optional
   1645      * @param position The location on the screen where it was dropped, optional
   1646      */
   1647     void processShortcutFromDrop(ComponentName componentName, long container, int screen,
   1648             int[] cell, int[] loc) {
   1649         resetAddInfo();
   1650         mPendingAddInfo.container = container;
   1651         mPendingAddInfo.screen = screen;
   1652         mPendingAddInfo.dropPos = loc;
   1653 
   1654         if (cell != null) {
   1655             mPendingAddInfo.cellX = cell[0];
   1656             mPendingAddInfo.cellY = cell[1];
   1657         }
   1658 
   1659         Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
   1660         createShortcutIntent.setComponent(componentName);
   1661         processShortcut(createShortcutIntent);
   1662     }
   1663 
   1664     /**
   1665      * Process a widget drop.
   1666      *
   1667      * @param info The PendingAppWidgetInfo of the widget being added.
   1668      * @param screen The screen where it should be added
   1669      * @param cell The cell it should be added to, optional
   1670      * @param position The location on the screen where it was dropped, optional
   1671      */
   1672     void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, int screen,
   1673             int[] cell, int[] span, int[] loc) {
   1674         resetAddInfo();
   1675         mPendingAddInfo.container = info.container = container;
   1676         mPendingAddInfo.screen = info.screen = screen;
   1677         mPendingAddInfo.dropPos = loc;
   1678         mPendingAddInfo.minSpanX = info.minSpanX;
   1679         mPendingAddInfo.minSpanY = info.minSpanY;
   1680 
   1681         if (cell != null) {
   1682             mPendingAddInfo.cellX = cell[0];
   1683             mPendingAddInfo.cellY = cell[1];
   1684         }
   1685         if (span != null) {
   1686             mPendingAddInfo.spanX = span[0];
   1687             mPendingAddInfo.spanY = span[1];
   1688         }
   1689 
   1690         AppWidgetHostView hostView = info.boundWidget;
   1691         int appWidgetId;
   1692         if (hostView != null) {
   1693             appWidgetId = hostView.getAppWidgetId();
   1694             addAppWidgetImpl(appWidgetId, info, hostView, info.info);
   1695         } else {
   1696             // In this case, we either need to start an activity to get permission to bind
   1697             // the widget, or we need to start an activity to configure the widget, or both.
   1698             appWidgetId = getAppWidgetHost().allocateAppWidgetId();
   1699             if (mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.componentName)) {
   1700                 addAppWidgetImpl(appWidgetId, info, null, info.info);
   1701             } else {
   1702                 mPendingAddWidgetInfo = info.info;
   1703                 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
   1704                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   1705                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
   1706                 startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
   1707             }
   1708         }
   1709     }
   1710 
   1711     void processShortcut(Intent intent) {
   1712         // Handle case where user selected "Applications"
   1713         String applicationName = getResources().getString(R.string.group_applications);
   1714         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
   1715 
   1716         if (applicationName != null && applicationName.equals(shortcutName)) {
   1717             Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
   1718             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
   1719 
   1720             Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
   1721             pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
   1722             pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
   1723             startActivityForResultSafely(pickIntent, REQUEST_PICK_APPLICATION);
   1724         } else {
   1725             startActivityForResultSafely(intent, REQUEST_CREATE_SHORTCUT);
   1726         }
   1727     }
   1728 
   1729     void processWallpaper(Intent intent) {
   1730         startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
   1731     }
   1732 
   1733     FolderIcon addFolder(CellLayout layout, long container, final int screen, int cellX,
   1734             int cellY) {
   1735         final FolderInfo folderInfo = new FolderInfo();
   1736         folderInfo.title = getText(R.string.folder_name);
   1737 
   1738         // Update the model
   1739         LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screen, cellX, cellY,
   1740                 false);
   1741         sFolders.put(folderInfo.id, folderInfo);
   1742 
   1743         // Create the view
   1744         FolderIcon newFolder =
   1745             FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
   1746         mWorkspace.addInScreen(newFolder, container, screen, cellX, cellY, 1, 1,
   1747                 isWorkspaceLocked());
   1748         return newFolder;
   1749     }
   1750 
   1751     void removeFolder(FolderInfo folder) {
   1752         sFolders.remove(folder.id);
   1753     }
   1754 
   1755     private void startWallpaper() {
   1756         showWorkspace(true);
   1757         final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
   1758         Intent chooser = Intent.createChooser(pickWallpaper,
   1759                 getText(R.string.chooser_wallpaper));
   1760         // NOTE: Adds a configure option to the chooser if the wallpaper supports it
   1761         //       Removed in Eclair MR1
   1762 //        WallpaperManager wm = (WallpaperManager)
   1763 //                getSystemService(Context.WALLPAPER_SERVICE);
   1764 //        WallpaperInfo wi = wm.getWallpaperInfo();
   1765 //        if (wi != null && wi.getSettingsActivity() != null) {
   1766 //            LabeledIntent li = new LabeledIntent(getPackageName(),
   1767 //                    R.string.configure_wallpaper, 0);
   1768 //            li.setClassName(wi.getPackageName(), wi.getSettingsActivity());
   1769 //            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li });
   1770 //        }
   1771         startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
   1772     }
   1773 
   1774     /**
   1775      * Registers various content observers. The current implementation registers
   1776      * only a favorites observer to keep track of the favorites applications.
   1777      */
   1778     private void registerContentObservers() {
   1779         ContentResolver resolver = getContentResolver();
   1780         resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
   1781                 true, mWidgetObserver);
   1782     }
   1783 
   1784     @Override
   1785     public boolean dispatchKeyEvent(KeyEvent event) {
   1786         if (event.getAction() == KeyEvent.ACTION_DOWN) {
   1787             switch (event.getKeyCode()) {
   1788                 case KeyEvent.KEYCODE_HOME:
   1789                     return true;
   1790                 case KeyEvent.KEYCODE_VOLUME_DOWN:
   1791                     if (SystemProperties.getInt("debug.launcher2.dumpstate", 0) != 0) {
   1792                         dumpState();
   1793                         return true;
   1794                     }
   1795                     break;
   1796             }
   1797         } else if (event.getAction() == KeyEvent.ACTION_UP) {
   1798             switch (event.getKeyCode()) {
   1799                 case KeyEvent.KEYCODE_HOME:
   1800                     return true;
   1801             }
   1802         }
   1803 
   1804         return super.dispatchKeyEvent(event);
   1805     }
   1806 
   1807     @Override
   1808     public void onBackPressed() {
   1809         if (isAllAppsVisible()) {
   1810             showWorkspace(true);
   1811         } else if (mWorkspace.getOpenFolder() != null) {
   1812             Folder openFolder = mWorkspace.getOpenFolder();
   1813             if (openFolder.isEditingName()) {
   1814                 openFolder.dismissEditingName();
   1815             } else {
   1816                 closeFolder();
   1817             }
   1818         } else {
   1819             mWorkspace.exitWidgetResizeMode();
   1820 
   1821             // Back button is a no-op here, but give at least some feedback for the button press
   1822             mWorkspace.showOutlinesTemporarily();
   1823         }
   1824     }
   1825 
   1826     /**
   1827      * Re-listen when widgets are reset.
   1828      */
   1829     private void onAppWidgetReset() {
   1830         if (mAppWidgetHost != null) {
   1831             mAppWidgetHost.startListening();
   1832         }
   1833     }
   1834 
   1835     /**
   1836      * Go through the and disconnect any of the callbacks in the drawables and the views or we
   1837      * leak the previous Home screen on orientation change.
   1838      */
   1839     private void unbindWorkspaceAndHotseatItems() {
   1840         if (mModel != null) {
   1841             mModel.unbindWorkspaceItems();
   1842         }
   1843     }
   1844 
   1845     /**
   1846      * Launches the intent referred by the clicked shortcut.
   1847      *
   1848      * @param v The view representing the clicked shortcut.
   1849      */
   1850     public void onClick(View v) {
   1851         // Make sure that rogue clicks don't get through while allapps is launching, or after the
   1852         // view has detached (it's possible for this to happen if the view is removed mid touch).
   1853         if (v.getWindowToken() == null) {
   1854             return;
   1855         }
   1856 
   1857         if (!mWorkspace.isFinishedSwitchingState()) {
   1858             return;
   1859         }
   1860 
   1861         Object tag = v.getTag();
   1862         if (tag instanceof ShortcutInfo) {
   1863             // Open shortcut
   1864             final Intent intent = ((ShortcutInfo) tag).intent;
   1865             int[] pos = new int[2];
   1866             v.getLocationOnScreen(pos);
   1867             intent.setSourceBounds(new Rect(pos[0], pos[1],
   1868                     pos[0] + v.getWidth(), pos[1] + v.getHeight()));
   1869 
   1870             boolean success = startActivitySafely(v, intent, tag);
   1871 
   1872             if (success && v instanceof BubbleTextView) {
   1873                 mWaitingForResume = (BubbleTextView) v;
   1874                 mWaitingForResume.setStayPressed(true);
   1875             }
   1876         } else if (tag instanceof FolderInfo) {
   1877             if (v instanceof FolderIcon) {
   1878                 FolderIcon fi = (FolderIcon) v;
   1879                 handleFolderClick(fi);
   1880             }
   1881         } else if (v == mAllAppsButton) {
   1882             if (isAllAppsVisible()) {
   1883                 showWorkspace(true);
   1884             } else {
   1885                 onClickAllAppsButton(v);
   1886             }
   1887         }
   1888     }
   1889 
   1890     public boolean onTouch(View v, MotionEvent event) {
   1891         // this is an intercepted event being forwarded from mWorkspace;
   1892         // clicking anywhere on the workspace causes the customization drawer to slide down
   1893         showWorkspace(true);
   1894         return false;
   1895     }
   1896 
   1897     /**
   1898      * Event handler for the search button
   1899      *
   1900      * @param v The view that was clicked.
   1901      */
   1902     public void onClickSearchButton(View v) {
   1903         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   1904 
   1905         onSearchRequested();
   1906     }
   1907 
   1908     /**
   1909      * Event handler for the voice button
   1910      *
   1911      * @param v The view that was clicked.
   1912      */
   1913     public void onClickVoiceButton(View v) {
   1914         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   1915 
   1916         try {
   1917             final SearchManager searchManager =
   1918                     (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   1919             ComponentName activityName = searchManager.getGlobalSearchActivity();
   1920             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   1921             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1922             if (activityName != null) {
   1923                 intent.setPackage(activityName.getPackageName());
   1924             }
   1925             startActivity(null, intent, "onClickVoiceButton");
   1926             overridePendingTransition(R.anim.fade_in_fast, R.anim.fade_out_fast);
   1927         } catch (ActivityNotFoundException e) {
   1928             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   1929             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1930             startActivitySafely(null, intent, "onClickVoiceButton");
   1931         }
   1932     }
   1933 
   1934     /**
   1935      * Event handler for the "grid" button that appears on the home screen, which
   1936      * enters all apps mode.
   1937      *
   1938      * @param v The view that was clicked.
   1939      */
   1940     public void onClickAllAppsButton(View v) {
   1941         showAllApps(true);
   1942     }
   1943 
   1944     public void onTouchDownAllAppsButton(View v) {
   1945         // Provide the same haptic feedback that the system offers for virtual keys.
   1946         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   1947     }
   1948 
   1949     public void onClickAppMarketButton(View v) {
   1950         if (mAppMarketIntent != null) {
   1951             startActivitySafely(v, mAppMarketIntent, "app market");
   1952         } else {
   1953             Log.e(TAG, "Invalid app market intent.");
   1954         }
   1955     }
   1956 
   1957     void startApplicationDetailsActivity(ComponentName componentName) {
   1958         String packageName = componentName.getPackageName();
   1959         Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
   1960                 Uri.fromParts("package", packageName, null));
   1961         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   1962         startActivitySafely(null, intent, "startApplicationDetailsActivity");
   1963     }
   1964 
   1965     void startApplicationUninstallActivity(ApplicationInfo appInfo) {
   1966         if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) == 0) {
   1967             // System applications cannot be installed. For now, show a toast explaining that.
   1968             // We may give them the option of disabling apps this way.
   1969             int messageId = R.string.uninstall_system_app_text;
   1970             Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
   1971         } else {
   1972             String packageName = appInfo.componentName.getPackageName();
   1973             String className = appInfo.componentName.getClassName();
   1974             Intent intent = new Intent(
   1975                     Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
   1976             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
   1977                     Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   1978             startActivity(intent);
   1979         }
   1980     }
   1981 
   1982     boolean startActivity(View v, Intent intent, Object tag) {
   1983         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1984 
   1985         try {
   1986             // Only launch using the new animation if the shortcut has not opted out (this is a
   1987             // private contract between launcher and may be ignored in the future).
   1988             boolean useLaunchAnimation = (v != null) &&
   1989                     !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
   1990             if (useLaunchAnimation) {
   1991                 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
   1992                         v.getMeasuredWidth(), v.getMeasuredHeight());
   1993 
   1994                 startActivity(intent, opts.toBundle());
   1995             } else {
   1996                 startActivity(intent);
   1997             }
   1998             return true;
   1999         } catch (SecurityException e) {
   2000             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2001             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   2002                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   2003                     "or use the exported attribute for this activity. "
   2004                     + "tag="+ tag + " intent=" + intent, e);
   2005         }
   2006         return false;
   2007     }
   2008 
   2009     boolean startActivitySafely(View v, Intent intent, Object tag) {
   2010         boolean success = false;
   2011         try {
   2012             success = startActivity(v, intent, tag);
   2013         } catch (ActivityNotFoundException e) {
   2014             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2015             Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
   2016         }
   2017         return success;
   2018     }
   2019 
   2020     void startActivityForResultSafely(Intent intent, int requestCode) {
   2021         try {
   2022             startActivityForResult(intent, requestCode);
   2023         } catch (ActivityNotFoundException e) {
   2024             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2025         } catch (SecurityException e) {
   2026             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2027             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   2028                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   2029                     "or use the exported attribute for this activity.", e);
   2030         }
   2031     }
   2032 
   2033     private void handleFolderClick(FolderIcon folderIcon) {
   2034         final FolderInfo info = folderIcon.mInfo;
   2035         Folder openFolder = mWorkspace.getFolderForTag(info);
   2036 
   2037         // If the folder info reports that the associated folder is open, then verify that
   2038         // it is actually opened. There have been a few instances where this gets out of sync.
   2039         if (info.opened && openFolder == null) {
   2040             Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
   2041                     + info.screen + " (" + info.cellX + ", " + info.cellY + ")");
   2042             info.opened = false;
   2043         }
   2044 
   2045         if (!info.opened) {
   2046             // Close any open folder
   2047             closeFolder();
   2048             // Open the requested folder
   2049             openFolder(folderIcon);
   2050         } else {
   2051             // Find the open folder...
   2052             int folderScreen;
   2053             if (openFolder != null) {
   2054                 folderScreen = mWorkspace.getPageForView(openFolder);
   2055                 // .. and close it
   2056                 closeFolder(openFolder);
   2057                 if (folderScreen != mWorkspace.getCurrentPage()) {
   2058                     // Close any folder open on the current screen
   2059                     closeFolder();
   2060                     // Pull the folder onto this screen
   2061                     openFolder(folderIcon);
   2062                 }
   2063             }
   2064         }
   2065     }
   2066 
   2067     /**
   2068      * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
   2069      * in the DragLayer in the exact absolute location of the original FolderIcon.
   2070      */
   2071     private void copyFolderIconToImage(FolderIcon fi) {
   2072         final int width = fi.getMeasuredWidth();
   2073         final int height = fi.getMeasuredHeight();
   2074 
   2075         // Lazy load ImageView, Bitmap and Canvas
   2076         if (mFolderIconImageView == null) {
   2077             mFolderIconImageView = new ImageView(this);
   2078         }
   2079         if (mFolderIconBitmap == null || mFolderIconBitmap.getWidth() != width ||
   2080                 mFolderIconBitmap.getHeight() != height) {
   2081             mFolderIconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
   2082             mFolderIconCanvas = new Canvas(mFolderIconBitmap);
   2083         }
   2084 
   2085         DragLayer.LayoutParams lp;
   2086         if (mFolderIconImageView.getLayoutParams() instanceof DragLayer.LayoutParams) {
   2087             lp = (DragLayer.LayoutParams) mFolderIconImageView.getLayoutParams();
   2088         } else {
   2089             lp = new DragLayer.LayoutParams(width, height);
   2090         }
   2091 
   2092         mDragLayer.getViewRectRelativeToSelf(fi, mRectForFolderAnimation);
   2093         lp.customPosition = true;
   2094         lp.x = mRectForFolderAnimation.left;
   2095         lp.y = mRectForFolderAnimation.top;
   2096         lp.width = width;
   2097         lp.height = height;
   2098 
   2099         mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
   2100         fi.draw(mFolderIconCanvas);
   2101         mFolderIconImageView.setImageBitmap(mFolderIconBitmap);
   2102         if (fi.mFolder != null) {
   2103             mFolderIconImageView.setPivotX(fi.mFolder.getPivotXForIconAnimation());
   2104             mFolderIconImageView.setPivotY(fi.mFolder.getPivotYForIconAnimation());
   2105         }
   2106         // Just in case this image view is still in the drag layer from a previous animation,
   2107         // we remove it and re-add it.
   2108         if (mDragLayer.indexOfChild(mFolderIconImageView) != -1) {
   2109             mDragLayer.removeView(mFolderIconImageView);
   2110         }
   2111         mDragLayer.addView(mFolderIconImageView, lp);
   2112         if (fi.mFolder != null) {
   2113             fi.mFolder.bringToFront();
   2114         }
   2115     }
   2116 
   2117     private void growAndFadeOutFolderIcon(FolderIcon fi) {
   2118         if (fi == null) return;
   2119         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
   2120         PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f);
   2121         PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f);
   2122 
   2123         FolderInfo info = (FolderInfo) fi.getTag();
   2124         if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   2125             CellLayout cl = (CellLayout) fi.getParent().getParent();
   2126             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) fi.getLayoutParams();
   2127             cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
   2128         }
   2129 
   2130         // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
   2131         copyFolderIconToImage(fi);
   2132         fi.setVisibility(View.INVISIBLE);
   2133 
   2134         ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(mFolderIconImageView, alpha,
   2135                 scaleX, scaleY);
   2136         oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
   2137         oa.start();
   2138     }
   2139 
   2140     private void shrinkAndFadeInFolderIcon(final FolderIcon fi) {
   2141         if (fi == null) return;
   2142         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
   2143         PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
   2144         PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
   2145 
   2146         final CellLayout cl = (CellLayout) fi.getParent().getParent();
   2147 
   2148         // We remove and re-draw the FolderIcon in-case it has changed
   2149         mDragLayer.removeView(mFolderIconImageView);
   2150         copyFolderIconToImage(fi);
   2151         ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(mFolderIconImageView, alpha,
   2152                 scaleX, scaleY);
   2153         oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
   2154         oa.addListener(new AnimatorListenerAdapter() {
   2155             @Override
   2156             public void onAnimationEnd(Animator animation) {
   2157                 if (cl != null) {
   2158                     cl.clearFolderLeaveBehind();
   2159                     // Remove the ImageView copy of the FolderIcon and make the original visible.
   2160                     mDragLayer.removeView(mFolderIconImageView);
   2161                     fi.setVisibility(View.VISIBLE);
   2162                 }
   2163             }
   2164         });
   2165         oa.start();
   2166     }
   2167 
   2168     /**
   2169      * Opens the user folder described by the specified tag. The opening of the folder
   2170      * is animated relative to the specified View. If the View is null, no animation
   2171      * is played.
   2172      *
   2173      * @param folderInfo The FolderInfo describing the folder to open.
   2174      */
   2175     public void openFolder(FolderIcon folderIcon) {
   2176         Folder folder = folderIcon.mFolder;
   2177         FolderInfo info = folder.mInfo;
   2178 
   2179         info.opened = true;
   2180 
   2181         // Just verify that the folder hasn't already been added to the DragLayer.
   2182         // There was a one-off crash where the folder had a parent already.
   2183         if (folder.getParent() == null) {
   2184             mDragLayer.addView(folder);
   2185             mDragController.addDropTarget((DropTarget) folder);
   2186         } else {
   2187             Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
   2188                     folder.getParent() + ").");
   2189         }
   2190         folder.animateOpen();
   2191         growAndFadeOutFolderIcon(folderIcon);
   2192     }
   2193 
   2194     public void closeFolder() {
   2195         Folder folder = mWorkspace.getOpenFolder();
   2196         if (folder != null) {
   2197             if (folder.isEditingName()) {
   2198                 folder.dismissEditingName();
   2199             }
   2200             closeFolder(folder);
   2201 
   2202             // Dismiss the folder cling
   2203             dismissFolderCling(null);
   2204         }
   2205     }
   2206 
   2207     void closeFolder(Folder folder) {
   2208         folder.getInfo().opened = false;
   2209 
   2210         ViewGroup parent = (ViewGroup) folder.getParent().getParent();
   2211         if (parent != null) {
   2212             FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
   2213             shrinkAndFadeInFolderIcon(fi);
   2214         }
   2215         folder.animateClosed();
   2216     }
   2217 
   2218     public boolean onLongClick(View v) {
   2219         if (!isDraggingEnabled()) return false;
   2220         if (isWorkspaceLocked()) return false;
   2221         if (mState != State.WORKSPACE) return false;
   2222 
   2223         if (!(v instanceof CellLayout)) {
   2224             v = (View) v.getParent().getParent();
   2225         }
   2226 
   2227         resetAddInfo();
   2228         CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag();
   2229         // This happens when long clicking an item with the dpad/trackball
   2230         if (longClickCellInfo == null) {
   2231             return true;
   2232         }
   2233 
   2234         // The hotseat touch handling does not go through Workspace, and we always allow long press
   2235         // on hotseat items.
   2236         final View itemUnderLongClick = longClickCellInfo.cell;
   2237         boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress();
   2238         if (allowLongPress && !mDragController.isDragging()) {
   2239             if (itemUnderLongClick == null) {
   2240                 // User long pressed on empty space
   2241                 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   2242                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   2243                 startWallpaper();
   2244             } else {
   2245                 if (!(itemUnderLongClick instanceof Folder)) {
   2246                     // User long pressed on an item
   2247                     mWorkspace.startDrag(longClickCellInfo);
   2248                 }
   2249             }
   2250         }
   2251         return true;
   2252     }
   2253 
   2254     boolean isHotseatLayout(View layout) {
   2255         return mHotseat != null && layout != null &&
   2256                 (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
   2257     }
   2258     Hotseat getHotseat() {
   2259         return mHotseat;
   2260     }
   2261     SearchDropTargetBar getSearchBar() {
   2262         return mSearchDropTargetBar;
   2263     }
   2264 
   2265     /**
   2266      * Returns the CellLayout of the specified container at the specified screen.
   2267      */
   2268     CellLayout getCellLayout(long container, int screen) {
   2269         if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   2270             if (mHotseat != null) {
   2271                 return mHotseat.getLayout();
   2272             } else {
   2273                 return null;
   2274             }
   2275         } else {
   2276             return (CellLayout) mWorkspace.getChildAt(screen);
   2277         }
   2278     }
   2279 
   2280     Workspace getWorkspace() {
   2281         return mWorkspace;
   2282     }
   2283 
   2284     // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
   2285     public boolean isAllAppsVisible() {
   2286         return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
   2287     }
   2288 
   2289     public boolean isAllAppsButtonRank(int rank) {
   2290         return mHotseat.isAllAppsButtonRank(rank);
   2291     }
   2292 
   2293     // AllAppsView.Watcher
   2294     public void zoomed(float zoom) {
   2295         if (zoom == 1.0f) {
   2296             mWorkspace.setVisibility(View.GONE);
   2297         }
   2298     }
   2299 
   2300     /**
   2301      * Helper method for the cameraZoomIn/cameraZoomOut animations
   2302      * @param view The view being animated
   2303      * @param state The state that we are moving in or out of (eg. APPS_CUSTOMIZE)
   2304      * @param scaleFactor The scale factor used for the zoom
   2305      */
   2306     private void setPivotsForZoom(View view, float scaleFactor) {
   2307         view.setPivotX(view.getWidth() / 2.0f);
   2308         view.setPivotY(view.getHeight() / 2.0f);
   2309     }
   2310 
   2311     void disableWallpaperIfInAllApps() {
   2312         // Only disable it if we are in all apps
   2313         if (isAllAppsVisible()) {
   2314             if (mAppsCustomizeTabHost != null &&
   2315                     !mAppsCustomizeTabHost.isTransitioning()) {
   2316                 updateWallpaperVisibility(false);
   2317             }
   2318         }
   2319     }
   2320 
   2321     void updateWallpaperVisibility(boolean visible) {
   2322         int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
   2323         int curflags = getWindow().getAttributes().flags
   2324                 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
   2325         if (wpflags != curflags) {
   2326             getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
   2327         }
   2328     }
   2329 
   2330     private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
   2331         if (v instanceof LauncherTransitionable) {
   2332             ((LauncherTransitionable) v).onLauncherTransitionPrepare(this, animated, toWorkspace);
   2333         }
   2334     }
   2335 
   2336     private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
   2337         if (v instanceof LauncherTransitionable) {
   2338             ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace);
   2339         }
   2340 
   2341         // Update the workspace transition step as well
   2342         dispatchOnLauncherTransitionStep(v, 0f);
   2343     }
   2344 
   2345     private void dispatchOnLauncherTransitionStep(View v, float t) {
   2346         if (v instanceof LauncherTransitionable) {
   2347             ((LauncherTransitionable) v).onLauncherTransitionStep(this, t);
   2348         }
   2349     }
   2350 
   2351     private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
   2352         if (v instanceof LauncherTransitionable) {
   2353             ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace);
   2354         }
   2355 
   2356         // Update the workspace transition step as well
   2357         dispatchOnLauncherTransitionStep(v, 1f);
   2358     }
   2359 
   2360     /**
   2361      * Things to test when changing the following seven functions.
   2362      *   - Home from workspace
   2363      *          - from center screen
   2364      *          - from other screens
   2365      *   - Home from all apps
   2366      *          - from center screen
   2367      *          - from other screens
   2368      *   - Back from all apps
   2369      *          - from center screen
   2370      *          - from other screens
   2371      *   - Launch app from workspace and quit
   2372      *          - with back
   2373      *          - with home
   2374      *   - Launch app from all apps and quit
   2375      *          - with back
   2376      *          - with home
   2377      *   - Go to a screen that's not the default, then all
   2378      *     apps, and launch and app, and go back
   2379      *          - with back
   2380      *          -with home
   2381      *   - On workspace, long press power and go back
   2382      *          - with back
   2383      *          - with home
   2384      *   - On all apps, long press power and go back
   2385      *          - with back
   2386      *          - with home
   2387      *   - On workspace, power off
   2388      *   - On all apps, power off
   2389      *   - Launch an app and turn off the screen while in that app
   2390      *          - Go back with home key
   2391      *          - Go back with back key  TODO: make this not go to workspace
   2392      *          - From all apps
   2393      *          - From workspace
   2394      *   - Enter and exit car mode (becuase it causes an extra configuration changed)
   2395      *          - From all apps
   2396      *          - From the center workspace
   2397      *          - From another workspace
   2398      */
   2399 
   2400     /**
   2401      * Zoom the camera out from the workspace to reveal 'toView'.
   2402      * Assumes that the view to show is anchored at either the very top or very bottom
   2403      * of the screen.
   2404      */
   2405     private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) {
   2406         if (mStateAnimation != null) {
   2407             mStateAnimation.cancel();
   2408             mStateAnimation = null;
   2409         }
   2410         final Resources res = getResources();
   2411 
   2412         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
   2413         final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
   2414         final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
   2415         final View fromView = mWorkspace;
   2416         final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
   2417         final int startDelay =
   2418                 res.getInteger(R.integer.config_workspaceAppsCustomizeAnimationStagger);
   2419 
   2420         setPivotsForZoom(toView, scale);
   2421 
   2422         // Shrink workspaces away if going to AppsCustomize from workspace
   2423         Animator workspaceAnim =
   2424                 mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
   2425 
   2426         if (animated) {
   2427             toView.setScaleX(scale);
   2428             toView.setScaleY(scale);
   2429             final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView);
   2430             scaleAnim.
   2431                 scaleX(1f).scaleY(1f).
   2432                 setDuration(duration).
   2433                 setInterpolator(new Workspace.ZoomOutInterpolator());
   2434 
   2435             toView.setVisibility(View.VISIBLE);
   2436             toView.setAlpha(0f);
   2437             final ObjectAnimator alphaAnim = ObjectAnimator
   2438                 .ofFloat(toView, "alpha", 0f, 1f)
   2439                 .setDuration(fadeDuration);
   2440             alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
   2441             alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
   2442                 @Override
   2443                 public void onAnimationUpdate(ValueAnimator animation) {
   2444                     float t = (Float) animation.getAnimatedValue();
   2445                     dispatchOnLauncherTransitionStep(fromView, t);
   2446                     dispatchOnLauncherTransitionStep(toView, t);
   2447                 }
   2448             });
   2449 
   2450             // toView should appear right at the end of the workspace shrink
   2451             // animation
   2452             mStateAnimation = new AnimatorSet();
   2453             mStateAnimation.play(scaleAnim).after(startDelay);
   2454             mStateAnimation.play(alphaAnim).after(startDelay);
   2455 
   2456             mStateAnimation.addListener(new AnimatorListenerAdapter() {
   2457                 boolean animationCancelled = false;
   2458 
   2459                 @Override
   2460                 public void onAnimationStart(Animator animation) {
   2461                     updateWallpaperVisibility(true);
   2462                     // Prepare the position
   2463                     toView.setTranslationX(0.0f);
   2464                     toView.setTranslationY(0.0f);
   2465                     toView.setVisibility(View.VISIBLE);
   2466                     toView.bringToFront();
   2467                 }
   2468                 @Override
   2469                 public void onAnimationEnd(Animator animation) {
   2470                     dispatchOnLauncherTransitionEnd(fromView, animated, false);
   2471                     dispatchOnLauncherTransitionEnd(toView, animated, false);
   2472 
   2473                     if (!springLoaded && !LauncherApplication.isScreenLarge()) {
   2474                         // Hide the workspace scrollbar
   2475                         mWorkspace.hideScrollingIndicator(true);
   2476                         hideDockDivider();
   2477                     }
   2478                     if (!animationCancelled) {
   2479                         updateWallpaperVisibility(false);
   2480                     }
   2481 
   2482                     // Hide the search bar
   2483                     mSearchDropTargetBar.hideSearchBar(false);
   2484                 }
   2485 
   2486                 @Override
   2487                 public void onAnimationCancel(Animator animation) {
   2488                     animationCancelled = true;
   2489                 }
   2490             });
   2491 
   2492             if (workspaceAnim != null) {
   2493                 mStateAnimation.play(workspaceAnim);
   2494             }
   2495 
   2496             boolean delayAnim = false;
   2497             final ViewTreeObserver observer;
   2498 
   2499             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
   2500             dispatchOnLauncherTransitionPrepare(toView, animated, false);
   2501 
   2502             // If any of the objects being animated haven't been measured/laid out
   2503             // yet, delay the animation until we get a layout pass
   2504             if ((((LauncherTransitionable) toView).getContent().getMeasuredWidth() == 0) ||
   2505                     (mWorkspace.getMeasuredWidth() == 0) ||
   2506                     (toView.getMeasuredWidth() == 0)) {
   2507                 observer = mWorkspace.getViewTreeObserver();
   2508                 delayAnim = true;
   2509             } else {
   2510                 observer = null;
   2511             }
   2512 
   2513             final AnimatorSet stateAnimation = mStateAnimation;
   2514             final Runnable startAnimRunnable = new Runnable() {
   2515                 public void run() {
   2516                     // Check that mStateAnimation hasn't changed while
   2517                     // we waited for a layout/draw pass
   2518                     if (mStateAnimation != stateAnimation)
   2519                         return;
   2520                     setPivotsForZoom(toView, scale);
   2521                     dispatchOnLauncherTransitionStart(fromView, animated, false);
   2522                     dispatchOnLauncherTransitionStart(toView, animated, false);
   2523                     toView.post(new Runnable() {
   2524                         public void run() {
   2525                             // Check that mStateAnimation hasn't changed while
   2526                             // we waited for a layout/draw pass
   2527                             if (mStateAnimation != stateAnimation)
   2528                                 return;
   2529                             mStateAnimation.start();
   2530                         }
   2531                     });
   2532                 }
   2533             };
   2534             if (delayAnim) {
   2535                 final OnGlobalLayoutListener delayedStart = new OnGlobalLayoutListener() {
   2536                     public void onGlobalLayout() {
   2537                         toView.post(startAnimRunnable);
   2538                         observer.removeOnGlobalLayoutListener(this);
   2539                     }
   2540                 };
   2541                 observer.addOnGlobalLayoutListener(delayedStart);
   2542             } else {
   2543                 startAnimRunnable.run();
   2544             }
   2545         } else {
   2546             toView.setTranslationX(0.0f);
   2547             toView.setTranslationY(0.0f);
   2548             toView.setScaleX(1.0f);
   2549             toView.setScaleY(1.0f);
   2550             toView.setVisibility(View.VISIBLE);
   2551             toView.bringToFront();
   2552 
   2553             if (!springLoaded && !LauncherApplication.isScreenLarge()) {
   2554                 // Hide the workspace scrollbar
   2555                 mWorkspace.hideScrollingIndicator(true);
   2556                 hideDockDivider();
   2557 
   2558                 // Hide the search bar
   2559                 mSearchDropTargetBar.hideSearchBar(false);
   2560             }
   2561             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
   2562             dispatchOnLauncherTransitionStart(fromView, animated, false);
   2563             dispatchOnLauncherTransitionEnd(fromView, animated, false);
   2564             dispatchOnLauncherTransitionPrepare(toView, animated, false);
   2565             dispatchOnLauncherTransitionStart(toView, animated, false);
   2566             dispatchOnLauncherTransitionEnd(toView, animated, false);
   2567             updateWallpaperVisibility(false);
   2568         }
   2569     }
   2570 
   2571     /**
   2572      * Zoom the camera back into the workspace, hiding 'fromView'.
   2573      * This is the opposite of showAppsCustomizeHelper.
   2574      * @param animated If true, the transition will be animated.
   2575      */
   2576     private void hideAppsCustomizeHelper(State toState, final boolean animated,
   2577             final boolean springLoaded, final Runnable onCompleteRunnable) {
   2578 
   2579         if (mStateAnimation != null) {
   2580             mStateAnimation.cancel();
   2581             mStateAnimation = null;
   2582         }
   2583         Resources res = getResources();
   2584 
   2585         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
   2586         final int fadeOutDuration =
   2587                 res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
   2588         final float scaleFactor = (float)
   2589                 res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
   2590         final View fromView = mAppsCustomizeTabHost;
   2591         final View toView = mWorkspace;
   2592         Animator workspaceAnim = null;
   2593 
   2594         if (toState == State.WORKSPACE) {
   2595             int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
   2596             workspaceAnim = mWorkspace.getChangeStateAnimation(
   2597                     Workspace.State.NORMAL, animated, stagger);
   2598         } else if (toState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
   2599             workspaceAnim = mWorkspace.getChangeStateAnimation(
   2600                     Workspace.State.SPRING_LOADED, animated);
   2601         }
   2602 
   2603         setPivotsForZoom(fromView, scaleFactor);
   2604         updateWallpaperVisibility(true);
   2605         showHotseat(animated);
   2606         if (animated) {
   2607             final LauncherViewPropertyAnimator scaleAnim =
   2608                     new LauncherViewPropertyAnimator(fromView);
   2609             scaleAnim.
   2610                 scaleX(scaleFactor).scaleY(scaleFactor).
   2611                 setDuration(duration).
   2612                 setInterpolator(new Workspace.ZoomInInterpolator());
   2613 
   2614             final ObjectAnimator alphaAnim = ObjectAnimator
   2615                 .ofFloat(fromView, "alpha", 1f, 0f)
   2616                 .setDuration(fadeOutDuration);
   2617             alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
   2618             alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
   2619                 @Override
   2620                 public void onAnimationUpdate(ValueAnimator animation) {
   2621                     float t = 1f - (Float) animation.getAnimatedValue();
   2622                     dispatchOnLauncherTransitionStep(fromView, t);
   2623                     dispatchOnLauncherTransitionStep(toView, t);
   2624                 }
   2625             });
   2626 
   2627             mStateAnimation = new AnimatorSet();
   2628 
   2629             dispatchOnLauncherTransitionPrepare(fromView, animated, true);
   2630             dispatchOnLauncherTransitionPrepare(toView, animated, true);
   2631 
   2632             mStateAnimation.addListener(new AnimatorListenerAdapter() {
   2633                 @Override
   2634                 public void onAnimationEnd(Animator animation) {
   2635                     updateWallpaperVisibility(true);
   2636                     fromView.setVisibility(View.GONE);
   2637                     dispatchOnLauncherTransitionEnd(fromView, animated, true);
   2638                     dispatchOnLauncherTransitionEnd(toView, animated, true);
   2639                     if (mWorkspace != null) {
   2640                         mWorkspace.hideScrollingIndicator(false);
   2641                     }
   2642                     if (onCompleteRunnable != null) {
   2643                         onCompleteRunnable.run();
   2644                     }
   2645                 }
   2646             });
   2647 
   2648             mStateAnimation.playTogether(scaleAnim, alphaAnim);
   2649             if (workspaceAnim != null) {
   2650                 mStateAnimation.play(workspaceAnim);
   2651             }
   2652             dispatchOnLauncherTransitionStart(fromView, animated, true);
   2653             dispatchOnLauncherTransitionStart(toView, animated, true);
   2654             final Animator stateAnimation = mStateAnimation;
   2655             mWorkspace.post(new Runnable() {
   2656                 public void run() {
   2657                     if (stateAnimation != mStateAnimation)
   2658                         return;
   2659                     mStateAnimation.start();
   2660                 }
   2661             });
   2662         } else {
   2663             fromView.setVisibility(View.GONE);
   2664             dispatchOnLauncherTransitionPrepare(fromView, animated, true);
   2665             dispatchOnLauncherTransitionStart(fromView, animated, true);
   2666             dispatchOnLauncherTransitionEnd(fromView, animated, true);
   2667             dispatchOnLauncherTransitionPrepare(toView, animated, true);
   2668             dispatchOnLauncherTransitionStart(toView, animated, true);
   2669             dispatchOnLauncherTransitionEnd(toView, animated, true);
   2670             mWorkspace.hideScrollingIndicator(false);
   2671         }
   2672     }
   2673 
   2674     @Override
   2675     public void onTrimMemory(int level) {
   2676         super.onTrimMemory(level);
   2677         if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
   2678             mAppsCustomizeTabHost.onTrimMemory();
   2679         }
   2680     }
   2681 
   2682     @Override
   2683     public void onWindowFocusChanged(boolean hasFocus) {
   2684         if (!hasFocus) {
   2685             // When another window occludes launcher (like the notification shade, or recents),
   2686             // ensure that we enable the wallpaper flag so that transitions are done correctly.
   2687             updateWallpaperVisibility(true);
   2688         } else {
   2689             // When launcher has focus again, disable the wallpaper if we are in AllApps
   2690             mWorkspace.postDelayed(new Runnable() {
   2691                 @Override
   2692                 public void run() {
   2693                     disableWallpaperIfInAllApps();
   2694                 }
   2695             }, 500);
   2696         }
   2697     }
   2698 
   2699     void showWorkspace(boolean animated) {
   2700         showWorkspace(animated, null);
   2701     }
   2702 
   2703     void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
   2704         if (mState != State.WORKSPACE) {
   2705             boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED);
   2706             mWorkspace.setVisibility(View.VISIBLE);
   2707             hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);
   2708 
   2709             // Show the search bar (only animate if we were showing the drop target bar in spring
   2710             // loaded mode)
   2711             mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode);
   2712 
   2713             // We only need to animate in the dock divider if we're going from spring loaded mode
   2714             showDockDivider(animated && wasInSpringLoadedMode);
   2715 
   2716             // Set focus to the AppsCustomize button
   2717             if (mAllAppsButton != null) {
   2718                 mAllAppsButton.requestFocus();
   2719             }
   2720         }
   2721 
   2722         mWorkspace.flashScrollingIndicator(animated);
   2723 
   2724         // Change the state *after* we've called all the transition code
   2725         mState = State.WORKSPACE;
   2726 
   2727         // Resume the auto-advance of widgets
   2728         mUserPresent = true;
   2729         updateRunning();
   2730 
   2731         // send an accessibility event to announce the context change
   2732         getWindow().getDecorView().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
   2733     }
   2734 
   2735     void showAllApps(boolean animated) {
   2736         if (mState != State.WORKSPACE) return;
   2737 
   2738         showAppsCustomizeHelper(animated, false);
   2739         mAppsCustomizeTabHost.requestFocus();
   2740 
   2741         // Change the state *after* we've called all the transition code
   2742         mState = State.APPS_CUSTOMIZE;
   2743 
   2744         // Pause the auto-advance of widgets until we are out of AllApps
   2745         mUserPresent = false;
   2746         updateRunning();
   2747         closeFolder();
   2748 
   2749         // Send an accessibility event to announce the context change
   2750         getWindow().getDecorView().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
   2751     }
   2752 
   2753     void enterSpringLoadedDragMode() {
   2754         if (isAllAppsVisible()) {
   2755             hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true, null);
   2756             hideDockDivider();
   2757             mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
   2758         }
   2759     }
   2760 
   2761     void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay,
   2762             final Runnable onCompleteRunnable) {
   2763         if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
   2764 
   2765         mHandler.postDelayed(new Runnable() {
   2766             @Override
   2767             public void run() {
   2768                 if (successfulDrop) {
   2769                     // Before we show workspace, hide all apps again because
   2770                     // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
   2771                     // clean up our state transition functions
   2772                     mAppsCustomizeTabHost.setVisibility(View.GONE);
   2773                     showWorkspace(true, onCompleteRunnable);
   2774                 } else {
   2775                     exitSpringLoadedDragMode();
   2776                 }
   2777             }
   2778         }, (extendedDelay ?
   2779                 EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT :
   2780                 EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT));
   2781     }
   2782 
   2783     void exitSpringLoadedDragMode() {
   2784         if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
   2785             final boolean animated = true;
   2786             final boolean springLoaded = true;
   2787             showAppsCustomizeHelper(animated, springLoaded);
   2788             mState = State.APPS_CUSTOMIZE;
   2789         }
   2790         // Otherwise, we are not in spring loaded mode, so don't do anything.
   2791     }
   2792 
   2793     void hideDockDivider() {
   2794         if (mQsbDivider != null && mDockDivider != null) {
   2795             mQsbDivider.setVisibility(View.INVISIBLE);
   2796             mDockDivider.setVisibility(View.INVISIBLE);
   2797         }
   2798     }
   2799 
   2800     void showDockDivider(boolean animated) {
   2801         if (mQsbDivider != null && mDockDivider != null) {
   2802             mQsbDivider.setVisibility(View.VISIBLE);
   2803             mDockDivider.setVisibility(View.VISIBLE);
   2804             if (mDividerAnimator != null) {
   2805                 mDividerAnimator.cancel();
   2806                 mQsbDivider.setAlpha(1f);
   2807                 mDockDivider.setAlpha(1f);
   2808                 mDividerAnimator = null;
   2809             }
   2810             if (animated) {
   2811                 mDividerAnimator = new AnimatorSet();
   2812                 mDividerAnimator.playTogether(ObjectAnimator.ofFloat(mQsbDivider, "alpha", 1f),
   2813                         ObjectAnimator.ofFloat(mDockDivider, "alpha", 1f));
   2814                 mDividerAnimator.setDuration(mSearchDropTargetBar.getTransitionInDuration());
   2815                 mDividerAnimator.start();
   2816             }
   2817         }
   2818     }
   2819 
   2820     void lockAllApps() {
   2821         // TODO
   2822     }
   2823 
   2824     void unlockAllApps() {
   2825         // TODO
   2826     }
   2827 
   2828     /**
   2829      * Shows the hotseat area.
   2830      */
   2831     void showHotseat(boolean animated) {
   2832         if (!LauncherApplication.isScreenLarge()) {
   2833             if (animated) {
   2834                 if (mHotseat.getAlpha() != 1f) {
   2835                     int duration = mSearchDropTargetBar.getTransitionInDuration();
   2836                     mHotseat.animate().alpha(1f).setDuration(duration);
   2837                 }
   2838             } else {
   2839                 mHotseat.setAlpha(1f);
   2840             }
   2841         }
   2842     }
   2843 
   2844     /**
   2845      * Hides the hotseat area.
   2846      */
   2847     void hideHotseat(boolean animated) {
   2848         if (!LauncherApplication.isScreenLarge()) {
   2849             if (animated) {
   2850                 if (mHotseat.getAlpha() != 0f) {
   2851                     int duration = mSearchDropTargetBar.getTransitionOutDuration();
   2852                     mHotseat.animate().alpha(0f).setDuration(duration);
   2853                 }
   2854             } else {
   2855                 mHotseat.setAlpha(0f);
   2856             }
   2857         }
   2858     }
   2859 
   2860     /**
   2861      * Add an item from all apps or customize onto the given workspace screen.
   2862      * If layout is null, add to the current screen.
   2863      */
   2864     void addExternalItemToScreen(ItemInfo itemInfo, final CellLayout layout) {
   2865         if (!mWorkspace.addExternalItemToScreen(itemInfo, layout)) {
   2866             showOutOfSpaceMessage(isHotseatLayout(layout));
   2867         }
   2868     }
   2869 
   2870     /** Maps the current orientation to an index for referencing orientation correct global icons */
   2871     private int getCurrentOrientationIndexForGlobalIcons() {
   2872         // default - 0, landscape - 1
   2873         switch (getResources().getConfiguration().orientation) {
   2874         case Configuration.ORIENTATION_LANDSCAPE:
   2875             return 1;
   2876         default:
   2877             return 0;
   2878         }
   2879     }
   2880 
   2881     private Drawable getExternalPackageToolbarIcon(ComponentName activityName, String resourceName) {
   2882         try {
   2883             PackageManager packageManager = getPackageManager();
   2884             // Look for the toolbar icon specified in the activity meta-data
   2885             Bundle metaData = packageManager.getActivityInfo(
   2886                     activityName, PackageManager.GET_META_DATA).metaData;
   2887             if (metaData != null) {
   2888                 int iconResId = metaData.getInt(resourceName);
   2889                 if (iconResId != 0) {
   2890                     Resources res = packageManager.getResourcesForActivity(activityName);
   2891                     return res.getDrawable(iconResId);
   2892                 }
   2893             }
   2894         } catch (NameNotFoundException e) {
   2895             // This can happen if the activity defines an invalid drawable
   2896             Log.w(TAG, "Failed to load toolbar icon; " + activityName.flattenToShortString() +
   2897                     " not found", e);
   2898         } catch (Resources.NotFoundException nfe) {
   2899             // This can happen if the activity defines an invalid drawable
   2900             Log.w(TAG, "Failed to load toolbar icon from " + activityName.flattenToShortString(),
   2901                     nfe);
   2902         }
   2903         return null;
   2904     }
   2905 
   2906     // if successful in getting icon, return it; otherwise, set button to use default drawable
   2907     private Drawable.ConstantState updateTextButtonWithIconFromExternalActivity(
   2908             int buttonId, ComponentName activityName, int fallbackDrawableId,
   2909             String toolbarResourceName) {
   2910         Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName);
   2911         Resources r = getResources();
   2912         int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
   2913         int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
   2914 
   2915         TextView button = (TextView) findViewById(buttonId);
   2916         // If we were unable to find the icon via the meta-data, use a generic one
   2917         if (toolbarIcon == null) {
   2918             toolbarIcon = r.getDrawable(fallbackDrawableId);
   2919             toolbarIcon.setBounds(0, 0, w, h);
   2920             if (button != null) {
   2921                 button.setCompoundDrawables(toolbarIcon, null, null, null);
   2922             }
   2923             return null;
   2924         } else {
   2925             toolbarIcon.setBounds(0, 0, w, h);
   2926             if (button != null) {
   2927                 button.setCompoundDrawables(toolbarIcon, null, null, null);
   2928             }
   2929             return toolbarIcon.getConstantState();
   2930         }
   2931     }
   2932 
   2933     // if successful in getting icon, return it; otherwise, set button to use default drawable
   2934     private Drawable.ConstantState updateButtonWithIconFromExternalActivity(
   2935             int buttonId, ComponentName activityName, int fallbackDrawableId,
   2936             String toolbarResourceName) {
   2937         ImageView button = (ImageView) findViewById(buttonId);
   2938         Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName);
   2939 
   2940         if (button != null) {
   2941             // If we were unable to find the icon via the meta-data, use a
   2942             // generic one
   2943             if (toolbarIcon == null) {
   2944                 button.setImageResource(fallbackDrawableId);
   2945             } else {
   2946                 button.setImageDrawable(toolbarIcon);
   2947             }
   2948         }
   2949 
   2950         return toolbarIcon != null ? toolbarIcon.getConstantState() : null;
   2951 
   2952     }
   2953 
   2954     private void updateTextButtonWithDrawable(int buttonId, Drawable d) {
   2955         TextView button = (TextView) findViewById(buttonId);
   2956         button.setCompoundDrawables(d, null, null, null);
   2957     }
   2958 
   2959     private void updateButtonWithDrawable(int buttonId, Drawable.ConstantState d) {
   2960         ImageView button = (ImageView) findViewById(buttonId);
   2961         button.setImageDrawable(d.newDrawable(getResources()));
   2962     }
   2963 
   2964     private void invalidatePressedFocusedStates(View container, View button) {
   2965         if (container instanceof HolographicLinearLayout) {
   2966             HolographicLinearLayout layout = (HolographicLinearLayout) container;
   2967             layout.invalidatePressedFocusedStates();
   2968         } else if (button instanceof HolographicImageView) {
   2969             HolographicImageView view = (HolographicImageView) button;
   2970             view.invalidatePressedFocusedStates();
   2971         }
   2972     }
   2973 
   2974     private boolean updateGlobalSearchIcon() {
   2975         final View searchButtonContainer = findViewById(R.id.search_button_container);
   2976         final ImageView searchButton = (ImageView) findViewById(R.id.search_button);
   2977         final View searchDivider = findViewById(R.id.search_divider);
   2978         final View voiceButtonContainer = findViewById(R.id.voice_button_container);
   2979         final View voiceButton = findViewById(R.id.voice_button);
   2980         final View voiceButtonProxy = findViewById(R.id.voice_button_proxy);
   2981 
   2982         final SearchManager searchManager =
   2983                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   2984         ComponentName activityName = searchManager.getGlobalSearchActivity();
   2985         if (activityName != null) {
   2986             int coi = getCurrentOrientationIndexForGlobalIcons();
   2987             sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   2988                     R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo,
   2989                     TOOLBAR_SEARCH_ICON_METADATA_NAME);
   2990             if (sGlobalSearchIcon[coi] == null) {
   2991                 sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   2992                         R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo,
   2993                         TOOLBAR_ICON_METADATA_NAME);
   2994             }
   2995 
   2996             if (searchDivider != null) searchDivider.setVisibility(View.VISIBLE);
   2997             if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.VISIBLE);
   2998             searchButton.setVisibility(View.VISIBLE);
   2999             invalidatePressedFocusedStates(searchButtonContainer, searchButton);
   3000             return true;
   3001         } else {
   3002             // We disable both search and voice search when there is no global search provider
   3003             if (searchDivider != null) searchDivider.setVisibility(View.GONE);
   3004             if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.GONE);
   3005             if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE);
   3006             searchButton.setVisibility(View.GONE);
   3007             voiceButton.setVisibility(View.GONE);
   3008             if (voiceButtonProxy != null) {
   3009                 voiceButtonProxy.setVisibility(View.GONE);
   3010             }
   3011             return false;
   3012         }
   3013     }
   3014 
   3015     private void updateGlobalSearchIcon(Drawable.ConstantState d) {
   3016         final View searchButtonContainer = findViewById(R.id.search_button_container);
   3017         final View searchButton = (ImageView) findViewById(R.id.search_button);
   3018         updateButtonWithDrawable(R.id.search_button, d);
   3019         invalidatePressedFocusedStates(searchButtonContainer, searchButton);
   3020     }
   3021 
   3022     private boolean updateVoiceSearchIcon(boolean searchVisible) {
   3023         final View searchDivider = findViewById(R.id.search_divider);
   3024         final View voiceButtonContainer = findViewById(R.id.voice_button_container);
   3025         final View voiceButton = findViewById(R.id.voice_button);
   3026         final View voiceButtonProxy = findViewById(R.id.voice_button_proxy);
   3027 
   3028         // We only show/update the voice search icon if the search icon is enabled as well
   3029         final SearchManager searchManager =
   3030                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   3031         ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
   3032 
   3033         ComponentName activityName = null;
   3034         if (globalSearchActivity != null) {
   3035             // Check if the global search activity handles voice search
   3036             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   3037             intent.setPackage(globalSearchActivity.getPackageName());
   3038             activityName = intent.resolveActivity(getPackageManager());
   3039         }
   3040 
   3041         if (activityName == null) {
   3042             // Fallback: check if an activity other than the global search activity
   3043             // resolves this
   3044             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   3045             activityName = intent.resolveActivity(getPackageManager());
   3046         }
   3047         if (searchVisible && activityName != null) {
   3048             int coi = getCurrentOrientationIndexForGlobalIcons();
   3049             sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   3050                     R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo,
   3051                     TOOLBAR_VOICE_SEARCH_ICON_METADATA_NAME);
   3052             if (sVoiceSearchIcon[coi] == null) {
   3053                 sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   3054                         R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo,
   3055                         TOOLBAR_ICON_METADATA_NAME);
   3056             }
   3057             if (searchDivider != null) searchDivider.setVisibility(View.VISIBLE);
   3058             if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.VISIBLE);
   3059             voiceButton.setVisibility(View.VISIBLE);
   3060             if (voiceButtonProxy != null) {
   3061                 voiceButtonProxy.setVisibility(View.VISIBLE);
   3062             }
   3063             invalidatePressedFocusedStates(voiceButtonContainer, voiceButton);
   3064             return true;
   3065         } else {
   3066             if (searchDivider != null) searchDivider.setVisibility(View.GONE);
   3067             if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE);
   3068             voiceButton.setVisibility(View.GONE);
   3069             if (voiceButtonProxy != null) {
   3070                 voiceButtonProxy.setVisibility(View.GONE);
   3071             }
   3072             return false;
   3073         }
   3074     }
   3075 
   3076     private void updateVoiceSearchIcon(Drawable.ConstantState d) {
   3077         final View voiceButtonContainer = findViewById(R.id.voice_button_container);
   3078         final View voiceButton = findViewById(R.id.voice_button);
   3079         updateButtonWithDrawable(R.id.voice_button, d);
   3080         invalidatePressedFocusedStates(voiceButtonContainer, voiceButton);
   3081     }
   3082 
   3083     /**
   3084      * Sets the app market icon
   3085      */
   3086     private void updateAppMarketIcon() {
   3087         final View marketButton = findViewById(R.id.market_button);
   3088         Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
   3089         // Find the app market activity by resolving an intent.
   3090         // (If multiple app markets are installed, it will return the ResolverActivity.)
   3091         ComponentName activityName = intent.resolveActivity(getPackageManager());
   3092         if (activityName != null) {
   3093             int coi = getCurrentOrientationIndexForGlobalIcons();
   3094             mAppMarketIntent = intent;
   3095             sAppMarketIcon[coi] = updateTextButtonWithIconFromExternalActivity(
   3096                     R.id.market_button, activityName, R.drawable.ic_launcher_market_holo,
   3097                     TOOLBAR_ICON_METADATA_NAME);
   3098             marketButton.setVisibility(View.VISIBLE);
   3099         } else {
   3100             // We should hide and disable the view so that we don't try and restore the visibility
   3101             // of it when we swap between drag & normal states from IconDropTarget subclasses.
   3102             marketButton.setVisibility(View.GONE);
   3103             marketButton.setEnabled(false);
   3104         }
   3105     }
   3106 
   3107     private void updateAppMarketIcon(Drawable.ConstantState d) {
   3108         // Ensure that the new drawable we are creating has the approprate toolbar icon bounds
   3109         Resources r = getResources();
   3110         Drawable marketIconDrawable = d.newDrawable(r);
   3111         int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
   3112         int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
   3113         marketIconDrawable.setBounds(0, 0, w, h);
   3114 
   3115         updateTextButtonWithDrawable(R.id.market_button, marketIconDrawable);
   3116     }
   3117 
   3118     @Override
   3119     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
   3120         boolean result = super.dispatchPopulateAccessibilityEvent(event);
   3121         final List<CharSequence> text = event.getText();
   3122         text.clear();
   3123         text.add(getString(R.string.home));
   3124         return result;
   3125     }
   3126 
   3127     /**
   3128      * Receives notifications when system dialogs are to be closed.
   3129      */
   3130     private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
   3131         @Override
   3132         public void onReceive(Context context, Intent intent) {
   3133             closeSystemDialogs();
   3134         }
   3135     }
   3136 
   3137     /**
   3138      * Receives notifications whenever the appwidgets are reset.
   3139      */
   3140     private class AppWidgetResetObserver extends ContentObserver {
   3141         public AppWidgetResetObserver() {
   3142             super(new Handler());
   3143         }
   3144 
   3145         @Override
   3146         public void onChange(boolean selfChange) {
   3147             onAppWidgetReset();
   3148         }
   3149     }
   3150 
   3151     /**
   3152      * If the activity is currently paused, signal that we need to re-run the loader
   3153      * in onResume.
   3154      *
   3155      * This needs to be called from incoming places where resources might have been loaded
   3156      * while we are paused.  That is becaues the Configuration might be wrong
   3157      * when we're not running, and if it comes back to what it was when we
   3158      * were paused, we are not restarted.
   3159      *
   3160      * Implementation of the method from LauncherModel.Callbacks.
   3161      *
   3162      * @return true if we are currently paused.  The caller might be able to
   3163      * skip some work in that case since we will come back again.
   3164      */
   3165     public boolean setLoadOnResume() {
   3166         if (mPaused) {
   3167             Log.i(TAG, "setLoadOnResume");
   3168             mOnResumeNeedsLoad = true;
   3169             return true;
   3170         } else {
   3171             return false;
   3172         }
   3173     }
   3174 
   3175     /**
   3176      * Implementation of the method from LauncherModel.Callbacks.
   3177      */
   3178     public int getCurrentWorkspaceScreen() {
   3179         if (mWorkspace != null) {
   3180             return mWorkspace.getCurrentPage();
   3181         } else {
   3182             return SCREEN_COUNT / 2;
   3183         }
   3184     }
   3185 
   3186     /**
   3187      * Refreshes the shortcuts shown on the workspace.
   3188      *
   3189      * Implementation of the method from LauncherModel.Callbacks.
   3190      */
   3191     public void startBinding() {
   3192         final Workspace workspace = mWorkspace;
   3193 
   3194         mNewShortcutAnimatePage = -1;
   3195         mNewShortcutAnimateViews.clear();
   3196         mWorkspace.clearDropTargets();
   3197         int count = workspace.getChildCount();
   3198         for (int i = 0; i < count; i++) {
   3199             // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
   3200             final CellLayout layoutParent = (CellLayout) workspace.getChildAt(i);
   3201             layoutParent.removeAllViewsInLayout();
   3202         }
   3203         mWidgetsToAdvance.clear();
   3204         if (mHotseat != null) {
   3205             mHotseat.resetLayout();
   3206         }
   3207     }
   3208 
   3209     /**
   3210      * Bind the items start-end from the list.
   3211      *
   3212      * Implementation of the method from LauncherModel.Callbacks.
   3213      */
   3214     public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
   3215         setLoadOnResume();
   3216 
   3217         // Get the list of added shortcuts and intersect them with the set of shortcuts here
   3218         Set<String> newApps = new HashSet<String>();
   3219         newApps = mSharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps);
   3220 
   3221         Workspace workspace = mWorkspace;
   3222         for (int i = start; i < end; i++) {
   3223             final ItemInfo item = shortcuts.get(i);
   3224 
   3225             // Short circuit if we are loading dock items for a configuration which has no dock
   3226             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
   3227                     mHotseat == null) {
   3228                 continue;
   3229             }
   3230 
   3231             switch (item.itemType) {
   3232                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   3233                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   3234                     ShortcutInfo info = (ShortcutInfo) item;
   3235                     String uri = info.intent.toUri(0).toString();
   3236                     View shortcut = createShortcut(info);
   3237                     workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
   3238                             item.cellY, 1, 1, false);
   3239                     boolean animateIconUp = false;
   3240                     synchronized (newApps) {
   3241                         if (newApps.contains(uri)) {
   3242                             animateIconUp = newApps.remove(uri);
   3243                         }
   3244                     }
   3245                     if (animateIconUp) {
   3246                         // Prepare the view to be animated up
   3247                         shortcut.setAlpha(0f);
   3248                         shortcut.setScaleX(0f);
   3249                         shortcut.setScaleY(0f);
   3250                         mNewShortcutAnimatePage = item.screen;
   3251                         if (!mNewShortcutAnimateViews.contains(shortcut)) {
   3252                             mNewShortcutAnimateViews.add(shortcut);
   3253                         }
   3254                     }
   3255                     break;
   3256                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
   3257                     FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
   3258                             (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
   3259                             (FolderInfo) item, mIconCache);
   3260                     workspace.addInScreen(newFolder, item.container, item.screen, item.cellX,
   3261                             item.cellY, 1, 1, false);
   3262                     break;
   3263             }
   3264         }
   3265 
   3266         workspace.requestLayout();
   3267     }
   3268 
   3269     /**
   3270      * Implementation of the method from LauncherModel.Callbacks.
   3271      */
   3272     public void bindFolders(HashMap<Long, FolderInfo> folders) {
   3273         setLoadOnResume();
   3274         sFolders.clear();
   3275         sFolders.putAll(folders);
   3276     }
   3277 
   3278     /**
   3279      * Add the views for a widget to the workspace.
   3280      *
   3281      * Implementation of the method from LauncherModel.Callbacks.
   3282      */
   3283     public void bindAppWidget(LauncherAppWidgetInfo item) {
   3284         setLoadOnResume();
   3285 
   3286         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
   3287         if (DEBUG_WIDGETS) {
   3288             Log.d(TAG, "bindAppWidget: " + item);
   3289         }
   3290         final Workspace workspace = mWorkspace;
   3291 
   3292         final int appWidgetId = item.appWidgetId;
   3293         final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   3294         if (DEBUG_WIDGETS) {
   3295             Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
   3296         }
   3297 
   3298         item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   3299 
   3300         item.hostView.setTag(item);
   3301         item.onBindAppWidget(this);
   3302 
   3303         workspace.addInScreen(item.hostView, item.container, item.screen, item.cellX,
   3304                 item.cellY, item.spanX, item.spanY, false);
   3305         addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
   3306 
   3307         workspace.requestLayout();
   3308 
   3309         if (DEBUG_WIDGETS) {
   3310             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
   3311                     + (SystemClock.uptimeMillis()-start) + "ms");
   3312         }
   3313     }
   3314 
   3315     public void onPageBoundSynchronously(int page) {
   3316         mSynchronouslyBoundPages.add(page);
   3317     }
   3318 
   3319     /**
   3320      * Callback saying that there aren't any more items to bind.
   3321      *
   3322      * Implementation of the method from LauncherModel.Callbacks.
   3323      */
   3324     public void finishBindingItems() {
   3325         setLoadOnResume();
   3326 
   3327         if (mSavedState != null) {
   3328             if (!mWorkspace.hasFocus()) {
   3329                 mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
   3330             }
   3331             mSavedState = null;
   3332         }
   3333 
   3334         mWorkspace.restoreInstanceStateForRemainingPages();
   3335 
   3336         // If we received the result of any pending adds while the loader was running (e.g. the
   3337         // widget configuration forced an orientation change), process them now.
   3338         for (int i = 0; i < sPendingAddList.size(); i++) {
   3339             completeAdd(sPendingAddList.get(i));
   3340         }
   3341         sPendingAddList.clear();
   3342 
   3343         // Update the market app icon as necessary (the other icons will be managed in response to
   3344         // package changes in bindSearchablesChanged()
   3345         updateAppMarketIcon();
   3346 
   3347         // Animate up any icons as necessary
   3348         if (mVisible || mWorkspaceLoading) {
   3349             Runnable newAppsRunnable = new Runnable() {
   3350                 @Override
   3351                 public void run() {
   3352                     runNewAppsAnimation(false);
   3353                 }
   3354             };
   3355 
   3356             boolean willSnapPage = mNewShortcutAnimatePage > -1 &&
   3357                     mNewShortcutAnimatePage != mWorkspace.getCurrentPage();
   3358             if (canRunNewAppsAnimation()) {
   3359                 // If the user has not interacted recently, then either snap to the new page to show
   3360                 // the new-apps animation or just run them if they are to appear on the current page
   3361                 if (willSnapPage) {
   3362                     mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable);
   3363                 } else {
   3364                     runNewAppsAnimation(false);
   3365                 }
   3366             } else {
   3367                 // If the user has interacted recently, then just add the items in place if they
   3368                 // are on another page (or just normally if they are added to the current page)
   3369                 runNewAppsAnimation(willSnapPage);
   3370             }
   3371         }
   3372 
   3373         mWorkspaceLoading = false;
   3374     }
   3375 
   3376     private boolean canRunNewAppsAnimation() {
   3377         long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
   3378         return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
   3379     }
   3380 
   3381     /**
   3382      * Runs a new animation that scales up icons that were added while Launcher was in the
   3383      * background.
   3384      *
   3385      * @param immediate whether to run the animation or show the results immediately
   3386      */
   3387     private void runNewAppsAnimation(boolean immediate) {
   3388         AnimatorSet anim = new AnimatorSet();
   3389         Collection<Animator> bounceAnims = new ArrayList<Animator>();
   3390 
   3391         // Order these new views spatially so that they animate in order
   3392         Collections.sort(mNewShortcutAnimateViews, new Comparator<View>() {
   3393             @Override
   3394             public int compare(View a, View b) {
   3395                 CellLayout.LayoutParams alp = (CellLayout.LayoutParams) a.getLayoutParams();
   3396                 CellLayout.LayoutParams blp = (CellLayout.LayoutParams) b.getLayoutParams();
   3397                 int cellCountX = LauncherModel.getCellCountX();
   3398                 return (alp.cellY * cellCountX + alp.cellX) - (blp.cellY * cellCountX + blp.cellX);
   3399             }
   3400         });
   3401 
   3402         // Animate each of the views in place (or show them immediately if requested)
   3403         if (immediate) {
   3404             for (View v : mNewShortcutAnimateViews) {
   3405                 v.setAlpha(1f);
   3406                 v.setScaleX(1f);
   3407                 v.setScaleY(1f);
   3408             }
   3409         } else {
   3410             for (int i = 0; i < mNewShortcutAnimateViews.size(); ++i) {
   3411                 View v = mNewShortcutAnimateViews.get(i);
   3412                 ValueAnimator bounceAnim = ObjectAnimator.ofPropertyValuesHolder(v,
   3413                         PropertyValuesHolder.ofFloat("alpha", 1f),
   3414                         PropertyValuesHolder.ofFloat("scaleX", 1f),
   3415                         PropertyValuesHolder.ofFloat("scaleY", 1f));
   3416                 bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
   3417                 bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
   3418                 bounceAnim.setInterpolator(new SmoothPagedView.OvershootInterpolator());
   3419                 bounceAnims.add(bounceAnim);
   3420             }
   3421             anim.playTogether(bounceAnims);
   3422             anim.addListener(new AnimatorListenerAdapter() {
   3423                 @Override
   3424                 public void onAnimationEnd(Animator animation) {
   3425                     mWorkspace.postDelayed(mBuildLayersRunnable, 500);
   3426                 }
   3427             });
   3428             anim.start();
   3429         }
   3430 
   3431         // Clean up
   3432         mNewShortcutAnimatePage = -1;
   3433         mNewShortcutAnimateViews.clear();
   3434         new Thread("clearNewAppsThread") {
   3435             public void run() {
   3436                 mSharedPrefs.edit()
   3437                             .putInt(InstallShortcutReceiver.NEW_APPS_PAGE_KEY, -1)
   3438                             .putStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, null)
   3439                             .commit();
   3440             }
   3441         }.start();
   3442     }
   3443 
   3444     @Override
   3445     public void bindSearchablesChanged() {
   3446         boolean searchVisible = updateGlobalSearchIcon();
   3447         boolean voiceVisible = updateVoiceSearchIcon(searchVisible);
   3448         mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
   3449     }
   3450 
   3451     /**
   3452      * Add the icons for all apps.
   3453      *
   3454      * Implementation of the method from LauncherModel.Callbacks.
   3455      */
   3456     public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {
   3457         Runnable setAllAppsRunnable = new Runnable() {
   3458             public void run() {
   3459                 if (mAppsCustomizeContent != null) {
   3460                     mAppsCustomizeContent.setApps(apps);
   3461                 }
   3462             }
   3463         };
   3464 
   3465         // Remove the progress bar entirely; we could also make it GONE
   3466         // but better to remove it since we know it's not going to be used
   3467         View progressBar = mAppsCustomizeTabHost.
   3468             findViewById(R.id.apps_customize_progress_bar);
   3469         if (progressBar != null) {
   3470             ((ViewGroup)progressBar.getParent()).removeView(progressBar);
   3471 
   3472             // We just post the call to setApps so the user sees the progress bar
   3473             // disappear-- otherwise, it just looks like the progress bar froze
   3474             // which doesn't look great
   3475             mAppsCustomizeTabHost.post(setAllAppsRunnable);
   3476         } else {
   3477             // If we did not initialize the spinner in onCreate, then we can directly set the
   3478             // list of applications without waiting for any progress bars views to be hidden.
   3479             setAllAppsRunnable.run();
   3480         }
   3481     }
   3482 
   3483     /**
   3484      * A package was installed.
   3485      *
   3486      * Implementation of the method from LauncherModel.Callbacks.
   3487      */
   3488     public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
   3489         setLoadOnResume();
   3490 
   3491         if (mAppsCustomizeContent != null) {
   3492             mAppsCustomizeContent.addApps(apps);
   3493         }
   3494     }
   3495 
   3496     /**
   3497      * A package was updated.
   3498      *
   3499      * Implementation of the method from LauncherModel.Callbacks.
   3500      */
   3501     public void bindAppsUpdated(ArrayList<ApplicationInfo> apps) {
   3502         setLoadOnResume();
   3503         if (mWorkspace != null) {
   3504             mWorkspace.updateShortcuts(apps);
   3505         }
   3506 
   3507         if (mAppsCustomizeContent != null) {
   3508             mAppsCustomizeContent.updateApps(apps);
   3509         }
   3510     }
   3511 
   3512     /**
   3513      * A package was uninstalled.
   3514      *
   3515      * Implementation of the method from LauncherModel.Callbacks.
   3516      */
   3517     public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent) {
   3518         if (permanent) {
   3519             mWorkspace.removeItems(apps);
   3520         }
   3521 
   3522         if (mAppsCustomizeContent != null) {
   3523             mAppsCustomizeContent.removeApps(apps);
   3524         }
   3525 
   3526         // Notify the drag controller
   3527         mDragController.onAppsRemoved(apps, this);
   3528     }
   3529 
   3530     /**
   3531      * A number of packages were updated.
   3532      */
   3533     public void bindPackagesUpdated() {
   3534         if (mAppsCustomizeContent != null) {
   3535             mAppsCustomizeContent.onPackagesUpdated(false);
   3536         }
   3537     }
   3538 
   3539     private int mapConfigurationOriActivityInfoOri(int configOri) {
   3540         final Display d = getWindowManager().getDefaultDisplay();
   3541         int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
   3542         switch (d.getRotation()) {
   3543         case Surface.ROTATION_0:
   3544         case Surface.ROTATION_180:
   3545             // We are currently in the same basic orientation as the natural orientation
   3546             naturalOri = configOri;
   3547             break;
   3548         case Surface.ROTATION_90:
   3549         case Surface.ROTATION_270:
   3550             // We are currently in the other basic orientation to the natural orientation
   3551             naturalOri = (configOri == Configuration.ORIENTATION_LANDSCAPE) ?
   3552                     Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
   3553             break;
   3554         }
   3555 
   3556         int[] oriMap = {
   3557                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
   3558                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
   3559                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
   3560                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
   3561         };
   3562         // Since the map starts at portrait, we need to offset if this device's natural orientation
   3563         // is landscape.
   3564         int indexOffset = 0;
   3565         if (naturalOri == Configuration.ORIENTATION_LANDSCAPE) {
   3566             indexOffset = 1;
   3567         }
   3568         return oriMap[(d.getRotation() + indexOffset) % 4];
   3569     }
   3570 
   3571     public boolean isRotationEnabled() {
   3572         boolean forceEnableRotation = "true".equalsIgnoreCase(SystemProperties.get(
   3573                 FORCE_ENABLE_ROTATION_PROPERTY, "false"));
   3574         boolean enableRotation = forceEnableRotation ||
   3575                 getResources().getBoolean(R.bool.allow_rotation);
   3576         return enableRotation;
   3577     }
   3578     public void lockScreenOrientation() {
   3579         if (isRotationEnabled()) {
   3580             setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
   3581                     .getConfiguration().orientation));
   3582         }
   3583     }
   3584     public void unlockScreenOrientation(boolean immediate) {
   3585         if (isRotationEnabled()) {
   3586             if (immediate) {
   3587                 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   3588             } else {
   3589                 mHandler.postDelayed(new Runnable() {
   3590                     public void run() {
   3591                         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   3592                     }
   3593                 }, mRestoreScreenOrientationDelay);
   3594             }
   3595         }
   3596     }
   3597 
   3598     /* Cling related */
   3599     private boolean isClingsEnabled() {
   3600         // disable clings when running in a test harness
   3601         if(ActivityManager.isRunningInTestHarness()) return false;
   3602 
   3603         return true;
   3604     }
   3605     private Cling initCling(int clingId, int[] positionData, boolean animate, int delay) {
   3606         Cling cling = (Cling) findViewById(clingId);
   3607         if (cling != null) {
   3608             cling.init(this, positionData);
   3609             cling.setVisibility(View.VISIBLE);
   3610             cling.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3611             cling.requestAccessibilityFocus();
   3612             if (animate) {
   3613                 cling.buildLayer();
   3614                 cling.setAlpha(0f);
   3615                 cling.animate()
   3616                     .alpha(1f)
   3617                     .setInterpolator(new AccelerateInterpolator())
   3618                     .setDuration(SHOW_CLING_DURATION)
   3619                     .setStartDelay(delay)
   3620                     .start();
   3621             } else {
   3622                 cling.setAlpha(1f);
   3623             }
   3624         }
   3625         return cling;
   3626     }
   3627     private void dismissCling(final Cling cling, final String flag, int duration) {
   3628         if (cling != null) {
   3629             ObjectAnimator anim = ObjectAnimator.ofFloat(cling, "alpha", 0f);
   3630             anim.setDuration(duration);
   3631             anim.addListener(new AnimatorListenerAdapter() {
   3632                 public void onAnimationEnd(Animator animation) {
   3633                     cling.setVisibility(View.GONE);
   3634                     cling.cleanup();
   3635                     // We should update the shared preferences on a background thread
   3636                     new Thread("dismissClingThread") {
   3637                         public void run() {
   3638                             SharedPreferences.Editor editor = mSharedPrefs.edit();
   3639                             editor.putBoolean(flag, true);
   3640                             editor.commit();
   3641                         }
   3642                     }.start();
   3643                 };
   3644             });
   3645             anim.start();
   3646         }
   3647     }
   3648     private void removeCling(int id) {
   3649         final View cling = findViewById(id);
   3650         if (cling != null) {
   3651             final ViewGroup parent = (ViewGroup) cling.getParent();
   3652             parent.post(new Runnable() {
   3653                 @Override
   3654                 public void run() {
   3655                     parent.removeView(cling);
   3656                 }
   3657             });
   3658         }
   3659     }
   3660 
   3661     private boolean skipCustomClingIfNoAccounts() {
   3662         Cling cling = (Cling) findViewById(R.id.workspace_cling);
   3663         boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
   3664         if (customCling) {
   3665             AccountManager am = AccountManager.get(this);
   3666             Account[] accounts = am.getAccountsByType("com.google");
   3667             return accounts.length == 0;
   3668         }
   3669         return false;
   3670     }
   3671 
   3672     public void showFirstRunWorkspaceCling() {
   3673         // Enable the clings only if they have not been dismissed before
   3674         if (isClingsEnabled() &&
   3675                 !mSharedPrefs.getBoolean(Cling.WORKSPACE_CLING_DISMISSED_KEY, false) &&
   3676                 !skipCustomClingIfNoAccounts() ) {
   3677             initCling(R.id.workspace_cling, null, false, 0);
   3678         } else {
   3679             removeCling(R.id.workspace_cling);
   3680         }
   3681     }
   3682     public void showFirstRunAllAppsCling(int[] position) {
   3683         // Enable the clings only if they have not been dismissed before
   3684         if (isClingsEnabled() &&
   3685                 !mSharedPrefs.getBoolean(Cling.ALLAPPS_CLING_DISMISSED_KEY, false)) {
   3686             initCling(R.id.all_apps_cling, position, true, 0);
   3687         } else {
   3688             removeCling(R.id.all_apps_cling);
   3689         }
   3690     }
   3691     public Cling showFirstRunFoldersCling() {
   3692         // Enable the clings only if they have not been dismissed before
   3693         if (isClingsEnabled() &&
   3694                 !mSharedPrefs.getBoolean(Cling.FOLDER_CLING_DISMISSED_KEY, false)) {
   3695             return initCling(R.id.folder_cling, null, true, 0);
   3696         } else {
   3697             removeCling(R.id.folder_cling);
   3698             return null;
   3699         }
   3700     }
   3701     public boolean isFolderClingVisible() {
   3702         Cling cling = (Cling) findViewById(R.id.folder_cling);
   3703         if (cling != null) {
   3704             return cling.getVisibility() == View.VISIBLE;
   3705         }
   3706         return false;
   3707     }
   3708     public void dismissWorkspaceCling(View v) {
   3709         Cling cling = (Cling) findViewById(R.id.workspace_cling);
   3710         dismissCling(cling, Cling.WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
   3711     }
   3712     public void dismissAllAppsCling(View v) {
   3713         Cling cling = (Cling) findViewById(R.id.all_apps_cling);
   3714         dismissCling(cling, Cling.ALLAPPS_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
   3715     }
   3716     public void dismissFolderCling(View v) {
   3717         Cling cling = (Cling) findViewById(R.id.folder_cling);
   3718         dismissCling(cling, Cling.FOLDER_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
   3719     }
   3720 
   3721     /**
   3722      * Prints out out state for debugging.
   3723      */
   3724     public void dumpState() {
   3725         Log.d(TAG, "BEGIN launcher2 dump state for launcher " + this);
   3726         Log.d(TAG, "mSavedState=" + mSavedState);
   3727         Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
   3728         Log.d(TAG, "mRestoring=" + mRestoring);
   3729         Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
   3730         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
   3731         Log.d(TAG, "sFolders.size=" + sFolders.size());
   3732         mModel.dumpState();
   3733 
   3734         if (mAppsCustomizeContent != null) {
   3735             mAppsCustomizeContent.dumpState();
   3736         }
   3737         Log.d(TAG, "END launcher2 dump state");
   3738     }
   3739 
   3740     @Override
   3741     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
   3742         super.dump(prefix, fd, writer, args);
   3743         writer.println(" ");
   3744         writer.println("Debug logs: ");
   3745         for (int i = 0; i < sDumpLogs.size(); i++) {
   3746             writer.println("  " + sDumpLogs.get(i));
   3747         }
   3748     }
   3749 }
   3750 
   3751 interface LauncherTransitionable {
   3752     View getContent();
   3753     void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);
   3754     void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
   3755     void onLauncherTransitionStep(Launcher l, float t);
   3756     void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
   3757 }
   3758