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