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