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