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