Launcher.java revision 5974adcc7261a006f4f3857074568d49bd05e1f4
1
2/*
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.launcher2;
19
20import com.android.common.Search;
21import com.android.launcher.R;
22
23import android.animation.Animator;
24import android.animation.AnimatorListenerAdapter;
25import android.animation.AnimatorSet;
26import android.animation.ObjectAnimator;
27import android.animation.PropertyValuesHolder;
28import android.animation.ValueAnimator;
29import android.app.Activity;
30import android.app.AlertDialog;
31import android.app.Dialog;
32import android.app.SearchManager;
33import android.app.StatusBarManager;
34import android.app.WallpaperManager;
35import android.appwidget.AppWidgetManager;
36import android.appwidget.AppWidgetProviderInfo;
37import android.content.ActivityNotFoundException;
38import android.content.BroadcastReceiver;
39import android.content.ComponentName;
40import android.content.ContentResolver;
41import android.content.Context;
42import android.content.DialogInterface;
43import android.content.Intent;
44import android.content.Intent.ShortcutIconResource;
45import android.content.IntentFilter;
46import android.content.pm.ActivityInfo;
47import android.content.pm.PackageManager;
48import android.content.pm.PackageManager.NameNotFoundException;
49import android.content.pm.ResolveInfo;
50import android.content.res.Configuration;
51import android.content.res.Resources;
52import android.content.res.TypedArray;
53import android.database.ContentObserver;
54import android.graphics.Bitmap;
55import android.graphics.Canvas;
56import android.graphics.Rect;
57import android.graphics.drawable.ColorDrawable;
58import android.graphics.drawable.Drawable;
59import android.net.Uri;
60import android.os.AsyncTask;
61import android.os.Bundle;
62import android.os.Environment;
63import android.os.Handler;
64import android.os.Parcelable;
65import android.os.SystemClock;
66import android.os.SystemProperties;
67import android.provider.LiveFolders;
68import android.provider.Settings;
69import android.text.Selection;
70import android.text.SpannableStringBuilder;
71import android.text.TextUtils;
72import android.text.method.TextKeyListener;
73import android.util.Log;
74import android.view.Display;
75import android.view.HapticFeedbackConstants;
76import android.view.KeyEvent;
77import android.view.LayoutInflater;
78import android.view.Menu;
79import android.view.MenuItem;
80import android.view.MotionEvent;
81import android.view.View;
82import android.view.View.OnLongClickListener;
83import android.view.ViewGroup;
84import android.view.WindowManager;
85import android.view.animation.AccelerateInterpolator;
86import android.view.animation.DecelerateInterpolator;
87import android.view.inputmethod.InputMethodManager;
88import android.widget.EditText;
89import android.widget.ImageView;
90import android.widget.LinearLayout;
91import android.widget.PopupWindow;
92import android.widget.TabHost;
93import android.widget.TabHost.OnTabChangeListener;
94import android.widget.TabHost.TabContentFactory;
95import android.widget.TextView;
96import android.widget.Toast;
97
98import java.io.DataInputStream;
99import java.io.DataOutputStream;
100import java.io.FileNotFoundException;
101import java.io.IOException;
102import java.util.ArrayList;
103import java.util.HashMap;
104import java.util.List;
105
106/**
107 * Default launcher application.
108 */
109public final class Launcher extends Activity
110        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
111                   AllAppsView.Watcher, View.OnTouchListener {
112    static final String TAG = "Launcher";
113    static final boolean LOGD = false;
114
115    static final boolean PROFILE_STARTUP = false;
116    static final boolean DEBUG_WIDGETS = false;
117    static final boolean DEBUG_USER_INTERFACE = false;
118
119    private static final int MENU_GROUP_ADD = 1;
120    private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1;
121
122    private static final int MENU_ADD = Menu.FIRST + 1;
123    private static final int MENU_MANAGE_APPS = MENU_ADD + 1;
124    private static final int MENU_WALLPAPER_SETTINGS = MENU_MANAGE_APPS + 1;
125    private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
126    private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1;
127    private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
128
129    private static final int REQUEST_CREATE_SHORTCUT = 1;
130    private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
131    private static final int REQUEST_CREATE_APPWIDGET = 5;
132    private static final int REQUEST_PICK_APPLICATION = 6;
133    private static final int REQUEST_PICK_SHORTCUT = 7;
134    private static final int REQUEST_PICK_LIVE_FOLDER = 8;
135    private static final int REQUEST_PICK_APPWIDGET = 9;
136    private static final int REQUEST_PICK_WALLPAPER = 10;
137
138    static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
139
140    static final int SCREEN_COUNT = 5;
141    static final int DEFAULT_SCREEN = 2;
142
143    static final int DIALOG_CREATE_SHORTCUT = 1;
144    static final int DIALOG_RENAME_FOLDER = 2;
145
146    private static final String PREFERENCES = "launcher.preferences";
147
148    // Type: int
149    private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
150    // Type: int
151    private static final String RUNTIME_STATE = "launcher.state";
152    // Type: long
153    private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder";
154    // Type: int
155    private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
156    // Type: int
157    private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX";
158    // Type: int
159    private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY";
160    // Type: boolean
161    private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
162    // Type: long
163    private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
164
165    // tags for the customization tabs
166    private static final String WIDGETS_TAG = "widgets";
167    private static final String APPLICATIONS_TAG = "applications";
168    private static final String SHORTCUTS_TAG = "shortcuts";
169    private static final String WALLPAPERS_TAG = "wallpapers";
170
171    private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
172
173    /** The different states that Launcher can be in. */
174    private enum State { WORKSPACE, ALL_APPS, CUSTOMIZE, OVERVIEW };
175    private State mState = State.WORKSPACE;
176    private AnimatorSet mStateAnimation;
177
178    static final int APPWIDGET_HOST_ID = 1024;
179
180    private static final Object sLock = new Object();
181    private static int sScreen = DEFAULT_SCREEN;
182
183    private final BroadcastReceiver mCloseSystemDialogsReceiver
184            = new CloseSystemDialogsIntentReceiver();
185    private final ContentObserver mWidgetObserver = new AppWidgetResetObserver();
186
187    private LayoutInflater mInflater;
188
189    private DragController mDragController;
190    private Workspace mWorkspace;
191
192    private AppWidgetManager mAppWidgetManager;
193    private LauncherAppWidgetHost mAppWidgetHost;
194
195    private int mAddScreen = -1;
196    private int mAddIntersectCellX = -1;
197    private int mAddIntersectCellY = -1;
198    private int[] mAddDropPosition;
199    private int[] mTmpAddItemCellCoordinates = new int[2];
200
201    private FolderInfo mFolderInfo;
202
203    private DeleteZone mDeleteZone;
204    private HandleView mHandleView;
205    private AllAppsView mAllAppsGrid;
206    private TabHost mHomeCustomizationDrawer;
207
208    private PagedView mAllAppsPagedView = null;
209    private CustomizePagedView mCustomizePagedView = null;
210
211    private Bundle mSavedState;
212
213    private SpannableStringBuilder mDefaultKeySsb = null;
214
215    private boolean mWorkspaceLoading = true;
216
217    private boolean mPaused = true;
218    private boolean mRestoring;
219    private boolean mWaitingForResult;
220
221    private Bundle mSavedInstanceState;
222
223    private LauncherModel mModel;
224    private IconCache mIconCache;
225
226    private static LocaleConfiguration sLocaleConfiguration = null;
227
228    private ArrayList<ItemInfo> mDesktopItems = new ArrayList<ItemInfo>();
229    private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
230
231    private ImageView mPreviousView;
232    private ImageView mNextView;
233
234    // Hotseats (quick-launch icons next to AllApps)
235    private String[] mHotseatConfig = null;
236    private Intent[] mHotseats = null;
237    private Drawable[] mHotseatIcons = null;
238    private CharSequence[] mHotseatLabels = null;
239
240    private Intent mAppMarketIntent = null;
241
242    @Override
243    protected void onCreate(Bundle savedInstanceState) {
244        super.onCreate(savedInstanceState);
245
246        if (LauncherApplication.isInPlaceRotationEnabled()) {
247            // hide the status bar (temporary until we get the status bar design figured out)
248            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
249            this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
250        }
251
252        LauncherApplication app = ((LauncherApplication)getApplication());
253        mModel = app.setLauncher(this);
254        mIconCache = app.getIconCache();
255        mDragController = new DragController(this);
256        mInflater = getLayoutInflater();
257
258        mAppWidgetManager = AppWidgetManager.getInstance(this);
259        mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
260        mAppWidgetHost.startListening();
261
262        if (PROFILE_STARTUP) {
263            android.os.Debug.startMethodTracing(
264                    Environment.getExternalStorageDirectory() + "/launcher");
265        }
266
267        loadHotseats();
268        checkForLocaleChange();
269        setWallpaperDimension();
270        setContentView(R.layout.launcher);
271        mHomeCustomizationDrawer = (TabHost) findViewById(com.android.internal.R.id.tabhost);
272        if (mHomeCustomizationDrawer != null) {
273            mHomeCustomizationDrawer.setup();
274
275            // share the same customization workspace across all the tabs
276            mCustomizePagedView = (CustomizePagedView) mInflater.inflate(
277                    R.layout.customization_drawer, mHomeCustomizationDrawer, false);
278            TabContentFactory contentFactory = new TabContentFactory() {
279                public View createTabContent(String tag) {
280                    return mCustomizePagedView;
281                }
282            };
283
284            String widgetsLabel = getString(R.string.widgets_tab_label);
285            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WIDGETS_TAG)
286                    .setIndicator(widgetsLabel).setContent(contentFactory));
287            String applicationsLabel = getString(R.string.applications_tab_label);
288            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(APPLICATIONS_TAG)
289                    .setIndicator(applicationsLabel).setContent(contentFactory));
290            String wallpapersLabel = getString(R.string.wallpapers_tab_label);
291            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WALLPAPERS_TAG)
292                    .setIndicator(wallpapersLabel).setContent(contentFactory));
293            String shortcutsLabel = getString(R.string.shortcuts_tab_label);
294            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(SHORTCUTS_TAG)
295                    .setIndicator(shortcutsLabel).setContent(contentFactory));
296
297            mHomeCustomizationDrawer.setOnTabChangedListener(new OnTabChangeListener() {
298                public void onTabChanged(String tabId) {
299                    // animate the changing of the tab content by fading pages in and out
300                    final int duration = 150;
301                    final float alpha = mCustomizePagedView.getAlpha();
302                    ValueAnimator alphaAnim = ObjectAnimator.ofFloat(mCustomizePagedView,
303                            "alpha", alpha, 0.0f);
304                    alphaAnim.setDuration(duration);
305                    alphaAnim.addListener(new AnimatorListenerAdapter() {
306                        @Override
307                        public void onAnimationEnd(Animator animation) {
308                            String tag = mHomeCustomizationDrawer.getCurrentTabTag();
309                            if (tag == WIDGETS_TAG) {
310                                mCustomizePagedView.setCustomizationFilter(
311                                    CustomizePagedView.CustomizationType.WidgetCustomization);
312                            } else if (tag == APPLICATIONS_TAG) {
313                                mCustomizePagedView.setCustomizationFilter(
314                                        CustomizePagedView.CustomizationType.ApplicationCustomization);
315                            } else if (tag == WALLPAPERS_TAG) {
316                                mCustomizePagedView.setCustomizationFilter(
317                                    CustomizePagedView.CustomizationType.WallpaperCustomization);
318                            } else if (tag == SHORTCUTS_TAG) {
319                                mCustomizePagedView.setCustomizationFilter(
320                                        CustomizePagedView.CustomizationType.ShortcutCustomization);
321                            }
322
323                            final float alpha = mCustomizePagedView.getAlpha();
324                            ValueAnimator alphaAnim = ObjectAnimator.ofFloat(
325                                    mCustomizePagedView, "alpha", alpha, 1.0f);
326                            alphaAnim.setDuration(duration);
327                            alphaAnim.start();
328                        }
329                    });
330                    alphaAnim.start();
331                }
332            });
333        }
334        setupViews();
335
336        registerContentObservers();
337
338        lockAllApps();
339
340        mSavedState = savedInstanceState;
341        restoreState(mSavedState);
342
343        if (PROFILE_STARTUP) {
344            android.os.Debug.stopMethodTracing();
345        }
346
347        if (!mRestoring) {
348            mModel.startLoader(this, true);
349        }
350
351        // For handling default keys
352        mDefaultKeySsb = new SpannableStringBuilder();
353        Selection.setSelection(mDefaultKeySsb, 0);
354
355        IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
356        registerReceiver(mCloseSystemDialogsReceiver, filter);
357    }
358
359    private void checkForLocaleChange() {
360        if (sLocaleConfiguration == null) {
361            new AsyncTask<Void, Void, LocaleConfiguration>() {
362                @Override
363                protected LocaleConfiguration doInBackground(Void... unused) {
364                    LocaleConfiguration localeConfiguration = new LocaleConfiguration();
365                    readConfiguration(Launcher.this, localeConfiguration);
366                    return localeConfiguration;
367                }
368
369                @Override
370                protected void onPostExecute(LocaleConfiguration result) {
371                    sLocaleConfiguration = result;
372                    checkForLocaleChange();  // recursive, but now with a locale configuration
373                }
374            }.execute();
375            return;
376        }
377
378        final Configuration configuration = getResources().getConfiguration();
379
380        final String previousLocale = sLocaleConfiguration.locale;
381        final String locale = configuration.locale.toString();
382
383        final int previousMcc = sLocaleConfiguration.mcc;
384        final int mcc = configuration.mcc;
385
386        final int previousMnc = sLocaleConfiguration.mnc;
387        final int mnc = configuration.mnc;
388
389        boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
390
391        if (localeChanged) {
392            sLocaleConfiguration.locale = locale;
393            sLocaleConfiguration.mcc = mcc;
394            sLocaleConfiguration.mnc = mnc;
395
396            mIconCache.flush();
397            loadHotseats();
398
399            final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
400            new Thread("WriteLocaleConfiguration") {
401                @Override
402                public void run() {
403                    writeConfiguration(Launcher.this, localeConfiguration);
404                }
405            }.start();
406        }
407    }
408
409    private static class LocaleConfiguration {
410        public String locale;
411        public int mcc = -1;
412        public int mnc = -1;
413    }
414
415    private static void readConfiguration(Context context, LocaleConfiguration configuration) {
416        DataInputStream in = null;
417        try {
418            in = new DataInputStream(context.openFileInput(PREFERENCES));
419            configuration.locale = in.readUTF();
420            configuration.mcc = in.readInt();
421            configuration.mnc = in.readInt();
422        } catch (FileNotFoundException e) {
423            // Ignore
424        } catch (IOException e) {
425            // Ignore
426        } finally {
427            if (in != null) {
428                try {
429                    in.close();
430                } catch (IOException e) {
431                    // Ignore
432                }
433            }
434        }
435    }
436
437    private static void writeConfiguration(Context context, LocaleConfiguration configuration) {
438        DataOutputStream out = null;
439        try {
440            out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE));
441            out.writeUTF(configuration.locale);
442            out.writeInt(configuration.mcc);
443            out.writeInt(configuration.mnc);
444            out.flush();
445        } catch (FileNotFoundException e) {
446            // Ignore
447        } catch (IOException e) {
448            //noinspection ResultOfMethodCallIgnored
449            context.getFileStreamPath(PREFERENCES).delete();
450        } finally {
451            if (out != null) {
452                try {
453                    out.close();
454                } catch (IOException e) {
455                    // Ignore
456                }
457            }
458        }
459    }
460
461    static int getScreen() {
462        synchronized (sLock) {
463            return sScreen;
464        }
465    }
466
467    static void setScreen(int screen) {
468        synchronized (sLock) {
469            sScreen = screen;
470        }
471    }
472
473    private void setWallpaperDimension() {
474        WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE);
475
476        Display display = getWindowManager().getDefaultDisplay();
477        // TODO: Put back when we decide about scrolling the wallpaper
478        // boolean isPortrait = display.getWidth() < display.getHeight();
479        // final int width = isPortrait ? display.getWidth() : display.getHeight();
480        // final int height = isPortrait ? display.getHeight() : display.getWidth();
481        wpm.suggestDesiredDimensions(Math.max(display.getWidth(), display.getHeight()),
482                Math.max(display.getWidth(), display.getHeight()));
483    }
484
485    // Note: This doesn't do all the client-id magic that BrowserProvider does
486    // in Browser. (http://b/2425179)
487    private Uri getDefaultBrowserUri() {
488        String url = getString(R.string.default_browser_url);
489        if (url.indexOf("{CID}") != -1) {
490            url = url.replace("{CID}", "android-google");
491        }
492        return Uri.parse(url);
493    }
494
495    // Load the Intent templates from arrays.xml to populate the hotseats. For
496    // each Intent, if it resolves to a single app, use that as the launch
497    // intent & use that app's label as the contentDescription. Otherwise,
498    // retain the ResolveActivity so the user can pick an app.
499    private void loadHotseats() {
500        if (mHotseatConfig == null) {
501            mHotseatConfig = getResources().getStringArray(R.array.hotseats);
502            if (mHotseatConfig.length > 0) {
503                mHotseats = new Intent[mHotseatConfig.length];
504                mHotseatLabels = new CharSequence[mHotseatConfig.length];
505                mHotseatIcons = new Drawable[mHotseatConfig.length];
506            } else {
507                mHotseats = null;
508                mHotseatIcons = null;
509                mHotseatLabels = null;
510            }
511
512            TypedArray hotseatIconDrawables = getResources().obtainTypedArray(R.array.hotseat_icons);
513            for (int i=0; i<mHotseatConfig.length; i++) {
514                // load icon for this slot; currently unrelated to the actual activity
515                try {
516                    mHotseatIcons[i] = hotseatIconDrawables.getDrawable(i);
517                } catch (ArrayIndexOutOfBoundsException ex) {
518                    Log.w(TAG, "Missing hotseat_icons array item #" + i);
519                    mHotseatIcons[i] = null;
520                }
521            }
522            hotseatIconDrawables.recycle();
523        }
524
525        PackageManager pm = getPackageManager();
526        for (int i=0; i<mHotseatConfig.length; i++) {
527            Intent intent = null;
528            if (mHotseatConfig[i].equals("*BROWSER*")) {
529                // magic value meaning "launch user's default web browser"
530                // replace it with a generic web request so we can see if there is indeed a default
531                String defaultUri = getString(R.string.default_browser_url);
532                intent = new Intent(
533                        Intent.ACTION_VIEW,
534                        ((defaultUri != null)
535                            ? Uri.parse(defaultUri)
536                            : getDefaultBrowserUri())
537                    ).addCategory(Intent.CATEGORY_BROWSABLE);
538                // note: if the user launches this without a default set, she
539                // will always be taken to the default URL above; this is
540                // unavoidable as we must specify a valid URL in order for the
541                // chooser to appear, and once the user selects something, that
542                // URL is unavoidably sent to the chosen app.
543            } else {
544                try {
545                    intent = Intent.parseUri(mHotseatConfig[i], 0);
546                } catch (java.net.URISyntaxException ex) {
547                    Log.w(TAG, "Invalid hotseat intent: " + mHotseatConfig[i]);
548                    // bogus; leave intent=null
549                }
550            }
551
552            if (intent == null) {
553                mHotseats[i] = null;
554                mHotseatLabels[i] = getText(R.string.activity_not_found);
555                continue;
556            }
557
558            if (LOGD) {
559                Log.d(TAG, "loadHotseats: hotseat " + i
560                    + " initial intent=["
561                    + intent.toUri(Intent.URI_INTENT_SCHEME)
562                    + "]");
563            }
564
565            ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
566            List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
567            if (LOGD) {
568                Log.d(TAG, "Best match for intent: " + bestMatch);
569                Log.d(TAG, "All matches: ");
570                for (ResolveInfo ri : allMatches) {
571                    Log.d(TAG, "  --> " + ri);
572                }
573            }
574            // did this resolve to a single app, or the resolver?
575            if (allMatches.size() == 0 || bestMatch == null) {
576                // can't find any activity to handle this. let's leave the
577                // intent as-is and let Launcher show a toast when it fails
578                // to launch.
579                mHotseats[i] = intent;
580
581                // set accessibility text to "Not installed"
582                mHotseatLabels[i] = getText(R.string.activity_not_found);
583            } else {
584                boolean found = false;
585                for (ResolveInfo ri : allMatches) {
586                    if (bestMatch.activityInfo.name.equals(ri.activityInfo.name)
587                        && bestMatch.activityInfo.applicationInfo.packageName
588                            .equals(ri.activityInfo.applicationInfo.packageName)) {
589                        found = true;
590                        break;
591                    }
592                }
593
594                if (!found) {
595                    if (LOGD) Log.d(TAG, "Multiple options, no default yet");
596                    // the bestMatch is probably the ResolveActivity, meaning the
597                    // user has not yet selected a default
598                    // so: we'll keep the original intent for now
599                    mHotseats[i] = intent;
600
601                    // set the accessibility text to "Select shortcut"
602                    mHotseatLabels[i] = getText(R.string.title_select_shortcut);
603                } else {
604                    // we have an app!
605                    // now reconstruct the intent to launch it through the front
606                    // door
607                    ComponentName com = new ComponentName(
608                        bestMatch.activityInfo.applicationInfo.packageName,
609                        bestMatch.activityInfo.name);
610                    mHotseats[i] = new Intent(Intent.ACTION_MAIN).setComponent(com);
611
612                    // load the app label for accessibility
613                    mHotseatLabels[i] = bestMatch.activityInfo.loadLabel(pm);
614                }
615            }
616
617            if (LOGD) {
618                Log.d(TAG, "loadHotseats: hotseat " + i
619                    + " final intent=["
620                    + ((mHotseats[i] == null)
621                        ? "null"
622                        : mHotseats[i].toUri(Intent.URI_INTENT_SCHEME))
623                    + "] label=[" + mHotseatLabels[i]
624                    + "]"
625                    );
626            }
627        }
628    }
629
630    @Override
631    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
632        mWaitingForResult = false;
633
634        // The pattern used here is that a user PICKs a specific application,
635        // which, depending on the target, might need to CREATE the actual target.
636
637        // For example, the user would PICK_SHORTCUT for "Music playlist", and we
638        // launch over to the Music app to actually CREATE_SHORTCUT.
639
640        if (resultCode == RESULT_OK && mAddScreen != -1) {
641            switch (requestCode) {
642                case REQUEST_PICK_APPLICATION:
643                    completeAddApplication(
644                            this, data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
645                    break;
646                case REQUEST_PICK_SHORTCUT:
647                    processShortcut(data);
648                    break;
649                case REQUEST_CREATE_SHORTCUT:
650                    completeAddShortcut(data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
651                    break;
652                case REQUEST_PICK_LIVE_FOLDER:
653                    addLiveFolder(data);
654                    break;
655                case REQUEST_CREATE_LIVE_FOLDER:
656                    completeAddLiveFolder(data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
657                    break;
658                case REQUEST_PICK_APPWIDGET:
659                    addAppWidgetFromPick(data);
660                    break;
661                case REQUEST_CREATE_APPWIDGET:
662                    int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
663                    completeAddAppWidget(appWidgetId, mAddScreen);
664                    break;
665                case REQUEST_PICK_WALLPAPER:
666                    // We just wanted the activity result here so we can clear mWaitingForResult
667                    break;
668            }
669        } else if ((requestCode == REQUEST_PICK_APPWIDGET ||
670                requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED &&
671                data != null) {
672            // Clean up the appWidgetId if we canceled
673            int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
674            if (appWidgetId != -1) {
675                mAppWidgetHost.deleteAppWidgetId(appWidgetId);
676            }
677        }
678    }
679
680    @Override
681    protected void onResume() {
682        super.onResume();
683
684        mPaused = false;
685
686        if (mRestoring) {
687            mWorkspaceLoading = true;
688            mModel.startLoader(this, true);
689            mRestoring = false;
690        }
691        // When we resume Launcher, a different Activity might be responsible for the app
692        // market intent, so refresh the icon
693        updateAppMarketIcon();
694    }
695
696    @Override
697    protected void onPause() {
698        super.onPause();
699        // Some launcher layouts don't have a previous and next view
700        if (mPreviousView != null) {
701            dismissPreview(mPreviousView);
702        }
703        if (mNextView != null) {
704            dismissPreview(mNextView);
705        }
706        mDragController.cancelDrag();
707    }
708
709    @Override
710    public Object onRetainNonConfigurationInstance() {
711        // Flag the loader to stop early before switching
712        mModel.stopLoader();
713        mAllAppsGrid.surrender();
714        return Boolean.TRUE;
715    }
716
717    // We can't hide the IME if it was forced open.  So don't bother
718    /*
719    @Override
720    public void onWindowFocusChanged(boolean hasFocus) {
721        super.onWindowFocusChanged(hasFocus);
722
723        if (hasFocus) {
724            final InputMethodManager inputManager = (InputMethodManager)
725                    getSystemService(Context.INPUT_METHOD_SERVICE);
726            WindowManager.LayoutParams lp = getWindow().getAttributes();
727            inputManager.hideSoftInputFromWindow(lp.token, 0, new android.os.ResultReceiver(new
728                        android.os.Handler()) {
729                        protected void onReceiveResult(int resultCode, Bundle resultData) {
730                            Log.d(TAG, "ResultReceiver got resultCode=" + resultCode);
731                        }
732                    });
733            Log.d(TAG, "called hideSoftInputFromWindow from onWindowFocusChanged");
734        }
735    }
736    */
737
738    private boolean acceptFilter() {
739        final InputMethodManager inputManager = (InputMethodManager)
740                getSystemService(Context.INPUT_METHOD_SERVICE);
741        return !inputManager.isFullscreenMode();
742    }
743
744    @Override
745    public boolean onKeyDown(int keyCode, KeyEvent event) {
746        boolean handled = super.onKeyDown(keyCode, event);
747        if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) {
748            boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
749                    keyCode, event);
750            if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
751                // something usable has been typed - start a search
752                // the typed text will be retrieved and cleared by
753                // showSearchDialog()
754                // If there are multiple keystrokes before the search dialog takes focus,
755                // onSearchRequested() will be called for every keystroke,
756                // but it is idempotent, so it's fine.
757                return onSearchRequested();
758            }
759        }
760
761        // Eat the long press event so the keyboard doesn't come up.
762        if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
763            return true;
764        }
765
766        return handled;
767    }
768
769    private String getTypedText() {
770        return mDefaultKeySsb.toString();
771    }
772
773    private void clearTypedText() {
774        mDefaultKeySsb.clear();
775        mDefaultKeySsb.clearSpans();
776        Selection.setSelection(mDefaultKeySsb, 0);
777    }
778
779    /**
780     * Given the integer (ordinal) value of a State enum instance, convert it to a variable of type
781     * State
782     */
783    private static State intToState(int stateOrdinal) {
784        State state = State.WORKSPACE;
785        final State[] stateValues = State.values();
786        for (int i = 0; i < stateValues.length; i++) {
787            if (stateValues[i].ordinal() == stateOrdinal) {
788                state = stateValues[i];
789                break;
790            }
791        }
792        return state;
793    }
794
795    /**
796     * Restores the previous state, if it exists.
797     *
798     * @param savedState The previous state.
799     */
800    private void restoreState(Bundle savedState) {
801        if (savedState == null) {
802            return;
803        }
804
805        State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()));
806
807        if (state == State.ALL_APPS) {
808            showAllApps(false);
809        } else if (state == State.CUSTOMIZE) {
810            showCustomizationDrawer(false);
811        }
812
813        final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
814        if (currentScreen > -1) {
815            mWorkspace.setCurrentPage(currentScreen);
816        }
817
818        final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
819
820        if (addScreen > -1) {
821            mAddScreen = addScreen;
822            mAddIntersectCellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
823            mAddIntersectCellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
824            mRestoring = true;
825        }
826
827        boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
828        if (renameFolder) {
829            long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
830            mFolderInfo = mModel.getFolderById(this, sFolders, id);
831            mRestoring = true;
832        }
833    }
834
835    /**
836     * Finds all the views we need and configure them properly.
837     */
838    private void setupViews() {
839        final DragController dragController = mDragController;
840
841        DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer);
842        dragLayer.setDragController(dragController);
843
844        mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);
845        mAllAppsGrid.setLauncher(this);
846        mAllAppsGrid.setDragController(dragController);
847        ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
848        // Manage focusability manually since this thing is always visible (in non-xlarge)
849        ((View) mAllAppsGrid).setFocusable(false);
850
851        if (LauncherApplication.isScreenXLarge()) {
852            // They need to be INVISIBLE initially so that they will be measured in the layout.
853            // Otherwise the animations are messed up when we show them for the first time.
854            ((View) mAllAppsGrid).setVisibility(View.INVISIBLE);
855            mHomeCustomizationDrawer.setVisibility(View.INVISIBLE);
856        }
857
858        mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
859        final Workspace workspace = mWorkspace;
860        workspace.setHapticFeedbackEnabled(false);
861
862        DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
863        mDeleteZone = deleteZone;
864
865        View handleView = findViewById(R.id.all_apps_button);
866        if (handleView != null && handleView instanceof HandleView) {
867            // we don't use handle view in xlarge mode
868            mHandleView = (HandleView)handleView;
869            mHandleView.setLauncher(this);
870            mHandleView.setOnClickListener(this);
871            mHandleView.setOnLongClickListener(this);
872        }
873
874        if (mCustomizePagedView != null) {
875            mCustomizePagedView.setLauncher(this);
876            mCustomizePagedView.setDragController(dragController);
877            mCustomizePagedView.update();
878        } else {
879             ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left);
880             hotseatLeft.setContentDescription(mHotseatLabels[0]);
881             hotseatLeft.setImageDrawable(mHotseatIcons[0]);
882             ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right);
883             hotseatRight.setContentDescription(mHotseatLabels[1]);
884             hotseatRight.setImageDrawable(mHotseatIcons[1]);
885
886             mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
887             mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
888
889             Drawable previous = mPreviousView.getDrawable();
890             Drawable next = mNextView.getDrawable();
891             mWorkspace.setIndicators(previous, next);
892
893             mPreviousView.setHapticFeedbackEnabled(false);
894             mPreviousView.setOnLongClickListener(this);
895             mNextView.setHapticFeedbackEnabled(false);
896             mNextView.setOnLongClickListener(this);
897        }
898
899        workspace.setOnLongClickListener(this);
900        workspace.setDragController(dragController);
901        workspace.setLauncher(this);
902
903        deleteZone.setLauncher(this);
904        deleteZone.setDragController(dragController);
905        int deleteZoneHandleId;
906        if (LauncherApplication.isScreenXLarge()) {
907            deleteZoneHandleId = R.id.all_apps_button;
908        } else {
909            deleteZoneHandleId = R.id.all_apps_button_cluster;
910        }
911        deleteZone.setHandle(findViewById(deleteZoneHandleId));
912        dragController.addDragListener(deleteZone);
913
914        ApplicationInfoDropTarget infoButton = (ApplicationInfoDropTarget)findViewById(R.id.info_button);
915        if (infoButton != null) {
916            infoButton.setLauncher(this);
917            infoButton.setHandle(findViewById(R.id.configure_button));
918            infoButton.setDragColor(getResources().getColor(R.color.app_info_filter));
919            dragController.addDragListener(infoButton);
920        }
921
922        dragController.setDragScoller(workspace);
923        dragController.setScrollView(dragLayer);
924        dragController.setMoveTarget(workspace);
925
926        // The order here is bottom to top.
927        dragController.addDropTarget(workspace);
928        dragController.addDropTarget(deleteZone);
929        if (infoButton != null) {
930            dragController.addDropTarget(infoButton);
931        }
932    }
933
934    @SuppressWarnings({"UnusedDeclaration"})
935    public void previousScreen(View v) {
936        if (mState != State.ALL_APPS) {
937            mWorkspace.scrollLeft();
938        }
939    }
940
941    @SuppressWarnings({"UnusedDeclaration"})
942    public void nextScreen(View v) {
943        if (mState != State.ALL_APPS) {
944            mWorkspace.scrollRight();
945        }
946    }
947
948    @SuppressWarnings({"UnusedDeclaration"})
949    public void launchHotSeat(View v) {
950        if (mState == State.ALL_APPS) return;
951
952        int index = -1;
953        if (v.getId() == R.id.hotseat_left) {
954            index = 0;
955        } else if (v.getId() == R.id.hotseat_right) {
956            index = 1;
957        }
958
959        // reload these every tap; you never know when they might change
960        loadHotseats();
961        if (index >= 0 && index < mHotseats.length && mHotseats[index] != null) {
962            Intent intent = mHotseats[index];
963            startActivitySafely(
964                mHotseats[index],
965                "hotseat"
966            );
967        }
968    }
969
970    /**
971     * Creates a view representing a shortcut.
972     *
973     * @param info The data structure describing the shortcut.
974     *
975     * @return A View inflated from R.layout.application.
976     */
977    View createShortcut(ShortcutInfo info) {
978        return createShortcut(R.layout.application,
979                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
980    }
981
982    /**
983     * Creates a view representing a shortcut inflated from the specified resource.
984     *
985     * @param layoutResId The id of the XML layout used to create the shortcut.
986     * @param parent The group the shortcut belongs to.
987     * @param info The data structure describing the shortcut.
988     *
989     * @return A View inflated from layoutResId.
990     */
991    View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
992        TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);
993
994        favorite.setCompoundDrawablesWithIntrinsicBounds(null,
995                new FastBitmapDrawable(info.getIcon(mIconCache)),
996                null, null);
997        favorite.setText(info.title);
998        favorite.setTag(info);
999        favorite.setOnClickListener(this);
1000
1001        return favorite;
1002    }
1003
1004    /**
1005     * Add an application shortcut to the workspace.
1006     *
1007     * @param data The intent describing the application.
1008     * @param cellInfo The position on screen where to create the shortcut.
1009     */
1010    void completeAddApplication(Context context, Intent data, int screen,
1011            int intersectCellX, int intersectCellY) {
1012        final int[] cellXY = mTmpAddItemCellCoordinates;
1013        final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
1014
1015        if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
1016            showOutOfSpaceMessage();
1017            return;
1018        }
1019
1020        final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(),
1021                data, context);
1022
1023        if (info != null) {
1024            info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
1025                    Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1026            info.container = ItemInfo.NO_ID;
1027            mWorkspace.addApplicationShortcut(info, screen, cellXY[0], cellXY[1],
1028                    isWorkspaceLocked(), mAddIntersectCellX, mAddIntersectCellY);
1029        } else {
1030            Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
1031        }
1032    }
1033
1034    /**
1035     * Add a shortcut to the workspace.
1036     *
1037     * @param data The intent describing the shortcut.
1038     * @param cellInfo The position on screen where to create the shortcut.
1039     */
1040    private void completeAddShortcut(Intent data, int screen,
1041            int intersectCellX, int intersectCellY) {
1042        final int[] cellXY = mTmpAddItemCellCoordinates;
1043        final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
1044
1045        if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
1046            showOutOfSpaceMessage();
1047            return;
1048        }
1049
1050        final ShortcutInfo info = mModel.addShortcut(
1051                this, data, screen, cellXY[0], cellXY[1], false);
1052
1053        if (!mRestoring) {
1054            final View view = createShortcut(info);
1055            mWorkspace.addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
1056        }
1057    }
1058
1059
1060    /**
1061     * Add a widget to the workspace.
1062     *
1063     * @param appWidgetId The app widget id
1064     * @param cellInfo The position on screen where to create the widget.
1065     */
1066    private void completeAddAppWidget(int appWidgetId, int screen) {
1067        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
1068
1069        // Calculate the grid spans needed to fit this widget
1070        CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
1071        int[] spanXY = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight, null);
1072
1073        // Try finding open space on Launcher screen
1074        // We have saved the position to which the widget was dragged-- this really only matters
1075        // if we are placing widgets on a "spring-loaded" screen
1076        final int[] cellXY = mTmpAddItemCellCoordinates;
1077
1078        // For now, we don't save the coordinate where we dropped the icon because we're not
1079        // supporting spring-loaded mini-screens; however, leaving the ability to directly place
1080        // a widget on the home screen in case we want to add it in the future
1081        final int[] touchXY = null;
1082        //final int[] touchXY = mAddDropPosition;
1083        boolean findNearestVacantAreaFailed = false;
1084        boolean foundCellSpan = false;
1085        if (touchXY != null) {
1086            // when dragging and dropping, just find the closest free spot
1087            CellLayout screenLayout = (CellLayout) mWorkspace.getChildAt(screen);
1088            int[] result = screenLayout.findNearestVacantArea(
1089                    touchXY[0], touchXY[1], spanXY[0], spanXY[1], cellXY);
1090            findNearestVacantAreaFailed = (result == null);
1091            foundCellSpan = !findNearestVacantAreaFailed;
1092        } else {
1093            if (mAddIntersectCellX != -1 && mAddIntersectCellY != -1) {
1094                // if we long pressed on an empty cell to bring up a menu,
1095                // make sure we intersect the empty cell
1096                foundCellSpan = layout.findCellForSpanThatIntersects(cellXY, spanXY[0], spanXY[1],
1097                        mAddIntersectCellX, mAddIntersectCellY);
1098            } else {
1099                // if we went through the menu -> add, just find any spot
1100                foundCellSpan = layout.findCellForSpan(cellXY, spanXY[0], spanXY[1]);
1101            }
1102        }
1103
1104        if (!foundCellSpan) {
1105            if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId);
1106            showOutOfSpaceMessage();
1107            return;
1108        }
1109
1110        // Build Launcher-specific widget info and save to database
1111        LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
1112        launcherInfo.spanX = spanXY[0];
1113        launcherInfo.spanY = spanXY[1];
1114
1115        LauncherModel.addItemToDatabase(this, launcherInfo,
1116                LauncherSettings.Favorites.CONTAINER_DESKTOP,
1117                screen, cellXY[0], cellXY[1], false);
1118
1119        if (!mRestoring) {
1120            mDesktopItems.add(launcherInfo);
1121
1122            // Perform actual inflation because we're live
1123            launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
1124
1125            launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
1126            launcherInfo.hostView.setTag(launcherInfo);
1127
1128            mWorkspace.addInScreen(launcherInfo.hostView, screen, cellXY[0], cellXY[1],
1129                    launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
1130        }
1131    }
1132
1133    void showOutOfSpaceMessage() {
1134        Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
1135    }
1136
1137    public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
1138        mDesktopItems.remove(launcherInfo);
1139        launcherInfo.hostView = null;
1140    }
1141
1142    public LauncherAppWidgetHost getAppWidgetHost() {
1143        return mAppWidgetHost;
1144    }
1145
1146    void closeSystemDialogs() {
1147        getWindow().closeAllPanels();
1148
1149        try {
1150            dismissDialog(DIALOG_CREATE_SHORTCUT);
1151            // Unlock the workspace if the dialog was showing
1152        } catch (Exception e) {
1153            // An exception is thrown if the dialog is not visible, which is fine
1154        }
1155
1156        try {
1157            dismissDialog(DIALOG_RENAME_FOLDER);
1158            // Unlock the workspace if the dialog was showing
1159        } catch (Exception e) {
1160            // An exception is thrown if the dialog is not visible, which is fine
1161        }
1162
1163        // Whatever we were doing is hereby canceled.
1164        mWaitingForResult = false;
1165    }
1166
1167    @Override
1168    protected void onNewIntent(Intent intent) {
1169        super.onNewIntent(intent);
1170
1171        // Close the menu
1172        if (Intent.ACTION_MAIN.equals(intent.getAction())) {
1173            // also will cancel mWaitingForResult.
1174            closeSystemDialogs();
1175
1176            boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
1177                        != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
1178
1179            // in all these cases, only animate if we're already on home
1180            if (LauncherApplication.isScreenXLarge()) {
1181                mWorkspace.unshrink(alreadyOnHome);
1182            }
1183            if (!mWorkspace.isDefaultPageShowing()) {
1184                // on the phone, we don't animate the change to the workspace if all apps is visible
1185                mWorkspace.moveToDefaultScreen(alreadyOnHome &&
1186                        (LauncherApplication.isScreenXLarge() || mState != State.ALL_APPS));
1187            }
1188            showWorkspace(alreadyOnHome);
1189
1190            final View v = getWindow().peekDecorView();
1191            if (v != null && v.getWindowToken() != null) {
1192                InputMethodManager imm = (InputMethodManager)getSystemService(
1193                        INPUT_METHOD_SERVICE);
1194                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
1195            }
1196        }
1197    }
1198
1199    @Override
1200    protected void onRestoreInstanceState(Bundle savedInstanceState) {
1201        // Do not call super here
1202        mSavedInstanceState = savedInstanceState;
1203
1204        if (mHomeCustomizationDrawer != null) {
1205            String cur = savedInstanceState.getString("currentTab");
1206            if (cur != null) {
1207                mHomeCustomizationDrawer.setCurrentTabByTag(cur);
1208            }
1209        }
1210    }
1211
1212    @Override
1213    protected void onSaveInstanceState(Bundle outState) {
1214        outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentPage());
1215
1216        final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
1217        if (folders.size() > 0) {
1218            final int count = folders.size();
1219            long[] ids = new long[count];
1220            for (int i = 0; i < count; i++) {
1221                final FolderInfo info = folders.get(i).getInfo();
1222                ids[i] = info.id;
1223            }
1224            outState.putLongArray(RUNTIME_STATE_USER_FOLDERS, ids);
1225        } else {
1226            super.onSaveInstanceState(outState);
1227        }
1228
1229        outState.putInt(RUNTIME_STATE, mState.ordinal());
1230
1231        if (mAddScreen > -1 && mWaitingForResult) {
1232            outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, mAddScreen);
1233            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mAddIntersectCellX);
1234            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mAddIntersectCellY);
1235        }
1236
1237        if (mFolderInfo != null && mWaitingForResult) {
1238            outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
1239            outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
1240        }
1241
1242        if (mHomeCustomizationDrawer != null) {
1243            String currentTabTag = mHomeCustomizationDrawer.getCurrentTabTag();
1244            if (currentTabTag != null) {
1245                outState.putString("currentTab", currentTabTag);
1246            }
1247        }
1248    }
1249
1250    @Override
1251    public void onDestroy() {
1252        super.onDestroy();
1253
1254        try {
1255            mAppWidgetHost.stopListening();
1256        } catch (NullPointerException ex) {
1257            Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
1258        }
1259
1260        TextKeyListener.getInstance().release();
1261
1262        mModel.stopLoader();
1263
1264        unbindDesktopItems();
1265
1266        getContentResolver().unregisterContentObserver(mWidgetObserver);
1267
1268        // Some launcher layouts don't have a previous and next view
1269        if (mPreviousView != null) {
1270            dismissPreview(mPreviousView);
1271        }
1272        if (mNextView != null) {
1273            dismissPreview(mNextView);
1274        }
1275
1276        unregisterReceiver(mCloseSystemDialogsReceiver);
1277    }
1278
1279    @Override
1280    public void startActivityForResult(Intent intent, int requestCode) {
1281        if (requestCode >= 0) mWaitingForResult = true;
1282        super.startActivityForResult(intent, requestCode);
1283    }
1284
1285    @Override
1286    public void startSearch(String initialQuery, boolean selectInitialQuery,
1287            Bundle appSearchData, boolean globalSearch) {
1288
1289        showWorkspace(true);
1290
1291        if (initialQuery == null) {
1292            // Use any text typed in the launcher as the initial query
1293            initialQuery = getTypedText();
1294            clearTypedText();
1295        }
1296        if (appSearchData == null) {
1297            appSearchData = new Bundle();
1298            appSearchData.putString(Search.SOURCE, "launcher-search");
1299        }
1300
1301        final SearchManager searchManager =
1302                (SearchManager) getSystemService(Context.SEARCH_SERVICE);
1303        searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
1304            appSearchData, globalSearch);
1305    }
1306
1307    @Override
1308    public boolean onCreateOptionsMenu(Menu menu) {
1309        if (isWorkspaceLocked()) {
1310            return false;
1311        }
1312
1313        super.onCreateOptionsMenu(menu);
1314
1315        menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
1316                .setIcon(android.R.drawable.ic_menu_add)
1317                .setAlphabeticShortcut('A');
1318        menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)
1319                .setIcon(android.R.drawable.ic_menu_manage)
1320                .setAlphabeticShortcut('M');
1321        menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
1322                 .setIcon(android.R.drawable.ic_menu_gallery)
1323                 .setAlphabeticShortcut('W');
1324        menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
1325                .setIcon(android.R.drawable.ic_search_category_default)
1326                .setAlphabeticShortcut(SearchManager.MENU_KEY);
1327        menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications)
1328                .setIcon(com.android.internal.R.drawable.ic_menu_notifications)
1329                .setAlphabeticShortcut('N');
1330
1331        final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
1332        settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1333                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1334
1335        menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings)
1336                .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P')
1337                .setIntent(settings);
1338
1339        return true;
1340    }
1341
1342    @Override
1343    public boolean onPrepareOptionsMenu(Menu menu) {
1344        super.onPrepareOptionsMenu(menu);
1345
1346        // If all apps is animating, don't show the menu, because we don't know
1347        // which one to show.
1348        if (mAllAppsGrid.isAnimating()) {
1349            return false;
1350        }
1351
1352        // Only show the add and wallpaper options when we're not in all apps.
1353        boolean visible = !mAllAppsGrid.isVisible();
1354        menu.setGroupVisible(MENU_GROUP_ADD, visible);
1355        menu.setGroupVisible(MENU_GROUP_WALLPAPER, visible);
1356
1357        // Disable add if the workspace is full.
1358        if (visible) {
1359            CellLayout layout = (CellLayout) mWorkspace.getChildAt(mWorkspace.getCurrentPage());
1360            menu.setGroupEnabled(MENU_GROUP_ADD, layout.existsEmptyCell());
1361        }
1362
1363        return true;
1364    }
1365
1366    @Override
1367    public boolean onOptionsItemSelected(MenuItem item) {
1368        switch (item.getItemId()) {
1369            case MENU_ADD:
1370                addItems();
1371                return true;
1372            case MENU_MANAGE_APPS:
1373                manageApps();
1374                return true;
1375            case MENU_WALLPAPER_SETTINGS:
1376                startWallpaper();
1377                return true;
1378            case MENU_SEARCH:
1379                onSearchRequested();
1380                return true;
1381            case MENU_NOTIFICATIONS:
1382                showNotifications();
1383                return true;
1384        }
1385
1386        return super.onOptionsItemSelected(item);
1387    }
1388
1389    /**
1390     * Indicates that we want global search for this activity by setting the globalSearch
1391     * argument for {@link #startSearch} to true.
1392     */
1393
1394    @Override
1395    public boolean onSearchRequested() {
1396        startSearch(null, false, null, true);
1397        return true;
1398    }
1399
1400    public boolean isWorkspaceLocked() {
1401        return mWorkspaceLoading || mWaitingForResult;
1402    }
1403
1404    private void addItems() {
1405        if (LauncherApplication.isScreenXLarge()) {
1406            // Animate the widget chooser up from the bottom of the screen
1407            if (mState != State.CUSTOMIZE) {
1408                showCustomizationDrawer(true);
1409            }
1410        } else {
1411            showWorkspace(true);
1412            showAddDialog(-1, -1);
1413        }
1414    }
1415
1416    private void resetAddInfo() {
1417        mAddScreen = -1;
1418        mAddIntersectCellX = -1;
1419        mAddIntersectCellY = -1;
1420        mAddDropPosition = null;
1421    }
1422
1423    void addAppWidgetFromDrop(ComponentName appWidgetProvider, int screen, int[] position) {
1424        resetAddInfo();
1425        mAddScreen = screen;
1426
1427        // only set mAddDropPosition if we dropped on home screen in "spring-loaded" manner
1428        mAddDropPosition = position;
1429
1430        int appWidgetId = getAppWidgetHost().allocateAppWidgetId();
1431        AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, appWidgetProvider);
1432        addAppWidgetImpl(appWidgetId);
1433    }
1434
1435    private void manageApps() {
1436        startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS));
1437    }
1438
1439    void addAppWidgetFromPick(Intent data) {
1440        // TODO: catch bad widget exception when sent
1441        int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
1442        // TODO: Is this log message meaningful?
1443        if (LOGD) Log.d(TAG, "dumping extras content=" + data.getExtras());
1444        addAppWidgetImpl(appWidgetId);
1445    }
1446
1447    void addAppWidgetImpl(int appWidgetId) {
1448        AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
1449
1450        if (appWidget.configure != null) {
1451            // Launch over to configure widget, if needed
1452            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
1453            intent.setComponent(appWidget.configure);
1454            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1455
1456            startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
1457        } else {
1458            // Otherwise just add it
1459            completeAddAppWidget(appWidgetId, mAddScreen);
1460        }
1461    }
1462
1463    void processShortcutFromDrop(ComponentName componentName, int screen, int[] position) {
1464        resetAddInfo();
1465        mAddScreen = screen;
1466        mAddDropPosition = position;
1467
1468        Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
1469        createShortcutIntent.setComponent(componentName);
1470        processShortcut(createShortcutIntent);
1471    }
1472
1473    void processShortcut(Intent intent) {
1474        // Handle case where user selected "Applications"
1475        String applicationName = getResources().getString(R.string.group_applications);
1476        String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1477
1478        if (applicationName != null && applicationName.equals(shortcutName)) {
1479            Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1480            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1481
1482            Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
1483            pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
1484            pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
1485            startActivityForResultSafely(pickIntent, REQUEST_PICK_APPLICATION);
1486        } else {
1487            startActivityForResultSafely(intent, REQUEST_CREATE_SHORTCUT);
1488        }
1489    }
1490
1491    void processWallpaper(Intent intent) {
1492        startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
1493    }
1494
1495    void addLiveFolderFromDrop(ComponentName componentName, int screen, int[] position) {
1496        resetAddInfo();
1497        mAddScreen = screen;
1498        mAddDropPosition = position;
1499
1500        Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
1501        createFolderIntent.setComponent(componentName);
1502
1503        addLiveFolder(createFolderIntent);
1504    }
1505
1506    void addLiveFolder(Intent intent) { // YYY add screen intersect etc. parameters here
1507        // Handle case where user selected "Folder"
1508        String folderName = getResources().getString(R.string.group_folder);
1509        String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1510
1511        if (folderName != null && folderName.equals(shortcutName)) {
1512            addFolder(mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
1513        } else {
1514            startActivityForResultSafely(intent, REQUEST_CREATE_LIVE_FOLDER);
1515        }
1516    }
1517
1518    void addFolder(int screen, int intersectCellX, int intersectCellY) {
1519        UserFolderInfo folderInfo = new UserFolderInfo();
1520        folderInfo.title = getText(R.string.folder_name);
1521
1522        final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
1523        final int[] cellXY = mTmpAddItemCellCoordinates;
1524        if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
1525            showOutOfSpaceMessage();
1526            return;
1527        }
1528
1529        // Update the model
1530        LauncherModel.addItemToDatabase(this, folderInfo,
1531                LauncherSettings.Favorites.CONTAINER_DESKTOP,
1532                screen, cellXY[0], cellXY[1], false);
1533        sFolders.put(folderInfo.id, folderInfo);
1534
1535        // Create the view
1536        FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
1537                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), folderInfo);
1538        mWorkspace.addInScreen(newFolder, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
1539    }
1540
1541    void removeFolder(FolderInfo folder) {
1542        sFolders.remove(folder.id);
1543    }
1544
1545    private void completeAddLiveFolder(
1546            Intent data, int screen, int intersectCellX, int intersectCellY) {
1547        final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
1548        final int[] cellXY = mTmpAddItemCellCoordinates;
1549        if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
1550            showOutOfSpaceMessage();
1551            return;
1552        }
1553
1554        final LiveFolderInfo info = addLiveFolder(this, data, screen, cellXY[0], cellXY[1], false);
1555
1556        if (!mRestoring) {
1557            final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
1558                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
1559            mWorkspace.addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
1560        }
1561    }
1562
1563    static LiveFolderInfo addLiveFolder(Context context, Intent data,
1564            int screen, int cellX, int cellY, boolean notify) {
1565
1566        Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT);
1567        String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME);
1568
1569        Drawable icon = null;
1570        Intent.ShortcutIconResource iconResource = null;
1571
1572        Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON);
1573        if (extra != null && extra instanceof Intent.ShortcutIconResource) {
1574            try {
1575                iconResource = (Intent.ShortcutIconResource) extra;
1576                final PackageManager packageManager = context.getPackageManager();
1577                Resources resources = packageManager.getResourcesForApplication(
1578                        iconResource.packageName);
1579                final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1580                icon = resources.getDrawable(id);
1581            } catch (Exception e) {
1582                Log.w(TAG, "Could not load live folder icon: " + extra);
1583            }
1584        }
1585
1586        if (icon == null) {
1587            icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder);
1588        }
1589
1590        final LiveFolderInfo info = new LiveFolderInfo();
1591        info.icon = Utilities.createIconBitmap(icon, context);
1592        info.title = name;
1593        info.iconResource = iconResource;
1594        info.uri = data.getData();
1595        info.baseIntent = baseIntent;
1596        info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
1597                LiveFolders.DISPLAY_MODE_GRID);
1598
1599        LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1600                screen, cellX, cellY, notify);
1601        sFolders.put(info.id, info);
1602
1603        return info;
1604    }
1605
1606    private void showNotifications() {
1607        final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
1608        if (statusBar != null) {
1609            statusBar.expand();
1610        }
1611    }
1612
1613    private void startWallpaper() {
1614        showWorkspace(true);
1615        final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
1616        Intent chooser = Intent.createChooser(pickWallpaper,
1617                getText(R.string.chooser_wallpaper));
1618        // NOTE: Adds a configure option to the chooser if the wallpaper supports it
1619        //       Removed in Eclair MR1
1620//        WallpaperManager wm = (WallpaperManager)
1621//                getSystemService(Context.WALLPAPER_SERVICE);
1622//        WallpaperInfo wi = wm.getWallpaperInfo();
1623//        if (wi != null && wi.getSettingsActivity() != null) {
1624//            LabeledIntent li = new LabeledIntent(getPackageName(),
1625//                    R.string.configure_wallpaper, 0);
1626//            li.setClassName(wi.getPackageName(), wi.getSettingsActivity());
1627//            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li });
1628//        }
1629        startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
1630    }
1631
1632    /**
1633     * Registers various content observers. The current implementation registers
1634     * only a favorites observer to keep track of the favorites applications.
1635     */
1636    private void registerContentObservers() {
1637        ContentResolver resolver = getContentResolver();
1638        resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
1639                true, mWidgetObserver);
1640    }
1641
1642    @Override
1643    public boolean dispatchKeyEvent(KeyEvent event) {
1644        if (event.getAction() == KeyEvent.ACTION_DOWN) {
1645            switch (event.getKeyCode()) {
1646                case KeyEvent.KEYCODE_HOME:
1647                    return true;
1648                case KeyEvent.KEYCODE_VOLUME_DOWN:
1649                    if (SystemProperties.getInt("debug.launcher2.dumpstate", 0) != 0) {
1650                        dumpState();
1651                        return true;
1652                    }
1653                    break;
1654            }
1655        } else if (event.getAction() == KeyEvent.ACTION_UP) {
1656            switch (event.getKeyCode()) {
1657                case KeyEvent.KEYCODE_HOME:
1658                    return true;
1659            }
1660        }
1661
1662        return super.dispatchKeyEvent(event);
1663    }
1664
1665    @Override
1666    public void onBackPressed() {
1667        if (mState == State.ALL_APPS || mState == State.CUSTOMIZE) {
1668            showWorkspace(true);
1669        } else {
1670            closeFolder();
1671        }
1672        // Some launcher layouts don't have a previous and next view
1673        if (mPreviousView != null) {
1674            dismissPreview(mPreviousView);
1675            dismissPreview(mNextView);
1676        }
1677    }
1678
1679    private void closeFolder() {
1680        Folder folder = mWorkspace.getOpenFolder();
1681        if (folder != null) {
1682            closeFolder(folder);
1683        }
1684    }
1685
1686    void closeFolder(Folder folder) {
1687        folder.getInfo().opened = false;
1688        ViewGroup parent = (ViewGroup) folder.getParent();
1689        if (parent != null) {
1690            CellLayout cl = (CellLayout) parent;
1691            cl.removeViewWithoutMarkingCells(folder);
1692            if (folder instanceof DropTarget) {
1693                // Live folders aren't DropTargets.
1694                mDragController.removeDropTarget((DropTarget)folder);
1695            }
1696        }
1697        folder.onClose();
1698    }
1699
1700    /**
1701     * Re-listen when widgets are reset.
1702     */
1703    private void onAppWidgetReset() {
1704        mAppWidgetHost.startListening();
1705    }
1706
1707    /**
1708     * Go through the and disconnect any of the callbacks in the drawables and the views or we
1709     * leak the previous Home screen on orientation change.
1710     */
1711    private void unbindDesktopItems() {
1712        for (ItemInfo item: mDesktopItems) {
1713            item.unbind();
1714        }
1715    }
1716
1717    /**
1718     * Launches the intent referred by the clicked shortcut.
1719     *
1720     * @param v The view representing the clicked shortcut.
1721     */
1722    public void onClick(View v) {
1723        Object tag = v.getTag();
1724        if (tag instanceof ShortcutInfo) {
1725            // Open shortcut
1726            final Intent intent = ((ShortcutInfo) tag).intent;
1727            int[] pos = new int[2];
1728            v.getLocationOnScreen(pos);
1729            intent.setSourceBounds(new Rect(pos[0], pos[1],
1730                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
1731            startActivitySafely(intent, tag);
1732        } else if (tag instanceof FolderInfo) {
1733            handleFolderClick((FolderInfo) tag);
1734        } else if (v == mHandleView) {
1735            if (mState == State.ALL_APPS) {
1736                showWorkspace(true);
1737            } else {
1738                showAllApps(true);
1739            }
1740        }
1741    }
1742
1743    public boolean onTouch(View v, MotionEvent event) {
1744        // this is an intercepted event being forwarded from mWorkspace;
1745        // clicking anywhere on the workspace causes the customization drawer to slide down
1746        showWorkspace(true);
1747        return false;
1748    }
1749
1750    /**
1751     * Event handler for the search button
1752     *
1753     * @param v The view that was clicked.
1754     */
1755    public void onClickSearchButton(View v) {
1756        startSearch(null, false, null, true);
1757    }
1758
1759    /**
1760     * Event handler for the "gear" button that appears on the home screen, which
1761     * enters home screen customization mode.
1762     *
1763     * @param v The view that was clicked.
1764     */
1765    public void onClickConfigureButton(View v) {
1766        addItems();
1767    }
1768
1769    /**
1770     * Event handler for the "grid" button that appears on the home screen, which
1771     * enters all apps mode.
1772     *
1773     * @param v The view that was clicked.
1774     */
1775    public void onClickAllAppsButton(View v) {
1776        showAllApps(true);
1777    }
1778
1779    public void onClickAppMarketButton(View v) {
1780        if (mAppMarketIntent != null) {
1781            startActivitySafely(mAppMarketIntent, "app market");
1782        }
1783    }
1784
1785    void startApplicationDetailsActivity(ComponentName componentName) {
1786        String packageName = componentName.getPackageName();
1787        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
1788                Uri.fromParts("package", packageName, null));
1789        startActivity(intent);
1790    }
1791
1792    void startApplicationUninstallActivity(ApplicationInfo appInfo) {
1793        if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) == 0) {
1794            // System applications cannot be installed. For now, show a toast explaining that.
1795            // We may give them the option of disabling apps this way.
1796            int messageId = R.string.uninstall_system_app_text;
1797            Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
1798        } else {
1799            String packageName = appInfo.componentName.getPackageName();
1800            String className = appInfo.componentName.getClassName();
1801            Intent intent = new Intent(
1802                    Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
1803            startActivity(intent);
1804        }
1805    }
1806
1807    void startActivitySafely(Intent intent, Object tag) {
1808        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1809        try {
1810            startActivity(intent);
1811        } catch (ActivityNotFoundException e) {
1812            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1813            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
1814        } catch (SecurityException e) {
1815            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1816            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
1817                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
1818                    "or use the exported attribute for this activity. "
1819                    + "tag="+ tag + " intent=" + intent, e);
1820        }
1821    }
1822
1823    void startActivityForResultSafely(Intent intent, int requestCode) {
1824        try {
1825            startActivityForResult(intent, requestCode);
1826        } catch (ActivityNotFoundException e) {
1827            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1828        } catch (SecurityException e) {
1829            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1830            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
1831                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
1832                    "or use the exported attribute for this activity.", e);
1833        }
1834    }
1835
1836    private void handleFolderClick(FolderInfo folderInfo) {
1837        if (!folderInfo.opened) {
1838            // Close any open folder
1839            closeFolder();
1840            // Open the requested folder
1841            openFolder(folderInfo);
1842        } else {
1843            // Find the open folder...
1844            Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
1845            int folderScreen;
1846            if (openFolder != null) {
1847                folderScreen = mWorkspace.getPageForView(openFolder);
1848                // .. and close it
1849                closeFolder(openFolder);
1850                if (folderScreen != mWorkspace.getCurrentPage()) {
1851                    // Close any folder open on the current screen
1852                    closeFolder();
1853                    // Pull the folder onto this screen
1854                    openFolder(folderInfo);
1855                }
1856            }
1857        }
1858    }
1859
1860    /**
1861     * Opens the user folder described by the specified tag. The opening of the folder
1862     * is animated relative to the specified View. If the View is null, no animation
1863     * is played.
1864     *
1865     * @param folderInfo The FolderInfo describing the folder to open.
1866     */
1867    public void openFolder(FolderInfo folderInfo) {
1868        Folder openFolder;
1869
1870        if (folderInfo instanceof UserFolderInfo) {
1871            openFolder = UserFolder.fromXml(this);
1872        } else if (folderInfo instanceof LiveFolderInfo) {
1873            openFolder = com.android.launcher2.LiveFolder.fromXml(this, folderInfo);
1874        } else {
1875            return;
1876        }
1877
1878        openFolder.setDragController(mDragController);
1879        openFolder.setLauncher(this);
1880
1881        openFolder.bind(folderInfo);
1882        folderInfo.opened = true;
1883
1884        mWorkspace.addInFullScreen(openFolder, folderInfo.screen);
1885
1886        openFolder.onOpen();
1887    }
1888
1889    public boolean onLongClick(View v) {
1890        switch (v.getId()) {
1891            case R.id.previous_screen:
1892                if (mState != State.ALL_APPS) {
1893                    mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1894                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1895                    showPreviews(v);
1896                }
1897                return true;
1898            case R.id.next_screen:
1899                if (mState != State.ALL_APPS) {
1900                    mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1901                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1902                    showPreviews(v);
1903                }
1904                return true;
1905            case R.id.all_apps_button:
1906                if (mState != State.ALL_APPS) {
1907                    mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1908                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1909                    showPreviews(v);
1910                }
1911                return true;
1912        }
1913
1914        if (isWorkspaceLocked()) {
1915            return false;
1916        }
1917
1918        if (!(v instanceof CellLayout)) {
1919            v = (View) v.getParent();
1920        }
1921
1922
1923        resetAddInfo();
1924        CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag();
1925        // This happens when long clicking an item with the dpad/trackball
1926        if (longClickCellInfo == null || !longClickCellInfo.valid) {
1927            return true;
1928        }
1929
1930        final View itemUnderLongClick = longClickCellInfo.cell;
1931
1932        if (mWorkspace.allowLongPress()) {
1933            if (itemUnderLongClick == null) {
1934                // User long pressed on empty space
1935                mWorkspace.setAllowLongPress(false);
1936                mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1937                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1938                if (!LauncherApplication.isScreenXLarge()) {
1939                    showAddDialog(longClickCellInfo.cellX, longClickCellInfo.cellY);
1940                }
1941            } else {
1942                if (!(itemUnderLongClick instanceof Folder)) {
1943                    // User long pressed on an item
1944                    mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1945                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1946                    mAddIntersectCellX = longClickCellInfo.cellX;
1947                    mAddIntersectCellY = longClickCellInfo.cellY;
1948                    mWorkspace.startDrag(longClickCellInfo);
1949                }
1950            }
1951        }
1952        return true;
1953    }
1954
1955    @SuppressWarnings({"unchecked"})
1956    private void dismissPreview(final View v) {
1957        final PopupWindow window = (PopupWindow) v.getTag();
1958        if (window != null) {
1959            window.setOnDismissListener(new PopupWindow.OnDismissListener() {
1960                public void onDismiss() {
1961                    ViewGroup group = (ViewGroup) v.getTag(R.id.workspace);
1962                    int count = group.getChildCount();
1963                    for (int i = 0; i < count; i++) {
1964                        ((ImageView) group.getChildAt(i)).setImageDrawable(null);
1965                    }
1966                    ArrayList<Bitmap> bitmaps = (ArrayList<Bitmap>) v.getTag(R.id.icon);
1967                    for (Bitmap bitmap : bitmaps) bitmap.recycle();
1968
1969                    v.setTag(R.id.workspace, null);
1970                    v.setTag(R.id.icon, null);
1971                    window.setOnDismissListener(null);
1972                }
1973            });
1974            window.dismiss();
1975        }
1976        v.setTag(null);
1977    }
1978
1979    private void showPreviews(View anchor) {
1980        showPreviews(anchor, 0, mWorkspace.getChildCount());
1981    }
1982
1983    private void showPreviews(final View anchor, int start, int end) {
1984        final Resources resources = getResources();
1985        final Workspace workspace = mWorkspace;
1986
1987        CellLayout cell = ((CellLayout) workspace.getChildAt(start));
1988
1989        float max = workspace.getChildCount();
1990
1991        final Rect r = new Rect();
1992        resources.getDrawable(R.drawable.preview_background).getPadding(r);
1993        int extraW = (int) ((r.left + r.right) * max);
1994        int extraH = r.top + r.bottom;
1995
1996        int aW = cell.getWidth() - extraW;
1997        float w = aW / max;
1998
1999        int width = cell.getWidth();
2000        int height = cell.getHeight();
2001        int x = cell.getLeftPadding();
2002        int y = cell.getTopPadding();
2003        width -= (x + cell.getRightPadding());
2004        height -= (y + cell.getBottomPadding());
2005
2006        float scale = w / width;
2007
2008        int count = end - start;
2009
2010        final float sWidth = width * scale;
2011        float sHeight = height * scale;
2012
2013        LinearLayout preview = new LinearLayout(this);
2014
2015        PreviewTouchHandler handler = new PreviewTouchHandler(anchor);
2016        ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>(count);
2017
2018        for (int i = start; i < end; i++) {
2019            ImageView image = new ImageView(this);
2020            cell = (CellLayout) workspace.getChildAt(i);
2021
2022            final Bitmap bitmap = Bitmap.createBitmap((int) sWidth, (int) sHeight,
2023                    Bitmap.Config.ARGB_8888);
2024
2025            final Canvas c = new Canvas(bitmap);
2026            c.scale(scale, scale);
2027            c.translate(-cell.getLeftPadding(), -cell.getTopPadding());
2028            cell.drawChildren(c);
2029
2030            image.setBackgroundDrawable(resources.getDrawable(R.drawable.preview_background));
2031            image.setImageBitmap(bitmap);
2032            image.setTag(i);
2033            image.setOnClickListener(handler);
2034            image.setOnFocusChangeListener(handler);
2035            image.setFocusable(true);
2036            if (i == mWorkspace.getCurrentPage()) image.requestFocus();
2037
2038            preview.addView(image,
2039                    LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
2040
2041            bitmaps.add(bitmap);
2042        }
2043
2044        final PopupWindow p = new PopupWindow(this);
2045        p.setContentView(preview);
2046        p.setWidth((int) (sWidth * count + extraW));
2047        p.setHeight((int) (sHeight + extraH));
2048        p.setAnimationStyle(R.style.AnimationPreview);
2049        p.setOutsideTouchable(true);
2050        p.setFocusable(true);
2051        p.setBackgroundDrawable(new ColorDrawable(0));
2052        p.showAsDropDown(anchor, 0, 0);
2053
2054        p.setOnDismissListener(new PopupWindow.OnDismissListener() {
2055            public void onDismiss() {
2056                dismissPreview(anchor);
2057            }
2058        });
2059
2060        anchor.setTag(p);
2061        anchor.setTag(R.id.workspace, preview);
2062        anchor.setTag(R.id.icon, bitmaps);
2063    }
2064
2065    class PreviewTouchHandler implements View.OnClickListener, Runnable, View.OnFocusChangeListener {
2066        private final View mAnchor;
2067
2068        public PreviewTouchHandler(View anchor) {
2069            mAnchor = anchor;
2070        }
2071
2072        public void onClick(View v) {
2073            mWorkspace.snapToPage((Integer) v.getTag());
2074            v.post(this);
2075        }
2076
2077        public void run() {
2078            dismissPreview(mAnchor);
2079        }
2080
2081        public void onFocusChange(View v, boolean hasFocus) {
2082            if (hasFocus) {
2083                mWorkspace.snapToPage((Integer) v.getTag());
2084            }
2085        }
2086    }
2087
2088    Workspace getWorkspace() {
2089        return mWorkspace;
2090    }
2091
2092    @Override
2093    protected Dialog onCreateDialog(int id) {
2094        switch (id) {
2095            case DIALOG_CREATE_SHORTCUT:
2096                return new CreateShortcut().createDialog();
2097            case DIALOG_RENAME_FOLDER:
2098                return new RenameFolder().createDialog();
2099        }
2100
2101        return super.onCreateDialog(id);
2102    }
2103
2104    @Override
2105    protected void onPrepareDialog(int id, Dialog dialog) {
2106        switch (id) {
2107            case DIALOG_CREATE_SHORTCUT:
2108                break;
2109            case DIALOG_RENAME_FOLDER:
2110                if (mFolderInfo != null) {
2111                    EditText input = (EditText) dialog.findViewById(R.id.folder_name);
2112                    final CharSequence text = mFolderInfo.title;
2113                    input.setText(text);
2114                    input.setSelection(0, text.length());
2115                }
2116                break;
2117        }
2118    }
2119
2120    void showRenameDialog(FolderInfo info) {
2121        mFolderInfo = info;
2122        mWaitingForResult = true;
2123        showDialog(DIALOG_RENAME_FOLDER);
2124    }
2125
2126    private void showAddDialog(int intersectX, int intersectY) {
2127        resetAddInfo();
2128        mAddIntersectCellX = intersectX;
2129        mAddIntersectCellY = intersectY;
2130        mAddScreen = mWorkspace.getCurrentPage();
2131        mWaitingForResult = true;
2132        showDialog(DIALOG_CREATE_SHORTCUT);
2133    }
2134
2135    private void pickShortcut() {
2136        // Insert extra item to handle picking application
2137        Bundle bundle = new Bundle();
2138
2139        ArrayList<String> shortcutNames = new ArrayList<String>();
2140        shortcutNames.add(getString(R.string.group_applications));
2141        bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
2142
2143        ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>();
2144        shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
2145                        R.drawable.ic_launcher_application));
2146        bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
2147
2148        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
2149        pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT));
2150        pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_shortcut));
2151        pickIntent.putExtras(bundle);
2152
2153        startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
2154    }
2155
2156    private class RenameFolder {
2157        private EditText mInput;
2158
2159        Dialog createDialog() {
2160            final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null);
2161            mInput = (EditText) layout.findViewById(R.id.folder_name);
2162
2163            AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
2164            builder.setIcon(0);
2165            builder.setTitle(getString(R.string.rename_folder_title));
2166            builder.setCancelable(true);
2167            builder.setOnCancelListener(new Dialog.OnCancelListener() {
2168                public void onCancel(DialogInterface dialog) {
2169                    cleanup();
2170                }
2171            });
2172            builder.setNegativeButton(getString(R.string.cancel_action),
2173                new Dialog.OnClickListener() {
2174                    public void onClick(DialogInterface dialog, int which) {
2175                        cleanup();
2176                    }
2177                }
2178            );
2179            builder.setPositiveButton(getString(R.string.rename_action),
2180                new Dialog.OnClickListener() {
2181                    public void onClick(DialogInterface dialog, int which) {
2182                        changeFolderName();
2183                    }
2184                }
2185            );
2186            builder.setView(layout);
2187
2188            final AlertDialog dialog = builder.create();
2189            dialog.setOnShowListener(new DialogInterface.OnShowListener() {
2190                public void onShow(DialogInterface dialog) {
2191                    mWaitingForResult = true;
2192                    mInput.requestFocus();
2193                    InputMethodManager inputManager = (InputMethodManager)
2194                            getSystemService(Context.INPUT_METHOD_SERVICE);
2195                    inputManager.showSoftInput(mInput, 0);
2196                }
2197            });
2198
2199            return dialog;
2200        }
2201
2202        private void changeFolderName() {
2203            final String name = mInput.getText().toString();
2204            if (!TextUtils.isEmpty(name)) {
2205                // Make sure we have the right folder info
2206                mFolderInfo = sFolders.get(mFolderInfo.id);
2207                mFolderInfo.title = name;
2208                LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
2209
2210                if (mWorkspaceLoading) {
2211                    lockAllApps();
2212                    mModel.startLoader(Launcher.this, false);
2213                } else {
2214                    final FolderIcon folderIcon = (FolderIcon)
2215                            mWorkspace.getViewForTag(mFolderInfo);
2216                    if (folderIcon != null) {
2217                        folderIcon.setText(name);
2218                        getWorkspace().requestLayout();
2219                    } else {
2220                        lockAllApps();
2221                        mWorkspaceLoading = true;
2222                        mModel.startLoader(Launcher.this, false);
2223                    }
2224                }
2225            }
2226            cleanup();
2227        }
2228
2229        private void cleanup() {
2230            dismissDialog(DIALOG_RENAME_FOLDER);
2231            mWaitingForResult = false;
2232            mFolderInfo = null;
2233        }
2234    }
2235
2236    // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
2237    public boolean isAllAppsVisible() {
2238        return mState == State.ALL_APPS;
2239    }
2240
2241    // AllAppsView.Watcher
2242    public void zoomed(float zoom) {
2243        // In XLarge view, we zoom down the workspace below all apps so it's still visible
2244        if (zoom == 1.0f && !LauncherApplication.isScreenXLarge()) {
2245            mWorkspace.setVisibility(View.GONE);
2246        }
2247    }
2248
2249    private void showToolbarButton(View button) {
2250        button.setAlpha(1.0f);
2251        button.setVisibility(View.VISIBLE);
2252        button.setFocusable(true);
2253        button.setClickable(true);
2254    }
2255
2256    private void hideToolbarButton(View button) {
2257        button.setAlpha(0.0f);
2258        // We can't set it to GONE, otherwise the RelativeLayout gets screwed up
2259        button.setVisibility(View.INVISIBLE);
2260        button.setFocusable(false);
2261        button.setClickable(false);
2262    }
2263
2264    /**
2265     * Helper function for showing or hiding a toolbar button, possibly animated.
2266     *
2267     * @param show If true, create an animation to the show the item. Otherwise, hide it.
2268     * @param view The toolbar button to be animated
2269     * @param seq A AnimatorSet that will be used to animate the transition. If null, the
2270     * transition will not be animated.
2271     */
2272    private void hideOrShowToolbarButton(boolean show, final View view, AnimatorSet seq) {
2273        final boolean showing = show;
2274        final boolean hiding = !show;
2275
2276        final int duration = show ?
2277                getResources().getInteger(R.integer.config_toolbarButtonFadeInTime) :
2278                getResources().getInteger(R.integer.config_toolbarButtonFadeOutTime);
2279
2280        if (seq != null) {
2281            Animator anim = ObjectAnimator.ofFloat(view, "alpha", show ? 1.0f : 0.0f);
2282            anim.setDuration(duration);
2283            anim.addListener(new AnimatorListenerAdapter() {
2284                @Override
2285                public void onAnimationStart(Animator animation) {
2286                    if (showing) showToolbarButton(view);
2287                }
2288                @Override
2289                public void onAnimationEnd(Animator animation) {
2290                    if (hiding) hideToolbarButton(view);
2291                }
2292            });
2293            seq.play(anim);
2294        } else {
2295            if (showing) {
2296                showToolbarButton(view);
2297            } else {
2298                hideToolbarButton(view);
2299            }
2300        }
2301    }
2302
2303    /**
2304     * Show/hide the appropriate toolbar buttons for newState.
2305     * If showSeq or hideSeq is null, the transition will be done immediately (not animated).
2306     *
2307     * @param newState The state that is being switched to
2308     * @param showSeq AnimatorSet in which to put "show" animations, or null.
2309     * @param hideSeq AnimatorSet in which to put "hide" animations, or null.
2310     */
2311    private void hideAndShowToolbarButtons(State newState, AnimatorSet showSeq, AnimatorSet hideSeq) {
2312        final View searchButton = findViewById(R.id.search_button);
2313        final View allAppsButton = findViewById(R.id.all_apps_button);
2314        final View marketButton = findViewById(R.id.market_button);
2315        final View configureButton = findViewById(R.id.configure_button);
2316
2317        switch (newState) {
2318        case WORKSPACE:
2319            hideOrShowToolbarButton(true, searchButton, showSeq);
2320            hideOrShowToolbarButton(true, allAppsButton, showSeq);
2321            hideOrShowToolbarButton(true, configureButton, showSeq);
2322            hideOrShowToolbarButton(false, marketButton, hideSeq);
2323            mDeleteZone.setHandle(allAppsButton);
2324            break;
2325        case ALL_APPS:
2326            hideOrShowToolbarButton(true, configureButton, showSeq);
2327            hideOrShowToolbarButton(true, marketButton, showSeq);
2328            hideOrShowToolbarButton(false, searchButton, hideSeq);
2329            hideOrShowToolbarButton(false, allAppsButton, hideSeq);
2330            mDeleteZone.setHandle(marketButton);
2331            break;
2332        case CUSTOMIZE:
2333            hideOrShowToolbarButton(true, allAppsButton, showSeq);
2334            hideOrShowToolbarButton(false, searchButton, hideSeq);
2335            hideOrShowToolbarButton(false, marketButton, hideSeq);
2336            hideOrShowToolbarButton(false, configureButton, hideSeq);
2337            mDeleteZone.setHandle(allAppsButton);
2338            break;
2339        }
2340    }
2341
2342    /**
2343     * Helper method for the cameraZoomIn/cameraZoomOut animations
2344     * @param view The view being animated
2345     * @param state The state that we are moving in or out of -- either ALL_APPS or CUSTOMIZE
2346     * @param scaleFactor The scale factor used for the zoom
2347     */
2348    private void setPivotsForZoom(View view, State state, float scaleFactor) {
2349        final int height = view.getHeight();
2350        view.setPivotX(view.getWidth() / 2.0f);
2351        // Set pivotY so that at the starting zoom factor, the view is off-screen by a small margin
2352        // Assumes that the view is normally anchored to either the top or bottom of the screen
2353        final int margin = getResources().getInteger(R.integer.config_allAppsVerticalOffset);
2354        if (state == State.ALL_APPS) {
2355            view.setPivotY(height + ((view.getTop() + height) / scaleFactor) + margin);
2356        } else {
2357            view.setPivotY(0.0f - (view.getTop() / scaleFactor) - margin);
2358        }
2359    }
2360
2361    /**
2362     * Zoom the camera out from the workspace to reveal 'toView'.
2363     * Assumes that the view to show is anchored at either the very top or very bottom
2364     * of the screen.
2365     * @param toState The state to zoom out to. Must be ALL_APPS or CUSTOMIZE.
2366     */
2367    private void cameraZoomOut(State toState, boolean animated) {
2368        final Resources res = getResources();
2369        final int duration = res.getInteger(R.integer.config_allAppsZoomInTime);
2370        final float scale = (float) res.getInteger(R.integer.config_allAppsZoomScaleFactor);
2371        final boolean toAllApps = (toState == State.ALL_APPS);
2372        final View toView = toAllApps ? (View) mAllAppsGrid : mHomeCustomizationDrawer;
2373
2374        setPivotsForZoom(toView, toState, scale);
2375
2376        if (toState == State.ALL_APPS) {
2377            mWorkspace.shrinkToBottom(animated);
2378        } else {
2379            mWorkspace.shrinkToTop(animated);
2380        }
2381
2382        if (animated) {
2383            ValueAnimator scaleAnim = ObjectAnimator.ofPropertyValuesHolder(toView,
2384                    PropertyValuesHolder.ofFloat("scaleX", scale, 1.0f),
2385                    PropertyValuesHolder.ofFloat("scaleY", scale, 1.0f));
2386            scaleAnim.setDuration(duration);
2387            scaleAnim.setInterpolator(new DecelerateInterpolator());
2388            scaleAnim.addListener(new AnimatorListenerAdapter() {
2389                @Override
2390                public void onAnimationStart(Animator animation) {
2391                    // Prepare the position
2392                    toView.setTranslationX(0.0f);
2393                    toView.setTranslationY(0.0f);
2394                    toView.setVisibility(View.VISIBLE);
2395                }
2396            });
2397
2398            AnimatorSet toolbarHideAnim = new AnimatorSet();
2399            AnimatorSet toolbarShowAnim = new AnimatorSet();
2400            hideAndShowToolbarButtons(toState, toolbarShowAnim, toolbarHideAnim);
2401
2402            // toView should appear right at the end of the workspace shrink animation
2403            final int startDelay = res.getInteger(R.integer.config_workspaceShrinkTime) - duration;
2404
2405            if (mStateAnimation != null) mStateAnimation.cancel();
2406            mStateAnimation = new AnimatorSet();
2407            mStateAnimation.playTogether(scaleAnim, toolbarHideAnim);
2408            mStateAnimation.play(scaleAnim).after(startDelay);
2409
2410            // Show the new toolbar buttons just as the main animation is ending
2411            final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
2412            mStateAnimation.play(toolbarShowAnim).after(duration + startDelay - fadeInTime);
2413            mStateAnimation.start();
2414        } else {
2415            toView.setTranslationX(0.0f);
2416            toView.setTranslationY(0.0f);
2417            toView.setScaleX(1.0f);
2418            toView.setScaleY(1.0f);
2419            toView.setVisibility(View.VISIBLE);
2420            hideAndShowToolbarButtons(toState, null, null);
2421        }
2422    }
2423
2424    /**
2425     * Zoom the camera back into the workspace, hiding 'fromView'.
2426     * This is the opposite of cameraZoomOut.
2427     * @param fromState The current state (must be ALL_APPS or CUSTOMIZE).
2428     * @param animated If true, the transition will be animated.
2429     */
2430    private void cameraZoomIn(State fromState, boolean animated) {
2431        Resources res = getResources();
2432        int duration = res.getInteger(R.integer.config_allAppsZoomOutTime);
2433        float scaleFactor = (float) res.getInteger(R.integer.config_allAppsZoomScaleFactor);
2434        final View fromView =
2435            (fromState == State.ALL_APPS) ? (View) mAllAppsGrid : mHomeCustomizationDrawer;
2436
2437        mCustomizePagedView.endChoiceMode();
2438        mAllAppsPagedView.endChoiceMode();
2439
2440        setPivotsForZoom(fromView, fromState, scaleFactor);
2441
2442        mWorkspace.unshrink(animated);
2443
2444        if (animated) {
2445            if (mStateAnimation != null) mStateAnimation.cancel();
2446            mStateAnimation = new AnimatorSet();
2447            ValueAnimator scaleAnim = ObjectAnimator.ofPropertyValuesHolder(fromView,
2448                    PropertyValuesHolder.ofFloat("scaleX", scaleFactor),
2449                    PropertyValuesHolder.ofFloat("scaleY", scaleFactor));
2450            scaleAnim.setDuration(duration);
2451            scaleAnim.setInterpolator(new AccelerateInterpolator());
2452            mStateAnimation.addListener(new AnimatorListenerAdapter() {
2453                @Override
2454                public void onAnimationEnd(Animator animation) {
2455                    fromView.setVisibility(View.GONE);
2456                    fromView.setScaleX(1.0f);
2457                    fromView.setScaleY(1.0f);
2458                }
2459            });
2460
2461            AnimatorSet toolbarHideAnim = new AnimatorSet();
2462            AnimatorSet toolbarShowAnim = new AnimatorSet();
2463            hideAndShowToolbarButtons(State.WORKSPACE, toolbarShowAnim, toolbarHideAnim);
2464
2465            mStateAnimation.playTogether(scaleAnim, toolbarHideAnim);
2466
2467            // Show the new toolbar buttons at the very end of the whole animation
2468            final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
2469            final int unshrinkTime = res.getInteger(R.integer.config_workspaceUnshrinkTime);
2470            mStateAnimation.play(toolbarShowAnim).after(unshrinkTime - fadeInTime);
2471            mStateAnimation.start();
2472        } else {
2473            fromView.setVisibility(View.GONE);
2474            hideAndShowToolbarButtons(State.WORKSPACE, null, null);
2475        }
2476    }
2477
2478    /**
2479     * Pan the camera in the vertical plane between 'fromView' and 'toView'.
2480     * This is the transition used on xlarge screens to go between all apps and
2481     * the home customization drawer.
2482     * @param fromState The view to pan away from. Must be ALL_APPS or CUSTOMIZE.
2483     * @param toState The view to pan into the frame. Must be ALL_APPS or CUSTOMIZE.
2484     * @param animated If true, the transition will be animated.
2485     */
2486    private void cameraPan(State fromState, State toState, boolean animated) {
2487        final Resources res = getResources();
2488        final int duration = res.getInteger(R.integer.config_allAppsCameraPanTime);
2489        final int workspaceHeight = mWorkspace.getHeight();
2490
2491        final boolean fromAllApps = (fromState == State.ALL_APPS);
2492        final View fromView = fromAllApps ? (View) mAllAppsGrid : mHomeCustomizationDrawer;
2493        final View toView = fromAllApps ? mHomeCustomizationDrawer : (View) mAllAppsGrid;
2494
2495        final float fromViewStartY = fromAllApps ? 0.0f : fromView.getY();
2496        final float fromViewEndY = fromAllApps ? -fromView.getHeight() * 2 : workspaceHeight * 2;
2497        final float toViewStartY = fromAllApps ? workspaceHeight * 2 : -toView.getHeight() * 2;
2498        final float toViewEndY = fromAllApps ? workspaceHeight - toView.getHeight() : 0.0f;
2499
2500        mCustomizePagedView.endChoiceMode();
2501        mAllAppsPagedView.endChoiceMode();
2502
2503        if (toState == State.ALL_APPS) {
2504            mWorkspace.shrinkToBottom(animated);
2505        } else {
2506            mWorkspace.shrinkToTop(animated);
2507        }
2508
2509        if (animated) {
2510            if (mStateAnimation != null) mStateAnimation.cancel();
2511            mStateAnimation = new AnimatorSet();
2512            mStateAnimation.addListener(new AnimatorListenerAdapter() {
2513                @Override
2514                public void onAnimationStart(Animator animation) {
2515                    toView.setVisibility(View.VISIBLE);
2516                    toView.setY(toViewStartY);
2517                }
2518                @Override
2519                public void onAnimationEnd(Animator animation) {
2520                    fromView.setVisibility(View.GONE);
2521                }
2522            });
2523
2524            AnimatorSet toolbarHideAnim = new AnimatorSet();
2525            AnimatorSet toolbarShowAnim = new AnimatorSet();
2526            hideAndShowToolbarButtons(toState, toolbarShowAnim, toolbarHideAnim);
2527
2528            ObjectAnimator fromAnim = ObjectAnimator.ofFloat(fromView, "y",
2529                    fromViewStartY, fromViewEndY);
2530            fromAnim.setDuration(duration);
2531            ObjectAnimator toAnim = ObjectAnimator.ofFloat(toView, "y",
2532                    toViewStartY, toViewEndY);
2533            fromAnim.setDuration(duration);
2534            mStateAnimation.playTogether(toolbarHideAnim, fromAnim, toAnim);
2535
2536            // Show the new toolbar buttons just as the main animation is ending
2537            final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
2538            mStateAnimation.play(toolbarShowAnim).after(duration - fadeInTime);
2539            mStateAnimation.start();
2540        } else {
2541            fromView.setY(fromViewEndY);
2542            fromView.setVisibility(View.GONE);
2543            toView.setY(toViewEndY);
2544            toView.setVisibility(View.VISIBLE);
2545            hideAndShowToolbarButtons(toState, null, null);
2546        }
2547    }
2548
2549    void showAllApps(boolean animated) {
2550        if (mState == State.ALL_APPS) {
2551            return;
2552        }
2553
2554        if (LauncherApplication.isScreenXLarge()) {
2555            if (mState == State.CUSTOMIZE) {
2556                cameraPan(State.CUSTOMIZE, State.ALL_APPS, animated);
2557            } else {
2558                cameraZoomOut(State.ALL_APPS, animated);
2559            }
2560        } else {
2561            mAllAppsGrid.zoom(1.0f, animated);
2562        }
2563
2564        ((View) mAllAppsGrid).setFocusable(true);
2565        ((View) mAllAppsGrid).requestFocus();
2566
2567        // TODO: fade these two too
2568        mDeleteZone.setVisibility(View.GONE);
2569        // Change the state *after* we've called all the transition code
2570        mState = State.ALL_APPS;
2571    }
2572
2573
2574    void showWorkspace(boolean animated) {
2575        showWorkspace(animated, null);
2576    }
2577
2578    void showWorkspace(boolean animated, CellLayout layout) {
2579        if (layout != null && animated) {
2580            mWorkspace.unshrink(layout);
2581        } else {
2582            mWorkspace.unshrink(animated);
2583        }
2584        if (mState == State.ALL_APPS) {
2585            closeAllApps(animated);
2586        } else if (mState == State.CUSTOMIZE) {
2587            hideCustomizationDrawer(animated);
2588        }
2589        // Change the state *after* we've called all the transition code
2590        mState = State.WORKSPACE;
2591    }
2592
2593    /**
2594     * Things to test when changing this code.
2595     *   - Home from workspace
2596     *          - from center screen
2597     *          - from other screens
2598     *   - Home from all apps
2599     *          - from center screen
2600     *          - from other screens
2601     *   - Back from all apps
2602     *          - from center screen
2603     *          - from other screens
2604     *   - Launch app from workspace and quit
2605     *          - with back
2606     *          - with home
2607     *   - Launch app from all apps and quit
2608     *          - with back
2609     *          - with home
2610     *   - Go to a screen that's not the default, then all
2611     *     apps, and launch and app, and go back
2612     *          - with back
2613     *          -with home
2614     *   - On workspace, long press power and go back
2615     *          - with back
2616     *          - with home
2617     *   - On all apps, long press power and go back
2618     *          - with back
2619     *          - with home
2620     *   - On workspace, power off
2621     *   - On all apps, power off
2622     *   - Launch an app and turn off the screen while in that app
2623     *          - Go back with home key
2624     *          - Go back with back key  TODO: make this not go to workspace
2625     *          - From all apps
2626     *          - From workspace
2627     *   - Enter and exit car mode (becuase it causes an extra configuration changed)
2628     *          - From all apps
2629     *          - From the center workspace
2630     *          - From another workspace
2631     */
2632    void closeAllApps(boolean animated) {
2633        if (mState == State.ALL_APPS) {
2634            mWorkspace.setVisibility(View.VISIBLE);
2635            if (LauncherApplication.isScreenXLarge()) {
2636                cameraZoomIn(State.ALL_APPS, animated);
2637            } else {
2638                mAllAppsGrid.zoom(0.0f, animated);
2639            }
2640            ((View)mAllAppsGrid).setFocusable(false);
2641            mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
2642        }
2643    }
2644
2645    void lockAllApps() {
2646        // TODO
2647    }
2648
2649    void unlockAllApps() {
2650        // TODO
2651    }
2652
2653    // Show the customization drawer (only exists in x-large configuration)
2654    private void showCustomizationDrawer(boolean animated) {
2655        if (mState == State.ALL_APPS) {
2656            cameraPan(State.ALL_APPS, State.CUSTOMIZE, animated);
2657        } else {
2658            cameraZoomOut(State.CUSTOMIZE, animated);
2659        }
2660        // Change the state *after* we've called all the transition code
2661        mState = State.CUSTOMIZE;
2662    }
2663
2664    // Hide the customization drawer (only exists in x-large configuration)
2665    void hideCustomizationDrawer(boolean animated) {
2666        if (mState == State.CUSTOMIZE) {
2667            cameraZoomIn(State.CUSTOMIZE, animated);
2668        }
2669    }
2670
2671    void addExternalItemToScreen(ItemInfo itemInfo, CellLayout layout) {
2672        if (!mWorkspace.addExternalItemToScreen(itemInfo, layout)) {
2673            showOutOfSpaceMessage();
2674        }
2675    }
2676
2677    void onWorkspaceClick(CellLayout layout) {
2678        showWorkspace(true, layout);
2679    }
2680
2681    private void updateButtonWithIconFromExternalActivity(
2682            int buttonId, ComponentName activityName, int fallbackDrawableId) {
2683        ImageView button = (ImageView) findViewById(buttonId);
2684        Drawable toolbarIcon = null;
2685        try {
2686            PackageManager packageManager = getPackageManager();
2687            // Look for the toolbar icon specified in the activity meta-data
2688            Bundle metaData = packageManager.getActivityInfo(
2689                    activityName, PackageManager.GET_META_DATA).metaData;
2690            if (metaData != null) {
2691                int iconResId = metaData.getInt(TOOLBAR_ICON_METADATA_NAME);
2692                if (iconResId != 0) {
2693                    Resources res = packageManager.getResourcesForActivity(activityName);
2694                    toolbarIcon = res.getDrawable(iconResId);
2695                }
2696            }
2697        } catch (NameNotFoundException e) {
2698            // Do nothing
2699        }
2700        // If we were unable to find the icon via the meta-data, use a generic one
2701        if (toolbarIcon == null) {
2702            button.setImageResource(fallbackDrawableId);
2703        } else {
2704            button.setImageDrawable(toolbarIcon);
2705        }
2706    }
2707
2708    private void updateGlobalSearchIcon() {
2709        if (LauncherApplication.isScreenXLarge()) {
2710            final SearchManager searchManager =
2711                    (SearchManager) getSystemService(Context.SEARCH_SERVICE);
2712            ComponentName activityName = searchManager.getGlobalSearchActivity();
2713            if (activityName != null) {
2714                updateButtonWithIconFromExternalActivity(
2715                        R.id.search_button, activityName, R.drawable.search_button_generic);
2716            }
2717        }
2718    }
2719
2720    /**
2721     * Sets the app market icon (shown when all apps is visible on x-large screens)
2722     */
2723    private void updateAppMarketIcon() {
2724        if (LauncherApplication.isScreenXLarge()) {
2725            Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
2726            // Find the app market activity by resolving an intent.
2727            // (If multiple app markets are installed, it will return the ResolverActivity.)
2728            ComponentName activityName = intent.resolveActivity(getPackageManager());
2729            if (activityName != null) {
2730                mAppMarketIntent = intent;
2731                updateButtonWithIconFromExternalActivity(
2732                        R.id.market_button, activityName, R.drawable.app_market_generic);
2733            }
2734        }
2735    }
2736
2737    /**
2738     * Displays the shortcut creation dialog and launches, if necessary, the
2739     * appropriate activity.
2740     */
2741    private class CreateShortcut implements DialogInterface.OnClickListener,
2742            DialogInterface.OnCancelListener, DialogInterface.OnDismissListener,
2743            DialogInterface.OnShowListener {
2744
2745        private AddAdapter mAdapter;
2746
2747        Dialog createDialog() {
2748            mAdapter = new AddAdapter(Launcher.this);
2749
2750            final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
2751            builder.setTitle(getString(R.string.menu_item_add_item));
2752            builder.setAdapter(mAdapter, this);
2753
2754            builder.setInverseBackgroundForced(true);
2755
2756            AlertDialog dialog = builder.create();
2757            dialog.setOnCancelListener(this);
2758            dialog.setOnDismissListener(this);
2759            dialog.setOnShowListener(this);
2760
2761            return dialog;
2762        }
2763
2764        public void onCancel(DialogInterface dialog) {
2765            mWaitingForResult = false;
2766            cleanup();
2767        }
2768
2769        public void onDismiss(DialogInterface dialog) {
2770        }
2771
2772        private void cleanup() {
2773            try {
2774                dismissDialog(DIALOG_CREATE_SHORTCUT);
2775            } catch (Exception e) {
2776                // An exception is thrown if the dialog is not visible, which is fine
2777            }
2778        }
2779
2780        /**
2781         * Handle the action clicked in the "Add to home" dialog.
2782         */
2783        public void onClick(DialogInterface dialog, int which) {
2784            Resources res = getResources();
2785            cleanup();
2786
2787            switch (which) {
2788                case AddAdapter.ITEM_SHORTCUT: {
2789                    pickShortcut();
2790                    break;
2791                }
2792
2793                case AddAdapter.ITEM_APPWIDGET: {
2794                    int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
2795
2796                    Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
2797                    pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
2798                    // start the pick activity
2799                    startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
2800                    break;
2801                }
2802
2803                case AddAdapter.ITEM_LIVE_FOLDER: {
2804                    // Insert extra item to handle inserting folder
2805                    Bundle bundle = new Bundle();
2806
2807                    ArrayList<String> shortcutNames = new ArrayList<String>();
2808                    shortcutNames.add(res.getString(R.string.group_folder));
2809                    bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
2810
2811                    ArrayList<ShortcutIconResource> shortcutIcons =
2812                            new ArrayList<ShortcutIconResource>();
2813                    shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
2814                            R.drawable.ic_launcher_folder));
2815                    bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
2816
2817                    Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
2818                    pickIntent.putExtra(Intent.EXTRA_INTENT,
2819                            new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER));
2820                    pickIntent.putExtra(Intent.EXTRA_TITLE,
2821                            getText(R.string.title_select_live_folder));
2822                    pickIntent.putExtras(bundle);
2823
2824                    startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
2825                    break;
2826                }
2827
2828                case AddAdapter.ITEM_WALLPAPER: {
2829                    startWallpaper();
2830                    break;
2831                }
2832            }
2833        }
2834
2835        public void onShow(DialogInterface dialog) {
2836            mWaitingForResult = true;
2837        }
2838    }
2839
2840    /**
2841     * Receives notifications when applications are added/removed.
2842     */
2843    private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
2844        @Override
2845        public void onReceive(Context context, Intent intent) {
2846            closeSystemDialogs();
2847            String reason = intent.getStringExtra("reason");
2848            if (!"homekey".equals(reason)) {
2849                boolean animate = true;
2850                if (mPaused || "lock".equals(reason)) {
2851                    animate = false;
2852                }
2853                showWorkspace(animate);
2854            }
2855        }
2856    }
2857
2858    /**
2859     * Receives notifications whenever the appwidgets are reset.
2860     */
2861    private class AppWidgetResetObserver extends ContentObserver {
2862        public AppWidgetResetObserver() {
2863            super(new Handler());
2864        }
2865
2866        @Override
2867        public void onChange(boolean selfChange) {
2868            onAppWidgetReset();
2869        }
2870    }
2871
2872    /**
2873     * Implementation of the method from LauncherModel.Callbacks.
2874     */
2875    public int getCurrentWorkspaceScreen() {
2876        if (mWorkspace != null) {
2877            return mWorkspace.getCurrentPage();
2878        } else {
2879            return SCREEN_COUNT / 2;
2880        }
2881    }
2882
2883    void setAllAppsPagedView(PagedView view) {
2884        mAllAppsPagedView = view;
2885    }
2886
2887    /**
2888     * Refreshes the shortcuts shown on the workspace.
2889     *
2890     * Implementation of the method from LauncherModel.Callbacks.
2891     */
2892    public void startBinding() {
2893        final Workspace workspace = mWorkspace;
2894        int count = workspace.getChildCount();
2895        for (int i = 0; i < count; i++) {
2896            // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
2897            ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
2898        }
2899
2900        if (DEBUG_USER_INTERFACE) {
2901            android.widget.Button finishButton = new android.widget.Button(this);
2902            finishButton.setText("Finish");
2903            workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
2904
2905            finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
2906                public void onClick(View v) {
2907                    finish();
2908                }
2909            });
2910        }
2911    }
2912
2913    /**
2914     * Bind the items start-end from the list.
2915     *
2916     * Implementation of the method from LauncherModel.Callbacks.
2917     */
2918    public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
2919
2920        final Workspace workspace = mWorkspace;
2921
2922        for (int i=start; i<end; i++) {
2923            final ItemInfo item = shortcuts.get(i);
2924            mDesktopItems.add(item);
2925            switch (item.itemType) {
2926                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
2927                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
2928                    final View shortcut = createShortcut((ShortcutInfo)item);
2929                    workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
2930                            false);
2931                    break;
2932                case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
2933                    final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
2934                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
2935                            (UserFolderInfo) item);
2936                    workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
2937                            false);
2938                    break;
2939                case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
2940                    final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
2941                            R.layout.live_folder_icon, this,
2942                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
2943                            (LiveFolderInfo) item);
2944                    workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
2945                            false);
2946                    break;
2947            }
2948        }
2949
2950        workspace.requestLayout();
2951    }
2952
2953    /**
2954     * Implementation of the method from LauncherModel.Callbacks.
2955     */
2956    public void bindFolders(HashMap<Long, FolderInfo> folders) {
2957        sFolders.clear();
2958        sFolders.putAll(folders);
2959    }
2960
2961    /**
2962     * Add the views for a widget to the workspace.
2963     *
2964     * Implementation of the method from LauncherModel.Callbacks.
2965     */
2966    public void bindAppWidget(LauncherAppWidgetInfo item) {
2967        final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
2968        if (DEBUG_WIDGETS) {
2969            Log.d(TAG, "bindAppWidget: " + item);
2970        }
2971        final Workspace workspace = mWorkspace;
2972
2973        final int appWidgetId = item.appWidgetId;
2974        final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
2975        if (DEBUG_WIDGETS) {
2976            Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
2977        }
2978
2979        item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
2980
2981        item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
2982        item.hostView.setTag(item);
2983
2984        workspace.addInScreen(item.hostView, item.screen, item.cellX,
2985                item.cellY, item.spanX, item.spanY, false);
2986
2987        workspace.requestLayout();
2988
2989        mDesktopItems.add(item);
2990
2991        if (DEBUG_WIDGETS) {
2992            Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
2993                    + (SystemClock.uptimeMillis()-start) + "ms");
2994        }
2995    }
2996
2997    /**
2998     * Callback saying that there aren't any more items to bind.
2999     *
3000     * Implementation of the method from LauncherModel.Callbacks.
3001     */
3002    public void finishBindingItems() {
3003        if (mSavedState != null) {
3004            if (!mWorkspace.hasFocus()) {
3005                mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
3006            }
3007
3008            final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
3009            if (userFolders != null) {
3010                for (long folderId : userFolders) {
3011                    final FolderInfo info = sFolders.get(folderId);
3012                    if (info != null) {
3013                        openFolder(info);
3014                    }
3015                }
3016                final Folder openFolder = mWorkspace.getOpenFolder();
3017                if (openFolder != null) {
3018                    openFolder.requestFocus();
3019                }
3020            }
3021
3022            mSavedState = null;
3023        }
3024
3025        if (mSavedInstanceState != null) {
3026            super.onRestoreInstanceState(mSavedInstanceState);
3027            mSavedInstanceState = null;
3028        }
3029
3030        mWorkspaceLoading = false;
3031    }
3032
3033    /**
3034     * Add the icons for all apps.
3035     *
3036     * Implementation of the method from LauncherModel.Callbacks.
3037     */
3038    public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
3039        mAllAppsGrid.setApps(apps);
3040        if (mCustomizePagedView != null) {
3041            mCustomizePagedView.setApps(apps);
3042        }
3043        updateAppMarketIcon();
3044        updateGlobalSearchIcon();
3045    }
3046
3047    /**
3048     * A package was installed.
3049     *
3050     * Implementation of the method from LauncherModel.Callbacks.
3051     */
3052    public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
3053        removeDialog(DIALOG_CREATE_SHORTCUT);
3054        mAllAppsGrid.addApps(apps);
3055        if (mCustomizePagedView != null) {
3056            mCustomizePagedView.addApps(apps);
3057        }
3058        updateAppMarketIcon();
3059        updateGlobalSearchIcon();
3060    }
3061
3062    /**
3063     * A package was updated.
3064     *
3065     * Implementation of the method from LauncherModel.Callbacks.
3066     */
3067    public void bindAppsUpdated(ArrayList<ApplicationInfo> apps) {
3068        removeDialog(DIALOG_CREATE_SHORTCUT);
3069        mWorkspace.updateShortcuts(apps);
3070        mAllAppsGrid.updateApps(apps);
3071        if (mCustomizePagedView != null) {
3072            mCustomizePagedView.updateApps(apps);
3073        }
3074        updateAppMarketIcon();
3075        updateGlobalSearchIcon();
3076    }
3077
3078    /**
3079     * A package was uninstalled.
3080     *
3081     * Implementation of the method from LauncherModel.Callbacks.
3082     */
3083    public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent) {
3084        removeDialog(DIALOG_CREATE_SHORTCUT);
3085        if (permanent) {
3086            mWorkspace.removeItems(apps);
3087        }
3088        mAllAppsGrid.removeApps(apps);
3089        if (mCustomizePagedView != null) {
3090            mCustomizePagedView.removeApps(apps);
3091        }
3092        updateAppMarketIcon();
3093        updateGlobalSearchIcon();
3094    }
3095
3096    /**
3097     * A number of packages were updated.
3098     */
3099    public void bindPackagesUpdated() {
3100        // update the customization drawer contents
3101        if (mCustomizePagedView != null) {
3102            mCustomizePagedView.update();
3103        }
3104    }
3105
3106    /**
3107     * Prints out out state for debugging.
3108     */
3109    public void dumpState() {
3110        Log.d(TAG, "BEGIN launcher2 dump state for launcher " + this);
3111        Log.d(TAG, "mSavedState=" + mSavedState);
3112        Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
3113        Log.d(TAG, "mRestoring=" + mRestoring);
3114        Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
3115        Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
3116        Log.d(TAG, "mDesktopItems.size=" + mDesktopItems.size());
3117        Log.d(TAG, "sFolders.size=" + sFolders.size());
3118        mModel.dumpState();
3119        mAllAppsGrid.dumpState();
3120        Log.d(TAG, "END launcher2 dump state");
3121    }
3122}
3123