Home | History | Annotate | Download | only in launcher2
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher2;
     18 
     19 import com.android.common.Search;
     20 
     21 import android.app.Activity;
     22 import android.app.AlertDialog;
     23 import android.app.Dialog;
     24 import android.app.SearchManager;
     25 import android.app.StatusBarManager;
     26 import android.app.WallpaperManager;
     27 import android.content.ActivityNotFoundException;
     28 import android.content.BroadcastReceiver;
     29 import android.content.ComponentName;
     30 import android.content.ContentResolver;
     31 import android.content.Context;
     32 import android.content.DialogInterface;
     33 import android.content.Intent;
     34 import android.content.Intent.ShortcutIconResource;
     35 import android.content.IntentFilter;
     36 import android.content.pm.ActivityInfo;
     37 import android.content.pm.PackageManager;
     38 import android.content.pm.ResolveInfo;
     39 import android.content.res.Configuration;
     40 import android.content.res.Resources;
     41 import android.content.res.TypedArray;
     42 import android.database.ContentObserver;
     43 import android.graphics.Bitmap;
     44 import android.graphics.Rect;
     45 import android.graphics.Canvas;
     46 import android.graphics.drawable.Drawable;
     47 import android.graphics.drawable.ColorDrawable;
     48 import android.net.Uri;
     49 import android.os.AsyncTask;
     50 import android.os.Bundle;
     51 import android.os.Handler;
     52 import android.os.Parcelable;
     53 import android.os.SystemClock;
     54 import android.os.SystemProperties;
     55 import android.provider.LiveFolders;
     56 import android.text.Selection;
     57 import android.text.SpannableStringBuilder;
     58 import android.text.TextUtils;
     59 import android.text.method.TextKeyListener;
     60 import android.util.Log;
     61 import android.view.Display;
     62 import android.view.HapticFeedbackConstants;
     63 import android.view.KeyEvent;
     64 import android.view.LayoutInflater;
     65 import android.view.Menu;
     66 import android.view.MenuItem;
     67 import android.view.View;
     68 import android.view.ViewGroup;
     69 import android.view.View.OnLongClickListener;
     70 import android.view.inputmethod.InputMethodManager;
     71 import android.widget.EditText;
     72 import android.widget.TextView;
     73 import android.widget.Toast;
     74 import android.widget.ImageView;
     75 import android.widget.PopupWindow;
     76 import android.widget.LinearLayout;
     77 import android.appwidget.AppWidgetManager;
     78 import android.appwidget.AppWidgetProviderInfo;
     79 
     80 import java.util.ArrayList;
     81 import java.util.List;
     82 import java.util.HashMap;
     83 import java.io.DataOutputStream;
     84 import java.io.FileNotFoundException;
     85 import java.io.IOException;
     86 import java.io.DataInputStream;
     87 
     88 import com.android.launcher.R;
     89 
     90 /**
     91  * Default launcher application.
     92  */
     93 public final class Launcher extends Activity
     94         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
     95     static final String TAG = "Launcher";
     96     static final boolean LOGD = false;
     97 
     98     static final boolean PROFILE_STARTUP = false;
     99     static final boolean DEBUG_WIDGETS = false;
    100     static final boolean DEBUG_USER_INTERFACE = false;
    101 
    102     private static final int WALLPAPER_SCREENS_SPAN = 2;
    103 
    104     private static final int MENU_GROUP_ADD = 1;
    105     private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1;
    106 
    107     private static final int MENU_ADD = Menu.FIRST + 1;
    108     private static final int MENU_MANAGE_APPS = MENU_ADD + 1;
    109     private static final int MENU_WALLPAPER_SETTINGS = MENU_MANAGE_APPS + 1;
    110     private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
    111     private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1;
    112     private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
    113 
    114     private static final int REQUEST_CREATE_SHORTCUT = 1;
    115     private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
    116     private static final int REQUEST_CREATE_APPWIDGET = 5;
    117     private static final int REQUEST_PICK_APPLICATION = 6;
    118     private static final int REQUEST_PICK_SHORTCUT = 7;
    119     private static final int REQUEST_PICK_LIVE_FOLDER = 8;
    120     private static final int REQUEST_PICK_APPWIDGET = 9;
    121     private static final int REQUEST_PICK_WALLPAPER = 10;
    122 
    123     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
    124 
    125     static final int SCREEN_COUNT = 5;
    126     static final int DEFAULT_SCREEN = 2;
    127     static final int NUMBER_CELLS_X = 4;
    128     static final int NUMBER_CELLS_Y = 4;
    129 
    130     static final int DIALOG_CREATE_SHORTCUT = 1;
    131     static final int DIALOG_RENAME_FOLDER = 2;
    132 
    133     private static final String PREFERENCES = "launcher.preferences";
    134 
    135     // Type: int
    136     private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
    137     // Type: boolean
    138     private static final String RUNTIME_STATE_ALL_APPS_FOLDER = "launcher.all_apps_folder";
    139     // Type: long
    140     private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder";
    141     // Type: int
    142     private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
    143     // Type: int
    144     private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX";
    145     // Type: int
    146     private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY";
    147     // Type: int
    148     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX";
    149     // Type: int
    150     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY";
    151     // Type: int
    152     private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX";
    153     // Type: int
    154     private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY";
    155     // Type: int[]
    156     private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells";
    157     // Type: boolean
    158     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
    159     // Type: long
    160     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
    161 
    162     static final int APPWIDGET_HOST_ID = 1024;
    163 
    164     private static final Object sLock = new Object();
    165     private static int sScreen = DEFAULT_SCREEN;
    166 
    167     private final BroadcastReceiver mCloseSystemDialogsReceiver
    168             = new CloseSystemDialogsIntentReceiver();
    169     private final ContentObserver mWidgetObserver = new AppWidgetResetObserver();
    170 
    171     private LayoutInflater mInflater;
    172 
    173     private DragController mDragController;
    174     private Workspace mWorkspace;
    175 
    176     private AppWidgetManager mAppWidgetManager;
    177     private LauncherAppWidgetHost mAppWidgetHost;
    178 
    179     private CellLayout.CellInfo mAddItemCellInfo;
    180     private CellLayout.CellInfo mMenuAddInfo;
    181     private final int[] mCellCoordinates = new int[2];
    182     private FolderInfo mFolderInfo;
    183 
    184     private DeleteZone mDeleteZone;
    185     private HandleView mHandleView;
    186     private AllAppsView mAllAppsGrid;
    187 
    188     private Bundle mSavedState;
    189 
    190     private SpannableStringBuilder mDefaultKeySsb = null;
    191 
    192     private boolean mWorkspaceLoading = true;
    193 
    194     private boolean mPaused = true;
    195     private boolean mRestoring;
    196     private boolean mWaitingForResult;
    197     private boolean mOnResumeNeedsLoad;
    198 
    199     private Bundle mSavedInstanceState;
    200 
    201     private LauncherModel mModel;
    202     private IconCache mIconCache;
    203 
    204     private static LocaleConfiguration sLocaleConfiguration = null;
    205 
    206     private ArrayList<ItemInfo> mDesktopItems = new ArrayList<ItemInfo>();
    207     private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
    208 
    209     private ImageView mPreviousView;
    210     private ImageView mNextView;
    211 
    212     // Hotseats (quick-launch icons next to AllApps)
    213     private static final int NUM_HOTSEATS = 2;
    214     private String[] mHotseatConfig = null;
    215     private Intent[] mHotseats = null;
    216     private Drawable[] mHotseatIcons = null;
    217     private CharSequence[] mHotseatLabels = null;
    218 
    219     @Override
    220     protected void onCreate(Bundle savedInstanceState) {
    221         super.onCreate(savedInstanceState);
    222 
    223         LauncherApplication app = ((LauncherApplication)getApplication());
    224         mModel = app.setLauncher(this);
    225         mIconCache = app.getIconCache();
    226         mDragController = new DragController(this);
    227         mInflater = getLayoutInflater();
    228 
    229         mAppWidgetManager = AppWidgetManager.getInstance(this);
    230         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
    231         mAppWidgetHost.startListening();
    232 
    233         if (PROFILE_STARTUP) {
    234             android.os.Debug.startMethodTracing("/sdcard/launcher");
    235         }
    236 
    237         loadHotseats();
    238         checkForLocaleChange();
    239         setWallpaperDimension();
    240 
    241         setContentView(R.layout.launcher);
    242         setupViews();
    243 
    244         registerContentObservers();
    245 
    246         lockAllApps();
    247 
    248         mSavedState = savedInstanceState;
    249         restoreState(mSavedState);
    250 
    251         if (PROFILE_STARTUP) {
    252             android.os.Debug.stopMethodTracing();
    253         }
    254 
    255         if (!mRestoring) {
    256             mModel.startLoader(this, true);
    257         }
    258 
    259         // For handling default keys
    260         mDefaultKeySsb = new SpannableStringBuilder();
    261         Selection.setSelection(mDefaultKeySsb, 0);
    262 
    263         IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    264         registerReceiver(mCloseSystemDialogsReceiver, filter);
    265     }
    266 
    267     private void checkForLocaleChange() {
    268         if (sLocaleConfiguration == null) {
    269             new AsyncTask<Void, Void, LocaleConfiguration>() {
    270                 @Override
    271                 protected LocaleConfiguration doInBackground(Void... unused) {
    272                     LocaleConfiguration localeConfiguration = new LocaleConfiguration();
    273                     readConfiguration(Launcher.this, localeConfiguration);
    274                     return localeConfiguration;
    275                 }
    276 
    277                 @Override
    278                 protected void onPostExecute(LocaleConfiguration result) {
    279                     sLocaleConfiguration = result;
    280                     checkForLocaleChange();  // recursive, but now with a locale configuration
    281                 }
    282             }.execute();
    283             return;
    284         }
    285 
    286         final Configuration configuration = getResources().getConfiguration();
    287 
    288         final String previousLocale = sLocaleConfiguration.locale;
    289         final String locale = configuration.locale.toString();
    290 
    291         final int previousMcc = sLocaleConfiguration.mcc;
    292         final int mcc = configuration.mcc;
    293 
    294         final int previousMnc = sLocaleConfiguration.mnc;
    295         final int mnc = configuration.mnc;
    296 
    297         boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
    298 
    299         if (localeChanged) {
    300             sLocaleConfiguration.locale = locale;
    301             sLocaleConfiguration.mcc = mcc;
    302             sLocaleConfiguration.mnc = mnc;
    303 
    304             mIconCache.flush();
    305             loadHotseats();
    306 
    307             final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
    308             new Thread("WriteLocaleConfiguration") {
    309                 public void run() {
    310                     writeConfiguration(Launcher.this, localeConfiguration);
    311                 }
    312             }.start();
    313         }
    314     }
    315 
    316     private static class LocaleConfiguration {
    317         public String locale;
    318         public int mcc = -1;
    319         public int mnc = -1;
    320     }
    321 
    322     private static void readConfiguration(Context context, LocaleConfiguration configuration) {
    323         DataInputStream in = null;
    324         try {
    325             in = new DataInputStream(context.openFileInput(PREFERENCES));
    326             configuration.locale = in.readUTF();
    327             configuration.mcc = in.readInt();
    328             configuration.mnc = in.readInt();
    329         } catch (FileNotFoundException e) {
    330             // Ignore
    331         } catch (IOException e) {
    332             // Ignore
    333         } finally {
    334             if (in != null) {
    335                 try {
    336                     in.close();
    337                 } catch (IOException e) {
    338                     // Ignore
    339                 }
    340             }
    341         }
    342     }
    343 
    344     private static void writeConfiguration(Context context, LocaleConfiguration configuration) {
    345         DataOutputStream out = null;
    346         try {
    347             out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE));
    348             out.writeUTF(configuration.locale);
    349             out.writeInt(configuration.mcc);
    350             out.writeInt(configuration.mnc);
    351             out.flush();
    352         } catch (FileNotFoundException e) {
    353             // Ignore
    354         } catch (IOException e) {
    355             //noinspection ResultOfMethodCallIgnored
    356             context.getFileStreamPath(PREFERENCES).delete();
    357         } finally {
    358             if (out != null) {
    359                 try {
    360                     out.close();
    361                 } catch (IOException e) {
    362                     // Ignore
    363                 }
    364             }
    365         }
    366     }
    367 
    368     static int getScreen() {
    369         synchronized (sLock) {
    370             return sScreen;
    371         }
    372     }
    373 
    374     static void setScreen(int screen) {
    375         synchronized (sLock) {
    376             sScreen = screen;
    377         }
    378     }
    379 
    380     private void setWallpaperDimension() {
    381         WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE);
    382 
    383         Display display = getWindowManager().getDefaultDisplay();
    384         boolean isPortrait = display.getWidth() < display.getHeight();
    385 
    386         final int width = isPortrait ? display.getWidth() : display.getHeight();
    387         final int height = isPortrait ? display.getHeight() : display.getWidth();
    388         wpm.suggestDesiredDimensions(width * WALLPAPER_SCREENS_SPAN, height);
    389     }
    390 
    391     // Note: This doesn't do all the client-id magic that BrowserProvider does
    392     // in Browser. (http://b/2425179)
    393     private Uri getDefaultBrowserUri() {
    394         String url = getString(R.string.default_browser_url);
    395         if (url.indexOf("{CID}") != -1) {
    396             url = url.replace("{CID}", "android-google");
    397         }
    398         return Uri.parse(url);
    399     }
    400 
    401     // Load the Intent templates from arrays.xml to populate the hotseats. For
    402     // each Intent, if it resolves to a single app, use that as the launch
    403     // intent & use that app's label as the contentDescription. Otherwise,
    404     // retain the ResolveActivity so the user can pick an app.
    405     private void loadHotseats() {
    406         if (mHotseatConfig == null) {
    407             mHotseatConfig = getResources().getStringArray(R.array.hotseats);
    408             if (mHotseatConfig.length > 0) {
    409                 mHotseats = new Intent[mHotseatConfig.length];
    410                 mHotseatLabels = new CharSequence[mHotseatConfig.length];
    411                 mHotseatIcons = new Drawable[mHotseatConfig.length];
    412             } else {
    413                 mHotseats = null;
    414                 mHotseatIcons = null;
    415                 mHotseatLabels = null;
    416             }
    417 
    418             TypedArray hotseatIconDrawables = getResources().obtainTypedArray(R.array.hotseat_icons);
    419             for (int i=0; i<mHotseatConfig.length; i++) {
    420                 // load icon for this slot; currently unrelated to the actual activity
    421                 try {
    422                     mHotseatIcons[i] = hotseatIconDrawables.getDrawable(i);
    423                 } catch (ArrayIndexOutOfBoundsException ex) {
    424                     Log.w(TAG, "Missing hotseat_icons array item #" + i);
    425                     mHotseatIcons[i] = null;
    426                 }
    427             }
    428             hotseatIconDrawables.recycle();
    429         }
    430 
    431         PackageManager pm = getPackageManager();
    432         for (int i=0; i<mHotseatConfig.length; i++) {
    433             Intent intent = null;
    434             if (mHotseatConfig[i].equals("*BROWSER*")) {
    435                 // magic value meaning "launch user's default web browser"
    436                 // replace it with a generic web request so we can see if there is indeed a default
    437                 String defaultUri = getString(R.string.default_browser_url);
    438                 intent = new Intent(
    439                         Intent.ACTION_VIEW,
    440                         ((defaultUri != null)
    441                             ? Uri.parse(defaultUri)
    442                             : getDefaultBrowserUri())
    443                     ).addCategory(Intent.CATEGORY_BROWSABLE);
    444                 // note: if the user launches this without a default set, she
    445                 // will always be taken to the default URL above; this is
    446                 // unavoidable as we must specify a valid URL in order for the
    447                 // chooser to appear, and once the user selects something, that
    448                 // URL is unavoidably sent to the chosen app.
    449             } else {
    450                 try {
    451                     intent = Intent.parseUri(mHotseatConfig[i], 0);
    452                 } catch (java.net.URISyntaxException ex) {
    453                     Log.w(TAG, "Invalid hotseat intent: " + mHotseatConfig[i]);
    454                     // bogus; leave intent=null
    455                 }
    456             }
    457 
    458             if (intent == null) {
    459                 mHotseats[i] = null;
    460                 mHotseatLabels[i] = getText(R.string.activity_not_found);
    461                 continue;
    462             }
    463 
    464             if (LOGD) {
    465                 Log.d(TAG, "loadHotseats: hotseat " + i
    466                     + " initial intent=["
    467                     + intent.toUri(Intent.URI_INTENT_SCHEME)
    468                     + "]");
    469             }
    470 
    471             ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
    472             List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    473             if (LOGD) {
    474                 Log.d(TAG, "Best match for intent: " + bestMatch);
    475                 Log.d(TAG, "All matches: ");
    476                 for (ResolveInfo ri : allMatches) {
    477                     Log.d(TAG, "  --> " + ri);
    478                 }
    479             }
    480             // did this resolve to a single app, or the resolver?
    481             if (allMatches.size() == 0 || bestMatch == null) {
    482                 // can't find any activity to handle this. let's leave the
    483                 // intent as-is and let Launcher show a toast when it fails
    484                 // to launch.
    485                 mHotseats[i] = intent;
    486 
    487                 // set accessibility text to "Not installed"
    488                 mHotseatLabels[i] = getText(R.string.activity_not_found);
    489             } else {
    490                 boolean found = false;
    491                 for (ResolveInfo ri : allMatches) {
    492                     if (bestMatch.activityInfo.name.equals(ri.activityInfo.name)
    493                         && bestMatch.activityInfo.applicationInfo.packageName
    494                             .equals(ri.activityInfo.applicationInfo.packageName)) {
    495                         found = true;
    496                         break;
    497                     }
    498                 }
    499 
    500                 if (!found) {
    501                     if (LOGD) Log.d(TAG, "Multiple options, no default yet");
    502                     // the bestMatch is probably the ResolveActivity, meaning the
    503                     // user has not yet selected a default
    504                     // so: we'll keep the original intent for now
    505                     mHotseats[i] = intent;
    506 
    507                     // set the accessibility text to "Select shortcut"
    508                     mHotseatLabels[i] = getText(R.string.title_select_shortcut);
    509                 } else {
    510                     // we have an app!
    511                     // now reconstruct the intent to launch it through the front
    512                     // door
    513                     ComponentName com = new ComponentName(
    514                         bestMatch.activityInfo.applicationInfo.packageName,
    515                         bestMatch.activityInfo.name);
    516                     mHotseats[i] = new Intent(Intent.ACTION_MAIN).setComponent(com);
    517 
    518                     // load the app label for accessibility
    519                     mHotseatLabels[i] = bestMatch.activityInfo.loadLabel(pm);
    520                 }
    521             }
    522 
    523             if (LOGD) {
    524                 Log.d(TAG, "loadHotseats: hotseat " + i
    525                     + " final intent=["
    526                     + ((mHotseats[i] == null)
    527                         ? "null"
    528                         : mHotseats[i].toUri(Intent.URI_INTENT_SCHEME))
    529                     + "] label=[" + mHotseatLabels[i]
    530                     + "]"
    531                     );
    532             }
    533         }
    534     }
    535 
    536     @Override
    537     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    538         mWaitingForResult = false;
    539 
    540         // The pattern used here is that a user PICKs a specific application,
    541         // which, depending on the target, might need to CREATE the actual target.
    542 
    543         // For example, the user would PICK_SHORTCUT for "Music playlist", and we
    544         // launch over to the Music app to actually CREATE_SHORTCUT.
    545 
    546         if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
    547             switch (requestCode) {
    548                 case REQUEST_PICK_APPLICATION:
    549                     completeAddApplication(this, data, mAddItemCellInfo);
    550                     break;
    551                 case REQUEST_PICK_SHORTCUT:
    552                     processShortcut(data);
    553                     break;
    554                 case REQUEST_CREATE_SHORTCUT:
    555                     completeAddShortcut(data, mAddItemCellInfo);
    556                     break;
    557                 case REQUEST_PICK_LIVE_FOLDER:
    558                     addLiveFolder(data);
    559                     break;
    560                 case REQUEST_CREATE_LIVE_FOLDER:
    561                     completeAddLiveFolder(data, mAddItemCellInfo);
    562                     break;
    563                 case REQUEST_PICK_APPWIDGET:
    564                     addAppWidget(data);
    565                     break;
    566                 case REQUEST_CREATE_APPWIDGET:
    567                     completeAddAppWidget(data, mAddItemCellInfo);
    568                     break;
    569                 case REQUEST_PICK_WALLPAPER:
    570                     // We just wanted the activity result here so we can clear mWaitingForResult
    571                     break;
    572             }
    573         } else if ((requestCode == REQUEST_PICK_APPWIDGET ||
    574                 requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED &&
    575                 data != null) {
    576             // Clean up the appWidgetId if we canceled
    577             int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    578             if (appWidgetId != -1) {
    579                 mAppWidgetHost.deleteAppWidgetId(appWidgetId);
    580             }
    581         }
    582     }
    583 
    584     @Override
    585     protected void onResume() {
    586         super.onResume();
    587         mPaused = false;
    588         if (mRestoring || mOnResumeNeedsLoad) {
    589             mWorkspaceLoading = true;
    590             mModel.startLoader(this, true);
    591             mRestoring = false;
    592             mOnResumeNeedsLoad = false;
    593         }
    594     }
    595 
    596     @Override
    597     protected void onPause() {
    598         super.onPause();
    599         mPaused = true;
    600         dismissPreview(mPreviousView);
    601         dismissPreview(mNextView);
    602         mDragController.cancelDrag();
    603     }
    604 
    605     @Override
    606     public Object onRetainNonConfigurationInstance() {
    607         // Flag the loader to stop early before switching
    608         mModel.stopLoader();
    609         mAllAppsGrid.surrender();
    610         return Boolean.TRUE;
    611     }
    612 
    613     // We can't hide the IME if it was forced open.  So don't bother
    614     /*
    615     @Override
    616     public void onWindowFocusChanged(boolean hasFocus) {
    617         super.onWindowFocusChanged(hasFocus);
    618 
    619         if (hasFocus) {
    620             final InputMethodManager inputManager = (InputMethodManager)
    621                     getSystemService(Context.INPUT_METHOD_SERVICE);
    622             WindowManager.LayoutParams lp = getWindow().getAttributes();
    623             inputManager.hideSoftInputFromWindow(lp.token, 0, new android.os.ResultReceiver(new
    624                         android.os.Handler()) {
    625                         protected void onReceiveResult(int resultCode, Bundle resultData) {
    626                             Log.d(TAG, "ResultReceiver got resultCode=" + resultCode);
    627                         }
    628                     });
    629             Log.d(TAG, "called hideSoftInputFromWindow from onWindowFocusChanged");
    630         }
    631     }
    632     */
    633 
    634     private boolean acceptFilter() {
    635         final InputMethodManager inputManager = (InputMethodManager)
    636                 getSystemService(Context.INPUT_METHOD_SERVICE);
    637         return !inputManager.isFullscreenMode();
    638     }
    639 
    640     @Override
    641     public boolean onKeyDown(int keyCode, KeyEvent event) {
    642         boolean handled = super.onKeyDown(keyCode, event);
    643         if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) {
    644             boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
    645                     keyCode, event);
    646             if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
    647                 // something usable has been typed - start a search
    648                 // the typed text will be retrieved and cleared by
    649                 // showSearchDialog()
    650                 // If there are multiple keystrokes before the search dialog takes focus,
    651                 // onSearchRequested() will be called for every keystroke,
    652                 // but it is idempotent, so it's fine.
    653                 return onSearchRequested();
    654             }
    655         }
    656 
    657         // Eat the long press event so the keyboard doesn't come up.
    658         if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
    659             return true;
    660         }
    661 
    662         return handled;
    663     }
    664 
    665     private String getTypedText() {
    666         return mDefaultKeySsb.toString();
    667     }
    668 
    669     private void clearTypedText() {
    670         mDefaultKeySsb.clear();
    671         mDefaultKeySsb.clearSpans();
    672         Selection.setSelection(mDefaultKeySsb, 0);
    673     }
    674 
    675     /**
    676      * Restores the previous state, if it exists.
    677      *
    678      * @param savedState The previous state.
    679      */
    680     private void restoreState(Bundle savedState) {
    681         if (savedState == null) {
    682             return;
    683         }
    684 
    685         final boolean allApps = savedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
    686         if (allApps) {
    687             showAllApps(false);
    688         }
    689 
    690         final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
    691         if (currentScreen > -1) {
    692             mWorkspace.setCurrentScreen(currentScreen);
    693         }
    694 
    695         final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
    696         if (addScreen > -1) {
    697             mAddItemCellInfo = new CellLayout.CellInfo();
    698             final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
    699             addItemCellInfo.valid = true;
    700             addItemCellInfo.screen = addScreen;
    701             addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
    702             addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
    703             addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
    704             addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
    705             addItemCellInfo.findVacantCellsFromOccupied(
    706                     savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS),
    707                     savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X),
    708                     savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y));
    709             mRestoring = true;
    710         }
    711 
    712         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
    713         if (renameFolder) {
    714             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
    715             mFolderInfo = mModel.getFolderById(this, sFolders, id);
    716             mRestoring = true;
    717         }
    718     }
    719 
    720     /**
    721      * Finds all the views we need and configure them properly.
    722      */
    723     private void setupViews() {
    724         DragController dragController = mDragController;
    725 
    726         DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer);
    727         dragLayer.setDragController(dragController);
    728 
    729         mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);
    730         mAllAppsGrid.setLauncher(this);
    731         mAllAppsGrid.setDragController(dragController);
    732         ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
    733         // Manage focusability manually since this thing is always visible
    734         ((View) mAllAppsGrid).setFocusable(false);
    735 
    736         mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
    737         final Workspace workspace = mWorkspace;
    738         workspace.setHapticFeedbackEnabled(false);
    739 
    740         DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
    741         mDeleteZone = deleteZone;
    742 
    743         mHandleView = (HandleView) findViewById(R.id.all_apps_button);
    744         mHandleView.setLauncher(this);
    745         mHandleView.setOnClickListener(this);
    746         mHandleView.setOnLongClickListener(this);
    747 
    748         ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left);
    749         hotseatLeft.setContentDescription(mHotseatLabels[0]);
    750         hotseatLeft.setImageDrawable(mHotseatIcons[0]);
    751         ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right);
    752         hotseatRight.setContentDescription(mHotseatLabels[1]);
    753         hotseatRight.setImageDrawable(mHotseatIcons[1]);
    754 
    755         mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
    756         mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
    757 
    758         Drawable previous = mPreviousView.getDrawable();
    759         Drawable next = mNextView.getDrawable();
    760         mWorkspace.setIndicators(previous, next);
    761 
    762         mPreviousView.setHapticFeedbackEnabled(false);
    763         mPreviousView.setOnLongClickListener(this);
    764         mNextView.setHapticFeedbackEnabled(false);
    765         mNextView.setOnLongClickListener(this);
    766 
    767         workspace.setOnLongClickListener(this);
    768         workspace.setDragController(dragController);
    769         workspace.setLauncher(this);
    770 
    771         deleteZone.setLauncher(this);
    772         deleteZone.setDragController(dragController);
    773         deleteZone.setHandle(findViewById(R.id.all_apps_button_cluster));
    774 
    775         dragController.setDragScoller(workspace);
    776         dragController.setDragListener(deleteZone);
    777         dragController.setScrollView(dragLayer);
    778         dragController.setMoveTarget(workspace);
    779 
    780         // The order here is bottom to top.
    781         dragController.addDropTarget(workspace);
    782         dragController.addDropTarget(deleteZone);
    783     }
    784 
    785     @SuppressWarnings({"UnusedDeclaration"})
    786     public void previousScreen(View v) {
    787         if (!isAllAppsVisible()) {
    788             mWorkspace.scrollLeft();
    789         }
    790     }
    791 
    792     @SuppressWarnings({"UnusedDeclaration"})
    793     public void nextScreen(View v) {
    794         if (!isAllAppsVisible()) {
    795             mWorkspace.scrollRight();
    796         }
    797     }
    798 
    799     @SuppressWarnings({"UnusedDeclaration"})
    800     public void launchHotSeat(View v) {
    801         if (isAllAppsVisible()) return;
    802 
    803         int index = -1;
    804         if (v.getId() == R.id.hotseat_left) {
    805             index = 0;
    806         } else if (v.getId() == R.id.hotseat_right) {
    807             index = 1;
    808         }
    809 
    810         // reload these every tap; you never know when they might change
    811         loadHotseats();
    812         if (index >= 0 && index < mHotseats.length && mHotseats[index] != null) {
    813             Intent intent = mHotseats[index];
    814             startActivitySafely(
    815                 mHotseats[index],
    816                 "hotseat"
    817             );
    818         }
    819     }
    820 
    821     /**
    822      * Creates a view representing a shortcut.
    823      *
    824      * @param info The data structure describing the shortcut.
    825      *
    826      * @return A View inflated from R.layout.application.
    827      */
    828     View createShortcut(ShortcutInfo info) {
    829         return createShortcut(R.layout.application,
    830                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
    831     }
    832 
    833     /**
    834      * Creates a view representing a shortcut inflated from the specified resource.
    835      *
    836      * @param layoutResId The id of the XML layout used to create the shortcut.
    837      * @param parent The group the shortcut belongs to.
    838      * @param info The data structure describing the shortcut.
    839      *
    840      * @return A View inflated from layoutResId.
    841      */
    842     View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
    843         TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);
    844 
    845         favorite.setCompoundDrawablesWithIntrinsicBounds(null,
    846                 new FastBitmapDrawable(info.getIcon(mIconCache)),
    847                 null, null);
    848         favorite.setText(info.title);
    849         favorite.setTag(info);
    850         favorite.setOnClickListener(this);
    851 
    852         return favorite;
    853     }
    854 
    855     /**
    856      * Add an application shortcut to the workspace.
    857      *
    858      * @param data The intent describing the application.
    859      * @param cellInfo The position on screen where to create the shortcut.
    860      */
    861     void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) {
    862         cellInfo.screen = mWorkspace.getCurrentScreen();
    863         if (!findSingleSlot(cellInfo)) return;
    864 
    865         final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(),
    866                 data, context);
    867 
    868         if (info != null) {
    869             info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
    870                     Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    871             info.container = ItemInfo.NO_ID;
    872             mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked());
    873         } else {
    874             Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
    875         }
    876     }
    877 
    878     /**
    879      * Add a shortcut to the workspace.
    880      *
    881      * @param data The intent describing the shortcut.
    882      * @param cellInfo The position on screen where to create the shortcut.
    883      */
    884     private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) {
    885         cellInfo.screen = mWorkspace.getCurrentScreen();
    886         if (!findSingleSlot(cellInfo)) return;
    887 
    888         final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false);
    889 
    890         if (!mRestoring) {
    891             final View view = createShortcut(info);
    892             mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
    893                     isWorkspaceLocked());
    894         }
    895     }
    896 
    897 
    898     /**
    899      * Add a widget to the workspace.
    900      *
    901      * @param data The intent describing the appWidgetId.
    902      * @param cellInfo The position on screen where to create the widget.
    903      */
    904     private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) {
    905         Bundle extras = data.getExtras();
    906         int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    907 
    908         if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString());
    909 
    910         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
    911 
    912         // Calculate the grid spans needed to fit this widget
    913         CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
    914         int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
    915 
    916         // Try finding open space on Launcher screen
    917         final int[] xy = mCellCoordinates;
    918         if (!findSlot(cellInfo, xy, spans[0], spans[1])) {
    919             if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId);
    920             return;
    921         }
    922 
    923         // Build Launcher-specific widget info and save to database
    924         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
    925         launcherInfo.spanX = spans[0];
    926         launcherInfo.spanY = spans[1];
    927 
    928         LauncherModel.addItemToDatabase(this, launcherInfo,
    929                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
    930                 mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
    931 
    932         if (!mRestoring) {
    933             mDesktopItems.add(launcherInfo);
    934 
    935             // Perform actual inflation because we're live
    936             launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
    937 
    938             launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
    939             launcherInfo.hostView.setTag(launcherInfo);
    940 
    941             mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
    942                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
    943         }
    944     }
    945 
    946     public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
    947         mDesktopItems.remove(launcherInfo);
    948         launcherInfo.hostView = null;
    949     }
    950 
    951     public LauncherAppWidgetHost getAppWidgetHost() {
    952         return mAppWidgetHost;
    953     }
    954 
    955     void closeSystemDialogs() {
    956         getWindow().closeAllPanels();
    957 
    958         try {
    959             dismissDialog(DIALOG_CREATE_SHORTCUT);
    960             // Unlock the workspace if the dialog was showing
    961         } catch (Exception e) {
    962             // An exception is thrown if the dialog is not visible, which is fine
    963         }
    964 
    965         try {
    966             dismissDialog(DIALOG_RENAME_FOLDER);
    967             // Unlock the workspace if the dialog was showing
    968         } catch (Exception e) {
    969             // An exception is thrown if the dialog is not visible, which is fine
    970         }
    971 
    972         // Whatever we were doing is hereby canceled.
    973         mWaitingForResult = false;
    974     }
    975 
    976     @Override
    977     protected void onNewIntent(Intent intent) {
    978         super.onNewIntent(intent);
    979 
    980         // Close the menu
    981         if (Intent.ACTION_MAIN.equals(intent.getAction())) {
    982             // also will cancel mWaitingForResult.
    983             closeSystemDialogs();
    984 
    985             boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
    986                         != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
    987             boolean allAppsVisible = isAllAppsVisible();
    988             if (!mWorkspace.isDefaultScreenShowing()) {
    989                 mWorkspace.moveToDefaultScreen(alreadyOnHome && !allAppsVisible);
    990             }
    991             closeAllApps(alreadyOnHome && allAppsVisible);
    992 
    993             final View v = getWindow().peekDecorView();
    994             if (v != null && v.getWindowToken() != null) {
    995                 InputMethodManager imm = (InputMethodManager)getSystemService(
    996                         INPUT_METHOD_SERVICE);
    997                 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
    998             }
    999         }
   1000     }
   1001 
   1002     @Override
   1003     protected void onRestoreInstanceState(Bundle savedInstanceState) {
   1004         // Do not call super here
   1005         mSavedInstanceState = savedInstanceState;
   1006     }
   1007 
   1008     @Override
   1009     protected void onSaveInstanceState(Bundle outState) {
   1010         outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
   1011 
   1012         final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
   1013         if (folders.size() > 0) {
   1014             final int count = folders.size();
   1015             long[] ids = new long[count];
   1016             for (int i = 0; i < count; i++) {
   1017                 final FolderInfo info = folders.get(i).getInfo();
   1018                 ids[i] = info.id;
   1019             }
   1020             outState.putLongArray(RUNTIME_STATE_USER_FOLDERS, ids);
   1021         } else {
   1022             super.onSaveInstanceState(outState);
   1023         }
   1024 
   1025         // TODO should not do this if the drawer is currently closing.
   1026         if (isAllAppsVisible()) {
   1027             outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true);
   1028         }
   1029 
   1030         if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) {
   1031             final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
   1032             final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen);
   1033 
   1034             outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen);
   1035             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX);
   1036             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY);
   1037             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX);
   1038             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY);
   1039             outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX());
   1040             outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY());
   1041             outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS,
   1042                    layout.getOccupiedCells());
   1043         }
   1044 
   1045         if (mFolderInfo != null && mWaitingForResult) {
   1046             outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
   1047             outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
   1048         }
   1049     }
   1050 
   1051     @Override
   1052     public void onDestroy() {
   1053         super.onDestroy();
   1054 
   1055         try {
   1056             mAppWidgetHost.stopListening();
   1057         } catch (NullPointerException ex) {
   1058             Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
   1059         }
   1060 
   1061         TextKeyListener.getInstance().release();
   1062 
   1063         mModel.stopLoader();
   1064 
   1065         unbindDesktopItems();
   1066 
   1067         getContentResolver().unregisterContentObserver(mWidgetObserver);
   1068 
   1069         dismissPreview(mPreviousView);
   1070         dismissPreview(mNextView);
   1071 
   1072         unregisterReceiver(mCloseSystemDialogsReceiver);
   1073     }
   1074 
   1075     @Override
   1076     public void startActivityForResult(Intent intent, int requestCode) {
   1077         if (requestCode >= 0) mWaitingForResult = true;
   1078         super.startActivityForResult(intent, requestCode);
   1079     }
   1080 
   1081     @Override
   1082     public void startSearch(String initialQuery, boolean selectInitialQuery,
   1083             Bundle appSearchData, boolean globalSearch) {
   1084 
   1085         closeAllApps(true);
   1086 
   1087         if (initialQuery == null) {
   1088             // Use any text typed in the launcher as the initial query
   1089             initialQuery = getTypedText();
   1090             clearTypedText();
   1091         }
   1092         if (appSearchData == null) {
   1093             appSearchData = new Bundle();
   1094             appSearchData.putString(Search.SOURCE, "launcher-search");
   1095         }
   1096 
   1097         final SearchManager searchManager =
   1098                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   1099         searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
   1100             appSearchData, globalSearch);
   1101     }
   1102 
   1103     @Override
   1104     public boolean onCreateOptionsMenu(Menu menu) {
   1105         if (isWorkspaceLocked()) {
   1106             return false;
   1107         }
   1108 
   1109         super.onCreateOptionsMenu(menu);
   1110 
   1111         menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
   1112                 .setIcon(android.R.drawable.ic_menu_add)
   1113                 .setAlphabeticShortcut('A');
   1114         menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)
   1115                 .setIcon(android.R.drawable.ic_menu_manage)
   1116                 .setAlphabeticShortcut('M');
   1117         menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
   1118                  .setIcon(android.R.drawable.ic_menu_gallery)
   1119                  .setAlphabeticShortcut('W');
   1120         menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
   1121                 .setIcon(android.R.drawable.ic_search_category_default)
   1122                 .setAlphabeticShortcut(SearchManager.MENU_KEY);
   1123         menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications)
   1124                 .setIcon(com.android.internal.R.drawable.ic_menu_notifications)
   1125                 .setAlphabeticShortcut('N');
   1126 
   1127         final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
   1128         settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
   1129                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
   1130 
   1131         menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings)
   1132                 .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P')
   1133                 .setIntent(settings);
   1134 
   1135         return true;
   1136     }
   1137 
   1138     @Override
   1139     public boolean onPrepareOptionsMenu(Menu menu) {
   1140         super.onPrepareOptionsMenu(menu);
   1141 
   1142         // If all apps is animating, don't show the menu, because we don't know
   1143         // which one to show.
   1144         if (mAllAppsGrid.isVisible() && !mAllAppsGrid.isOpaque()) {
   1145             return false;
   1146         }
   1147 
   1148         // Only show the add and wallpaper options when we're not in all apps.
   1149         boolean visible = !mAllAppsGrid.isOpaque();
   1150         menu.setGroupVisible(MENU_GROUP_ADD, visible);
   1151         menu.setGroupVisible(MENU_GROUP_WALLPAPER, visible);
   1152 
   1153         // Disable add if the workspace is full.
   1154         if (visible) {
   1155             mMenuAddInfo = mWorkspace.findAllVacantCells(null);
   1156             menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid);
   1157         }
   1158 
   1159         return true;
   1160     }
   1161 
   1162     @Override
   1163     public boolean onOptionsItemSelected(MenuItem item) {
   1164         switch (item.getItemId()) {
   1165             case MENU_ADD:
   1166                 addItems();
   1167                 return true;
   1168             case MENU_MANAGE_APPS:
   1169                 manageApps();
   1170                 return true;
   1171             case MENU_WALLPAPER_SETTINGS:
   1172                 startWallpaper();
   1173                 return true;
   1174             case MENU_SEARCH:
   1175                 onSearchRequested();
   1176                 return true;
   1177             case MENU_NOTIFICATIONS:
   1178                 showNotifications();
   1179                 return true;
   1180         }
   1181 
   1182         return super.onOptionsItemSelected(item);
   1183     }
   1184 
   1185     /**
   1186      * Indicates that we want global search for this activity by setting the globalSearch
   1187      * argument for {@link #startSearch} to true.
   1188      */
   1189 
   1190     @Override
   1191     public boolean onSearchRequested() {
   1192         startSearch(null, false, null, true);
   1193         return true;
   1194     }
   1195 
   1196     public boolean isWorkspaceLocked() {
   1197         return mWorkspaceLoading || mWaitingForResult;
   1198     }
   1199 
   1200     private void addItems() {
   1201         closeAllApps(true);
   1202         showAddDialog(mMenuAddInfo);
   1203     }
   1204 
   1205     private void manageApps() {
   1206         startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS));
   1207     }
   1208 
   1209     void addAppWidget(Intent data) {
   1210         // TODO: catch bad widget exception when sent
   1211         int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
   1212         AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   1213 
   1214         if (appWidget.configure != null) {
   1215             // Launch over to configure widget, if needed
   1216             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
   1217             intent.setComponent(appWidget.configure);
   1218             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   1219 
   1220             startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
   1221         } else {
   1222             // Otherwise just add it
   1223             onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
   1224         }
   1225     }
   1226 
   1227     void processShortcut(Intent intent) {
   1228         // Handle case where user selected "Applications"
   1229         String applicationName = getResources().getString(R.string.group_applications);
   1230         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
   1231 
   1232         if (applicationName != null && applicationName.equals(shortcutName)) {
   1233             Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
   1234             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
   1235 
   1236             Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
   1237             pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
   1238             pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
   1239             startActivityForResultSafely(pickIntent, REQUEST_PICK_APPLICATION);
   1240         } else {
   1241             startActivityForResultSafely(intent, REQUEST_CREATE_SHORTCUT);
   1242         }
   1243     }
   1244 
   1245     void addLiveFolder(Intent intent) {
   1246         // Handle case where user selected "Folder"
   1247         String folderName = getResources().getString(R.string.group_folder);
   1248         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
   1249 
   1250         if (folderName != null && folderName.equals(shortcutName)) {
   1251             addFolder();
   1252         } else {
   1253             startActivityForResultSafely(intent, REQUEST_CREATE_LIVE_FOLDER);
   1254         }
   1255     }
   1256 
   1257     void addFolder() {
   1258         UserFolderInfo folderInfo = new UserFolderInfo();
   1259         folderInfo.title = getText(R.string.folder_name);
   1260 
   1261         CellLayout.CellInfo cellInfo = mAddItemCellInfo;
   1262         cellInfo.screen = mWorkspace.getCurrentScreen();
   1263         if (!findSingleSlot(cellInfo)) return;
   1264 
   1265         // Update the model
   1266         LauncherModel.addItemToDatabase(this, folderInfo,
   1267                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
   1268                 mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
   1269         sFolders.put(folderInfo.id, folderInfo);
   1270 
   1271         // Create the view
   1272         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
   1273                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
   1274         mWorkspace.addInCurrentScreen(newFolder,
   1275                 cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked());
   1276     }
   1277 
   1278     void removeFolder(FolderInfo folder) {
   1279         sFolders.remove(folder.id);
   1280     }
   1281 
   1282     private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) {
   1283         cellInfo.screen = mWorkspace.getCurrentScreen();
   1284         if (!findSingleSlot(cellInfo)) return;
   1285 
   1286         final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
   1287 
   1288         if (!mRestoring) {
   1289             final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
   1290                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
   1291             mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
   1292                     isWorkspaceLocked());
   1293         }
   1294     }
   1295 
   1296     static LiveFolderInfo addLiveFolder(Context context, Intent data,
   1297             CellLayout.CellInfo cellInfo, boolean notify) {
   1298 
   1299         Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT);
   1300         String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME);
   1301 
   1302         Drawable icon = null;
   1303         Intent.ShortcutIconResource iconResource = null;
   1304 
   1305         Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON);
   1306         if (extra != null && extra instanceof Intent.ShortcutIconResource) {
   1307             try {
   1308                 iconResource = (Intent.ShortcutIconResource) extra;
   1309                 final PackageManager packageManager = context.getPackageManager();
   1310                 Resources resources = packageManager.getResourcesForApplication(
   1311                         iconResource.packageName);
   1312                 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
   1313                 icon = resources.getDrawable(id);
   1314             } catch (Exception e) {
   1315                 Log.w(TAG, "Could not load live folder icon: " + extra);
   1316             }
   1317         }
   1318 
   1319         if (icon == null) {
   1320             icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder);
   1321         }
   1322 
   1323         final LiveFolderInfo info = new LiveFolderInfo();
   1324         info.icon = Utilities.createIconBitmap(icon, context);
   1325         info.title = name;
   1326         info.iconResource = iconResource;
   1327         info.uri = data.getData();
   1328         info.baseIntent = baseIntent;
   1329         info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
   1330                 LiveFolders.DISPLAY_MODE_GRID);
   1331 
   1332         LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
   1333                 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
   1334         sFolders.put(info.id, info);
   1335 
   1336         return info;
   1337     }
   1338 
   1339     private boolean findSingleSlot(CellLayout.CellInfo cellInfo) {
   1340         final int[] xy = new int[2];
   1341         if (findSlot(cellInfo, xy, 1, 1)) {
   1342             cellInfo.cellX = xy[0];
   1343             cellInfo.cellY = xy[1];
   1344             return true;
   1345         }
   1346         return false;
   1347     }
   1348 
   1349     private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
   1350         if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
   1351             boolean[] occupied = mSavedState != null ?
   1352                     mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null;
   1353             cellInfo = mWorkspace.findAllVacantCells(occupied);
   1354             if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
   1355                 Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
   1356                 return false;
   1357             }
   1358         }
   1359         return true;
   1360     }
   1361 
   1362     private void showNotifications() {
   1363         final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
   1364         if (statusBar != null) {
   1365             statusBar.expand();
   1366         }
   1367     }
   1368 
   1369     private void startWallpaper() {
   1370         closeAllApps(true);
   1371         final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
   1372         Intent chooser = Intent.createChooser(pickWallpaper,
   1373                 getText(R.string.chooser_wallpaper));
   1374         // NOTE: Adds a configure option to the chooser if the wallpaper supports it
   1375         //       Removed in Eclair MR1
   1376 //        WallpaperManager wm = (WallpaperManager)
   1377 //                getSystemService(Context.WALLPAPER_SERVICE);
   1378 //        WallpaperInfo wi = wm.getWallpaperInfo();
   1379 //        if (wi != null && wi.getSettingsActivity() != null) {
   1380 //            LabeledIntent li = new LabeledIntent(getPackageName(),
   1381 //                    R.string.configure_wallpaper, 0);
   1382 //            li.setClassName(wi.getPackageName(), wi.getSettingsActivity());
   1383 //            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li });
   1384 //        }
   1385         startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
   1386     }
   1387 
   1388     /**
   1389      * Registers various content observers. The current implementation registers
   1390      * only a favorites observer to keep track of the favorites applications.
   1391      */
   1392     private void registerContentObservers() {
   1393         ContentResolver resolver = getContentResolver();
   1394         resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
   1395                 true, mWidgetObserver);
   1396     }
   1397 
   1398     @Override
   1399     public boolean dispatchKeyEvent(KeyEvent event) {
   1400         if (event.getAction() == KeyEvent.ACTION_DOWN) {
   1401             switch (event.getKeyCode()) {
   1402                 case KeyEvent.KEYCODE_HOME:
   1403                     return true;
   1404                 case KeyEvent.KEYCODE_VOLUME_DOWN:
   1405                     if (SystemProperties.getInt("debug.launcher2.dumpstate", 0) != 0) {
   1406                         dumpState();
   1407                         return true;
   1408                     }
   1409                     break;
   1410             }
   1411         } else if (event.getAction() == KeyEvent.ACTION_UP) {
   1412             switch (event.getKeyCode()) {
   1413                 case KeyEvent.KEYCODE_HOME:
   1414                     return true;
   1415             }
   1416         }
   1417 
   1418         return super.dispatchKeyEvent(event);
   1419     }
   1420 
   1421     @Override
   1422     public void onBackPressed() {
   1423         if (isAllAppsVisible()) {
   1424             closeAllApps(true);
   1425         } else {
   1426             closeFolder();
   1427         }
   1428         dismissPreview(mPreviousView);
   1429         dismissPreview(mNextView);
   1430     }
   1431 
   1432     private void closeFolder() {
   1433         Folder folder = mWorkspace.getOpenFolder();
   1434         if (folder != null) {
   1435             closeFolder(folder);
   1436         }
   1437     }
   1438 
   1439     void closeFolder(Folder folder) {
   1440         folder.getInfo().opened = false;
   1441         ViewGroup parent = (ViewGroup) folder.getParent();
   1442         if (parent != null) {
   1443             parent.removeView(folder);
   1444             if (folder instanceof DropTarget) {
   1445                 // Live folders aren't DropTargets.
   1446                 mDragController.removeDropTarget((DropTarget)folder);
   1447             }
   1448         }
   1449         folder.onClose();
   1450     }
   1451 
   1452     /**
   1453      * Re-listen when widgets are reset.
   1454      */
   1455     private void onAppWidgetReset() {
   1456         mAppWidgetHost.startListening();
   1457     }
   1458 
   1459     /**
   1460      * Go through the and disconnect any of the callbacks in the drawables and the views or we
   1461      * leak the previous Home screen on orientation change.
   1462      */
   1463     private void unbindDesktopItems() {
   1464         for (ItemInfo item: mDesktopItems) {
   1465             item.unbind();
   1466         }
   1467     }
   1468 
   1469     /**
   1470      * Launches the intent referred by the clicked shortcut.
   1471      *
   1472      * @param v The view representing the clicked shortcut.
   1473      */
   1474     public void onClick(View v) {
   1475         Object tag = v.getTag();
   1476         if (tag instanceof ShortcutInfo) {
   1477             // Open shortcut
   1478             final Intent intent = ((ShortcutInfo) tag).intent;
   1479             int[] pos = new int[2];
   1480             v.getLocationOnScreen(pos);
   1481             intent.setSourceBounds(new Rect(pos[0], pos[1],
   1482                     pos[0] + v.getWidth(), pos[1] + v.getHeight()));
   1483             startActivitySafely(intent, tag);
   1484         } else if (tag instanceof FolderInfo) {
   1485             handleFolderClick((FolderInfo) tag);
   1486         } else if (v == mHandleView) {
   1487             if (isAllAppsVisible()) {
   1488                 closeAllApps(true);
   1489             } else {
   1490                 showAllApps(true);
   1491             }
   1492         }
   1493     }
   1494 
   1495     void startActivitySafely(Intent intent, Object tag) {
   1496         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1497         try {
   1498             startActivity(intent);
   1499         } catch (ActivityNotFoundException e) {
   1500             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   1501             Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
   1502         } catch (SecurityException e) {
   1503             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   1504             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   1505                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   1506                     "or use the exported attribute for this activity. "
   1507                     + "tag="+ tag + " intent=" + intent, e);
   1508         }
   1509     }
   1510 
   1511     void startActivityForResultSafely(Intent intent, int requestCode) {
   1512         try {
   1513             startActivityForResult(intent, requestCode);
   1514         } catch (ActivityNotFoundException e) {
   1515             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   1516         } catch (SecurityException e) {
   1517             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   1518             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   1519                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   1520                     "or use the exported attribute for this activity.", e);
   1521         }
   1522     }
   1523 
   1524     private void handleFolderClick(FolderInfo folderInfo) {
   1525         if (!folderInfo.opened) {
   1526             // Close any open folder
   1527             closeFolder();
   1528             // Open the requested folder
   1529             openFolder(folderInfo);
   1530         } else {
   1531             // Find the open folder...
   1532             Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
   1533             int folderScreen;
   1534             if (openFolder != null) {
   1535                 folderScreen = mWorkspace.getScreenForView(openFolder);
   1536                 // .. and close it
   1537                 closeFolder(openFolder);
   1538                 if (folderScreen != mWorkspace.getCurrentScreen()) {
   1539                     // Close any folder open on the current screen
   1540                     closeFolder();
   1541                     // Pull the folder onto this screen
   1542                     openFolder(folderInfo);
   1543                 }
   1544             }
   1545         }
   1546     }
   1547 
   1548     /**
   1549      * Opens the user fodler described by the specified tag. The opening of the folder
   1550      * is animated relative to the specified View. If the View is null, no animation
   1551      * is played.
   1552      *
   1553      * @param folderInfo The FolderInfo describing the folder to open.
   1554      */
   1555     private void openFolder(FolderInfo folderInfo) {
   1556         Folder openFolder;
   1557 
   1558         if (folderInfo instanceof UserFolderInfo) {
   1559             openFolder = UserFolder.fromXml(this);
   1560         } else if (folderInfo instanceof LiveFolderInfo) {
   1561             openFolder = com.android.launcher2.LiveFolder.fromXml(this, folderInfo);
   1562         } else {
   1563             return;
   1564         }
   1565 
   1566         openFolder.setDragController(mDragController);
   1567         openFolder.setLauncher(this);
   1568 
   1569         openFolder.bind(folderInfo);
   1570         folderInfo.opened = true;
   1571 
   1572         mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4);
   1573         openFolder.onOpen();
   1574     }
   1575 
   1576     public boolean onLongClick(View v) {
   1577         switch (v.getId()) {
   1578             case R.id.previous_screen:
   1579                 if (!isAllAppsVisible()) {
   1580                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1581                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1582                     showPreviews(v);
   1583                 }
   1584                 return true;
   1585             case R.id.next_screen:
   1586                 if (!isAllAppsVisible()) {
   1587                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1588                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1589                     showPreviews(v);
   1590                 }
   1591                 return true;
   1592             case R.id.all_apps_button:
   1593                 if (!isAllAppsVisible()) {
   1594                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1595                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1596                     showPreviews(v);
   1597                 }
   1598                 return true;
   1599         }
   1600 
   1601         if (isWorkspaceLocked()) {
   1602             return false;
   1603         }
   1604 
   1605         if (!(v instanceof CellLayout)) {
   1606             v = (View) v.getParent();
   1607         }
   1608 
   1609         CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
   1610 
   1611         // This happens when long clicking an item with the dpad/trackball
   1612         if (cellInfo == null) {
   1613             return true;
   1614         }
   1615 
   1616         if (mWorkspace.allowLongPress()) {
   1617             if (cellInfo.cell == null) {
   1618                 if (cellInfo.valid) {
   1619                     // User long pressed on empty space
   1620                     mWorkspace.setAllowLongPress(false);
   1621                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1622                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1623                     showAddDialog(cellInfo);
   1624                 }
   1625             } else {
   1626                 if (!(cellInfo.cell instanceof Folder)) {
   1627                     // User long pressed on an item
   1628                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1629                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1630                     mWorkspace.startDrag(cellInfo);
   1631                 }
   1632             }
   1633         }
   1634         return true;
   1635     }
   1636 
   1637     @SuppressWarnings({"unchecked"})
   1638     private void dismissPreview(final View v) {
   1639         final PopupWindow window = (PopupWindow) v.getTag();
   1640         if (window != null) {
   1641             window.setOnDismissListener(new PopupWindow.OnDismissListener() {
   1642                 public void onDismiss() {
   1643                     ViewGroup group = (ViewGroup) v.getTag(R.id.workspace);
   1644                     int count = group.getChildCount();
   1645                     for (int i = 0; i < count; i++) {
   1646                         ((ImageView) group.getChildAt(i)).setImageDrawable(null);
   1647                     }
   1648                     ArrayList<Bitmap> bitmaps = (ArrayList<Bitmap>) v.getTag(R.id.icon);
   1649                     for (Bitmap bitmap : bitmaps) bitmap.recycle();
   1650 
   1651                     v.setTag(R.id.workspace, null);
   1652                     v.setTag(R.id.icon, null);
   1653                     window.setOnDismissListener(null);
   1654                 }
   1655             });
   1656             window.dismiss();
   1657         }
   1658         v.setTag(null);
   1659     }
   1660 
   1661     private void showPreviews(View anchor) {
   1662         showPreviews(anchor, 0, mWorkspace.getChildCount());
   1663     }
   1664 
   1665     private void showPreviews(final View anchor, int start, int end) {
   1666         final Resources resources = getResources();
   1667         final Workspace workspace = mWorkspace;
   1668 
   1669         CellLayout cell = ((CellLayout) workspace.getChildAt(start));
   1670 
   1671         float max = workspace.getChildCount();
   1672 
   1673         final Rect r = new Rect();
   1674         resources.getDrawable(R.drawable.preview_background).getPadding(r);
   1675         int extraW = (int) ((r.left + r.right) * max);
   1676         int extraH = r.top + r.bottom;
   1677 
   1678         int aW = cell.getWidth() - extraW;
   1679         float w = aW / max;
   1680 
   1681         int width = cell.getWidth();
   1682         int height = cell.getHeight();
   1683         int x = cell.getLeftPadding();
   1684         int y = cell.getTopPadding();
   1685         width -= (x + cell.getRightPadding());
   1686         height -= (y + cell.getBottomPadding());
   1687 
   1688         float scale = w / width;
   1689 
   1690         int count = end - start;
   1691 
   1692         final float sWidth = width * scale;
   1693         float sHeight = height * scale;
   1694 
   1695         LinearLayout preview = new LinearLayout(this);
   1696 
   1697         PreviewTouchHandler handler = new PreviewTouchHandler(anchor);
   1698         ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>(count);
   1699 
   1700         for (int i = start; i < end; i++) {
   1701             ImageView image = new ImageView(this);
   1702             cell = (CellLayout) workspace.getChildAt(i);
   1703 
   1704             final Bitmap bitmap = Bitmap.createBitmap((int) sWidth, (int) sHeight,
   1705                     Bitmap.Config.ARGB_8888);
   1706 
   1707             final Canvas c = new Canvas(bitmap);
   1708             c.scale(scale, scale);
   1709             c.translate(-cell.getLeftPadding(), -cell.getTopPadding());
   1710             cell.dispatchDraw(c);
   1711 
   1712             image.setBackgroundDrawable(resources.getDrawable(R.drawable.preview_background));
   1713             image.setImageBitmap(bitmap);
   1714             image.setTag(i);
   1715             image.setOnClickListener(handler);
   1716             image.setOnFocusChangeListener(handler);
   1717             image.setFocusable(true);
   1718             if (i == mWorkspace.getCurrentScreen()) image.requestFocus();
   1719 
   1720             preview.addView(image,
   1721                     LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
   1722 
   1723             bitmaps.add(bitmap);
   1724         }
   1725 
   1726         final PopupWindow p = new PopupWindow(this);
   1727         p.setContentView(preview);
   1728         p.setWidth((int) (sWidth * count + extraW));
   1729         p.setHeight((int) (sHeight + extraH));
   1730         p.setAnimationStyle(R.style.AnimationPreview);
   1731         p.setOutsideTouchable(true);
   1732         p.setFocusable(true);
   1733         p.setBackgroundDrawable(new ColorDrawable(0));
   1734         p.showAsDropDown(anchor, 0, 0);
   1735 
   1736         p.setOnDismissListener(new PopupWindow.OnDismissListener() {
   1737             public void onDismiss() {
   1738                 dismissPreview(anchor);
   1739             }
   1740         });
   1741 
   1742         anchor.setTag(p);
   1743         anchor.setTag(R.id.workspace, preview);
   1744         anchor.setTag(R.id.icon, bitmaps);
   1745     }
   1746 
   1747     class PreviewTouchHandler implements View.OnClickListener, Runnable, View.OnFocusChangeListener {
   1748         private final View mAnchor;
   1749 
   1750         public PreviewTouchHandler(View anchor) {
   1751             mAnchor = anchor;
   1752         }
   1753 
   1754         public void onClick(View v) {
   1755             mWorkspace.snapToScreen((Integer) v.getTag());
   1756             v.post(this);
   1757         }
   1758 
   1759         public void run() {
   1760             dismissPreview(mAnchor);
   1761         }
   1762 
   1763         public void onFocusChange(View v, boolean hasFocus) {
   1764             if (hasFocus) {
   1765                 mWorkspace.snapToScreen((Integer) v.getTag());
   1766             }
   1767         }
   1768     }
   1769 
   1770     Workspace getWorkspace() {
   1771         return mWorkspace;
   1772     }
   1773 
   1774     @Override
   1775     protected Dialog onCreateDialog(int id) {
   1776         switch (id) {
   1777             case DIALOG_CREATE_SHORTCUT:
   1778                 return new CreateShortcut().createDialog();
   1779             case DIALOG_RENAME_FOLDER:
   1780                 return new RenameFolder().createDialog();
   1781         }
   1782 
   1783         return super.onCreateDialog(id);
   1784     }
   1785 
   1786     @Override
   1787     protected void onPrepareDialog(int id, Dialog dialog) {
   1788         switch (id) {
   1789             case DIALOG_CREATE_SHORTCUT:
   1790                 break;
   1791             case DIALOG_RENAME_FOLDER:
   1792                 if (mFolderInfo != null) {
   1793                     EditText input = (EditText) dialog.findViewById(R.id.folder_name);
   1794                     final CharSequence text = mFolderInfo.title;
   1795                     input.setText(text);
   1796                     input.setSelection(0, text.length());
   1797                 }
   1798                 break;
   1799         }
   1800     }
   1801 
   1802     void showRenameDialog(FolderInfo info) {
   1803         mFolderInfo = info;
   1804         mWaitingForResult = true;
   1805         showDialog(DIALOG_RENAME_FOLDER);
   1806     }
   1807 
   1808     private void showAddDialog(CellLayout.CellInfo cellInfo) {
   1809         mAddItemCellInfo = cellInfo;
   1810         mWaitingForResult = true;
   1811         showDialog(DIALOG_CREATE_SHORTCUT);
   1812     }
   1813 
   1814     private void pickShortcut() {
   1815         Bundle bundle = new Bundle();
   1816 
   1817         ArrayList<String> shortcutNames = new ArrayList<String>();
   1818         shortcutNames.add(getString(R.string.group_applications));
   1819         bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
   1820 
   1821         ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>();
   1822         shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
   1823                         R.drawable.ic_launcher_application));
   1824         bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
   1825 
   1826         Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
   1827         pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT));
   1828         pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_shortcut));
   1829         pickIntent.putExtras(bundle);
   1830 
   1831         startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
   1832     }
   1833 
   1834     private class RenameFolder {
   1835         private EditText mInput;
   1836 
   1837         Dialog createDialog() {
   1838             final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null);
   1839             mInput = (EditText) layout.findViewById(R.id.folder_name);
   1840 
   1841             AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
   1842             builder.setIcon(0);
   1843             builder.setTitle(getString(R.string.rename_folder_title));
   1844             builder.setCancelable(true);
   1845             builder.setOnCancelListener(new Dialog.OnCancelListener() {
   1846                 public void onCancel(DialogInterface dialog) {
   1847                     cleanup();
   1848                 }
   1849             });
   1850             builder.setNegativeButton(getString(R.string.cancel_action),
   1851                 new Dialog.OnClickListener() {
   1852                     public void onClick(DialogInterface dialog, int which) {
   1853                         cleanup();
   1854                     }
   1855                 }
   1856             );
   1857             builder.setPositiveButton(getString(R.string.rename_action),
   1858                 new Dialog.OnClickListener() {
   1859                     public void onClick(DialogInterface dialog, int which) {
   1860                         changeFolderName();
   1861                     }
   1862                 }
   1863             );
   1864             builder.setView(layout);
   1865 
   1866             final AlertDialog dialog = builder.create();
   1867             dialog.setOnShowListener(new DialogInterface.OnShowListener() {
   1868                 public void onShow(DialogInterface dialog) {
   1869                     mWaitingForResult = true;
   1870                     mInput.requestFocus();
   1871                     InputMethodManager inputManager = (InputMethodManager)
   1872                             getSystemService(Context.INPUT_METHOD_SERVICE);
   1873                     inputManager.showSoftInput(mInput, 0);
   1874                 }
   1875             });
   1876 
   1877             return dialog;
   1878         }
   1879 
   1880         private void changeFolderName() {
   1881             final String name = mInput.getText().toString();
   1882             if (!TextUtils.isEmpty(name)) {
   1883                 // Make sure we have the right folder info
   1884                 mFolderInfo = sFolders.get(mFolderInfo.id);
   1885                 mFolderInfo.title = name;
   1886                 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
   1887 
   1888                 if (mWorkspaceLoading) {
   1889                     lockAllApps();
   1890                     mModel.startLoader(Launcher.this, false);
   1891                 } else {
   1892                     final FolderIcon folderIcon = (FolderIcon)
   1893                             mWorkspace.getViewForTag(mFolderInfo);
   1894                     if (folderIcon != null) {
   1895                         folderIcon.setText(name);
   1896                         getWorkspace().requestLayout();
   1897                     } else {
   1898                         lockAllApps();
   1899                         mWorkspaceLoading = true;
   1900                         mModel.startLoader(Launcher.this, false);
   1901                     }
   1902                 }
   1903             }
   1904             cleanup();
   1905         }
   1906 
   1907         private void cleanup() {
   1908             dismissDialog(DIALOG_RENAME_FOLDER);
   1909             mWaitingForResult = false;
   1910             mFolderInfo = null;
   1911         }
   1912     }
   1913 
   1914     // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
   1915     public boolean isAllAppsVisible() {
   1916         return (mAllAppsGrid != null) ? mAllAppsGrid.isVisible() : false;
   1917     }
   1918 
   1919     // AllAppsView.Watcher
   1920     public void zoomed(float zoom) {
   1921         if (zoom == 1.0f) {
   1922             mWorkspace.setVisibility(View.GONE);
   1923         }
   1924     }
   1925 
   1926     void showAllApps(boolean animated) {
   1927         mAllAppsGrid.zoom(1.0f, animated);
   1928 
   1929         ((View) mAllAppsGrid).setFocusable(true);
   1930         ((View) mAllAppsGrid).requestFocus();
   1931 
   1932         // TODO: fade these two too
   1933         mDeleteZone.setVisibility(View.GONE);
   1934     }
   1935 
   1936     /**
   1937      * Things to test when changing this code.
   1938      *   - Home from workspace
   1939      *          - from center screen
   1940      *          - from other screens
   1941      *   - Home from all apps
   1942      *          - from center screen
   1943      *          - from other screens
   1944      *   - Back from all apps
   1945      *          - from center screen
   1946      *          - from other screens
   1947      *   - Launch app from workspace and quit
   1948      *          - with back
   1949      *          - with home
   1950      *   - Launch app from all apps and quit
   1951      *          - with back
   1952      *          - with home
   1953      *   - Go to a screen that's not the default, then all
   1954      *     apps, and launch and app, and go back
   1955      *          - with back
   1956      *          -with home
   1957      *   - On workspace, long press power and go back
   1958      *          - with back
   1959      *          - with home
   1960      *   - On all apps, long press power and go back
   1961      *          - with back
   1962      *          - with home
   1963      *   - On workspace, power off
   1964      *   - On all apps, power off
   1965      *   - Launch an app and turn off the screen while in that app
   1966      *          - Go back with home key
   1967      *          - Go back with back key  TODO: make this not go to workspace
   1968      *          - From all apps
   1969      *          - From workspace
   1970      *   - Enter and exit car mode (becuase it causes an extra configuration changed)
   1971      *          - From all apps
   1972      *          - From the center workspace
   1973      *          - From another workspace
   1974      */
   1975     void closeAllApps(boolean animated) {
   1976         if (mAllAppsGrid.isVisible()) {
   1977             mWorkspace.setVisibility(View.VISIBLE);
   1978             mAllAppsGrid.zoom(0.0f, animated);
   1979             ((View)mAllAppsGrid).setFocusable(false);
   1980             mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
   1981         }
   1982     }
   1983 
   1984     void lockAllApps() {
   1985         // TODO
   1986     }
   1987 
   1988     void unlockAllApps() {
   1989         // TODO
   1990     }
   1991 
   1992     /**
   1993      * Displays the shortcut creation dialog and launches, if necessary, the
   1994      * appropriate activity.
   1995      */
   1996     private class CreateShortcut implements DialogInterface.OnClickListener,
   1997             DialogInterface.OnCancelListener, DialogInterface.OnDismissListener,
   1998             DialogInterface.OnShowListener {
   1999 
   2000         private AddAdapter mAdapter;
   2001 
   2002         Dialog createDialog() {
   2003             mAdapter = new AddAdapter(Launcher.this);
   2004 
   2005             final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
   2006             builder.setTitle(getString(R.string.menu_item_add_item));
   2007             builder.setAdapter(mAdapter, this);
   2008 
   2009             builder.setInverseBackgroundForced(true);
   2010 
   2011             AlertDialog dialog = builder.create();
   2012             dialog.setOnCancelListener(this);
   2013             dialog.setOnDismissListener(this);
   2014             dialog.setOnShowListener(this);
   2015 
   2016             return dialog;
   2017         }
   2018 
   2019         public void onCancel(DialogInterface dialog) {
   2020             mWaitingForResult = false;
   2021             cleanup();
   2022         }
   2023 
   2024         public void onDismiss(DialogInterface dialog) {
   2025         }
   2026 
   2027         private void cleanup() {
   2028             try {
   2029                 dismissDialog(DIALOG_CREATE_SHORTCUT);
   2030             } catch (Exception e) {
   2031                 // An exception is thrown if the dialog is not visible, which is fine
   2032             }
   2033         }
   2034 
   2035         /**
   2036          * Handle the action clicked in the "Add to home" dialog.
   2037          */
   2038         public void onClick(DialogInterface dialog, int which) {
   2039             Resources res = getResources();
   2040             cleanup();
   2041 
   2042             switch (which) {
   2043                 case AddAdapter.ITEM_SHORTCUT: {
   2044                     // Insert extra item to handle picking application
   2045                     pickShortcut();
   2046                     break;
   2047                 }
   2048 
   2049                 case AddAdapter.ITEM_APPWIDGET: {
   2050                     int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
   2051 
   2052                     Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
   2053                     pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   2054                     // start the pick activity
   2055                     startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
   2056                     break;
   2057                 }
   2058 
   2059                 case AddAdapter.ITEM_LIVE_FOLDER: {
   2060                     // Insert extra item to handle inserting folder
   2061                     Bundle bundle = new Bundle();
   2062 
   2063                     ArrayList<String> shortcutNames = new ArrayList<String>();
   2064                     shortcutNames.add(res.getString(R.string.group_folder));
   2065                     bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
   2066 
   2067                     ArrayList<ShortcutIconResource> shortcutIcons =
   2068                             new ArrayList<ShortcutIconResource>();
   2069                     shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
   2070                             R.drawable.ic_launcher_folder));
   2071                     bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
   2072 
   2073                     Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
   2074                     pickIntent.putExtra(Intent.EXTRA_INTENT,
   2075                             new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER));
   2076                     pickIntent.putExtra(Intent.EXTRA_TITLE,
   2077                             getText(R.string.title_select_live_folder));
   2078                     pickIntent.putExtras(bundle);
   2079 
   2080                     startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
   2081                     break;
   2082                 }
   2083 
   2084                 case AddAdapter.ITEM_WALLPAPER: {
   2085                     startWallpaper();
   2086                     break;
   2087                 }
   2088             }
   2089         }
   2090 
   2091         public void onShow(DialogInterface dialog) {
   2092             mWaitingForResult = true;
   2093         }
   2094     }
   2095 
   2096     /**
   2097      * Receives notifications when applications are added/removed.
   2098      */
   2099     private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
   2100         @Override
   2101         public void onReceive(Context context, Intent intent) {
   2102             closeSystemDialogs();
   2103             String reason = intent.getStringExtra("reason");
   2104             if (!"homekey".equals(reason)) {
   2105                 boolean animate = true;
   2106                 if (mPaused || "lock".equals(reason)) {
   2107                     animate = false;
   2108                 }
   2109                 closeAllApps(animate);
   2110             }
   2111         }
   2112     }
   2113 
   2114     /**
   2115      * Receives notifications whenever the appwidgets are reset.
   2116      */
   2117     private class AppWidgetResetObserver extends ContentObserver {
   2118         public AppWidgetResetObserver() {
   2119             super(new Handler());
   2120         }
   2121 
   2122         @Override
   2123         public void onChange(boolean selfChange) {
   2124             onAppWidgetReset();
   2125         }
   2126     }
   2127 
   2128     /**
   2129      * If the activity is currently paused, signal that we need to re-run the loader
   2130      * in onResume.
   2131      *
   2132      * This needs to be called from incoming places where resources might have been loaded
   2133      * while we are paused.  That is becaues the Configuration might be wrong
   2134      * when we're not running, and if it comes back to what it was when we
   2135      * were paused, we are not restarted.
   2136      *
   2137      * Implementation of the method from LauncherModel.Callbacks.
   2138      *
   2139      * @return true if we are currently paused.  The caller might be able to
   2140      * skip some work in that case since we will come back again.
   2141      */
   2142     public boolean setLoadOnResume() {
   2143         if (mPaused) {
   2144             Log.i(TAG, "setLoadOnResume");
   2145             mOnResumeNeedsLoad = true;
   2146             return true;
   2147         } else {
   2148             return false;
   2149         }
   2150     }
   2151 
   2152     /**
   2153      * Implementation of the method from LauncherModel.Callbacks.
   2154      */
   2155     public int getCurrentWorkspaceScreen() {
   2156         if (mWorkspace != null) {
   2157             return mWorkspace.getCurrentScreen();
   2158         } else {
   2159             return SCREEN_COUNT / 2;
   2160         }
   2161     }
   2162 
   2163     /**
   2164      * Refreshes the shortcuts shown on the workspace.
   2165      *
   2166      * Implementation of the method from LauncherModel.Callbacks.
   2167      */
   2168     public void startBinding() {
   2169         final Workspace workspace = mWorkspace;
   2170         int count = workspace.getChildCount();
   2171         for (int i = 0; i < count; i++) {
   2172             // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
   2173             ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
   2174         }
   2175 
   2176         if (DEBUG_USER_INTERFACE) {
   2177             android.widget.Button finishButton = new android.widget.Button(this);
   2178             finishButton.setText("Finish");
   2179             workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
   2180 
   2181             finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
   2182                 public void onClick(View v) {
   2183                     finish();
   2184                 }
   2185             });
   2186         }
   2187     }
   2188 
   2189     /**
   2190      * Bind the items start-end from the list.
   2191      *
   2192      * Implementation of the method from LauncherModel.Callbacks.
   2193      */
   2194     public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
   2195 
   2196         setLoadOnResume();
   2197 
   2198         final Workspace workspace = mWorkspace;
   2199 
   2200         for (int i=start; i<end; i++) {
   2201             final ItemInfo item = shortcuts.get(i);
   2202             mDesktopItems.add(item);
   2203             switch (item.itemType) {
   2204                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   2205                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   2206                     final View shortcut = createShortcut((ShortcutInfo)item);
   2207                     workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
   2208                             false);
   2209                     break;
   2210                 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
   2211                     final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
   2212                             (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
   2213                             (UserFolderInfo) item);
   2214                     workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
   2215                             false);
   2216                     break;
   2217                 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
   2218                     final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
   2219                             R.layout.live_folder_icon, this,
   2220                             (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
   2221                             (LiveFolderInfo) item);
   2222                     workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
   2223                             false);
   2224                     break;
   2225             }
   2226         }
   2227 
   2228         workspace.requestLayout();
   2229     }
   2230 
   2231     /**
   2232      * Implementation of the method from LauncherModel.Callbacks.
   2233      */
   2234     public void bindFolders(HashMap<Long, FolderInfo> folders) {
   2235         setLoadOnResume();
   2236         sFolders.clear();
   2237         sFolders.putAll(folders);
   2238     }
   2239 
   2240     /**
   2241      * Add the views for a widget to the workspace.
   2242      *
   2243      * Implementation of the method from LauncherModel.Callbacks.
   2244      */
   2245     public void bindAppWidget(LauncherAppWidgetInfo item) {
   2246         setLoadOnResume();
   2247 
   2248         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
   2249         if (DEBUG_WIDGETS) {
   2250             Log.d(TAG, "bindAppWidget: " + item);
   2251         }
   2252         final Workspace workspace = mWorkspace;
   2253 
   2254         final int appWidgetId = item.appWidgetId;
   2255         final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   2256         if (DEBUG_WIDGETS) {
   2257             Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
   2258         }
   2259 
   2260         item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   2261 
   2262         item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
   2263         item.hostView.setTag(item);
   2264 
   2265         workspace.addInScreen(item.hostView, item.screen, item.cellX,
   2266                 item.cellY, item.spanX, item.spanY, false);
   2267 
   2268         workspace.requestLayout();
   2269 
   2270         mDesktopItems.add(item);
   2271 
   2272         if (DEBUG_WIDGETS) {
   2273             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
   2274                     + (SystemClock.uptimeMillis()-start) + "ms");
   2275         }
   2276     }
   2277 
   2278     /**
   2279      * Callback saying that there aren't any more items to bind.
   2280      *
   2281      * Implementation of the method from LauncherModel.Callbacks.
   2282      */
   2283     public void finishBindingItems() {
   2284         setLoadOnResume();
   2285 
   2286         if (mSavedState != null) {
   2287             if (!mWorkspace.hasFocus()) {
   2288                 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
   2289             }
   2290 
   2291             final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
   2292             if (userFolders != null) {
   2293                 for (long folderId : userFolders) {
   2294                     final FolderInfo info = sFolders.get(folderId);
   2295                     if (info != null) {
   2296                         openFolder(info);
   2297                     }
   2298                 }
   2299                 final Folder openFolder = mWorkspace.getOpenFolder();
   2300                 if (openFolder != null) {
   2301                     openFolder.requestFocus();
   2302                 }
   2303             }
   2304 
   2305             mSavedState = null;
   2306         }
   2307 
   2308         if (mSavedInstanceState != null) {
   2309             super.onRestoreInstanceState(mSavedInstanceState);
   2310             mSavedInstanceState = null;
   2311         }
   2312 
   2313         mWorkspaceLoading = false;
   2314     }
   2315 
   2316     /**
   2317      * Add the icons for all apps.
   2318      *
   2319      * Implementation of the method from LauncherModel.Callbacks.
   2320      */
   2321     public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
   2322         mAllAppsGrid.setApps(apps);
   2323     }
   2324 
   2325     /**
   2326      * A package was installed.
   2327      *
   2328      * Implementation of the method from LauncherModel.Callbacks.
   2329      */
   2330     public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
   2331         setLoadOnResume();
   2332         removeDialog(DIALOG_CREATE_SHORTCUT);
   2333         mAllAppsGrid.addApps(apps);
   2334     }
   2335 
   2336     /**
   2337      * A package was updated.
   2338      *
   2339      * Implementation of the method from LauncherModel.Callbacks.
   2340      */
   2341     public void bindAppsUpdated(ArrayList<ApplicationInfo> apps) {
   2342         setLoadOnResume();
   2343         removeDialog(DIALOG_CREATE_SHORTCUT);
   2344         mWorkspace.updateShortcuts(apps);
   2345         mAllAppsGrid.updateApps(apps);
   2346     }
   2347 
   2348     /**
   2349      * A package was uninstalled.
   2350      *
   2351      * Implementation of the method from LauncherModel.Callbacks.
   2352      */
   2353     public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent) {
   2354         removeDialog(DIALOG_CREATE_SHORTCUT);
   2355         if (permanent) {
   2356             mWorkspace.removeItems(apps);
   2357         }
   2358         mAllAppsGrid.removeApps(apps);
   2359     }
   2360 
   2361     /**
   2362      * Prints out out state for debugging.
   2363      */
   2364     public void dumpState() {
   2365         Log.d(TAG, "BEGIN launcher2 dump state for launcher " + this);
   2366         Log.d(TAG, "mSavedState=" + mSavedState);
   2367         Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
   2368         Log.d(TAG, "mRestoring=" + mRestoring);
   2369         Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
   2370         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
   2371         Log.d(TAG, "mDesktopItems.size=" + mDesktopItems.size());
   2372         Log.d(TAG, "sFolders.size=" + sFolders.size());
   2373         mModel.dumpState();
   2374         mAllAppsGrid.dumpState();
   2375         Log.d(TAG, "END launcher2 dump state");
   2376     }
   2377 }
   2378