Launcher.java revision 4554ee16f122a3dbd9a8d3b828f1c8c7e7b7fe99
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.AnimatorSet;
23import android.animation.ObjectAnimator;
24import android.animation.PropertyValuesHolder;
25import android.animation.ValueAnimator;
26import android.app.Activity;
27import android.app.AlertDialog;
28import android.app.Dialog;
29import android.app.SearchManager;
30import android.app.StatusBarManager;
31import android.appwidget.AppWidgetManager;
32import android.appwidget.AppWidgetProviderInfo;
33import android.content.ActivityNotFoundException;
34import android.content.BroadcastReceiver;
35import android.content.ClipData;
36import android.content.ClipDescription;
37import android.content.ComponentName;
38import android.content.ContentResolver;
39import android.content.Context;
40import android.content.DialogInterface;
41import android.content.Intent;
42import android.content.IntentFilter;
43import android.content.Intent.ShortcutIconResource;
44import android.content.pm.ActivityInfo;
45import android.content.pm.PackageManager;
46import android.content.pm.PackageManager.NameNotFoundException;
47import android.content.res.Configuration;
48import android.content.res.Resources;
49import android.database.ContentObserver;
50import android.graphics.Rect;
51import android.graphics.drawable.Drawable;
52import android.net.Uri;
53import android.os.AsyncTask;
54import android.os.Build;
55import android.os.Bundle;
56import android.os.Environment;
57import android.os.Handler;
58import android.os.Message;
59import android.os.SystemClock;
60import android.os.SystemProperties;
61import android.provider.Settings;
62import android.speech.RecognizerIntent;
63import android.text.Selection;
64import android.text.SpannableStringBuilder;
65import android.text.TextUtils;
66import android.text.method.TextKeyListener;
67import android.util.Log;
68import android.view.Display;
69import android.view.HapticFeedbackConstants;
70import android.view.KeyEvent;
71import android.view.LayoutInflater;
72import android.view.Menu;
73import android.view.MenuItem;
74import android.view.MotionEvent;
75import android.view.Surface;
76import android.view.View;
77import android.view.ViewGroup;
78import android.view.View.OnLongClickListener;
79import android.view.accessibility.AccessibilityEvent;
80import android.view.animation.DecelerateInterpolator;
81import android.view.inputmethod.InputMethodManager;
82import android.widget.Advanceable;
83import android.widget.EditText;
84import android.widget.ImageView;
85import android.widget.TextView;
86import android.widget.Toast;
87
88import com.android.common.Search;
89import com.android.launcher.R;
90
91import java.io.DataInputStream;
92import java.io.DataOutputStream;
93import java.io.FileNotFoundException;
94import java.io.IOException;
95import java.util.ArrayList;
96import java.util.HashMap;
97
98/**
99 * Default launcher application.
100 */
101public final class Launcher extends Activity
102        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
103                   AllAppsView.Watcher, View.OnTouchListener {
104    static final String TAG = "Launcher";
105    static final boolean LOGD = false;
106
107    static final boolean PROFILE_STARTUP = false;
108    static final boolean DEBUG_WIDGETS = false;
109
110    private static final int MENU_GROUP_ADD = 1;
111    private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1;
112
113    private static final int MENU_ADD = Menu.FIRST + 1;
114    private static final int MENU_MANAGE_APPS = MENU_ADD + 1;
115    private static final int MENU_WALLPAPER_SETTINGS = MENU_MANAGE_APPS + 1;
116    private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
117    private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1;
118    private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
119
120    private static final int REQUEST_CREATE_SHORTCUT = 1;
121    private static final int REQUEST_CREATE_APPWIDGET = 5;
122    private static final int REQUEST_PICK_APPLICATION = 6;
123    private static final int REQUEST_PICK_SHORTCUT = 7;
124    private static final int REQUEST_PICK_APPWIDGET = 9;
125    private static final int REQUEST_PICK_WALLPAPER = 10;
126
127    static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
128
129    static final int SCREEN_COUNT = 5;
130    static final int DEFAULT_SCREEN = 2;
131
132    static final int DIALOG_CREATE_SHORTCUT = 1;
133    static final int DIALOG_RENAME_FOLDER = 2;
134
135    private static final String PREFERENCES = "launcher.preferences";
136
137    // Type: int
138    private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
139    // Type: int
140    private static final String RUNTIME_STATE = "launcher.state";
141    // Type: int
142    private static final String RUNTIME_STATE_PENDING_ADD_CONTAINER = "launcher.add_container";
143    // Type: int
144    private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
145    // Type: int
146    private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x";
147    // Type: int
148    private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y";
149    // Type: boolean
150    private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
151    // Type: long
152    private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
153
154    private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
155
156    /** The different states that Launcher can be in. */
157    private enum State { WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED };
158    private State mState = State.WORKSPACE;
159    private AnimatorSet mStateAnimation;
160
161    static final int APPWIDGET_HOST_ID = 1024;
162    private static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
163    private static final int EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT = 600;
164
165    private static final Object sLock = new Object();
166    private static int sScreen = DEFAULT_SCREEN;
167
168    private final BroadcastReceiver mCloseSystemDialogsReceiver
169            = new CloseSystemDialogsIntentReceiver();
170    private final ContentObserver mWidgetObserver = new AppWidgetResetObserver();
171
172    private LayoutInflater mInflater;
173
174    private DragController mDragController;
175    private Workspace mWorkspace;
176
177    private AppWidgetManager mAppWidgetManager;
178    private LauncherAppWidgetHost mAppWidgetHost;
179
180    private ItemInfo mPendingAddInfo = new ItemInfo();
181    private int[] mTmpAddItemCellCoordinates = new int[2];
182
183    private FolderInfo mFolderInfo;
184
185    private Hotseat mHotseat;
186    private View mAllAppsButton;
187
188    private SearchDropTargetBar mSearchDeleteBar;
189    private AppsCustomizeTabHost mAppsCustomizeTabHost;
190    private AppsCustomizePagedView mAppsCustomizeContent;
191    private boolean mAutoAdvanceRunning = false;
192
193    private Bundle mSavedState;
194
195    private SpannableStringBuilder mDefaultKeySsb = null;
196
197    private boolean mWorkspaceLoading = true;
198
199    private boolean mPaused = true;
200    private boolean mRestoring;
201    private boolean mWaitingForResult;
202    private boolean mOnResumeNeedsLoad;
203
204    private Bundle mSavedInstanceState;
205
206    private LauncherModel mModel;
207    private IconCache mIconCache;
208    private boolean mUserPresent = true;
209    private boolean mVisible = false;
210    private boolean mAttached = false;
211
212    private static LocaleConfiguration sLocaleConfiguration = null;
213
214    private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
215
216    private Intent mAppMarketIntent = null;
217
218    // Related to the auto-advancing of widgets
219    private final int ADVANCE_MSG = 1;
220    private final int mAdvanceInterval = 20000;
221    private final int mAdvanceStagger = 250;
222    private long mAutoAdvanceSentTime;
223    private long mAutoAdvanceTimeLeft = -1;
224    private HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance =
225        new HashMap<View, AppWidgetProviderInfo>();
226
227    // Determines how long to wait after a rotation before restoring the screen orientation to
228    // match the sensor state.
229    private final int mRestoreScreenOrientationDelay = 500;
230
231    // External icons saved in case of resource changes, orientation, etc.
232    private static Drawable.ConstantState sGlobalSearchIcon;
233    private static Drawable.ConstantState sVoiceSearchIcon;
234    private static Drawable.ConstantState sAppMarketIcon;
235
236    private DragLayer mDragLayer;
237
238    private BubbleTextView mWaitingForResume;
239
240    private static ArrayList<PendingAddArguments> sPendingAddList
241            = new ArrayList<PendingAddArguments>();
242
243    private static class PendingAddArguments {
244        int requestCode;
245        Intent intent;
246        long container;
247        int screen;
248        int cellX;
249        int cellY;
250    }
251
252    @Override
253    protected void onCreate(Bundle savedInstanceState) {
254        super.onCreate(savedInstanceState);
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(
267                    Environment.getExternalStorageDirectory() + "/launcher");
268        }
269
270        checkForLocaleChange();
271        setContentView(R.layout.launcher);
272        setupViews();
273
274        registerContentObservers();
275
276        lockAllApps();
277
278        mSavedState = savedInstanceState;
279        restoreState(mSavedState);
280
281        // Update customization drawer _after_ restoring the states
282        if (mAppsCustomizeContent != null) {
283            mAppsCustomizeContent.onPackagesUpdated();
284        }
285
286        if (PROFILE_STARTUP) {
287            android.os.Debug.stopMethodTracing();
288        }
289
290        if (!mRestoring) {
291            mModel.startLoader(this, true);
292        }
293
294        // For handling default keys
295        mDefaultKeySsb = new SpannableStringBuilder();
296        Selection.setSelection(mDefaultKeySsb, 0);
297
298        IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
299        registerReceiver(mCloseSystemDialogsReceiver, filter);
300
301        // If we have a saved version of these external icons, we load them up immediately
302        if (sGlobalSearchIcon == null || sVoiceSearchIcon == null || sAppMarketIcon == null) {
303            updateIconsAffectedByPackageManagerChanges();
304            updateGlobalSearchIcon();
305        }
306        if (sGlobalSearchIcon != null) {
307             updateGlobalSearchIcon(sGlobalSearchIcon);
308        }
309        if (sVoiceSearchIcon != null) {
310            updateVoiceSearchIcon(sVoiceSearchIcon);
311        }
312        if (sAppMarketIcon != null) {
313            updateAppMarketIcon(sAppMarketIcon);
314        }
315    }
316
317    private void checkForLocaleChange() {
318        if (sLocaleConfiguration == null) {
319            new AsyncTask<Void, Void, LocaleConfiguration>() {
320                @Override
321                protected LocaleConfiguration doInBackground(Void... unused) {
322                    LocaleConfiguration localeConfiguration = new LocaleConfiguration();
323                    readConfiguration(Launcher.this, localeConfiguration);
324                    return localeConfiguration;
325                }
326
327                @Override
328                protected void onPostExecute(LocaleConfiguration result) {
329                    sLocaleConfiguration = result;
330                    checkForLocaleChange();  // recursive, but now with a locale configuration
331                }
332            }.execute();
333            return;
334        }
335
336        final Configuration configuration = getResources().getConfiguration();
337
338        final String previousLocale = sLocaleConfiguration.locale;
339        final String locale = configuration.locale.toString();
340
341        final int previousMcc = sLocaleConfiguration.mcc;
342        final int mcc = configuration.mcc;
343
344        final int previousMnc = sLocaleConfiguration.mnc;
345        final int mnc = configuration.mnc;
346
347        boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
348
349        if (localeChanged) {
350            sLocaleConfiguration.locale = locale;
351            sLocaleConfiguration.mcc = mcc;
352            sLocaleConfiguration.mnc = mnc;
353
354            mIconCache.flush();
355
356            final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
357            new Thread("WriteLocaleConfiguration") {
358                @Override
359                public void run() {
360                    writeConfiguration(Launcher.this, localeConfiguration);
361                }
362            }.start();
363        }
364    }
365
366    private static class LocaleConfiguration {
367        public String locale;
368        public int mcc = -1;
369        public int mnc = -1;
370    }
371
372    private static void readConfiguration(Context context, LocaleConfiguration configuration) {
373        DataInputStream in = null;
374        try {
375            in = new DataInputStream(context.openFileInput(PREFERENCES));
376            configuration.locale = in.readUTF();
377            configuration.mcc = in.readInt();
378            configuration.mnc = in.readInt();
379        } catch (FileNotFoundException e) {
380            // Ignore
381        } catch (IOException e) {
382            // Ignore
383        } finally {
384            if (in != null) {
385                try {
386                    in.close();
387                } catch (IOException e) {
388                    // Ignore
389                }
390            }
391        }
392    }
393
394    private static void writeConfiguration(Context context, LocaleConfiguration configuration) {
395        DataOutputStream out = null;
396        try {
397            out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE));
398            out.writeUTF(configuration.locale);
399            out.writeInt(configuration.mcc);
400            out.writeInt(configuration.mnc);
401            out.flush();
402        } catch (FileNotFoundException e) {
403            // Ignore
404        } catch (IOException e) {
405            //noinspection ResultOfMethodCallIgnored
406            context.getFileStreamPath(PREFERENCES).delete();
407        } finally {
408            if (out != null) {
409                try {
410                    out.close();
411                } catch (IOException e) {
412                    // Ignore
413                }
414            }
415        }
416    }
417
418    public DragLayer getDragLayer() {
419        return mDragLayer;
420    }
421
422    static int getScreen() {
423        synchronized (sLock) {
424            return sScreen;
425        }
426    }
427
428    static void setScreen(int screen) {
429        synchronized (sLock) {
430            sScreen = screen;
431        }
432    }
433
434    /**
435     * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
436     * a configuration step, this allows the proper animations to run after other transitions.
437     */
438    private boolean completeAdd(PendingAddArguments args) {
439        switch (args.requestCode) {
440            case REQUEST_PICK_APPLICATION:
441                completeAddApplication(args.intent, args.container, args.screen, args.cellX,
442                        args.cellY);
443                break;
444            case REQUEST_PICK_SHORTCUT:
445                processShortcut(args.intent);
446                break;
447            case REQUEST_CREATE_SHORTCUT:
448                completeAddShortcut(args.intent, args.container, args.screen, args.cellX,
449                        args.cellY);
450                return true;
451            case REQUEST_PICK_APPWIDGET:
452                addAppWidgetFromPick(args.intent);
453                break;
454            case REQUEST_CREATE_APPWIDGET:
455                int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
456                completeAddAppWidget(appWidgetId, args.container, args.screen);
457                return true;
458            case REQUEST_PICK_WALLPAPER:
459                // We just wanted the activity result here so we can clear mWaitingForResult
460                break;
461        }
462        return false;
463    }
464
465    @Override
466    protected void onActivityResult(final int requestCode, int resultCode, final Intent data) {
467        boolean delayExitSpringLoadedMode = false;
468        mWaitingForResult = false;
469
470        // The pattern used here is that a user PICKs a specific application,
471        // which, depending on the target, might need to CREATE the actual target.
472
473        // For example, the user would PICK_SHORTCUT for "Music playlist", and we
474        // launch over to the Music app to actually CREATE_SHORTCUT.
475        if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID  &&
476                mPendingAddInfo.screen > -1) {
477            final PendingAddArguments args = new PendingAddArguments();
478            args.requestCode = requestCode;
479            args.intent = data;
480            args.container = mPendingAddInfo.container;
481            args.screen = mPendingAddInfo.screen;
482            args.cellX = mPendingAddInfo.cellX;
483            args.cellY = mPendingAddInfo.cellY;
484
485            // If the loader is still running, defer the add until it is done.
486            if (isWorkspaceLocked()) {
487                sPendingAddList.add(args);
488            } else {
489                delayExitSpringLoadedMode = completeAdd(args);
490            }
491        } else if ((requestCode == REQUEST_PICK_APPWIDGET ||
492                requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED) {
493            if (data != null) {
494                // Clean up the appWidgetId if we canceled
495                int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
496                if (appWidgetId != -1) {
497                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
498                }
499            }
500        }
501
502        // Exit spring loaded mode if necessary after cancelling the configuration of a widget
503        exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode);
504    }
505
506    @Override
507    protected void onResume() {
508        super.onResume();
509        mPaused = false;
510        if (mRestoring || mOnResumeNeedsLoad) {
511            mWorkspaceLoading = true;
512            mModel.startLoader(this, true);
513            mRestoring = false;
514            mOnResumeNeedsLoad = false;
515        }
516        if (mWaitingForResume != null) {
517            mWaitingForResume.setStayPressed(false);
518        }
519        // When we resume Launcher, a different Activity might be responsible for the app
520        // market intent, so refresh the icon
521        updateAppMarketIcon();
522    }
523
524    @Override
525    protected void onPause() {
526        super.onPause();
527        mPaused = true;
528        mDragController.cancelDrag();
529    }
530
531    @Override
532    public Object onRetainNonConfigurationInstance() {
533        // Flag the loader to stop early before switching
534        mModel.stopLoader();
535        if (mAppsCustomizeContent != null) {
536            mAppsCustomizeContent.surrender();
537        }
538        return Boolean.TRUE;
539    }
540
541    // We can't hide the IME if it was forced open.  So don't bother
542    /*
543    @Override
544    public void onWindowFocusChanged(boolean hasFocus) {
545        super.onWindowFocusChanged(hasFocus);
546
547        if (hasFocus) {
548            final InputMethodManager inputManager = (InputMethodManager)
549                    getSystemService(Context.INPUT_METHOD_SERVICE);
550            WindowManager.LayoutParams lp = getWindow().getAttributes();
551            inputManager.hideSoftInputFromWindow(lp.token, 0, new android.os.ResultReceiver(new
552                        android.os.Handler()) {
553                        protected void onReceiveResult(int resultCode, Bundle resultData) {
554                            Log.d(TAG, "ResultReceiver got resultCode=" + resultCode);
555                        }
556                    });
557            Log.d(TAG, "called hideSoftInputFromWindow from onWindowFocusChanged");
558        }
559    }
560    */
561
562    private boolean acceptFilter() {
563        final InputMethodManager inputManager = (InputMethodManager)
564                getSystemService(Context.INPUT_METHOD_SERVICE);
565        return !inputManager.isFullscreenMode();
566    }
567
568    @Override
569    public boolean onKeyDown(int keyCode, KeyEvent event) {
570        final int uniChar = event.getUnicodeChar();
571        final boolean handled = super.onKeyDown(keyCode, event);
572        final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar);
573        if (!handled && acceptFilter() && isKeyNotWhitespace) {
574            boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
575                    keyCode, event);
576            if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
577                // something usable has been typed - start a search
578                // the typed text will be retrieved and cleared by
579                // showSearchDialog()
580                // If there are multiple keystrokes before the search dialog takes focus,
581                // onSearchRequested() will be called for every keystroke,
582                // but it is idempotent, so it's fine.
583                return onSearchRequested();
584            }
585        }
586
587        // Eat the long press event so the keyboard doesn't come up.
588        if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
589            return true;
590        }
591
592        return handled;
593    }
594
595    private String getTypedText() {
596        return mDefaultKeySsb.toString();
597    }
598
599    private void clearTypedText() {
600        mDefaultKeySsb.clear();
601        mDefaultKeySsb.clearSpans();
602        Selection.setSelection(mDefaultKeySsb, 0);
603    }
604
605    /**
606     * Given the integer (ordinal) value of a State enum instance, convert it to a variable of type
607     * State
608     */
609    private static State intToState(int stateOrdinal) {
610        State state = State.WORKSPACE;
611        final State[] stateValues = State.values();
612        for (int i = 0; i < stateValues.length; i++) {
613            if (stateValues[i].ordinal() == stateOrdinal) {
614                state = stateValues[i];
615                break;
616            }
617        }
618        return state;
619    }
620
621    /**
622     * Restores the previous state, if it exists.
623     *
624     * @param savedState The previous state.
625     */
626    private void restoreState(Bundle savedState) {
627        if (savedState == null) {
628            return;
629        }
630
631        State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()));
632
633        if (state == State.APPS_CUSTOMIZE) {
634            showAllApps(false);
635        }
636
637        final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
638        if (currentScreen > -1) {
639            mWorkspace.setCurrentPage(currentScreen);
640        }
641
642        final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
643        final int pendingAddScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
644
645        if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
646            mPendingAddInfo.container = pendingAddContainer;
647            mPendingAddInfo.screen = pendingAddScreen;
648            mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
649            mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
650            mRestoring = true;
651        }
652
653        boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
654        if (renameFolder) {
655            long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
656            mFolderInfo = mModel.getFolderById(this, sFolders, id);
657            mRestoring = true;
658        }
659
660
661        // Restore the AppsCustomize tab
662        if (mAppsCustomizeTabHost != null) {
663            String curTab = savedState.getString("apps_customize_currentTab");
664            if (curTab != null) {
665                // We set this directly so that there is no delay before the tab is set
666                mAppsCustomizeContent.setContentType(
667                        mAppsCustomizeTabHost.getContentTypeForTabTag(curTab));
668                mAppsCustomizeTabHost.setCurrentTabByTag(curTab);
669            }
670
671            // Note: currently we do not restore the page for the AppsCustomize pane because the
672            // change in layout can drastically affect the saved page index
673        }
674    }
675
676    /**
677     * Finds all the views we need and configure them properly.
678     */
679    private void setupViews() {
680        final DragController dragController = mDragController;
681
682        mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
683        mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
684
685        // Setup the drag layer
686        mDragLayer.setup(this, dragController);
687
688        // Setup the hotseat
689        mHotseat = (Hotseat) findViewById(R.id.hotseat);
690        if (mHotseat != null) {
691            mHotseat.setup(this);
692        }
693
694        // Setup the workspace
695        mWorkspace.setHapticFeedbackEnabled(false);
696        mWorkspace.setOnLongClickListener(this);
697        mWorkspace.setup(this, dragController);
698        dragController.addDragListener(mWorkspace);
699
700        // Get the search/delete bar
701        mSearchDeleteBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.qsb_bar);
702
703        // Setup AppsCustomize
704        mAppsCustomizeTabHost = (AppsCustomizeTabHost)
705                findViewById(R.id.apps_customize_pane);
706        mAppsCustomizeContent = (AppsCustomizePagedView)
707                mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
708        mAppsCustomizeContent.setup(this, dragController);
709
710
711
712        // Setup the drag controller (drop targets have to be added in reverse order in priority)
713        dragController.setDragScoller(mWorkspace);
714        dragController.setScrollView(mDragLayer);
715        dragController.setMoveTarget(mWorkspace);
716        dragController.addDropTarget(mWorkspace);
717        if (mSearchDeleteBar != null) {
718            mSearchDeleteBar.setup(this, dragController);
719        }
720    }
721
722
723
724    /**
725     * Creates a view representing a shortcut.
726     *
727     * @param info The data structure describing the shortcut.
728     *
729     * @return A View inflated from R.layout.application.
730     */
731    View createShortcut(ShortcutInfo info) {
732        return createShortcut(R.layout.application,
733                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
734    }
735
736    /**
737     * Creates a view representing a shortcut inflated from the specified resource.
738     *
739     * @param layoutResId The id of the XML layout used to create the shortcut.
740     * @param parent The group the shortcut belongs to.
741     * @param info The data structure describing the shortcut.
742     *
743     * @return A View inflated from layoutResId.
744     */
745    View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
746        BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
747        favorite.applyFromShortcutInfo(info, mIconCache);
748        favorite.setOnClickListener(this);
749        return favorite;
750    }
751
752    /**
753     * Add an application shortcut to the workspace.
754     *
755     * @param data The intent describing the application.
756     * @param cellInfo The position on screen where to create the shortcut.
757     */
758    void completeAddApplication(Intent data, long container, int screen, int cellX, int cellY) {
759        final int[] cellXY = mTmpAddItemCellCoordinates;
760        final CellLayout layout = getCellLayout(container, screen);
761
762        // First we check if we already know the exact location where we want to add this item.
763        if (cellX >= 0 && cellY >= 0) {
764            cellXY[0] = cellX;
765            cellXY[1] = cellY;
766        } else if (!layout.findCellForSpan(cellXY, 1, 1)) {
767            showOutOfSpaceMessage();
768            return;
769        }
770
771        final ShortcutInfo info = mModel.getShortcutInfo(getPackageManager(), data, this);
772
773        if (info != null) {
774            info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
775                    Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
776            info.container = ItemInfo.NO_ID;
777            mWorkspace.addApplicationShortcut(info, layout, container, screen, cellXY[0], cellXY[1],
778                    isWorkspaceLocked(), cellX, cellY);
779        } else {
780            Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
781        }
782    }
783
784    /**
785     * Add a shortcut to the workspace.
786     *
787     * @param data The intent describing the shortcut.
788     * @param cellInfo The position on screen where to create the shortcut.
789     */
790    private void completeAddShortcut(Intent data, long container, int screen, int cellX,
791            int cellY) {
792        int[] cellXY = mTmpAddItemCellCoordinates;
793        int[] touchXY = mPendingAddInfo.dropPos;
794        CellLayout layout = getCellLayout(container, screen);
795
796        boolean foundCellSpan = false;
797
798        // First we check if we already know the exact location where we want to add this item.
799        if (cellX >= 0 && cellY >= 0) {
800            cellXY[0] = cellX;
801            cellXY[1] = cellY;
802            foundCellSpan = true;
803        } else if (touchXY != null) {
804            // when dragging and dropping, just find the closest free spot
805            int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY);
806            foundCellSpan = (result != null);
807        } else {
808            foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
809        }
810
811        if (!foundCellSpan) {
812            showOutOfSpaceMessage();
813            return;
814        }
815
816        final ShortcutInfo info = mModel.addShortcut(
817                this, data, container, screen, cellXY[0], cellXY[1], false);
818
819        if (!mRestoring) {
820            final View view = createShortcut(info);
821            mWorkspace.addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1,
822                    isWorkspaceLocked());
823        }
824    }
825
826    /**
827     * Add a widget to the workspace.
828     *
829     * @param appWidgetId The app widget id
830     * @param cellInfo The position on screen where to create the widget.
831     */
832    private void completeAddAppWidget(final int appWidgetId, long container, int screen) {
833        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
834
835        // Calculate the grid spans needed to fit this widget
836        CellLayout layout = getCellLayout(container, screen);
837
838        // We want to account for the extra amount of padding that we are adding to the widget
839        // to ensure that it gets the full amount of space that it has requested
840        Resources r = getResources();
841        int requiredWidth = appWidgetInfo.minWidth +
842                r.getDimensionPixelSize(R.dimen.app_widget_padding_left) +
843                r.getDimensionPixelSize(R.dimen.app_widget_padding_right);
844        int requiredHeight = appWidgetInfo.minHeight +
845                r.getDimensionPixelSize(R.dimen.app_widget_padding_top) +
846                r.getDimensionPixelSize(R.dimen.app_widget_padding_bottom);
847        int[] spanXY = layout.rectToCell(requiredWidth, requiredHeight, null);
848
849        // Try finding open space on Launcher screen
850        // We have saved the position to which the widget was dragged-- this really only matters
851        // if we are placing widgets on a "spring-loaded" screen
852        int[] cellXY = mTmpAddItemCellCoordinates;
853        int[] touchXY = mPendingAddInfo.dropPos;
854        boolean foundCellSpan = false;
855        if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) {
856            cellXY[0] = mPendingAddInfo.cellX;
857            cellXY[1] = mPendingAddInfo.cellY;
858            foundCellSpan = true;
859        } else if (touchXY != null) {
860            // when dragging and dropping, just find the closest free spot
861            int[] result = layout.findNearestVacantArea(
862                    touchXY[0], touchXY[1], spanXY[0], spanXY[1], cellXY);
863            foundCellSpan = (result != null);
864        } else {
865            foundCellSpan = layout.findCellForSpan(cellXY, spanXY[0], spanXY[1]);
866        }
867
868        if (!foundCellSpan) {
869            if (appWidgetId != -1) {
870                // Deleting an app widget ID is a void call but writes to disk before returning
871                // to the caller...
872                new Thread("deleteAppWidgetId") {
873                    public void run() {
874                        mAppWidgetHost.deleteAppWidgetId(appWidgetId);
875                    }
876                }.start();
877            }
878            showOutOfSpaceMessage();
879            return;
880        }
881
882        // Build Launcher-specific widget info and save to database
883        LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
884        launcherInfo.spanX = spanXY[0];
885        launcherInfo.spanY = spanXY[1];
886
887        LauncherModel.addItemToDatabase(this, launcherInfo,
888                container, screen, cellXY[0], cellXY[1], false);
889
890        if (!mRestoring) {
891            // Perform actual inflation because we're live
892            launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
893
894            launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
895            launcherInfo.hostView.setTag(launcherInfo);
896
897            mWorkspace.addInScreen(launcherInfo.hostView, container, screen, cellXY[0], cellXY[1],
898                    launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
899
900            addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
901        }
902    }
903
904    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
905        @Override
906        public void onReceive(Context context, Intent intent) {
907            final String action = intent.getAction();
908            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
909                mUserPresent = false;
910                mDragLayer.clearAllResizeFrames();
911                updateRunning();
912
913                // Reset AllApps to it's initial state
914                if (mAppsCustomizeContent != null) {
915                    mAppsCustomizeContent.reset();
916                }
917            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
918                mUserPresent = true;
919                updateRunning();
920            }
921        }
922    };
923
924    @Override
925    public void onAttachedToWindow() {
926        super.onAttachedToWindow();
927
928        // Listen for broadcasts related to user-presence
929        final IntentFilter filter = new IntentFilter();
930        filter.addAction(Intent.ACTION_SCREEN_OFF);
931        filter.addAction(Intent.ACTION_USER_PRESENT);
932        registerReceiver(mReceiver, filter);
933
934        mAttached = true;
935        mVisible = true;
936    }
937
938    @Override
939    public void onDetachedFromWindow() {
940        super.onDetachedFromWindow();
941        mVisible = false;
942        mDragLayer.clearAllResizeFrames();
943
944        if (mAttached) {
945            unregisterReceiver(mReceiver);
946            mAttached = false;
947        }
948        updateRunning();
949    }
950
951    public void onWindowVisibilityChanged(int visibility) {
952        mVisible = visibility == View.VISIBLE;
953        updateRunning();
954    }
955
956    private void sendAdvanceMessage(long delay) {
957        mHandler.removeMessages(ADVANCE_MSG);
958        Message msg = mHandler.obtainMessage(ADVANCE_MSG);
959        mHandler.sendMessageDelayed(msg, delay);
960        mAutoAdvanceSentTime = System.currentTimeMillis();
961    }
962
963    private void updateRunning() {
964        boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty();
965        if (autoAdvanceRunning != mAutoAdvanceRunning) {
966            mAutoAdvanceRunning = autoAdvanceRunning;
967            if (autoAdvanceRunning) {
968                long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft;
969                sendAdvanceMessage(delay);
970            } else {
971                if (!mWidgetsToAdvance.isEmpty()) {
972                    mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval -
973                            (System.currentTimeMillis() - mAutoAdvanceSentTime));
974                }
975                mHandler.removeMessages(ADVANCE_MSG);
976                mHandler.removeMessages(0); // Remove messages sent using postDelayed()
977            }
978        }
979    }
980
981    private final Handler mHandler = new Handler() {
982        @Override
983        public void handleMessage(Message msg) {
984            if (msg.what == ADVANCE_MSG) {
985                int i = 0;
986                for (View key: mWidgetsToAdvance.keySet()) {
987                    final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId);
988                    final int delay = mAdvanceStagger * i;
989                    if (v instanceof Advanceable) {
990                       postDelayed(new Runnable() {
991                           public void run() {
992                               ((Advanceable) v).advance();
993                           }
994                       }, delay);
995                    }
996                    i++;
997                }
998                sendAdvanceMessage(mAdvanceInterval);
999            }
1000        }
1001    };
1002
1003    void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) {
1004        if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return;
1005        View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId);
1006        if (v instanceof Advanceable) {
1007            mWidgetsToAdvance.put(hostView, appWidgetInfo);
1008            ((Advanceable) v).fyiWillBeAdvancedByHostKThx();
1009            updateRunning();
1010        }
1011    }
1012
1013    void removeWidgetToAutoAdvance(View hostView) {
1014        if (mWidgetsToAdvance.containsKey(hostView)) {
1015            mWidgetsToAdvance.remove(hostView);
1016            updateRunning();
1017        }
1018    }
1019
1020    public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
1021        removeWidgetToAutoAdvance(launcherInfo.hostView);
1022        launcherInfo.hostView = null;
1023    }
1024
1025    void showOutOfSpaceMessage() {
1026        Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
1027    }
1028
1029    public LauncherAppWidgetHost getAppWidgetHost() {
1030        return mAppWidgetHost;
1031    }
1032
1033    public LauncherModel getModel() {
1034        return mModel;
1035    }
1036
1037    void closeSystemDialogs() {
1038        getWindow().closeAllPanels();
1039
1040        try {
1041            dismissDialog(DIALOG_CREATE_SHORTCUT);
1042            // Unlock the workspace if the dialog was showing
1043        } catch (Exception e) {
1044            // An exception is thrown if the dialog is not visible, which is fine
1045        }
1046
1047        try {
1048            dismissDialog(DIALOG_RENAME_FOLDER);
1049            // Unlock the workspace if the dialog was showing
1050        } catch (Exception e) {
1051            // An exception is thrown if the dialog is not visible, which is fine
1052        }
1053
1054        // Whatever we were doing is hereby canceled.
1055        mWaitingForResult = false;
1056    }
1057
1058    @Override
1059    protected void onNewIntent(Intent intent) {
1060        super.onNewIntent(intent);
1061
1062        // Close the menu
1063        if (Intent.ACTION_MAIN.equals(intent.getAction())) {
1064            // also will cancel mWaitingForResult.
1065            closeSystemDialogs();
1066
1067            closeFolder();
1068
1069            boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
1070                        != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
1071
1072            // In all these cases, only animate if we're already on home
1073            mWorkspace.exitWidgetResizeMode();
1074            if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive()) {
1075                mWorkspace.moveToDefaultScreen(true);
1076            }
1077            exitSpringLoadedDragMode();
1078            showWorkspace(alreadyOnHome);
1079
1080            final View v = getWindow().peekDecorView();
1081            if (v != null && v.getWindowToken() != null) {
1082                InputMethodManager imm = (InputMethodManager)getSystemService(
1083                        INPUT_METHOD_SERVICE);
1084                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
1085            }
1086
1087            // Reset AllApps to its initial state
1088            if (mAppsCustomizeContent != null) {
1089                mAppsCustomizeContent.reset();
1090            }
1091        }
1092    }
1093
1094    @Override
1095    protected void onRestoreInstanceState(Bundle savedInstanceState) {
1096        // Do not call super here
1097        mSavedInstanceState = savedInstanceState;
1098    }
1099
1100    @Override
1101    protected void onSaveInstanceState(Bundle outState) {
1102        outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentPage());
1103        super.onSaveInstanceState(outState);
1104
1105        outState.putInt(RUNTIME_STATE, mState.ordinal());
1106        // We close any open folder since it will not be re-opened, and we need to make sure
1107        // this state is reflected.
1108        closeFolder();
1109
1110        if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screen > -1 &&
1111                mWaitingForResult) {
1112            outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container);
1113            outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screen);
1114            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX);
1115            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
1116        }
1117
1118        if (mFolderInfo != null && mWaitingForResult) {
1119            outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
1120            outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
1121        }
1122
1123        // Save the current AppsCustomize tab
1124        if (mAppsCustomizeTabHost != null) {
1125            String currentTabTag = mAppsCustomizeTabHost.getCurrentTabTag();
1126            if (currentTabTag != null) {
1127                outState.putString("apps_customize_currentTab", currentTabTag);
1128            }
1129        }
1130    }
1131
1132    @Override
1133    public void onDestroy() {
1134        super.onDestroy();
1135
1136        // Stop callbacks from LauncherModel
1137        LauncherApplication app = ((LauncherApplication) getApplication());
1138        mModel.stopLoader();
1139        app.setLauncher(null);
1140
1141        try {
1142            mAppWidgetHost.stopListening();
1143        } catch (NullPointerException ex) {
1144            Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
1145        }
1146        mAppWidgetHost = null;
1147
1148        mWidgetsToAdvance.clear();
1149
1150        TextKeyListener.getInstance().release();
1151
1152
1153        unbindWorkspaceAndHotseatItems();
1154
1155        getContentResolver().unregisterContentObserver(mWidgetObserver);
1156        unregisterReceiver(mCloseSystemDialogsReceiver);
1157
1158        ((ViewGroup) mWorkspace.getParent()).removeAllViews();
1159        mWorkspace.removeAllViews();
1160        mWorkspace = null;
1161        mDragController = null;
1162
1163        ValueAnimator.clearAllAnimations();
1164    }
1165
1166    public DragController getDragController() {
1167        return mDragController;
1168    }
1169
1170    @Override
1171    public void startActivityForResult(Intent intent, int requestCode) {
1172        if (requestCode >= 0) mWaitingForResult = true;
1173        super.startActivityForResult(intent, requestCode);
1174    }
1175
1176    @Override
1177    public void startSearch(String initialQuery, boolean selectInitialQuery,
1178            Bundle appSearchData, boolean globalSearch) {
1179
1180        showWorkspace(true);
1181
1182        if (initialQuery == null) {
1183            // Use any text typed in the launcher as the initial query
1184            initialQuery = getTypedText();
1185            clearTypedText();
1186        }
1187        if (appSearchData == null) {
1188            appSearchData = new Bundle();
1189            appSearchData.putString(Search.SOURCE, "launcher-search");
1190        }
1191
1192        final SearchManager searchManager =
1193                (SearchManager) getSystemService(Context.SEARCH_SERVICE);
1194        searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
1195            appSearchData, globalSearch);
1196    }
1197
1198    @Override
1199    public boolean onCreateOptionsMenu(Menu menu) {
1200        super.onCreateOptionsMenu(menu);
1201        return true;
1202    }
1203
1204    @Override
1205    public boolean onPrepareOptionsMenu(Menu menu) {
1206        super.onPrepareOptionsMenu(menu);
1207
1208        startWallpaper();
1209        return true;
1210    }
1211
1212    @Override
1213    public boolean onOptionsItemSelected(MenuItem item) {
1214        switch (item.getItemId()) {
1215            case MENU_ADD:
1216                addItems();
1217                return true;
1218            case MENU_MANAGE_APPS:
1219                manageApps();
1220                return true;
1221            case MENU_WALLPAPER_SETTINGS:
1222                startWallpaper();
1223                return true;
1224            case MENU_SEARCH:
1225                onSearchRequested();
1226                return true;
1227            case MENU_NOTIFICATIONS:
1228                showNotifications();
1229                return true;
1230        }
1231
1232        return super.onOptionsItemSelected(item);
1233    }
1234
1235    /**
1236     * Indicates that we want global search for this activity by setting the globalSearch
1237     * argument for {@link #startSearch} to true.
1238     */
1239
1240    @Override
1241    public boolean onSearchRequested() {
1242        startSearch(null, false, null, true);
1243        return true;
1244    }
1245
1246    public boolean isWorkspaceLocked() {
1247        return mWorkspaceLoading || mWaitingForResult;
1248    }
1249
1250    private void addItems() {
1251        showWorkspace(true);
1252        showAddDialog();
1253    }
1254
1255    private void resetAddInfo() {
1256        mPendingAddInfo.container = ItemInfo.NO_ID;
1257        mPendingAddInfo.screen = -1;
1258        mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
1259        mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
1260        mPendingAddInfo.dropPos = null;
1261    }
1262
1263    private void manageApps() {
1264        startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS));
1265    }
1266
1267    void addAppWidgetFromPick(Intent data) {
1268        // TODO: catch bad widget exception when sent
1269        int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
1270        // TODO: Is this log message meaningful?
1271        if (LOGD) Log.d(TAG, "dumping extras content=" + data.getExtras());
1272        addAppWidgetImpl(appWidgetId, null);
1273    }
1274
1275    void addAppWidgetImpl(int appWidgetId, PendingAddWidgetInfo info) {
1276        AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
1277
1278        if (appWidget.configure != null) {
1279            // Launch over to configure widget, if needed
1280            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
1281            intent.setComponent(appWidget.configure);
1282            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1283            if (info != null) {
1284                if (info.mimeType != null && !info.mimeType.isEmpty()) {
1285                    intent.putExtra(
1286                            InstallWidgetReceiver.EXTRA_APPWIDGET_CONFIGURATION_DATA_MIME_TYPE,
1287                            info.mimeType);
1288
1289                    final String mimeType = info.mimeType;
1290                    final ClipData clipData = (ClipData) info.configurationData;
1291                    final ClipDescription clipDesc = clipData.getDescription();
1292                    for (int i = 0; i < clipDesc.getMimeTypeCount(); ++i) {
1293                        if (clipDesc.getMimeType(i).equals(mimeType)) {
1294                            final ClipData.Item item = clipData.getItemAt(i);
1295                            final CharSequence stringData = item.getText();
1296                            final Uri uriData = item.getUri();
1297                            final Intent intentData = item.getIntent();
1298                            final String key =
1299                                InstallWidgetReceiver.EXTRA_APPWIDGET_CONFIGURATION_DATA;
1300                            if (uriData != null) {
1301                                intent.putExtra(key, uriData);
1302                            } else if (intentData != null) {
1303                                intent.putExtra(key, intentData);
1304                            } else if (stringData != null) {
1305                                intent.putExtra(key, stringData);
1306                            }
1307                            break;
1308                        }
1309                    }
1310                }
1311            }
1312
1313            startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
1314        } else {
1315            // Otherwise just add it
1316            completeAddAppWidget(appWidgetId, info.container, info.screen);
1317
1318            // Exit spring loaded mode if necessary after adding the widget
1319            exitSpringLoadedDragModeDelayed(true, false);
1320        }
1321    }
1322
1323    /**
1324     * Process a shortcut drop.
1325     *
1326     * @param componentName The name of the component
1327     * @param screen The screen where it should be added
1328     * @param cell The cell it should be added to, optional
1329     * @param position The location on the screen where it was dropped, optional
1330     */
1331    void processShortcutFromDrop(ComponentName componentName, long container, int screen,
1332            int[] cell, int[] loc) {
1333        resetAddInfo();
1334        mPendingAddInfo.container = container;
1335        mPendingAddInfo.screen = screen;
1336        mPendingAddInfo.dropPos = loc;
1337
1338        if (cell != null) {
1339            mPendingAddInfo.cellX = cell[0];
1340            mPendingAddInfo.cellY = cell[1];
1341        }
1342
1343        Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
1344        createShortcutIntent.setComponent(componentName);
1345        processShortcut(createShortcutIntent);
1346    }
1347
1348    /**
1349     * Process a widget drop.
1350     *
1351     * @param info The PendingAppWidgetInfo of the widget being added.
1352     * @param screen The screen where it should be added
1353     * @param cell The cell it should be added to, optional
1354     * @param position The location on the screen where it was dropped, optional
1355     */
1356    void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, int screen,
1357            int[] cell, int[] loc) {
1358        resetAddInfo();
1359        mPendingAddInfo.container = info.container = container;
1360        mPendingAddInfo.screen = info.screen = screen;
1361        mPendingAddInfo.dropPos = loc;
1362        if (cell != null) {
1363            mPendingAddInfo.cellX = cell[0];
1364            mPendingAddInfo.cellY = cell[1];
1365        }
1366
1367        int appWidgetId = getAppWidgetHost().allocateAppWidgetId();
1368        AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, info.componentName);
1369        addAppWidgetImpl(appWidgetId, info);
1370    }
1371
1372    void processShortcut(Intent intent) {
1373        // Handle case where user selected "Applications"
1374        String applicationName = getResources().getString(R.string.group_applications);
1375        String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1376
1377        if (applicationName != null && applicationName.equals(shortcutName)) {
1378            Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1379            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1380
1381            Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
1382            pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
1383            pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
1384            startActivityForResultSafely(pickIntent, REQUEST_PICK_APPLICATION);
1385        } else {
1386            startActivityForResultSafely(intent, REQUEST_CREATE_SHORTCUT);
1387        }
1388    }
1389
1390    void processWallpaper(Intent intent) {
1391        startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
1392    }
1393
1394    FolderIcon addFolder(CellLayout layout, long container, final int screen, int cellX,
1395            int cellY) {
1396        final FolderInfo folderInfo = new FolderInfo();
1397        folderInfo.title = getText(R.string.folder_name);
1398
1399        // Update the model
1400        LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screen, cellX, cellY,
1401                false);
1402        sFolders.put(folderInfo.id, folderInfo);
1403
1404        // Create the view
1405        FolderIcon newFolder =
1406            FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
1407        mWorkspace.addInScreen(newFolder, container, screen, cellX, cellY, 1, 1,
1408                isWorkspaceLocked());
1409        return newFolder;
1410    }
1411
1412    void removeFolder(FolderInfo folder) {
1413        sFolders.remove(folder.id);
1414    }
1415
1416    private void showNotifications() {
1417        final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
1418        if (statusBar != null) {
1419            statusBar.expand();
1420        }
1421    }
1422
1423    private void startWallpaper() {
1424        showWorkspace(true);
1425        final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
1426        Intent chooser = Intent.createChooser(pickWallpaper,
1427                getText(R.string.chooser_wallpaper));
1428        // NOTE: Adds a configure option to the chooser if the wallpaper supports it
1429        //       Removed in Eclair MR1
1430//        WallpaperManager wm = (WallpaperManager)
1431//                getSystemService(Context.WALLPAPER_SERVICE);
1432//        WallpaperInfo wi = wm.getWallpaperInfo();
1433//        if (wi != null && wi.getSettingsActivity() != null) {
1434//            LabeledIntent li = new LabeledIntent(getPackageName(),
1435//                    R.string.configure_wallpaper, 0);
1436//            li.setClassName(wi.getPackageName(), wi.getSettingsActivity());
1437//            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li });
1438//        }
1439        startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
1440    }
1441
1442    /**
1443     * Registers various content observers. The current implementation registers
1444     * only a favorites observer to keep track of the favorites applications.
1445     */
1446    private void registerContentObservers() {
1447        ContentResolver resolver = getContentResolver();
1448        resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
1449                true, mWidgetObserver);
1450    }
1451
1452    @Override
1453    public boolean dispatchKeyEvent(KeyEvent event) {
1454        if (event.getAction() == KeyEvent.ACTION_DOWN) {
1455            switch (event.getKeyCode()) {
1456                case KeyEvent.KEYCODE_HOME:
1457                    return true;
1458                case KeyEvent.KEYCODE_VOLUME_DOWN:
1459                    if (SystemProperties.getInt("debug.launcher2.dumpstate", 0) != 0) {
1460                        dumpState();
1461                        return true;
1462                    }
1463                    break;
1464            }
1465        } else if (event.getAction() == KeyEvent.ACTION_UP) {
1466            switch (event.getKeyCode()) {
1467                case KeyEvent.KEYCODE_HOME:
1468                    return true;
1469            }
1470        }
1471
1472        return super.dispatchKeyEvent(event);
1473    }
1474
1475    @Override
1476    public void onBackPressed() {
1477        if (mState == State.APPS_CUSTOMIZE) {
1478            showWorkspace(true);
1479        } else if (mWorkspace.getOpenFolder() != null) {
1480            Folder openFolder = mWorkspace.getOpenFolder();
1481            if (openFolder.isEditingName()) {
1482                openFolder.dismissEditingName();
1483            } else {
1484                closeFolder();
1485            }
1486        } else {
1487            mWorkspace.exitWidgetResizeMode();
1488
1489            // Back button is a no-op here, but give at least some feedback for the button press
1490            mWorkspace.showOutlinesTemporarily();
1491        }
1492    }
1493
1494    /**
1495     * Re-listen when widgets are reset.
1496     */
1497    private void onAppWidgetReset() {
1498        if (mAppWidgetHost != null) {
1499            mAppWidgetHost.startListening();
1500        }
1501    }
1502
1503    /**
1504     * Go through the and disconnect any of the callbacks in the drawables and the views or we
1505     * leak the previous Home screen on orientation change.
1506     */
1507    private void unbindWorkspaceAndHotseatItems() {
1508        LauncherModel.unbindWorkspaceItems();
1509    }
1510
1511    /**
1512     * Launches the intent referred by the clicked shortcut.
1513     *
1514     * @param v The view representing the clicked shortcut.
1515     */
1516    public void onClick(View v) {
1517        // Make sure that rogue clicks don't get through while allapps is launching, or after the
1518        // view has detached (it's possible for this to happen if the view is removed mid touch).
1519        if (v.getWindowToken() == null) {
1520            return;
1521        }
1522
1523        if (mWorkspace.isSwitchingState()) {
1524            return;
1525        }
1526
1527        Object tag = v.getTag();
1528        if (tag instanceof ShortcutInfo) {
1529            // Open shortcut
1530            final Intent intent = ((ShortcutInfo) tag).intent;
1531            int[] pos = new int[2];
1532            v.getLocationOnScreen(pos);
1533            intent.setSourceBounds(new Rect(pos[0], pos[1],
1534                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
1535            boolean success = startActivitySafely(intent, tag);
1536
1537            if (success && v instanceof BubbleTextView) {
1538                mWaitingForResume = (BubbleTextView) v;
1539                mWaitingForResume.setStayPressed(true);
1540            }
1541        } else if (tag instanceof FolderInfo) {
1542            if (v instanceof FolderIcon) {
1543                FolderIcon fi = (FolderIcon) v;
1544                handleFolderClick(fi);
1545            }
1546        } else if (v == mAllAppsButton) {
1547            if (mState == State.APPS_CUSTOMIZE) {
1548                showWorkspace(true);
1549            } else {
1550                showAllApps(true);
1551            }
1552        }
1553    }
1554
1555    public boolean onTouch(View v, MotionEvent event) {
1556        // this is an intercepted event being forwarded from mWorkspace;
1557        // clicking anywhere on the workspace causes the customization drawer to slide down
1558        showWorkspace(true);
1559        return false;
1560    }
1561
1562    /**
1563     * Event handler for the search button
1564     *
1565     * @param v The view that was clicked.
1566     */
1567    public void onClickSearchButton(View v) {
1568        startSearch(null, false, null, true);
1569        // Use a custom animation for launching search
1570        overridePendingTransition(R.anim.fade_in_fast, R.anim.fade_out_fast);
1571    }
1572
1573    /**
1574     * Event handler for the voice button
1575     *
1576     * @param v The view that was clicked.
1577     */
1578    public void onClickVoiceButton(View v) {
1579        startVoiceSearch();
1580    }
1581
1582    private void startVoiceSearch() {
1583        Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
1584        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1585        startActivity(intent);
1586    }
1587
1588    /**
1589     * Event handler for the "grid" button that appears on the home screen, which
1590     * enters all apps mode.
1591     *
1592     * @param v The view that was clicked.
1593     */
1594    public void onClickAllAppsButton(View v) {
1595        showAllApps(true);
1596    }
1597
1598    public void onClickAppMarketButton(View v) {
1599        if (mAppMarketIntent != null) {
1600            startActivitySafely(mAppMarketIntent, "app market");
1601        }
1602    }
1603
1604    void startApplicationDetailsActivity(ComponentName componentName) {
1605        String packageName = componentName.getPackageName();
1606        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
1607                Uri.fromParts("package", packageName, null));
1608        startActivity(intent);
1609    }
1610
1611    void startApplicationUninstallActivity(ApplicationInfo appInfo) {
1612        if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) == 0) {
1613            // System applications cannot be installed. For now, show a toast explaining that.
1614            // We may give them the option of disabling apps this way.
1615            int messageId = R.string.uninstall_system_app_text;
1616            Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
1617        } else {
1618            String packageName = appInfo.componentName.getPackageName();
1619            String className = appInfo.componentName.getClassName();
1620            Intent intent = new Intent(
1621                    Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
1622            startActivity(intent);
1623        }
1624    }
1625
1626    boolean startActivitySafely(Intent intent, Object tag) {
1627        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1628        try {
1629            startActivity(intent);
1630            return true;
1631        } catch (ActivityNotFoundException e) {
1632            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1633            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
1634        } catch (SecurityException e) {
1635            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1636            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
1637                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
1638                    "or use the exported attribute for this activity. "
1639                    + "tag="+ tag + " intent=" + intent, e);
1640        }
1641        return false;
1642    }
1643
1644    void startActivityForResultSafely(Intent intent, int requestCode) {
1645        try {
1646            startActivityForResult(intent, requestCode);
1647        } catch (ActivityNotFoundException e) {
1648            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1649        } catch (SecurityException e) {
1650            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1651            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
1652                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
1653                    "or use the exported attribute for this activity.", e);
1654        }
1655    }
1656
1657    private void handleFolderClick(FolderIcon folderIcon) {
1658        final FolderInfo info = folderIcon.mInfo;
1659        if (!info.opened) {
1660            // Close any open folder
1661            closeFolder();
1662            // Open the requested folder
1663            openFolder(folderIcon);
1664        } else {
1665            // Find the open folder...
1666            Folder openFolder = mWorkspace.getFolderForTag(info);
1667            int folderScreen;
1668            if (openFolder != null) {
1669                folderScreen = mWorkspace.getPageForView(openFolder);
1670                // .. and close it
1671                closeFolder(openFolder);
1672                if (folderScreen != mWorkspace.getCurrentPage()) {
1673                    // Close any folder open on the current screen
1674                    closeFolder();
1675                    // Pull the folder onto this screen
1676                    openFolder(folderIcon);
1677                }
1678            }
1679        }
1680    }
1681
1682    private void growAndFadeOutFolderIcon(FolderIcon fi) {
1683        if (fi == null) return;
1684        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
1685        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f);
1686        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f);
1687
1688        FolderInfo info = (FolderInfo) fi.getTag();
1689        if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1690            CellLayout cl = (CellLayout) fi.getParent().getParent();
1691            cl.setFolderLeaveBehindCell(info.cellX, info.cellY);
1692        }
1693
1694        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(fi, alpha, scaleX, scaleY);
1695        oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
1696        oa.start();
1697    }
1698
1699    private void shrinkAndFadeInFolderIcon(FolderIcon fi) {
1700        if (fi == null) return;
1701        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
1702        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
1703        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
1704
1705        FolderInfo info = (FolderInfo) fi.getTag();
1706        CellLayout cl = null;
1707        if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1708            cl = (CellLayout) fi.getParent().getParent();
1709        }
1710
1711        final CellLayout layout = cl;
1712        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(fi, alpha, scaleX, scaleY);
1713        oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
1714        oa.addListener(new AnimatorListenerAdapter() {
1715            @Override
1716            public void onAnimationEnd(Animator animation) {
1717                if (layout != null) {
1718                    layout.clearFolderLeaveBehind();
1719                }
1720            }
1721        });
1722        oa.start();
1723    }
1724
1725    /**
1726     * Opens the user folder described by the specified tag. The opening of the folder
1727     * is animated relative to the specified View. If the View is null, no animation
1728     * is played.
1729     *
1730     * @param folderInfo The FolderInfo describing the folder to open.
1731     */
1732    public void openFolder(FolderIcon folderIcon) {
1733        Folder folder = folderIcon.mFolder;
1734        FolderInfo info = folder.mInfo;
1735
1736        growAndFadeOutFolderIcon(folderIcon);
1737        info.opened = true;
1738
1739        // Just verify that the folder hasn't already been added to the DragLayer.
1740        // There was a one-off crash where the folder had a parent already.
1741        if (folder.getParent() == null) {
1742            mDragLayer.addView(folder);
1743            mDragController.addDropTarget((DropTarget) folder);
1744        } else {
1745            Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
1746                    folder.getParent() + ").");
1747        }
1748        folder.animateOpen();
1749    }
1750
1751    public void closeFolder() {
1752        Folder folder = mWorkspace.getOpenFolder();
1753        if (folder != null) {
1754            closeFolder(folder);
1755        }
1756    }
1757
1758    void closeFolder(Folder folder) {
1759        folder.getInfo().opened = false;
1760
1761        ViewGroup parent = (ViewGroup) folder.getParent().getParent();
1762        if (parent != null) {
1763            FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
1764            shrinkAndFadeInFolderIcon(fi);
1765        }
1766        folder.animateClosed();
1767    }
1768
1769    public boolean onLongClick(View v) {
1770        if (mState != State.WORKSPACE) {
1771            return false;
1772        }
1773
1774        if (isWorkspaceLocked()) {
1775            return false;
1776        }
1777
1778        if (!(v instanceof CellLayout)) {
1779            v = (View) v.getParent().getParent();
1780        }
1781
1782        resetAddInfo();
1783        CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag();
1784        // This happens when long clicking an item with the dpad/trackball
1785        if (longClickCellInfo == null) {
1786            return true;
1787        }
1788
1789        // The hotseat touch handling does not go through Workspace, and we always allow long press
1790        // on hotseat items.
1791        final View itemUnderLongClick = longClickCellInfo.cell;
1792        boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress();
1793        if (allowLongPress && !mDragController.isDragging()) {
1794            if (itemUnderLongClick == null) {
1795                // User long pressed on empty space
1796                mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1797                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1798                startWallpaper();
1799            } else {
1800                if (!(itemUnderLongClick instanceof Folder)) {
1801                    // User long pressed on an item
1802                    mWorkspace.startDrag(longClickCellInfo);
1803                }
1804            }
1805        }
1806        return true;
1807    }
1808
1809    boolean isHotseatLayout(View layout) {
1810        return mHotseat != null && layout != null &&
1811                (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
1812    }
1813    Hotseat getHotseat() {
1814        return mHotseat;
1815    }
1816
1817    /**
1818     * Returns the CellLayout of the specified container at the specified screen.
1819     */
1820    CellLayout getCellLayout(long container, int screen) {
1821        if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1822            if (mHotseat != null) {
1823                return mHotseat.getLayout();
1824            } else {
1825                return null;
1826            }
1827        } else {
1828            return (CellLayout) mWorkspace.getChildAt(screen);
1829        }
1830    }
1831
1832    Workspace getWorkspace() {
1833        return mWorkspace;
1834    }
1835
1836    @Override
1837    protected Dialog onCreateDialog(int id) {
1838        switch (id) {
1839            case DIALOG_CREATE_SHORTCUT:
1840                return new CreateShortcut().createDialog();
1841            case DIALOG_RENAME_FOLDER:
1842                return new RenameFolder().createDialog();
1843        }
1844
1845        return super.onCreateDialog(id);
1846    }
1847
1848    @Override
1849    protected void onPrepareDialog(int id, Dialog dialog) {
1850        switch (id) {
1851            case DIALOG_CREATE_SHORTCUT:
1852                break;
1853            case DIALOG_RENAME_FOLDER:
1854                if (mFolderInfo != null) {
1855                    EditText input = (EditText) dialog.findViewById(R.id.folder_name);
1856                    final CharSequence text = mFolderInfo.title;
1857                    input.setText(text);
1858                    input.setSelection(0, text.length());
1859                }
1860                break;
1861        }
1862    }
1863
1864    void showRenameDialog(FolderInfo info) {
1865        mFolderInfo = info;
1866        mWaitingForResult = true;
1867        showDialog(DIALOG_RENAME_FOLDER);
1868    }
1869
1870    private void showAddDialog() {
1871        resetAddInfo();
1872        mPendingAddInfo.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
1873        mPendingAddInfo.screen = mWorkspace.getCurrentPage();
1874        mWaitingForResult = true;
1875        showDialog(DIALOG_CREATE_SHORTCUT);
1876    }
1877
1878    private class RenameFolder {
1879        private EditText mInput;
1880
1881        Dialog createDialog() {
1882            final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null);
1883            mInput = (EditText) layout.findViewById(R.id.folder_name);
1884
1885            AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
1886            builder.setIcon(0);
1887            builder.setTitle(getString(R.string.rename_folder_title));
1888            builder.setCancelable(true);
1889            builder.setOnCancelListener(new Dialog.OnCancelListener() {
1890                public void onCancel(DialogInterface dialog) {
1891                    cleanup();
1892                }
1893            });
1894            builder.setNegativeButton(getString(R.string.cancel_action),
1895                new Dialog.OnClickListener() {
1896                    public void onClick(DialogInterface dialog, int which) {
1897                        cleanup();
1898                    }
1899                }
1900            );
1901            builder.setPositiveButton(getString(R.string.rename_action),
1902                new Dialog.OnClickListener() {
1903                    public void onClick(DialogInterface dialog, int which) {
1904                        changeFolderName();
1905                    }
1906                }
1907            );
1908            builder.setView(layout);
1909
1910            final AlertDialog dialog = builder.create();
1911            dialog.setOnShowListener(new DialogInterface.OnShowListener() {
1912                public void onShow(DialogInterface dialog) {
1913                    mWaitingForResult = true;
1914                    mInput.requestFocus();
1915                    InputMethodManager inputManager = (InputMethodManager)
1916                            getSystemService(Context.INPUT_METHOD_SERVICE);
1917                    inputManager.showSoftInput(mInput, 0);
1918                }
1919            });
1920
1921            return dialog;
1922        }
1923
1924        private void changeFolderName() {
1925            final String name = mInput.getText().toString();
1926            if (!TextUtils.isEmpty(name)) {
1927                // Make sure we have the right folder info
1928                mFolderInfo = sFolders.get(mFolderInfo.id);
1929                mFolderInfo.title = name;
1930                LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
1931
1932                if (mWorkspaceLoading) {
1933                    lockAllApps();
1934                    mModel.startLoader(Launcher.this, false);
1935                } else {
1936                    final FolderIcon folderIcon = (FolderIcon)
1937                            mWorkspace.getViewForTag(mFolderInfo);
1938                    if (folderIcon != null) {
1939                        // TODO: At some point we'll probably want some version of setting
1940                        // the text for a folder icon.
1941                        //folderIcon.setText(name);
1942                        getWorkspace().requestLayout();
1943                    } else {
1944                        lockAllApps();
1945                        mWorkspaceLoading = true;
1946                        mModel.startLoader(Launcher.this, false);
1947                    }
1948                }
1949            }
1950            cleanup();
1951        }
1952
1953        private void cleanup() {
1954            dismissDialog(DIALOG_RENAME_FOLDER);
1955            mWaitingForResult = false;
1956            mFolderInfo = null;
1957        }
1958    }
1959
1960    // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
1961    public boolean isAllAppsVisible() {
1962        return (mState == State.APPS_CUSTOMIZE);
1963    }
1964
1965    // AllAppsView.Watcher
1966    public void zoomed(float zoom) {
1967        if (zoom == 1.0f) {
1968            mWorkspace.setVisibility(View.GONE);
1969        }
1970    }
1971
1972    /**
1973     * Helper method for the cameraZoomIn/cameraZoomOut animations
1974     * @param view The view being animated
1975     * @param state The state that we are moving in or out of (eg. APPS_CUSTOMIZE)
1976     * @param scaleFactor The scale factor used for the zoom
1977     */
1978    private void setPivotsForZoom(View view, State state, float scaleFactor) {
1979        final int height = view.getHeight();
1980
1981        view.setPivotX(view.getWidth() / 2.0f);
1982        // Set pivotY so that at the starting zoom factor, the view is partially
1983        // visible. Modifying initialHeightFactor changes how much of the view is
1984        // initially showing, and hence the perceived angle from which the view enters.
1985        if (state == State.APPS_CUSTOMIZE) {
1986            final float initialHeightFactor = 0.175f;
1987            view.setPivotY((1 - initialHeightFactor) * height);
1988        } else {
1989            final float initialHeightFactor = 0.2f;
1990            view.setPivotY(-initialHeightFactor * height);
1991        }
1992    }
1993
1994    /**
1995     * Zoom the camera out from the workspace to reveal 'toView'.
1996     * Assumes that the view to show is anchored at either the very top or very bottom
1997     * of the screen.
1998     * @param toState The state to zoom out to. Must be APPS_CUSTOMIZE.
1999     */
2000    private void cameraZoomOut(State toState, boolean animated, final boolean springLoaded) {
2001        final Resources res = getResources();
2002
2003        final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
2004        final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
2005        final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
2006        final View toView = mAppsCustomizeTabHost;
2007
2008        setPivotsForZoom(toView, toState, scale);
2009
2010        // Shrink workspaces away if going to AppsCustomize from workspace
2011        mWorkspace.shrink(Workspace.State.SMALL, animated);
2012        hideHotseat(animated);
2013
2014        if (animated) {
2015            final ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
2016            scaleAnim.setInterpolator(new Workspace.ZoomOutInterpolator());
2017            scaleAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
2018                public void onAnimationUpdate(float a, float b) {
2019                    ((View) toView.getParent()).fastInvalidate();
2020                    toView.setFastScaleX(a * scale + b * 1f);
2021                    toView.setFastScaleY(a * scale + b * 1f);
2022                }
2023            });
2024
2025            toView.setVisibility(View.VISIBLE);
2026            toView.setFastAlpha(0f);
2027            ValueAnimator alphaAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(fadeDuration);
2028            alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
2029            alphaAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
2030                public void onAnimationUpdate(float a, float b) {
2031                    // don't need to invalidate because we do so above
2032                    toView.setFastAlpha(a * 0f + b * 1f);
2033                }
2034            });
2035            alphaAnim.start();
2036
2037            if (toView instanceof LauncherTransitionable) {
2038                ((LauncherTransitionable) toView).onLauncherTransitionStart(scaleAnim, false);
2039            }
2040            scaleAnim.addListener(new AnimatorListenerAdapter() {
2041                @Override
2042                public void onAnimationStart(Animator animation) {
2043                    // Prepare the position
2044                    toView.setTranslationX(0.0f);
2045                    toView.setTranslationY(0.0f);
2046                    toView.setVisibility(View.VISIBLE);
2047                    toView.bringToFront();
2048                }
2049                @Override
2050                public void onAnimationEnd(Animator animation) {
2051                    // If we don't set the final scale values here, if this animation is cancelled
2052                    // it will have the wrong scale value and subsequent cameraPan animations will
2053                    // not fix that
2054                    toView.setScaleX(1.0f);
2055                    toView.setScaleY(1.0f);
2056                    if (toView instanceof LauncherTransitionable) {
2057                        ((LauncherTransitionable) toView).onLauncherTransitionEnd(scaleAnim, false);
2058                    }
2059
2060                    if (!springLoaded && !LauncherApplication.isScreenLarge()) {
2061                        // Hide the workspace scrollbar
2062                        mWorkspace.hideScrollingIndicator(true);
2063                        mWorkspace.hideDockDivider(true);
2064                    }
2065                }
2066            });
2067
2068            // toView should appear right at the end of the workspace shrink animation
2069            final int startDelay = 0;
2070
2071            if (mStateAnimation != null) mStateAnimation.cancel();
2072            mStateAnimation = new AnimatorSet();
2073            mStateAnimation.play(scaleAnim).after(startDelay);
2074            mStateAnimation.start();
2075        } else {
2076            toView.setTranslationX(0.0f);
2077            toView.setTranslationY(0.0f);
2078            toView.setScaleX(1.0f);
2079            toView.setScaleY(1.0f);
2080            toView.setVisibility(View.VISIBLE);
2081            toView.bringToFront();
2082            if (toView instanceof LauncherTransitionable) {
2083                ((LauncherTransitionable) toView).onLauncherTransitionStart(null, false);
2084                ((LauncherTransitionable) toView).onLauncherTransitionEnd(null, false);
2085
2086                if (!springLoaded && !LauncherApplication.isScreenLarge()) {
2087                    // Hide the workspace scrollbar
2088                    mWorkspace.hideScrollingIndicator(true);
2089                    mWorkspace.hideDockDivider(true);
2090                }
2091            }
2092        }
2093    }
2094
2095    /**
2096     * Zoom the camera back into the workspace, hiding 'fromView'.
2097     * This is the opposite of cameraZoomOut.
2098     * @param fromState The current state (must be APPS_CUSTOMIZE).
2099     * @param animated If true, the transition will be animated.
2100     */
2101    private void cameraZoomIn(State fromState, boolean animated, final boolean springLoaded) {
2102        Resources res = getResources();
2103
2104        final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
2105        final float scaleFactor = (float)
2106                res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
2107        final View fromView = mAppsCustomizeTabHost;
2108
2109        setPivotsForZoom(fromView, fromState, scaleFactor);
2110
2111        if (!springLoaded) {
2112            mWorkspace.unshrink(animated);
2113        }
2114        showHotseat(animated);
2115        if (animated) {
2116            if (mStateAnimation != null) mStateAnimation.cancel();
2117            mStateAnimation = new AnimatorSet();
2118
2119            final float oldScaleX = fromView.getScaleX();
2120            final float oldScaleY = fromView.getScaleY();
2121
2122            ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
2123            scaleAnim.setInterpolator(new Workspace.ZoomInInterpolator());
2124            scaleAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
2125                public void onAnimationUpdate(float a, float b) {
2126                    ((View)fromView.getParent()).fastInvalidate();
2127                    fromView.setFastScaleX(a * oldScaleX + b * scaleFactor);
2128                    fromView.setFastScaleY(a * oldScaleY + b * scaleFactor);
2129                }
2130            });
2131            final ValueAnimator alphaAnim = ValueAnimator.ofFloat(0f, 1f);
2132            alphaAnim.setDuration(res.getInteger(R.integer.config_appsCustomizeFadeOutTime));
2133            alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
2134            alphaAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
2135                public void onAnimationUpdate(float a, float b) {
2136                    // don't need to invalidate because we do so above
2137                    fromView.setFastAlpha(a * 1f + b * 0f);
2138                }
2139            });
2140            if (fromView instanceof LauncherTransitionable) {
2141                ((LauncherTransitionable) fromView).onLauncherTransitionStart(alphaAnim, true);
2142            }
2143            alphaAnim.addListener(new AnimatorListenerAdapter() {
2144                @Override
2145                public void onAnimationStart(android.animation.Animator animation) {
2146                    if (!springLoaded) {
2147                        mWorkspace.showDockDivider(false);
2148                    }
2149                }
2150                @Override
2151                public void onAnimationEnd(Animator animation) {
2152                    fromView.setVisibility(View.GONE);
2153                    if (fromView instanceof LauncherTransitionable) {
2154                        ((LauncherTransitionable) fromView).onLauncherTransitionEnd(alphaAnim,true);
2155                    }
2156                    mWorkspace.flashScrollingIndicator();
2157                }
2158            });
2159
2160            mStateAnimation.playTogether(scaleAnim, alphaAnim);
2161            mStateAnimation.start();
2162        } else {
2163            fromView.setVisibility(View.GONE);
2164            if (fromView instanceof LauncherTransitionable) {
2165                ((LauncherTransitionable) fromView).onLauncherTransitionStart(null, true);
2166                ((LauncherTransitionable) fromView).onLauncherTransitionEnd(null, true);
2167
2168                if (!springLoaded && !LauncherApplication.isScreenLarge()) {
2169                    // Flash the workspace scrollbar
2170                    mWorkspace.showDockDivider(true);
2171                    mWorkspace.flashScrollingIndicator();
2172                }
2173            }
2174        }
2175    }
2176
2177    void showWorkspace(boolean animated) {
2178        showWorkspace(animated, null);
2179    }
2180
2181    void showWorkspace(boolean animated, CellLayout layout) {
2182        if (layout != null) {
2183            // always animated, but that's ok since we never specify a layout and
2184            // want no animation
2185            mWorkspace.unshrink(layout);
2186        } else {
2187            mWorkspace.unshrink(animated);
2188        }
2189        if (mState == State.APPS_CUSTOMIZE) {
2190            closeAllApps(animated);
2191        }
2192
2193        // Change the state *after* we've called all the transition code
2194        mState = State.WORKSPACE;
2195
2196        // Resume the auto-advance of widgets
2197        mUserPresent = true;
2198        updateRunning();
2199
2200        // send an accessibility event to announce the context change
2201        getWindow().getDecorView().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
2202    }
2203
2204    void enterSpringLoadedDragMode(CellLayout layout) {
2205        if (mState == State.APPS_CUSTOMIZE) {
2206            mWorkspace.enterSpringLoadedDragMode(layout);
2207            cameraZoomIn(State.APPS_CUSTOMIZE, true, true);
2208            mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
2209        }
2210        // Otherwise, we are not in spring loaded mode, so don't do anything.
2211    }
2212    void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay) {
2213        mWorkspace.postDelayed(new Runnable() {
2214            @Override
2215            public void run() {
2216                exitSpringLoadedDragMode();
2217                if (successfulDrop) {
2218                    showWorkspace(true);
2219                }
2220            }
2221        }, (extendedDelay ?
2222                EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT :
2223                EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT));
2224    }
2225    void exitSpringLoadedDragMode() {
2226        if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
2227            mWorkspace.exitSpringLoadedDragMode(Workspace.State.SMALL);
2228            cameraZoomOut(State.APPS_CUSTOMIZE, true, true);
2229            mState = State.APPS_CUSTOMIZE;
2230        }
2231        // Otherwise, we are not in spring loaded mode, so don't do anything.
2232    }
2233
2234    public boolean isAllAppsCustomizeOpen() {
2235        return mState == State.APPS_CUSTOMIZE;
2236    }
2237
2238    /**
2239     * Shows the hotseat area.
2240     */
2241    void showHotseat(boolean animated) {
2242        if (!LauncherApplication.isScreenLarge()) {
2243            if (animated) {
2244                int duration = mSearchDeleteBar.getTransitionInDuration();
2245                mHotseat.animate().alpha(1f).setDuration(duration);
2246            } else {
2247                mHotseat.setAlpha(1f);
2248            }
2249        }
2250    }
2251
2252    /**
2253     * Hides the hotseat area.
2254     */
2255    void hideHotseat(boolean animated) {
2256        if (!LauncherApplication.isScreenLarge()) {
2257            if (animated) {
2258                int duration = mSearchDeleteBar.getTransitionOutDuration();
2259                mHotseat.animate().alpha(0f).setDuration(duration);
2260            } else {
2261                mHotseat.setAlpha(0f);
2262            }
2263        }
2264    }
2265
2266    void showAllApps(boolean animated) {
2267        if (mState != State.WORKSPACE) return;
2268
2269        cameraZoomOut(State.APPS_CUSTOMIZE, animated, false);
2270        mAppsCustomizeTabHost.requestFocus();
2271
2272        // Hide the search bar and hotseat
2273        mSearchDeleteBar.hideSearchBar(animated);
2274
2275        // Change the state *after* we've called all the transition code
2276        mState = State.APPS_CUSTOMIZE;
2277
2278        // Pause the auto-advance of widgets until we are out of AllApps
2279        mUserPresent = false;
2280        updateRunning();
2281        closeFolder();
2282
2283        // Send an accessibility event to announce the context change
2284        getWindow().getDecorView().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
2285    }
2286
2287    /**
2288     * Things to test when changing this code.
2289     *   - Home from workspace
2290     *          - from center screen
2291     *          - from other screens
2292     *   - Home from all apps
2293     *          - from center screen
2294     *          - from other screens
2295     *   - Back from all apps
2296     *          - from center screen
2297     *          - from other screens
2298     *   - Launch app from workspace and quit
2299     *          - with back
2300     *          - with home
2301     *   - Launch app from all apps and quit
2302     *          - with back
2303     *          - with home
2304     *   - Go to a screen that's not the default, then all
2305     *     apps, and launch and app, and go back
2306     *          - with back
2307     *          -with home
2308     *   - On workspace, long press power and go back
2309     *          - with back
2310     *          - with home
2311     *   - On all apps, long press power and go back
2312     *          - with back
2313     *          - with home
2314     *   - On workspace, power off
2315     *   - On all apps, power off
2316     *   - Launch an app and turn off the screen while in that app
2317     *          - Go back with home key
2318     *          - Go back with back key  TODO: make this not go to workspace
2319     *          - From all apps
2320     *          - From workspace
2321     *   - Enter and exit car mode (becuase it causes an extra configuration changed)
2322     *          - From all apps
2323     *          - From the center workspace
2324     *          - From another workspace
2325     */
2326    void closeAllApps(boolean animated) {
2327        if (mState == State.APPS_CUSTOMIZE || mState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
2328            mWorkspace.setVisibility(View.VISIBLE);
2329            cameraZoomIn(State.APPS_CUSTOMIZE, animated, false);
2330
2331            // Show the search bar and hotseat
2332            mSearchDeleteBar.showSearchBar(animated);
2333
2334            // Set focus to the AppsCustomize button
2335            if (mAllAppsButton != null) {
2336                mAllAppsButton.requestFocus();
2337            }
2338        }
2339    }
2340
2341    void lockAllApps() {
2342        // TODO
2343    }
2344
2345    void unlockAllApps() {
2346        // TODO
2347    }
2348
2349    /**
2350     * Add an item from all apps or customize onto the given workspace screen.
2351     * If layout is null, add to the current screen.
2352     */
2353    void addExternalItemToScreen(ItemInfo itemInfo, final CellLayout layout) {
2354        if (!mWorkspace.addExternalItemToScreen(itemInfo, layout)) {
2355            showOutOfSpaceMessage();
2356        } else {
2357            layout.animateDrop();
2358        }
2359    }
2360
2361    private Drawable getExternalPackageToolbarIcon(ComponentName activityName) {
2362        try {
2363            PackageManager packageManager = getPackageManager();
2364            // Look for the toolbar icon specified in the activity meta-data
2365            Bundle metaData = packageManager.getActivityInfo(
2366                    activityName, PackageManager.GET_META_DATA).metaData;
2367            if (metaData != null) {
2368                int iconResId = metaData.getInt(TOOLBAR_ICON_METADATA_NAME);
2369                if (iconResId != 0) {
2370                    Resources res = packageManager.getResourcesForActivity(activityName);
2371                    return res.getDrawable(iconResId);
2372                }
2373            }
2374        } catch (NameNotFoundException e) {
2375            // This can happen if the activity defines an invalid drawable
2376            Log.w(TAG, "Failed to load toolbar icon; " + activityName.flattenToShortString() +
2377                    " not found", e);
2378        } catch (Resources.NotFoundException nfe) {
2379            // This can happen if the activity defines an invalid drawable
2380            Log.w(TAG, "Failed to load toolbar icon from " + activityName.flattenToShortString(),
2381                    nfe);
2382        }
2383        return null;
2384    }
2385
2386    // if successful in getting icon, return it; otherwise, set button to use default drawable
2387    private Drawable.ConstantState updateTextButtonWithIconFromExternalActivity(
2388            int buttonId, ComponentName activityName, int fallbackDrawableId) {
2389        TextView button = (TextView) findViewById(buttonId);
2390        Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName);
2391
2392        // If we were unable to find the icon via the meta-data, use a generic one
2393        if (toolbarIcon == null) {
2394            button.setCompoundDrawablesWithIntrinsicBounds(fallbackDrawableId, 0, 0, 0);
2395            return null;
2396        } else {
2397            button.setCompoundDrawablesWithIntrinsicBounds(toolbarIcon, null, null, null);
2398            return toolbarIcon.getConstantState();
2399        }
2400    }
2401
2402    // if successful in getting icon, return it; otherwise, set button to use default drawable
2403    private Drawable.ConstantState updateButtonWithIconFromExternalActivity(
2404            int buttonId, ComponentName activityName, int fallbackDrawableId) {
2405        ImageView button = (ImageView) findViewById(buttonId);
2406        Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName);
2407
2408        if (button != null) {
2409            // If we were unable to find the icon via the meta-data, use a
2410            // generic one
2411            if (toolbarIcon == null) {
2412                button.setImageResource(fallbackDrawableId);
2413            } else {
2414                button.setImageDrawable(toolbarIcon);
2415            }
2416        }
2417
2418        return toolbarIcon != null ? toolbarIcon.getConstantState() : null;
2419
2420    }
2421
2422    private void updateTextButtonWithDrawable(int buttonId, Drawable.ConstantState d) {
2423        TextView button = (TextView) findViewById(buttonId);
2424        button.setCompoundDrawables(d.newDrawable(getResources()), null, null, null);
2425    }
2426
2427    private void updateButtonWithDrawable(int buttonId, Drawable.ConstantState d) {
2428        ImageView button = (ImageView) findViewById(buttonId);
2429        button.setImageDrawable(d.newDrawable(getResources()));
2430    }
2431
2432    private void updateGlobalSearchIcon() {
2433        final ImageView searchButton = (ImageView) findViewById(R.id.search_button);
2434        final View searchDivider = findViewById(R.id.search_divider);
2435
2436        final SearchManager searchManager =
2437                (SearchManager) getSystemService(Context.SEARCH_SERVICE);
2438        ComponentName activityName = searchManager.getGlobalSearchActivity();
2439        if (activityName != null) {
2440            sGlobalSearchIcon = updateButtonWithIconFromExternalActivity(
2441                    R.id.search_button, activityName, R.drawable.ic_search_normal_holo);
2442            searchButton.setVisibility(View.VISIBLE);
2443            if (searchDivider != null) searchDivider.setVisibility(View.VISIBLE);
2444        } else {
2445            searchButton.setVisibility(View.GONE);
2446            if (searchDivider != null) searchDivider.setVisibility(View.GONE);
2447        }
2448    }
2449
2450    private void updateGlobalSearchIcon(Drawable.ConstantState d) {
2451        updateButtonWithDrawable(R.id.search_button, d);
2452    }
2453
2454    private void updateVoiceSearchIcon() {
2455        final View searchDivider = findViewById(R.id.search_divider);
2456        final View voiceButton = findViewById(R.id.voice_button);
2457
2458        Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
2459        ComponentName activityName = intent.resolveActivity(getPackageManager());
2460        if (activityName != null) {
2461            sVoiceSearchIcon = updateButtonWithIconFromExternalActivity(
2462                    R.id.voice_button, activityName, R.drawable.ic_voice_search_holo);
2463            if (searchDivider != null) searchDivider.setVisibility(View.VISIBLE);
2464            voiceButton.setVisibility(View.VISIBLE);
2465        } else {
2466            if (searchDivider != null) searchDivider.setVisibility(View.GONE);
2467            voiceButton.setVisibility(View.GONE);
2468        }
2469    }
2470
2471    private void updateVoiceSearchIcon(Drawable.ConstantState d) {
2472        updateButtonWithDrawable(R.id.voice_button, d);
2473    }
2474
2475    /**
2476     * Sets the app market icon
2477     */
2478    private void updateAppMarketIcon() {
2479        final View marketButton = findViewById(R.id.market_button);
2480        Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
2481        // Find the app market activity by resolving an intent.
2482        // (If multiple app markets are installed, it will return the ResolverActivity.)
2483        ComponentName activityName = intent.resolveActivity(getPackageManager());
2484        if (activityName != null) {
2485            mAppMarketIntent = intent;
2486            sAppMarketIcon = updateTextButtonWithIconFromExternalActivity(
2487                    R.id.market_button, activityName, R.drawable.ic_launcher_market_holo);
2488            marketButton.setVisibility(View.VISIBLE);
2489
2490            // Remove the shop icon text in the Phone UI
2491            if (!LauncherApplication.isScreenLarge()) {
2492                ((TextView) marketButton).setText("");
2493            }
2494        } else {
2495            // We should hide and disable the view so that we don't try and restore the visibility
2496            // of it when we swap between drag & normal states from IconDropTarget subclasses.
2497            marketButton.setVisibility(View.GONE);
2498            marketButton.setEnabled(false);
2499        }
2500    }
2501
2502    private void updateAppMarketIcon(Drawable.ConstantState d) {
2503        updateTextButtonWithDrawable(R.id.market_button, d);
2504    }
2505
2506    /**
2507     * Displays the shortcut creation dialog and launches, if necessary, the
2508     * appropriate activity.
2509     */
2510    private class CreateShortcut implements DialogInterface.OnClickListener,
2511            DialogInterface.OnCancelListener, DialogInterface.OnDismissListener,
2512            DialogInterface.OnShowListener {
2513
2514        private AddAdapter mAdapter;
2515
2516        Dialog createDialog() {
2517            mAdapter = new AddAdapter(Launcher.this);
2518
2519            final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this,
2520                    AlertDialog.THEME_HOLO_DARK);
2521            builder.setAdapter(mAdapter, this);
2522
2523            AlertDialog dialog = builder.create();
2524            dialog.setOnCancelListener(this);
2525            dialog.setOnDismissListener(this);
2526            dialog.setOnShowListener(this);
2527
2528            return dialog;
2529        }
2530
2531        public void onCancel(DialogInterface dialog) {
2532            mWaitingForResult = false;
2533            cleanup();
2534        }
2535
2536        public void onDismiss(DialogInterface dialog) {
2537            mWaitingForResult = false;
2538            cleanup();
2539        }
2540
2541        private void cleanup() {
2542            try {
2543                dismissDialog(DIALOG_CREATE_SHORTCUT);
2544            } catch (Exception e) {
2545                // An exception is thrown if the dialog is not visible, which is fine
2546            }
2547        }
2548
2549        /**
2550         * Handle the action clicked in the "Add to home" dialog.
2551         */
2552        public void onClick(DialogInterface dialog, int which) {
2553            cleanup();
2554
2555            AddAdapter.ListItem item = (AddAdapter.ListItem) mAdapter.getItem(which);
2556            switch (item.actionTag) {
2557                case AddAdapter.ITEM_APPLICATION: {
2558                    if (mAppsCustomizeTabHost != null) {
2559                        mAppsCustomizeTabHost.selectAppsTab();
2560                    }
2561                    showAllApps(true);
2562                    break;
2563                }
2564                case AddAdapter.ITEM_APPWIDGET: {
2565                    if (mAppsCustomizeTabHost != null) {
2566                        mAppsCustomizeTabHost.selectWidgetsTab();
2567                    }
2568                    showAllApps(true);
2569                    break;
2570                }
2571                case AddAdapter.ITEM_WALLPAPER: {
2572                    startWallpaper();
2573                    break;
2574                }
2575            }
2576        }
2577
2578        public void onShow(DialogInterface dialog) {
2579            mWaitingForResult = true;
2580        }
2581    }
2582
2583    /**
2584     * Receives notifications when applications are added/removed.
2585     */
2586    private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
2587        @Override
2588        public void onReceive(Context context, Intent intent) {
2589            closeSystemDialogs();
2590            String reason = intent.getStringExtra("reason");
2591            if (!"homekey".equals(reason)) {
2592                boolean animate = true;
2593                if (mPaused || "lock".equals(reason)) {
2594                    animate = false;
2595                }
2596                showWorkspace(animate);
2597            }
2598        }
2599    }
2600
2601    /**
2602     * Receives notifications whenever the appwidgets are reset.
2603     */
2604    private class AppWidgetResetObserver extends ContentObserver {
2605        public AppWidgetResetObserver() {
2606            super(new Handler());
2607        }
2608
2609        @Override
2610        public void onChange(boolean selfChange) {
2611            onAppWidgetReset();
2612        }
2613    }
2614
2615    /**
2616     * If the activity is currently paused, signal that we need to re-run the loader
2617     * in onResume.
2618     *
2619     * This needs to be called from incoming places where resources might have been loaded
2620     * while we are paused.  That is becaues the Configuration might be wrong
2621     * when we're not running, and if it comes back to what it was when we
2622     * were paused, we are not restarted.
2623     *
2624     * Implementation of the method from LauncherModel.Callbacks.
2625     *
2626     * @return true if we are currently paused.  The caller might be able to
2627     * skip some work in that case since we will come back again.
2628     */
2629    public boolean setLoadOnResume() {
2630        if (mPaused) {
2631            Log.i(TAG, "setLoadOnResume");
2632            mOnResumeNeedsLoad = true;
2633            return true;
2634        } else {
2635            return false;
2636        }
2637    }
2638
2639    /**
2640     * Implementation of the method from LauncherModel.Callbacks.
2641     */
2642    public int getCurrentWorkspaceScreen() {
2643        if (mWorkspace != null) {
2644            return mWorkspace.getCurrentPage();
2645        } else {
2646            return SCREEN_COUNT / 2;
2647        }
2648    }
2649
2650
2651    /**
2652     * Refreshes the shortcuts shown on the workspace.
2653     *
2654     * Implementation of the method from LauncherModel.Callbacks.
2655     */
2656    public void startBinding() {
2657        final Workspace workspace = mWorkspace;
2658
2659        mWorkspace.clearDropTargets();
2660        int count = workspace.getChildCount();
2661        for (int i = 0; i < count; i++) {
2662            // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
2663            final CellLayout layoutParent = (CellLayout) workspace.getChildAt(i);
2664            layoutParent.removeAllViewsInLayout();
2665        }
2666        if (mHotseat != null) {
2667            mHotseat.resetLayout();
2668        }
2669
2670        // This wasn't being called before which resulted in a leak of AppWidgetHostViews
2671        unbindWorkspaceAndHotseatItems();
2672    }
2673
2674    /**
2675     * Bind the items start-end from the list.
2676     *
2677     * Implementation of the method from LauncherModel.Callbacks.
2678     */
2679    public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
2680        setLoadOnResume();
2681
2682        final Workspace workspace = mWorkspace;
2683        for (int i=start; i<end; i++) {
2684            final ItemInfo item = shortcuts.get(i);
2685
2686            // Short circuit if we are loading dock items for a configuration which has no dock
2687            if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
2688                    mHotseat == null) {
2689                continue;
2690            }
2691
2692            switch (item.itemType) {
2693                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
2694                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
2695                    View shortcut = createShortcut((ShortcutInfo)item);
2696                    workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
2697                            item.cellY, 1, 1, false);
2698                    break;
2699                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
2700                    FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
2701                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
2702                            (FolderInfo) item, mIconCache);
2703                    workspace.addInScreen(newFolder, item.container, item.screen, item.cellX,
2704                            item.cellY, 1, 1, false);
2705                    break;
2706            }
2707        }
2708        workspace.requestLayout();
2709    }
2710
2711    /**
2712     * Implementation of the method from LauncherModel.Callbacks.
2713     */
2714    public void bindFolders(HashMap<Long, FolderInfo> folders) {
2715        setLoadOnResume();
2716        sFolders.clear();
2717        sFolders.putAll(folders);
2718    }
2719
2720    /**
2721     * Add the views for a widget to the workspace.
2722     *
2723     * Implementation of the method from LauncherModel.Callbacks.
2724     */
2725    public void bindAppWidget(LauncherAppWidgetInfo item) {
2726        setLoadOnResume();
2727
2728        final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
2729        if (DEBUG_WIDGETS) {
2730            Log.d(TAG, "bindAppWidget: " + item);
2731        }
2732        final Workspace workspace = mWorkspace;
2733
2734        final int appWidgetId = item.appWidgetId;
2735        final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
2736        if (DEBUG_WIDGETS) {
2737            Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
2738        }
2739
2740        item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
2741
2742        item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
2743        item.hostView.setTag(item);
2744
2745        workspace.addInScreen(item.hostView, item.container, item.screen, item.cellX,
2746                item.cellY, item.spanX, item.spanY, false);
2747
2748        addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
2749
2750        workspace.requestLayout();
2751
2752        if (DEBUG_WIDGETS) {
2753            Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
2754                    + (SystemClock.uptimeMillis()-start) + "ms");
2755        }
2756    }
2757
2758    /**
2759     * Callback saying that there aren't any more items to bind.
2760     *
2761     * Implementation of the method from LauncherModel.Callbacks.
2762     */
2763    public void finishBindingItems() {
2764        setLoadOnResume();
2765
2766        if (mSavedState != null) {
2767            if (!mWorkspace.hasFocus()) {
2768                mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
2769            }
2770            mSavedState = null;
2771        }
2772
2773        if (mSavedInstanceState != null) {
2774            super.onRestoreInstanceState(mSavedInstanceState);
2775            mSavedInstanceState = null;
2776        }
2777
2778        mWorkspaceLoading = false;
2779
2780        // If we received the result of any pending adds while the loader was running (e.g. the
2781        // widget configuration forced an orientation change), process them now.
2782        for (int i = 0; i < sPendingAddList.size(); i++) {
2783            completeAdd(sPendingAddList.get(i));
2784        }
2785        sPendingAddList.clear();
2786    }
2787
2788    /**
2789     * Updates the icons on the launcher that are affected by changes to the package list
2790     * on the device.
2791     */
2792    private void updateIconsAffectedByPackageManagerChanges() {
2793        updateAppMarketIcon();
2794        updateVoiceSearchIcon();
2795    }
2796
2797    @Override
2798    public void bindSearchablesChanged() {
2799        updateGlobalSearchIcon();
2800    }
2801
2802    /**
2803     * Add the icons for all apps.
2804     *
2805     * Implementation of the method from LauncherModel.Callbacks.
2806     */
2807    public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
2808        if (mAppsCustomizeContent != null) {
2809            mAppsCustomizeContent.setApps(apps);
2810        }
2811        updateIconsAffectedByPackageManagerChanges();
2812        updateGlobalSearchIcon();
2813    }
2814
2815    /**
2816     * A package was installed.
2817     *
2818     * Implementation of the method from LauncherModel.Callbacks.
2819     */
2820    public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
2821        setLoadOnResume();
2822        removeDialog(DIALOG_CREATE_SHORTCUT);
2823
2824        if (mAppsCustomizeContent != null) {
2825            mAppsCustomizeContent.addApps(apps);
2826        }
2827        updateIconsAffectedByPackageManagerChanges();
2828    }
2829
2830    /**
2831     * A package was updated.
2832     *
2833     * Implementation of the method from LauncherModel.Callbacks.
2834     */
2835    public void bindAppsUpdated(ArrayList<ApplicationInfo> apps) {
2836        setLoadOnResume();
2837        removeDialog(DIALOG_CREATE_SHORTCUT);
2838        if (mWorkspace != null) {
2839            mWorkspace.updateShortcuts(apps);
2840        }
2841
2842        if (mAppsCustomizeContent != null) {
2843            mAppsCustomizeContent.updateApps(apps);
2844        }
2845        updateIconsAffectedByPackageManagerChanges();
2846    }
2847
2848    /**
2849     * A package was uninstalled.
2850     *
2851     * Implementation of the method from LauncherModel.Callbacks.
2852     */
2853    public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent) {
2854        removeDialog(DIALOG_CREATE_SHORTCUT);
2855        if (permanent) {
2856            mWorkspace.removeItems(apps);
2857        }
2858
2859        if (mAppsCustomizeContent != null) {
2860            mAppsCustomizeContent.removeApps(apps);
2861        }
2862        updateIconsAffectedByPackageManagerChanges();
2863    }
2864
2865    /**
2866     * A number of packages were updated.
2867     */
2868    public void bindPackagesUpdated() {
2869
2870        if (mAppsCustomizeContent != null) {
2871            mAppsCustomizeContent.onPackagesUpdated();
2872        }
2873    }
2874
2875    private int mapConfigurationOriActivityInfoOri(int configOri) {
2876        final Display d = getWindowManager().getDefaultDisplay();
2877        int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
2878        switch (d.getRotation()) {
2879        case Surface.ROTATION_0:
2880        case Surface.ROTATION_180:
2881            // We are currently in the same basic orientation as the natural orientation
2882            naturalOri = configOri;
2883            break;
2884        case Surface.ROTATION_90:
2885        case Surface.ROTATION_270:
2886            // We are currently in the other basic orientation to the natural orientation
2887            naturalOri = (configOri == Configuration.ORIENTATION_LANDSCAPE) ?
2888                    Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
2889            break;
2890        }
2891
2892        int[] oriMap = {
2893                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
2894                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
2895                ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
2896                ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
2897        };
2898        // Since the map starts at portrait, we need to offset if this device's natural orientation
2899        // is landscape.
2900        int indexOffset = 0;
2901        if (naturalOri == Configuration.ORIENTATION_LANDSCAPE) {
2902            indexOffset = 1;
2903        }
2904        return oriMap[(d.getRotation() + indexOffset) % 4];
2905    }
2906    public void lockScreenOrientation() {
2907        setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
2908                .getConfiguration().orientation));
2909    }
2910    public void unlockScreenOrientation() {
2911        mHandler.postDelayed(new Runnable() {
2912            public void run() {
2913                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
2914            }
2915        }, mRestoreScreenOrientationDelay);
2916    }
2917
2918    /**
2919     * Prints out out state for debugging.
2920     */
2921    public void dumpState() {
2922        Log.d(TAG, "BEGIN launcher2 dump state for launcher " + this);
2923        Log.d(TAG, "mSavedState=" + mSavedState);
2924        Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
2925        Log.d(TAG, "mRestoring=" + mRestoring);
2926        Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
2927        Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
2928        Log.d(TAG, "sFolders.size=" + sFolders.size());
2929        mModel.dumpState();
2930
2931        if (mAppsCustomizeContent != null) {
2932            mAppsCustomizeContent.dumpState();
2933        }
2934        Log.d(TAG, "END launcher2 dump state");
2935    }
2936}
2937
2938interface LauncherTransitionable {
2939    void onLauncherTransitionStart(Animator animation, boolean toWorkspace);
2940    void onLauncherTransitionEnd(Animator animation, boolean toWorkspace);
2941}
2942