Launcher.java revision cb222e85d256e37da41f0ffa3744656c9b53083c
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.accounts.Account;
21import android.accounts.AccountManager;
22import android.animation.Animator;
23import android.animation.AnimatorListenerAdapter;
24import android.animation.AnimatorSet;
25import android.animation.ObjectAnimator;
26import android.animation.PropertyValuesHolder;
27import android.animation.ValueAnimator;
28import android.animation.ValueAnimator.AnimatorUpdateListener;
29import android.app.Activity;
30import android.app.ActivityManager;
31import android.app.ActivityOptions;
32import android.app.SearchManager;
33import android.appwidget.AppWidgetHostView;
34import android.appwidget.AppWidgetManager;
35import android.appwidget.AppWidgetProviderInfo;
36import android.content.ActivityNotFoundException;
37import android.content.BroadcastReceiver;
38import android.content.ComponentCallbacks2;
39import android.content.ComponentName;
40import android.content.ContentResolver;
41import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
44import android.content.SharedPreferences;
45import android.content.pm.ActivityInfo;
46import android.content.pm.PackageManager;
47import android.content.pm.PackageManager.NameNotFoundException;
48import android.content.res.Configuration;
49import android.content.res.Resources;
50import android.database.ContentObserver;
51import android.graphics.Bitmap;
52import android.graphics.Canvas;
53import android.graphics.PorterDuff;
54import android.graphics.Rect;
55import android.graphics.drawable.Drawable;
56import android.net.Uri;
57import android.os.AsyncTask;
58import android.os.Bundle;
59import android.os.Environment;
60import android.os.Handler;
61import android.os.Message;
62import android.os.StrictMode;
63import android.os.SystemClock;
64import android.provider.Settings;
65import android.speech.RecognizerIntent;
66import android.text.Selection;
67import android.text.SpannableStringBuilder;
68import android.text.TextUtils;
69import android.text.method.TextKeyListener;
70import android.util.Log;
71import android.view.Display;
72import android.view.HapticFeedbackConstants;
73import android.view.KeyEvent;
74import android.view.LayoutInflater;
75import android.view.Menu;
76import android.view.MenuItem;
77import android.view.MotionEvent;
78import android.view.Surface;
79import android.view.View;
80import android.view.View.OnLongClickListener;
81import android.view.ViewGroup;
82import android.view.ViewTreeObserver;
83import android.view.ViewTreeObserver.OnGlobalLayoutListener;
84import android.view.WindowManager;
85import android.view.accessibility.AccessibilityEvent;
86import android.view.animation.AccelerateDecelerateInterpolator;
87import android.view.animation.AccelerateInterpolator;
88import android.view.animation.DecelerateInterpolator;
89import android.view.inputmethod.InputMethodManager;
90import android.widget.Advanceable;
91import android.widget.ImageView;
92import android.widget.TextView;
93import android.widget.Toast;
94
95import com.android.common.Search;
96import com.android.launcher.R;
97import com.android.launcher2.DropTarget.DragObject;
98
99import java.io.DataInputStream;
100import java.io.DataOutputStream;
101import java.io.FileDescriptor;
102import java.io.FileInputStream;
103import java.io.FileNotFoundException;
104import java.io.IOException;
105import java.io.PrintWriter;
106import java.util.ArrayList;
107import java.util.Collection;
108import java.util.Collections;
109import java.util.Comparator;
110import java.util.HashMap;
111import java.util.HashSet;
112import java.util.List;
113import java.util.Set;
114
115/**
116 * Default launcher application.
117 */
118public final class Launcher extends Activity
119        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
120                   View.OnTouchListener {
121    static final String TAG = "Launcher";
122    static final boolean LOGD = false;
123
124    static final boolean PROFILE_STARTUP = false;
125    static final boolean DEBUG_WIDGETS = false;
126    static final boolean DEBUG_STRICT_MODE = false;
127
128    private static final int MENU_GROUP_WALLPAPER = 1;
129    private static final int MENU_WALLPAPER_SETTINGS = Menu.FIRST + 1;
130    private static final int MENU_MANAGE_APPS = MENU_WALLPAPER_SETTINGS + 1;
131    private static final int MENU_SYSTEM_SETTINGS = MENU_MANAGE_APPS + 1;
132    private static final int MENU_HELP = MENU_SYSTEM_SETTINGS + 1;
133
134    private static final int REQUEST_CREATE_SHORTCUT = 1;
135    private static final int REQUEST_CREATE_APPWIDGET = 5;
136    private static final int REQUEST_PICK_APPLICATION = 6;
137    private static final int REQUEST_PICK_SHORTCUT = 7;
138    private static final int REQUEST_PICK_APPWIDGET = 9;
139    private static final int REQUEST_PICK_WALLPAPER = 10;
140
141    private static final int REQUEST_BIND_APPWIDGET = 11;
142
143    static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
144
145    static final int SCREEN_COUNT = 5;
146    static final int DEFAULT_SCREEN = 2;
147
148    private static final String PREFERENCES = "launcher.preferences";
149    static final String FORCE_ENABLE_ROTATION_PROPERTY = "debug.force_enable_rotation";
150    static final String DUMP_STATE_PROPERTY = "debug.dumpstate";
151
152    // The Intent extra that defines whether to ignore the launch animation
153    static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
154            "com.android.launcher.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
155
156    // Type: int
157    private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
158    // Type: int
159    private static final String RUNTIME_STATE = "launcher.state";
160    // Type: int
161    private static final String RUNTIME_STATE_PENDING_ADD_CONTAINER = "launcher.add_container";
162    // Type: int
163    private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
164    // Type: int
165    private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x";
166    // Type: int
167    private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y";
168    // Type: boolean
169    private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
170    // Type: long
171    private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
172    // Type: int
173    private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x";
174    // Type: int
175    private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y";
176    // Type: parcelable
177    private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
178
179    private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
180    private static final String TOOLBAR_SEARCH_ICON_METADATA_NAME =
181            "com.android.launcher.toolbar_search_icon";
182    private static final String TOOLBAR_VOICE_SEARCH_ICON_METADATA_NAME =
183            "com.android.launcher.toolbar_voice_search_icon";
184
185    /** The different states that Launcher can be in. */
186    private enum State { NONE, WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED };
187    private State mState = State.WORKSPACE;
188    private AnimatorSet mStateAnimation;
189    private AnimatorSet mDividerAnimator;
190
191    static final int APPWIDGET_HOST_ID = 1024;
192    private static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
193    private static final int EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT = 600;
194    private static final int SHOW_CLING_DURATION = 550;
195    private static final int DISMISS_CLING_DURATION = 250;
196
197    private static final Object sLock = new Object();
198    private static int sScreen = DEFAULT_SCREEN;
199
200    // How long to wait before the new-shortcut animation automatically pans the workspace
201    private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 10;
202
203    private final BroadcastReceiver mCloseSystemDialogsReceiver
204            = new CloseSystemDialogsIntentReceiver();
205    private final ContentObserver mWidgetObserver = new AppWidgetResetObserver();
206
207    private LayoutInflater mInflater;
208
209    private Workspace mWorkspace;
210    private View mQsbDivider;
211    private View mDockDivider;
212    private DragLayer mDragLayer;
213    private DragController mDragController;
214
215    private AppWidgetManager mAppWidgetManager;
216    private LauncherAppWidgetHost mAppWidgetHost;
217
218    private ItemInfo mPendingAddInfo = new ItemInfo();
219    private AppWidgetProviderInfo mPendingAddWidgetInfo;
220
221    private int[] mTmpAddItemCellCoordinates = new int[2];
222
223    private FolderInfo mFolderInfo;
224
225    private Hotseat mHotseat;
226    private View mAllAppsButton;
227
228    private SearchDropTargetBar mSearchDropTargetBar;
229    private AppsCustomizeTabHost mAppsCustomizeTabHost;
230    private AppsCustomizePagedView mAppsCustomizeContent;
231    private boolean mAutoAdvanceRunning = false;
232
233    private Bundle mSavedState;
234    // We set the state in both onCreate and then onNewIntent in some cases, which causes both
235    // scroll issues (because the workspace may not have been measured yet) and extra work.
236    // Instead, just save the state that we need to restore Launcher to, and commit it in onResume.
237    private State mOnResumeState = State.NONE;
238
239    private SpannableStringBuilder mDefaultKeySsb = null;
240
241    private boolean mWorkspaceLoading = true;
242
243    private boolean mPaused = true;
244    private boolean mRestoring;
245    private boolean mWaitingForResult;
246    private boolean mOnResumeNeedsLoad;
247
248    // Keep track of whether the user has left launcher
249    private static boolean sPausedFromUserAction = false;
250
251    private Bundle mSavedInstanceState;
252
253    private LauncherModel mModel;
254    private IconCache mIconCache;
255    private boolean mUserPresent = true;
256    private boolean mVisible = false;
257    private boolean mAttached = false;
258
259    private static LocaleConfiguration sLocaleConfiguration = null;
260
261    private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
262
263    private Intent mAppMarketIntent = null;
264
265    // Related to the auto-advancing of widgets
266    private final int ADVANCE_MSG = 1;
267    private final int mAdvanceInterval = 20000;
268    private final int mAdvanceStagger = 250;
269    private long mAutoAdvanceSentTime;
270    private long mAutoAdvanceTimeLeft = -1;
271    private HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance =
272        new HashMap<View, AppWidgetProviderInfo>();
273
274    // Determines how long to wait after a rotation before restoring the screen orientation to
275    // match the sensor state.
276    private final int mRestoreScreenOrientationDelay = 500;
277
278    // External icons saved in case of resource changes, orientation, etc.
279    private static Drawable.ConstantState[] sGlobalSearchIcon = new Drawable.ConstantState[2];
280    private static Drawable.ConstantState[] sVoiceSearchIcon = new Drawable.ConstantState[2];
281    private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2];
282
283    private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
284
285    static final ArrayList<String> sDumpLogs = new ArrayList<String>();
286
287    // We only want to get the SharedPreferences once since it does an FS stat each time we get
288    // it from the context.
289    private SharedPreferences mSharedPrefs;
290
291    // Holds the page that we need to animate to, and the icon views that we need to animate up
292    // when we scroll to that page on resume.
293    private int mNewShortcutAnimatePage = -1;
294    private ArrayList<View> mNewShortcutAnimateViews = new ArrayList<View>();
295    private ImageView mFolderIconImageView;
296    private Bitmap mFolderIconBitmap;
297    private Canvas mFolderIconCanvas;
298    private Rect mRectForFolderAnimation = new Rect();
299
300    private BubbleTextView mWaitingForResume;
301
302    private HideFromAccessibilityHelper mHideFromAccessibilityHelper
303        = new HideFromAccessibilityHelper();
304
305    private Runnable mBuildLayersRunnable = new Runnable() {
306        public void run() {
307            if (mWorkspace != null) {
308                mWorkspace.buildPageHardwareLayers();
309            }
310        }
311    };
312
313    private static ArrayList<PendingAddArguments> sPendingAddList
314            = new ArrayList<PendingAddArguments>();
315
316    private static class PendingAddArguments {
317        int requestCode;
318        Intent intent;
319        long container;
320        int screen;
321        int cellX;
322        int cellY;
323    }
324
325
326    private boolean doesFileExist(String filename) {
327        FileInputStream fis = null;
328        try {
329            fis = openFileInput(filename);
330            fis.close();
331            return true;
332        } catch (java.io.FileNotFoundException e) {
333            return false;
334        } catch (java.io.IOException e) {
335            return true;
336        }
337    }
338
339    @Override
340    protected void onCreate(Bundle savedInstanceState) {
341        if (DEBUG_STRICT_MODE) {
342            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
343                    .detectDiskReads()
344                    .detectDiskWrites()
345                    .detectNetwork()   // or .detectAll() for all detectable problems
346                    .penaltyLog()
347                    .build());
348            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
349                    .detectLeakedSqlLiteObjects()
350                    .detectLeakedClosableObjects()
351                    .penaltyLog()
352                    .penaltyDeath()
353                    .build());
354        }
355
356        super.onCreate(savedInstanceState);
357        LauncherApplication app = ((LauncherApplication)getApplication());
358        mSharedPrefs = getSharedPreferences(LauncherApplication.getSharedPreferencesKey(),
359                Context.MODE_PRIVATE);
360        mModel = app.setLauncher(this);
361        mIconCache = app.getIconCache();
362        mDragController = new DragController(this);
363        mInflater = getLayoutInflater();
364
365        mAppWidgetManager = AppWidgetManager.getInstance(this);
366        mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
367        mAppWidgetHost.startListening();
368
369        // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
370        // this also ensures that any synchronous binding below doesn't re-trigger another
371        // LauncherModel load.
372        mPaused = false;
373
374        if (PROFILE_STARTUP) {
375            android.os.Debug.startMethodTracing(
376                    Environment.getExternalStorageDirectory() + "/launcher");
377        }
378
379        checkForLocaleChange();
380        setContentView(R.layout.launcher);
381        setupViews();
382        showFirstRunWorkspaceCling();
383
384        registerContentObservers();
385
386        lockAllApps();
387
388        mSavedState = savedInstanceState;
389        restoreState(mSavedState);
390
391        // Update customization drawer _after_ restoring the states
392        if (mAppsCustomizeContent != null) {
393            mAppsCustomizeContent.onPackagesUpdated();
394        }
395
396        if (PROFILE_STARTUP) {
397            android.os.Debug.stopMethodTracing();
398        }
399
400        if (!mRestoring) {
401            if (sPausedFromUserAction) {
402                // If the user leaves launcher, then we should just load items asynchronously when
403                // they return.
404                mModel.startLoader(true, -1);
405            } else {
406                // We only load the page synchronously if the user rotates (or triggers a
407                // configuration change) while launcher is in the foreground
408                mModel.startLoader(true, mWorkspace.getCurrentPage());
409            }
410        }
411
412        if (!mModel.isAllAppsLoaded()) {
413            ViewGroup appsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent();
414            mInflater.inflate(R.layout.apps_customize_progressbar, appsCustomizeContentParent);
415        }
416
417        // For handling default keys
418        mDefaultKeySsb = new SpannableStringBuilder();
419        Selection.setSelection(mDefaultKeySsb, 0);
420
421        IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
422        registerReceiver(mCloseSystemDialogsReceiver, filter);
423
424        updateGlobalIcons();
425
426        // On large interfaces, we want the screen to auto-rotate based on the current orientation
427        unlockScreenOrientation(true);
428    }
429
430    protected void onUserLeaveHint() {
431        super.onUserLeaveHint();
432        sPausedFromUserAction = true;
433    }
434
435    private void updateGlobalIcons() {
436        boolean searchVisible = false;
437        boolean voiceVisible = false;
438        // If we have a saved version of these external icons, we load them up immediately
439        int coi = getCurrentOrientationIndexForGlobalIcons();
440        if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null ||
441                sAppMarketIcon[coi] == null) {
442            updateAppMarketIcon();
443            searchVisible = updateGlobalSearchIcon();
444            voiceVisible = updateVoiceSearchIcon(searchVisible);
445        }
446        if (sGlobalSearchIcon[coi] != null) {
447             updateGlobalSearchIcon(sGlobalSearchIcon[coi]);
448             searchVisible = true;
449        }
450        if (sVoiceSearchIcon[coi] != null) {
451            updateVoiceSearchIcon(sVoiceSearchIcon[coi]);
452            voiceVisible = true;
453        }
454        if (sAppMarketIcon[coi] != null) {
455            updateAppMarketIcon(sAppMarketIcon[coi]);
456        }
457        if (mSearchDropTargetBar != null) {
458            mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
459        }
460    }
461
462    private void checkForLocaleChange() {
463        if (sLocaleConfiguration == null) {
464            new AsyncTask<Void, Void, LocaleConfiguration>() {
465                @Override
466                protected LocaleConfiguration doInBackground(Void... unused) {
467                    LocaleConfiguration localeConfiguration = new LocaleConfiguration();
468                    readConfiguration(Launcher.this, localeConfiguration);
469                    return localeConfiguration;
470                }
471
472                @Override
473                protected void onPostExecute(LocaleConfiguration result) {
474                    sLocaleConfiguration = result;
475                    checkForLocaleChange();  // recursive, but now with a locale configuration
476                }
477            }.execute();
478            return;
479        }
480
481        final Configuration configuration = getResources().getConfiguration();
482
483        final String previousLocale = sLocaleConfiguration.locale;
484        final String locale = configuration.locale.toString();
485
486        final int previousMcc = sLocaleConfiguration.mcc;
487        final int mcc = configuration.mcc;
488
489        final int previousMnc = sLocaleConfiguration.mnc;
490        final int mnc = configuration.mnc;
491
492        boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
493
494        if (localeChanged) {
495            sLocaleConfiguration.locale = locale;
496            sLocaleConfiguration.mcc = mcc;
497            sLocaleConfiguration.mnc = mnc;
498
499            mIconCache.flush();
500
501            final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
502            new Thread("WriteLocaleConfiguration") {
503                @Override
504                public void run() {
505                    writeConfiguration(Launcher.this, localeConfiguration);
506                }
507            }.start();
508        }
509    }
510
511    private static class LocaleConfiguration {
512        public String locale;
513        public int mcc = -1;
514        public int mnc = -1;
515    }
516
517    private static void readConfiguration(Context context, LocaleConfiguration configuration) {
518        DataInputStream in = null;
519        try {
520            in = new DataInputStream(context.openFileInput(PREFERENCES));
521            configuration.locale = in.readUTF();
522            configuration.mcc = in.readInt();
523            configuration.mnc = in.readInt();
524        } catch (FileNotFoundException e) {
525            // Ignore
526        } catch (IOException e) {
527            // Ignore
528        } finally {
529            if (in != null) {
530                try {
531                    in.close();
532                } catch (IOException e) {
533                    // Ignore
534                }
535            }
536        }
537    }
538
539    private static void writeConfiguration(Context context, LocaleConfiguration configuration) {
540        DataOutputStream out = null;
541        try {
542            out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE));
543            out.writeUTF(configuration.locale);
544            out.writeInt(configuration.mcc);
545            out.writeInt(configuration.mnc);
546            out.flush();
547        } catch (FileNotFoundException e) {
548            // Ignore
549        } catch (IOException e) {
550            //noinspection ResultOfMethodCallIgnored
551            context.getFileStreamPath(PREFERENCES).delete();
552        } finally {
553            if (out != null) {
554                try {
555                    out.close();
556                } catch (IOException e) {
557                    // Ignore
558                }
559            }
560        }
561    }
562
563    public DragLayer getDragLayer() {
564        return mDragLayer;
565    }
566
567    boolean isDraggingEnabled() {
568        // We prevent dragging when we are loading the workspace as it is possible to pick up a view
569        // that is subsequently removed from the workspace in startBinding().
570        return !mModel.isLoadingWorkspace();
571    }
572
573    static int getScreen() {
574        synchronized (sLock) {
575            return sScreen;
576        }
577    }
578
579    static void setScreen(int screen) {
580        synchronized (sLock) {
581            sScreen = screen;
582        }
583    }
584
585    /**
586     * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
587     * a configuration step, this allows the proper animations to run after other transitions.
588     */
589    private boolean completeAdd(PendingAddArguments args) {
590        boolean result = false;
591        switch (args.requestCode) {
592            case REQUEST_PICK_APPLICATION:
593                completeAddApplication(args.intent, args.container, args.screen, args.cellX,
594                        args.cellY);
595                break;
596            case REQUEST_PICK_SHORTCUT:
597                processShortcut(args.intent);
598                break;
599            case REQUEST_CREATE_SHORTCUT:
600                completeAddShortcut(args.intent, args.container, args.screen, args.cellX,
601                        args.cellY);
602                result = true;
603                break;
604            case REQUEST_CREATE_APPWIDGET:
605                int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
606                completeAddAppWidget(appWidgetId, args.container, args.screen, null, null);
607                result = true;
608                break;
609            case REQUEST_PICK_WALLPAPER:
610                // We just wanted the activity result here so we can clear mWaitingForResult
611                break;
612        }
613        // Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
614        // if you turned the screen off and then back while in All Apps, Launcher would not
615        // return to the workspace. Clearing mAddInfo.container here fixes this issue
616        resetAddInfo();
617        return result;
618    }
619
620    @Override
621    protected void onActivityResult(
622            final int requestCode, final int resultCode, final Intent data) {
623        if (requestCode == REQUEST_BIND_APPWIDGET) {
624            int appWidgetId = data != null ?
625                    data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
626            if (resultCode == RESULT_CANCELED) {
627                completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
628            } else if (resultCode == RESULT_OK) {
629                addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo);
630            }
631            return;
632        }
633        boolean delayExitSpringLoadedMode = false;
634        boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
635                requestCode == REQUEST_CREATE_APPWIDGET);
636        mWaitingForResult = false;
637
638        // We have special handling for widgets
639        if (isWidgetDrop) {
640            int appWidgetId = data != null ?
641                    data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
642            if (appWidgetId < 0) {
643                Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \\" +
644                        "widget configuration activity.");
645                completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
646            } else {
647                completeTwoStageWidgetDrop(resultCode, appWidgetId);
648            }
649            return;
650        }
651
652        // The pattern used here is that a user PICKs a specific application,
653        // which, depending on the target, might need to CREATE the actual target.
654
655        // For example, the user would PICK_SHORTCUT for "Music playlist", and we
656        // launch over to the Music app to actually CREATE_SHORTCUT.
657        if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
658            final PendingAddArguments args = new PendingAddArguments();
659            args.requestCode = requestCode;
660            args.intent = data;
661            args.container = mPendingAddInfo.container;
662            args.screen = mPendingAddInfo.screen;
663            args.cellX = mPendingAddInfo.cellX;
664            args.cellY = mPendingAddInfo.cellY;
665            if (isWorkspaceLocked()) {
666                sPendingAddList.add(args);
667            } else {
668                delayExitSpringLoadedMode = completeAdd(args);
669            }
670        }
671        mDragLayer.clearAnimatedView();
672        // Exit spring loaded mode if necessary after cancelling the configuration of a widget
673        exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode,
674                null);
675    }
676
677    private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
678        CellLayout cellLayout =
679                (CellLayout) mWorkspace.getChildAt(mPendingAddInfo.screen);
680        Runnable onCompleteRunnable = null;
681        int animationType = 0;
682
683        AppWidgetHostView boundWidget = null;
684        if (resultCode == RESULT_OK) {
685            animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
686            final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
687                    mPendingAddWidgetInfo);
688            boundWidget = layout;
689            onCompleteRunnable = new Runnable() {
690                @Override
691                public void run() {
692                    completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
693                            mPendingAddInfo.screen, layout, null);
694                    exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
695                            null);
696                }
697            };
698        } else if (resultCode == RESULT_CANCELED) {
699            animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
700            onCompleteRunnable = new Runnable() {
701                @Override
702                public void run() {
703                    exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
704                            null);
705                }
706            };
707        }
708        if (mDragLayer.getAnimatedView() != null) {
709            mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout,
710                    (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
711                    animationType, boundWidget, true);
712        } else {
713            // The animated view may be null in the case of a rotation during widget configuration
714            onCompleteRunnable.run();
715        }
716    }
717
718    @Override
719    protected void onResume() {
720        super.onResume();
721
722        // Restore the previous launcher state
723        if (mOnResumeState == State.WORKSPACE) {
724            showWorkspace(false);
725        } else if (mOnResumeState == State.APPS_CUSTOMIZE) {
726            showAllApps(false);
727        }
728        mOnResumeState = State.NONE;
729
730        // Process any items that were added while Launcher was away
731        InstallShortcutReceiver.flushInstallQueue(this);
732
733        mPaused = false;
734        sPausedFromUserAction = false;
735        if (mRestoring || mOnResumeNeedsLoad) {
736            mWorkspaceLoading = true;
737            mModel.startLoader(true, -1);
738            mRestoring = false;
739            mOnResumeNeedsLoad = false;
740        }
741
742        // Reset the pressed state of icons that were locked in the press state while activities
743        // were launching
744        if (mWaitingForResume != null) {
745            // Resets the previous workspace icon press state
746            mWaitingForResume.setStayPressed(false);
747        }
748        if (mAppsCustomizeContent != null) {
749            // Resets the previous all apps icon press state
750            mAppsCustomizeContent.resetDrawableState();
751        }
752        // It is possible that widgets can receive updates while launcher is not in the foreground.
753        // Consequently, the widgets will be inflated in the orientation of the foreground activity
754        // (framework issue). On resuming, we ensure that any widgets are inflated for the current
755        // orientation.
756        getWorkspace().reinflateWidgetsIfNecessary();
757
758        // Again, as with the above scenario, it's possible that one or more of the global icons
759        // were updated in the wrong orientation.
760        updateGlobalIcons();
761    }
762
763    @Override
764    protected void onPause() {
765        // NOTE: We want all transitions from launcher to act as if the wallpaper were enabled
766        // to be consistent.  So re-enable the flag here, and we will re-disable it as necessary
767        // when Launcher resumes and we are still in AllApps.
768        updateWallpaperVisibility(true);
769
770        super.onPause();
771        mPaused = true;
772        mDragController.cancelDrag();
773        mDragController.resetLastGestureUpTime();
774    }
775
776    @Override
777    public Object onRetainNonConfigurationInstance() {
778        // Flag the loader to stop early before switching
779        mModel.stopLoader();
780        if (mAppsCustomizeContent != null) {
781            mAppsCustomizeContent.surrender();
782        }
783        return Boolean.TRUE;
784    }
785
786    // We can't hide the IME if it was forced open.  So don't bother
787    /*
788    @Override
789    public void onWindowFocusChanged(boolean hasFocus) {
790        super.onWindowFocusChanged(hasFocus);
791
792        if (hasFocus) {
793            final InputMethodManager inputManager = (InputMethodManager)
794                    getSystemService(Context.INPUT_METHOD_SERVICE);
795            WindowManager.LayoutParams lp = getWindow().getAttributes();
796            inputManager.hideSoftInputFromWindow(lp.token, 0, new android.os.ResultReceiver(new
797                        android.os.Handler()) {
798                        protected void onReceiveResult(int resultCode, Bundle resultData) {
799                            Log.d(TAG, "ResultReceiver got resultCode=" + resultCode);
800                        }
801                    });
802            Log.d(TAG, "called hideSoftInputFromWindow from onWindowFocusChanged");
803        }
804    }
805    */
806
807    private boolean acceptFilter() {
808        final InputMethodManager inputManager = (InputMethodManager)
809                getSystemService(Context.INPUT_METHOD_SERVICE);
810        return !inputManager.isFullscreenMode();
811    }
812
813    @Override
814    public boolean onKeyDown(int keyCode, KeyEvent event) {
815        final int uniChar = event.getUnicodeChar();
816        final boolean handled = super.onKeyDown(keyCode, event);
817        final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar);
818        if (!handled && acceptFilter() && isKeyNotWhitespace) {
819            boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
820                    keyCode, event);
821            if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
822                // something usable has been typed - start a search
823                // the typed text will be retrieved and cleared by
824                // showSearchDialog()
825                // If there are multiple keystrokes before the search dialog takes focus,
826                // onSearchRequested() will be called for every keystroke,
827                // but it is idempotent, so it's fine.
828                return onSearchRequested();
829            }
830        }
831
832        // Eat the long press event so the keyboard doesn't come up.
833        if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
834            return true;
835        }
836
837        return handled;
838    }
839
840    private String getTypedText() {
841        return mDefaultKeySsb.toString();
842    }
843
844    private void clearTypedText() {
845        mDefaultKeySsb.clear();
846        mDefaultKeySsb.clearSpans();
847        Selection.setSelection(mDefaultKeySsb, 0);
848    }
849
850    /**
851     * Given the integer (ordinal) value of a State enum instance, convert it to a variable of type
852     * State
853     */
854    private static State intToState(int stateOrdinal) {
855        State state = State.WORKSPACE;
856        final State[] stateValues = State.values();
857        for (int i = 0; i < stateValues.length; i++) {
858            if (stateValues[i].ordinal() == stateOrdinal) {
859                state = stateValues[i];
860                break;
861            }
862        }
863        return state;
864    }
865
866    /**
867     * Restores the previous state, if it exists.
868     *
869     * @param savedState The previous state.
870     */
871    private void restoreState(Bundle savedState) {
872        if (savedState == null) {
873            return;
874        }
875
876        State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()));
877        if (state == State.APPS_CUSTOMIZE) {
878            mOnResumeState = State.APPS_CUSTOMIZE;
879        }
880
881        int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
882        if (currentScreen > -1) {
883            mWorkspace.setCurrentPage(currentScreen);
884        }
885
886        final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
887        final int pendingAddScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
888
889        if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
890            mPendingAddInfo.container = pendingAddContainer;
891            mPendingAddInfo.screen = pendingAddScreen;
892            mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
893            mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
894            mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
895            mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
896            mPendingAddWidgetInfo = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
897            mWaitingForResult = true;
898            mRestoring = true;
899        }
900
901
902        boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
903        if (renameFolder) {
904            long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
905            mFolderInfo = mModel.getFolderById(this, sFolders, id);
906            mRestoring = true;
907        }
908
909
910        // Restore the AppsCustomize tab
911        if (mAppsCustomizeTabHost != null) {
912            String curTab = savedState.getString("apps_customize_currentTab");
913            if (curTab != null) {
914                mAppsCustomizeTabHost.setContentTypeImmediate(
915                        mAppsCustomizeTabHost.getContentTypeForTabTag(curTab));
916                mAppsCustomizeContent.loadAssociatedPages(
917                        mAppsCustomizeContent.getCurrentPage());
918            }
919
920            int currentIndex = savedState.getInt("apps_customize_currentIndex");
921            mAppsCustomizeContent.restorePageForIndex(currentIndex);
922        }
923    }
924
925    /**
926     * Finds all the views we need and configure them properly.
927     */
928    private void setupViews() {
929        final DragController dragController = mDragController;
930
931        mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
932        mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
933        mQsbDivider = (ImageView) findViewById(R.id.qsb_divider);
934        mDockDivider = (ImageView) findViewById(R.id.dock_divider);
935
936        // Setup the drag layer
937        mDragLayer.setup(this, dragController);
938
939        // Setup the hotseat
940        mHotseat = (Hotseat) findViewById(R.id.hotseat);
941        if (mHotseat != null) {
942            mHotseat.setup(this);
943        }
944
945        // Setup the workspace
946        mWorkspace.setHapticFeedbackEnabled(false);
947        mWorkspace.setOnLongClickListener(this);
948        mWorkspace.setup(dragController);
949        dragController.addDragListener(mWorkspace);
950
951        // Get the search/delete bar
952        mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.qsb_bar);
953
954        // Setup AppsCustomize
955        mAppsCustomizeTabHost = (AppsCustomizeTabHost)
956                findViewById(R.id.apps_customize_pane);
957        mAppsCustomizeContent = (AppsCustomizePagedView)
958                mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
959        mAppsCustomizeContent.setup(this, dragController);
960
961        // Setup the drag controller (drop targets have to be added in reverse order in priority)
962        dragController.setDragScoller(mWorkspace);
963        dragController.setScrollView(mDragLayer);
964        dragController.setMoveTarget(mWorkspace);
965        dragController.addDropTarget(mWorkspace);
966        if (mSearchDropTargetBar != null) {
967            mSearchDropTargetBar.setup(this, dragController);
968        }
969    }
970
971    /**
972     * Creates a view representing a shortcut.
973     *
974     * @param info The data structure describing the shortcut.
975     *
976     * @return A View inflated from R.layout.application.
977     */
978    View createShortcut(ShortcutInfo info) {
979        return createShortcut(R.layout.application,
980                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
981    }
982
983    /**
984     * Creates a view representing a shortcut inflated from the specified resource.
985     *
986     * @param layoutResId The id of the XML layout used to create the shortcut.
987     * @param parent The group the shortcut belongs to.
988     * @param info The data structure describing the shortcut.
989     *
990     * @return A View inflated from layoutResId.
991     */
992    View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
993        BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
994        favorite.applyFromShortcutInfo(info, mIconCache);
995        favorite.setOnClickListener(this);
996        return favorite;
997    }
998
999    /**
1000     * Add an application shortcut to the workspace.
1001     *
1002     * @param data The intent describing the application.
1003     * @param cellInfo The position on screen where to create the shortcut.
1004     */
1005    void completeAddApplication(Intent data, long container, int screen, int cellX, int cellY) {
1006        final int[] cellXY = mTmpAddItemCellCoordinates;
1007        final CellLayout layout = getCellLayout(container, screen);
1008
1009        // First we check if we already know the exact location where we want to add this item.
1010        if (cellX >= 0 && cellY >= 0) {
1011            cellXY[0] = cellX;
1012            cellXY[1] = cellY;
1013        } else if (!layout.findCellForSpan(cellXY, 1, 1)) {
1014            showOutOfSpaceMessage(isHotseatLayout(layout));
1015            return;
1016        }
1017
1018        final ShortcutInfo info = mModel.getShortcutInfo(getPackageManager(), data, this);
1019
1020        if (info != null) {
1021            info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
1022                    Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1023            info.container = ItemInfo.NO_ID;
1024            mWorkspace.addApplicationShortcut(info, layout, container, screen, cellXY[0], cellXY[1],
1025                    isWorkspaceLocked(), cellX, cellY);
1026        } else {
1027            Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
1028        }
1029    }
1030
1031    /**
1032     * Add a shortcut to the workspace.
1033     *
1034     * @param data The intent describing the shortcut.
1035     * @param cellInfo The position on screen where to create the shortcut.
1036     */
1037    private void completeAddShortcut(Intent data, long container, int screen, int cellX,
1038            int cellY) {
1039        int[] cellXY = mTmpAddItemCellCoordinates;
1040        int[] touchXY = mPendingAddInfo.dropPos;
1041        CellLayout layout = getCellLayout(container, screen);
1042
1043        boolean foundCellSpan = false;
1044
1045        ShortcutInfo info = mModel.infoFromShortcutIntent(this, data, null);
1046        if (info == null) {
1047            return;
1048        }
1049        final View view = createShortcut(info);
1050
1051        // First we check if we already know the exact location where we want to add this item.
1052        if (cellX >= 0 && cellY >= 0) {
1053            cellXY[0] = cellX;
1054            cellXY[1] = cellY;
1055            foundCellSpan = true;
1056
1057            // If appropriate, either create a folder or add to an existing folder
1058            if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
1059                    true, null,null)) {
1060                return;
1061            }
1062            DragObject dragObject = new DragObject();
1063            dragObject.dragInfo = info;
1064            if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
1065                    true)) {
1066                return;
1067            }
1068        } else if (touchXY != null) {
1069            // when dragging and dropping, just find the closest free spot
1070            int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY);
1071            foundCellSpan = (result != null);
1072        } else {
1073            foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
1074        }
1075
1076        if (!foundCellSpan) {
1077            showOutOfSpaceMessage(isHotseatLayout(layout));
1078            return;
1079        }
1080
1081        LauncherModel.addItemToDatabase(this, info, container, screen, cellXY[0], cellXY[1], false);
1082
1083        if (!mRestoring) {
1084            mWorkspace.addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1,
1085                    isWorkspaceLocked());
1086        }
1087    }
1088
1089    static int[] getSpanForWidget(Context context, ComponentName component, int minWidth,
1090            int minHeight) {
1091        Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null);
1092        // We want to account for the extra amount of padding that we are adding to the widget
1093        // to ensure that it gets the full amount of space that it has requested
1094        int requiredWidth = minWidth + padding.left + padding.right;
1095        int requiredHeight = minHeight + padding.top + padding.bottom;
1096        return CellLayout.rectToCell(context.getResources(), requiredWidth, requiredHeight, null);
1097    }
1098
1099    static int[] getSpanForWidget(Context context, AppWidgetProviderInfo info) {
1100        return getSpanForWidget(context, info.provider, info.minWidth, info.minHeight);
1101    }
1102
1103    static int[] getMinSpanForWidget(Context context, AppWidgetProviderInfo info) {
1104        return getSpanForWidget(context, info.provider, info.minResizeWidth, info.minResizeHeight);
1105    }
1106
1107    static int[] getSpanForWidget(Context context, PendingAddWidgetInfo info) {
1108        return getSpanForWidget(context, info.componentName, info.minWidth, info.minHeight);
1109    }
1110
1111    static int[] getMinSpanForWidget(Context context, PendingAddWidgetInfo info) {
1112        return getSpanForWidget(context, info.componentName, info.minResizeWidth,
1113                info.minResizeHeight);
1114    }
1115
1116    /**
1117     * Add a widget to the workspace.
1118     *
1119     * @param appWidgetId The app widget id
1120     * @param cellInfo The position on screen where to create the widget.
1121     */
1122    private void completeAddAppWidget(final int appWidgetId, long container, int screen,
1123            AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) {
1124        if (appWidgetInfo == null) {
1125            appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
1126        }
1127
1128        // Calculate the grid spans needed to fit this widget
1129        CellLayout layout = getCellLayout(container, screen);
1130
1131        int[] minSpanXY = getMinSpanForWidget(this, appWidgetInfo);
1132        int[] spanXY = getSpanForWidget(this, appWidgetInfo);
1133
1134        // Try finding open space on Launcher screen
1135        // We have saved the position to which the widget was dragged-- this really only matters
1136        // if we are placing widgets on a "spring-loaded" screen
1137        int[] cellXY = mTmpAddItemCellCoordinates;
1138        int[] touchXY = mPendingAddInfo.dropPos;
1139        int[] finalSpan = new int[2];
1140        boolean foundCellSpan = false;
1141        if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) {
1142            cellXY[0] = mPendingAddInfo.cellX;
1143            cellXY[1] = mPendingAddInfo.cellY;
1144            spanXY[0] = mPendingAddInfo.spanX;
1145            spanXY[1] = mPendingAddInfo.spanY;
1146            foundCellSpan = true;
1147        } else if (touchXY != null) {
1148            // when dragging and dropping, just find the closest free spot
1149            int[] result = layout.findNearestVacantArea(
1150                    touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0],
1151                    spanXY[1], cellXY, finalSpan);
1152            spanXY[0] = finalSpan[0];
1153            spanXY[1] = finalSpan[1];
1154            foundCellSpan = (result != null);
1155        } else {
1156            foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]);
1157        }
1158
1159        if (!foundCellSpan) {
1160            if (appWidgetId != -1) {
1161                // Deleting an app widget ID is a void call but writes to disk before returning
1162                // to the caller...
1163                new Thread("deleteAppWidgetId") {
1164                    public void run() {
1165                        mAppWidgetHost.deleteAppWidgetId(appWidgetId);
1166                    }
1167                }.start();
1168            }
1169            showOutOfSpaceMessage(isHotseatLayout(layout));
1170            return;
1171        }
1172
1173        // Build Launcher-specific widget info and save to database
1174        LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId,
1175                appWidgetInfo.provider);
1176        launcherInfo.spanX = spanXY[0];
1177        launcherInfo.spanY = spanXY[1];
1178        launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
1179        launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
1180
1181        LauncherModel.addItemToDatabase(this, launcherInfo,
1182                container, screen, cellXY[0], cellXY[1], false);
1183
1184        if (!mRestoring) {
1185            if (hostView == null) {
1186                // Perform actual inflation because we're live
1187                launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
1188                launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
1189            } else {
1190                // The AppWidgetHostView has already been inflated and instantiated
1191                launcherInfo.hostView = hostView;
1192            }
1193
1194            launcherInfo.hostView.setTag(launcherInfo);
1195            launcherInfo.hostView.setVisibility(View.VISIBLE);
1196            launcherInfo.notifyWidgetSizeChanged(this);
1197
1198            mWorkspace.addInScreen(launcherInfo.hostView, container, screen, cellXY[0], cellXY[1],
1199                    launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
1200
1201            addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
1202        }
1203        resetAddInfo();
1204    }
1205
1206    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1207        @Override
1208        public void onReceive(Context context, Intent intent) {
1209            final String action = intent.getAction();
1210            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
1211                mUserPresent = false;
1212                mDragLayer.clearAllResizeFrames();
1213                updateRunning();
1214
1215                // Reset AllApps to its initial state only if we are not in the middle of
1216                // processing a multi-step drop
1217                if (mAppsCustomizeTabHost != null && mPendingAddInfo.container == ItemInfo.NO_ID) {
1218                    mAppsCustomizeTabHost.reset();
1219                    showWorkspace(false);
1220                }
1221            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
1222                mUserPresent = true;
1223                updateRunning();
1224            }
1225        }
1226    };
1227
1228    @Override
1229    public void onAttachedToWindow() {
1230        super.onAttachedToWindow();
1231
1232        // Listen for broadcasts related to user-presence
1233        final IntentFilter filter = new IntentFilter();
1234        filter.addAction(Intent.ACTION_SCREEN_OFF);
1235        filter.addAction(Intent.ACTION_USER_PRESENT);
1236        registerReceiver(mReceiver, filter);
1237
1238        mAttached = true;
1239        mVisible = true;
1240    }
1241
1242    @Override
1243    public void onDetachedFromWindow() {
1244        super.onDetachedFromWindow();
1245        mVisible = false;
1246
1247        if (mAttached) {
1248            unregisterReceiver(mReceiver);
1249            mAttached = false;
1250        }
1251        updateRunning();
1252    }
1253
1254    public void onWindowVisibilityChanged(int visibility) {
1255        mVisible = visibility == View.VISIBLE;
1256        updateRunning();
1257        // The following code used to be in onResume, but it turns out onResume is called when
1258        // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
1259        // is a more appropriate event to handle
1260        if (mVisible) {
1261            mAppsCustomizeTabHost.onWindowVisible();
1262            if (!mWorkspaceLoading) {
1263                final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
1264                // We want to let Launcher draw itself at least once before we force it to build
1265                // layers on all the workspace pages, so that transitioning to Launcher from other
1266                // apps is nice and speedy. Usually the first call to preDraw doesn't correspond to
1267                // a true draw so we wait until the second preDraw call to be safe
1268                observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
1269                    public boolean onPreDraw() {
1270                        // We delay the layer building a bit in order to give
1271                        // other message processing a time to run.  In particular
1272                        // this avoids a delay in hiding the IME if it was
1273                        // currently shown, because doing that may involve
1274                        // some communication back with the app.
1275                        mWorkspace.postDelayed(mBuildLayersRunnable, 500);
1276
1277                        observer.removeOnPreDrawListener(this);
1278                        return true;
1279                    }
1280                });
1281            }
1282            // When Launcher comes back to foreground, a different Activity might be responsible for
1283            // the app market intent, so refresh the icon
1284            updateAppMarketIcon();
1285            clearTypedText();
1286        }
1287    }
1288
1289    private void sendAdvanceMessage(long delay) {
1290        mHandler.removeMessages(ADVANCE_MSG);
1291        Message msg = mHandler.obtainMessage(ADVANCE_MSG);
1292        mHandler.sendMessageDelayed(msg, delay);
1293        mAutoAdvanceSentTime = System.currentTimeMillis();
1294    }
1295
1296    private void updateRunning() {
1297        boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty();
1298        if (autoAdvanceRunning != mAutoAdvanceRunning) {
1299            mAutoAdvanceRunning = autoAdvanceRunning;
1300            if (autoAdvanceRunning) {
1301                long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft;
1302                sendAdvanceMessage(delay);
1303            } else {
1304                if (!mWidgetsToAdvance.isEmpty()) {
1305                    mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval -
1306                            (System.currentTimeMillis() - mAutoAdvanceSentTime));
1307                }
1308                mHandler.removeMessages(ADVANCE_MSG);
1309                mHandler.removeMessages(0); // Remove messages sent using postDelayed()
1310            }
1311        }
1312    }
1313
1314    private final Handler mHandler = new Handler() {
1315        @Override
1316        public void handleMessage(Message msg) {
1317            if (msg.what == ADVANCE_MSG) {
1318                int i = 0;
1319                for (View key: mWidgetsToAdvance.keySet()) {
1320                    final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId);
1321                    final int delay = mAdvanceStagger * i;
1322                    if (v instanceof Advanceable) {
1323                       postDelayed(new Runnable() {
1324                           public void run() {
1325                               ((Advanceable) v).advance();
1326                           }
1327                       }, delay);
1328                    }
1329                    i++;
1330                }
1331                sendAdvanceMessage(mAdvanceInterval);
1332            }
1333        }
1334    };
1335
1336    void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) {
1337        if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return;
1338        View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId);
1339        if (v instanceof Advanceable) {
1340            mWidgetsToAdvance.put(hostView, appWidgetInfo);
1341            ((Advanceable) v).fyiWillBeAdvancedByHostKThx();
1342            updateRunning();
1343        }
1344    }
1345
1346    void removeWidgetToAutoAdvance(View hostView) {
1347        if (mWidgetsToAdvance.containsKey(hostView)) {
1348            mWidgetsToAdvance.remove(hostView);
1349            updateRunning();
1350        }
1351    }
1352
1353    public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
1354        removeWidgetToAutoAdvance(launcherInfo.hostView);
1355        launcherInfo.hostView = null;
1356    }
1357
1358    void showOutOfSpaceMessage(boolean isHotseatLayout) {
1359        int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
1360        Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
1361    }
1362
1363    public LauncherAppWidgetHost getAppWidgetHost() {
1364        return mAppWidgetHost;
1365    }
1366
1367    public LauncherModel getModel() {
1368        return mModel;
1369    }
1370
1371    void closeSystemDialogs() {
1372        getWindow().closeAllPanels();
1373
1374        // Whatever we were doing is hereby canceled.
1375        mWaitingForResult = false;
1376    }
1377
1378    @Override
1379    protected void onNewIntent(Intent intent) {
1380        super.onNewIntent(intent);
1381
1382        // Close the menu
1383        if (Intent.ACTION_MAIN.equals(intent.getAction())) {
1384            // also will cancel mWaitingForResult.
1385            closeSystemDialogs();
1386
1387            final boolean alreadyOnHome =
1388                    ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
1389                        != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
1390
1391            Runnable processIntent = new Runnable() {
1392                public void run() {
1393                    Folder openFolder = mWorkspace.getOpenFolder();
1394                    // In all these cases, only animate if we're already on home
1395                    mWorkspace.exitWidgetResizeMode();
1396                    if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
1397                            openFolder == null) {
1398                        mWorkspace.moveToDefaultScreen(true);
1399                    }
1400
1401                    closeFolder();
1402                    exitSpringLoadedDragMode();
1403
1404                    // If we are already on home, then just animate back to the workspace,
1405                    // otherwise, just wait until onResume to set the state back to Workspace
1406                    if (alreadyOnHome) {
1407                        showWorkspace(true);
1408                    } else {
1409                        mOnResumeState = State.WORKSPACE;
1410                    }
1411
1412                    final View v = getWindow().peekDecorView();
1413                    if (v != null && v.getWindowToken() != null) {
1414                        InputMethodManager imm = (InputMethodManager)getSystemService(
1415                                INPUT_METHOD_SERVICE);
1416                        imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
1417                    }
1418
1419                    // Reset AllApps to its initial state
1420                    if (!alreadyOnHome && mAppsCustomizeTabHost != null) {
1421                        mAppsCustomizeTabHost.reset();
1422                    }
1423                }
1424            };
1425
1426            if (alreadyOnHome && !mWorkspace.hasWindowFocus()) {
1427                // Delay processing of the intent to allow the status bar animation to finish
1428                // first in order to avoid janky animations.
1429                mWorkspace.postDelayed(processIntent, 350);
1430            } else {
1431                // Process the intent immediately.
1432                processIntent.run();
1433            }
1434
1435        }
1436    }
1437
1438    @Override
1439    public void onRestoreInstanceState(Bundle state) {
1440        super.onRestoreInstanceState(state);
1441        for (int page: mSynchronouslyBoundPages) {
1442            mWorkspace.restoreInstanceStateForChild(page);
1443        }
1444    }
1445
1446    @Override
1447    protected void onSaveInstanceState(Bundle outState) {
1448        outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage());
1449        super.onSaveInstanceState(outState);
1450
1451        outState.putInt(RUNTIME_STATE, mState.ordinal());
1452        // We close any open folder since it will not be re-opened, and we need to make sure
1453        // this state is reflected.
1454        closeFolder();
1455
1456        if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screen > -1 &&
1457                mWaitingForResult) {
1458            outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container);
1459            outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screen);
1460            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX);
1461            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
1462            outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
1463            outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY);
1464            outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
1465        }
1466
1467        if (mFolderInfo != null && mWaitingForResult) {
1468            outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
1469            outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
1470        }
1471
1472        // Save the current AppsCustomize tab
1473        if (mAppsCustomizeTabHost != null) {
1474            String currentTabTag = mAppsCustomizeTabHost.getCurrentTabTag();
1475            if (currentTabTag != null) {
1476                outState.putString("apps_customize_currentTab", currentTabTag);
1477            }
1478            int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex();
1479            outState.putInt("apps_customize_currentIndex", currentIndex);
1480        }
1481    }
1482
1483    @Override
1484    public void onDestroy() {
1485        super.onDestroy();
1486
1487        // Remove all pending runnables
1488        mHandler.removeMessages(ADVANCE_MSG);
1489        mHandler.removeMessages(0);
1490        mWorkspace.removeCallbacks(mBuildLayersRunnable);
1491
1492        // Stop callbacks from LauncherModel
1493        LauncherApplication app = ((LauncherApplication) getApplication());
1494        mModel.stopLoader();
1495        app.setLauncher(null);
1496
1497        try {
1498            mAppWidgetHost.stopListening();
1499        } catch (NullPointerException ex) {
1500            Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
1501        }
1502        mAppWidgetHost = null;
1503
1504        mWidgetsToAdvance.clear();
1505
1506        TextKeyListener.getInstance().release();
1507
1508        // Disconnect any of the callbacks and drawables associated with ItemInfos on the workspace
1509        // to prevent leaking Launcher activities on orientation change.
1510        if (mModel != null) {
1511            mModel.unbindItemInfosAndClearQueuedBindRunnables();
1512        }
1513
1514        getContentResolver().unregisterContentObserver(mWidgetObserver);
1515        unregisterReceiver(mCloseSystemDialogsReceiver);
1516
1517        mDragLayer.clearAllResizeFrames();
1518        ((ViewGroup) mWorkspace.getParent()).removeAllViews();
1519        mWorkspace.removeAllViews();
1520        mWorkspace = null;
1521        mDragController = null;
1522
1523        LauncherAnimUtils.onDestroyActivity();
1524    }
1525
1526    public DragController getDragController() {
1527        return mDragController;
1528    }
1529
1530    @Override
1531    public void startActivityForResult(Intent intent, int requestCode) {
1532        if (requestCode >= 0) mWaitingForResult = true;
1533        super.startActivityForResult(intent, requestCode);
1534    }
1535
1536    /**
1537     * Indicates that we want global search for this activity by setting the globalSearch
1538     * argument for {@link #startSearch} to true.
1539     */
1540    @Override
1541    public void startSearch(String initialQuery, boolean selectInitialQuery,
1542            Bundle appSearchData, boolean globalSearch) {
1543
1544        showWorkspace(true);
1545
1546        if (initialQuery == null) {
1547            // Use any text typed in the launcher as the initial query
1548            initialQuery = getTypedText();
1549        }
1550        if (appSearchData == null) {
1551            appSearchData = new Bundle();
1552            appSearchData.putString(Search.SOURCE, "launcher-search");
1553        }
1554        Rect sourceBounds = new Rect();
1555        if (mSearchDropTargetBar != null) {
1556            sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
1557        }
1558
1559        startGlobalSearch(initialQuery, selectInitialQuery,
1560            appSearchData, sourceBounds);
1561    }
1562
1563    /**
1564     * Starts the global search activity. This code is a copied from SearchManager
1565     */
1566    public void startGlobalSearch(String initialQuery,
1567            boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
1568        final SearchManager searchManager =
1569            (SearchManager) getSystemService(Context.SEARCH_SERVICE);
1570        ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
1571        if (globalSearchActivity == null) {
1572            Log.w(TAG, "No global search activity found.");
1573            return;
1574        }
1575        Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
1576        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1577        intent.setComponent(globalSearchActivity);
1578        // Make sure that we have a Bundle to put source in
1579        if (appSearchData == null) {
1580            appSearchData = new Bundle();
1581        } else {
1582            appSearchData = new Bundle(appSearchData);
1583        }
1584        // Set source to package name of app that starts global search, if not set already.
1585        if (!appSearchData.containsKey("source")) {
1586            appSearchData.putString("source", getPackageName());
1587        }
1588        intent.putExtra(SearchManager.APP_DATA, appSearchData);
1589        if (!TextUtils.isEmpty(initialQuery)) {
1590            intent.putExtra(SearchManager.QUERY, initialQuery);
1591        }
1592        if (selectInitialQuery) {
1593            intent.putExtra(SearchManager.EXTRA_SELECT_QUERY, selectInitialQuery);
1594        }
1595        intent.setSourceBounds(sourceBounds);
1596        try {
1597            startActivity(intent);
1598        } catch (ActivityNotFoundException ex) {
1599            Log.e(TAG, "Global search activity not found: " + globalSearchActivity);
1600        }
1601    }
1602
1603    @Override
1604    public boolean onCreateOptionsMenu(Menu menu) {
1605        if (isWorkspaceLocked()) {
1606            return false;
1607        }
1608
1609        super.onCreateOptionsMenu(menu);
1610
1611        Intent manageApps = new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS);
1612        manageApps.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1613                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
1614        Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
1615        settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1616                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1617        String helpUrl = getString(R.string.help_url);
1618        Intent help = new Intent(Intent.ACTION_VIEW, Uri.parse(helpUrl));
1619        help.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1620                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
1621
1622        menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
1623            .setIcon(android.R.drawable.ic_menu_gallery)
1624            .setAlphabeticShortcut('W');
1625        menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)
1626            .setIcon(android.R.drawable.ic_menu_manage)
1627            .setIntent(manageApps)
1628            .setAlphabeticShortcut('M');
1629        menu.add(0, MENU_SYSTEM_SETTINGS, 0, R.string.menu_settings)
1630            .setIcon(android.R.drawable.ic_menu_preferences)
1631            .setIntent(settings)
1632            .setAlphabeticShortcut('P');
1633        if (!helpUrl.isEmpty()) {
1634            menu.add(0, MENU_HELP, 0, R.string.menu_help)
1635                .setIcon(android.R.drawable.ic_menu_help)
1636                .setIntent(help)
1637                .setAlphabeticShortcut('H');
1638        }
1639        return true;
1640    }
1641
1642    @Override
1643    public boolean onPrepareOptionsMenu(Menu menu) {
1644        super.onPrepareOptionsMenu(menu);
1645
1646        if (mAppsCustomizeTabHost.isTransitioning()) {
1647            return false;
1648        }
1649        boolean allAppsVisible = (mAppsCustomizeTabHost.getVisibility() == View.VISIBLE);
1650        menu.setGroupVisible(MENU_GROUP_WALLPAPER, !allAppsVisible);
1651
1652        return true;
1653    }
1654
1655    @Override
1656    public boolean onOptionsItemSelected(MenuItem item) {
1657        switch (item.getItemId()) {
1658        case MENU_WALLPAPER_SETTINGS:
1659            startWallpaper();
1660            return true;
1661        }
1662
1663        return super.onOptionsItemSelected(item);
1664    }
1665
1666    @Override
1667    public boolean onSearchRequested() {
1668        startSearch(null, false, null, true);
1669        // Use a custom animation for launching search
1670        return true;
1671    }
1672
1673    public boolean isWorkspaceLocked() {
1674        return mWorkspaceLoading || mWaitingForResult;
1675    }
1676
1677    private void resetAddInfo() {
1678        mPendingAddInfo.container = ItemInfo.NO_ID;
1679        mPendingAddInfo.screen = -1;
1680        mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
1681        mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
1682        mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
1683        mPendingAddInfo.dropPos = null;
1684    }
1685
1686    void addAppWidgetImpl(final int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
1687            AppWidgetProviderInfo appWidgetInfo) {
1688        if (appWidgetInfo.configure != null) {
1689            mPendingAddWidgetInfo = appWidgetInfo;
1690
1691            // Launch over to configure widget, if needed
1692            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
1693            intent.setComponent(appWidgetInfo.configure);
1694            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1695            startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
1696        } else {
1697            // Otherwise just add it
1698            completeAddAppWidget(appWidgetId, info.container, info.screen, boundWidget,
1699                    appWidgetInfo);
1700            // Exit spring loaded mode if necessary after adding the widget
1701            exitSpringLoadedDragModeDelayed(true, false, null);
1702        }
1703    }
1704
1705    /**
1706     * Process a shortcut drop.
1707     *
1708     * @param componentName The name of the component
1709     * @param screen The screen where it should be added
1710     * @param cell The cell it should be added to, optional
1711     * @param position The location on the screen where it was dropped, optional
1712     */
1713    void processShortcutFromDrop(ComponentName componentName, long container, int screen,
1714            int[] cell, int[] loc) {
1715        resetAddInfo();
1716        mPendingAddInfo.container = container;
1717        mPendingAddInfo.screen = screen;
1718        mPendingAddInfo.dropPos = loc;
1719
1720        if (cell != null) {
1721            mPendingAddInfo.cellX = cell[0];
1722            mPendingAddInfo.cellY = cell[1];
1723        }
1724
1725        Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
1726        createShortcutIntent.setComponent(componentName);
1727        processShortcut(createShortcutIntent);
1728    }
1729
1730    /**
1731     * Process a widget drop.
1732     *
1733     * @param info The PendingAppWidgetInfo of the widget being added.
1734     * @param screen The screen where it should be added
1735     * @param cell The cell it should be added to, optional
1736     * @param position The location on the screen where it was dropped, optional
1737     */
1738    void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, int screen,
1739            int[] cell, int[] span, int[] loc) {
1740        resetAddInfo();
1741        mPendingAddInfo.container = info.container = container;
1742        mPendingAddInfo.screen = info.screen = screen;
1743        mPendingAddInfo.dropPos = loc;
1744        mPendingAddInfo.minSpanX = info.minSpanX;
1745        mPendingAddInfo.minSpanY = info.minSpanY;
1746
1747        if (cell != null) {
1748            mPendingAddInfo.cellX = cell[0];
1749            mPendingAddInfo.cellY = cell[1];
1750        }
1751        if (span != null) {
1752            mPendingAddInfo.spanX = span[0];
1753            mPendingAddInfo.spanY = span[1];
1754        }
1755
1756        AppWidgetHostView hostView = info.boundWidget;
1757        int appWidgetId;
1758        if (hostView != null) {
1759            appWidgetId = hostView.getAppWidgetId();
1760            addAppWidgetImpl(appWidgetId, info, hostView, info.info);
1761        } else {
1762            // In this case, we either need to start an activity to get permission to bind
1763            // the widget, or we need to start an activity to configure the widget, or both.
1764            appWidgetId = getAppWidgetHost().allocateAppWidgetId();
1765            Bundle options = info.bindOptions;
1766
1767            boolean success = false;
1768            if (options != null) {
1769                success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
1770                        info.componentName, options);
1771            } else {
1772                success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
1773                        info.componentName);
1774            }
1775            if (success) {
1776                addAppWidgetImpl(appWidgetId, info, null, info.info);
1777            } else {
1778                mPendingAddWidgetInfo = info.info;
1779                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
1780                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1781                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
1782                // TODO: we need to make sure that this accounts for the options bundle.
1783                // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
1784                startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
1785            }
1786        }
1787    }
1788
1789    void processShortcut(Intent intent) {
1790        // Handle case where user selected "Applications"
1791        String applicationName = getResources().getString(R.string.group_applications);
1792        String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1793
1794        if (applicationName != null && applicationName.equals(shortcutName)) {
1795            Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1796            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1797
1798            Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
1799            pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
1800            pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
1801            startActivityForResultSafely(pickIntent, REQUEST_PICK_APPLICATION);
1802        } else {
1803            startActivityForResultSafely(intent, REQUEST_CREATE_SHORTCUT);
1804        }
1805    }
1806
1807    void processWallpaper(Intent intent) {
1808        startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
1809    }
1810
1811    FolderIcon addFolder(CellLayout layout, long container, final int screen, int cellX,
1812            int cellY) {
1813        final FolderInfo folderInfo = new FolderInfo();
1814        folderInfo.title = getText(R.string.folder_name);
1815
1816        // Update the model
1817        LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screen, cellX, cellY,
1818                false);
1819        sFolders.put(folderInfo.id, folderInfo);
1820
1821        // Create the view
1822        FolderIcon newFolder =
1823            FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
1824        mWorkspace.addInScreen(newFolder, container, screen, cellX, cellY, 1, 1,
1825                isWorkspaceLocked());
1826        return newFolder;
1827    }
1828
1829    void removeFolder(FolderInfo folder) {
1830        sFolders.remove(folder.id);
1831    }
1832
1833    private void startWallpaper() {
1834        showWorkspace(true);
1835        final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
1836        Intent chooser = Intent.createChooser(pickWallpaper,
1837                getText(R.string.chooser_wallpaper));
1838        // NOTE: Adds a configure option to the chooser if the wallpaper supports it
1839        //       Removed in Eclair MR1
1840//        WallpaperManager wm = (WallpaperManager)
1841//                getSystemService(Context.WALLPAPER_SERVICE);
1842//        WallpaperInfo wi = wm.getWallpaperInfo();
1843//        if (wi != null && wi.getSettingsActivity() != null) {
1844//            LabeledIntent li = new LabeledIntent(getPackageName(),
1845//                    R.string.configure_wallpaper, 0);
1846//            li.setClassName(wi.getPackageName(), wi.getSettingsActivity());
1847//            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li });
1848//        }
1849        startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
1850    }
1851
1852    /**
1853     * Registers various content observers. The current implementation registers
1854     * only a favorites observer to keep track of the favorites applications.
1855     */
1856    private void registerContentObservers() {
1857        ContentResolver resolver = getContentResolver();
1858        resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
1859                true, mWidgetObserver);
1860    }
1861
1862    @Override
1863    public boolean dispatchKeyEvent(KeyEvent event) {
1864        if (event.getAction() == KeyEvent.ACTION_DOWN) {
1865            switch (event.getKeyCode()) {
1866                case KeyEvent.KEYCODE_HOME:
1867                    return true;
1868                case KeyEvent.KEYCODE_VOLUME_DOWN:
1869                    if (doesFileExist(DUMP_STATE_PROPERTY)) {
1870                        dumpState();
1871                        return true;
1872                    }
1873                    break;
1874            }
1875        } else if (event.getAction() == KeyEvent.ACTION_UP) {
1876            switch (event.getKeyCode()) {
1877                case KeyEvent.KEYCODE_HOME:
1878                    return true;
1879            }
1880        }
1881
1882        return super.dispatchKeyEvent(event);
1883    }
1884
1885    @Override
1886    public void onBackPressed() {
1887        if (isAllAppsVisible()) {
1888            showWorkspace(true);
1889        } else if (mWorkspace.getOpenFolder() != null) {
1890            Folder openFolder = mWorkspace.getOpenFolder();
1891            if (openFolder.isEditingName()) {
1892                openFolder.dismissEditingName();
1893            } else {
1894                closeFolder();
1895            }
1896        } else {
1897            mWorkspace.exitWidgetResizeMode();
1898
1899            // Back button is a no-op here, but give at least some feedback for the button press
1900            mWorkspace.showOutlinesTemporarily();
1901        }
1902    }
1903
1904    /**
1905     * Re-listen when widgets are reset.
1906     */
1907    private void onAppWidgetReset() {
1908        if (mAppWidgetHost != null) {
1909            mAppWidgetHost.startListening();
1910        }
1911    }
1912
1913    /**
1914     * Launches the intent referred by the clicked shortcut.
1915     *
1916     * @param v The view representing the clicked shortcut.
1917     */
1918    public void onClick(View v) {
1919        // Make sure that rogue clicks don't get through while allapps is launching, or after the
1920        // view has detached (it's possible for this to happen if the view is removed mid touch).
1921        if (v.getWindowToken() == null) {
1922            return;
1923        }
1924
1925        if (!mWorkspace.isFinishedSwitchingState()) {
1926            return;
1927        }
1928
1929        Object tag = v.getTag();
1930        if (tag instanceof ShortcutInfo) {
1931            // Open shortcut
1932            final Intent intent = ((ShortcutInfo) tag).intent;
1933            int[] pos = new int[2];
1934            v.getLocationOnScreen(pos);
1935            intent.setSourceBounds(new Rect(pos[0], pos[1],
1936                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
1937
1938            boolean success = startActivitySafely(v, intent, tag);
1939
1940            if (success && v instanceof BubbleTextView) {
1941                mWaitingForResume = (BubbleTextView) v;
1942                mWaitingForResume.setStayPressed(true);
1943            }
1944        } else if (tag instanceof FolderInfo) {
1945            if (v instanceof FolderIcon) {
1946                FolderIcon fi = (FolderIcon) v;
1947                handleFolderClick(fi);
1948            }
1949        } else if (v == mAllAppsButton) {
1950            if (isAllAppsVisible()) {
1951                showWorkspace(true);
1952            } else {
1953                onClickAllAppsButton(v);
1954            }
1955        }
1956    }
1957
1958    public boolean onTouch(View v, MotionEvent event) {
1959        // this is an intercepted event being forwarded from mWorkspace;
1960        // clicking anywhere on the workspace causes the customization drawer to slide down
1961        showWorkspace(true);
1962        return false;
1963    }
1964
1965    /**
1966     * Event handler for the search button
1967     *
1968     * @param v The view that was clicked.
1969     */
1970    public void onClickSearchButton(View v) {
1971        v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
1972
1973        onSearchRequested();
1974    }
1975
1976    /**
1977     * Event handler for the voice button
1978     *
1979     * @param v The view that was clicked.
1980     */
1981    public void onClickVoiceButton(View v) {
1982        v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
1983
1984        try {
1985            final SearchManager searchManager =
1986                    (SearchManager) getSystemService(Context.SEARCH_SERVICE);
1987            ComponentName activityName = searchManager.getGlobalSearchActivity();
1988            Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
1989            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1990            if (activityName != null) {
1991                intent.setPackage(activityName.getPackageName());
1992            }
1993            startActivity(null, intent, "onClickVoiceButton");
1994        } catch (ActivityNotFoundException e) {
1995            Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
1996            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1997            startActivitySafely(null, intent, "onClickVoiceButton");
1998        }
1999    }
2000
2001    /**
2002     * Event handler for the "grid" button that appears on the home screen, which
2003     * enters all apps mode.
2004     *
2005     * @param v The view that was clicked.
2006     */
2007    public void onClickAllAppsButton(View v) {
2008        showAllApps(true);
2009    }
2010
2011    public void onTouchDownAllAppsButton(View v) {
2012        // Provide the same haptic feedback that the system offers for virtual keys.
2013        v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
2014    }
2015
2016    public void onClickAppMarketButton(View v) {
2017        if (mAppMarketIntent != null) {
2018            startActivitySafely(v, mAppMarketIntent, "app market");
2019        } else {
2020            Log.e(TAG, "Invalid app market intent.");
2021        }
2022    }
2023
2024    void startApplicationDetailsActivity(ComponentName componentName) {
2025        String packageName = componentName.getPackageName();
2026        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
2027                Uri.fromParts("package", packageName, null));
2028        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
2029        startActivitySafely(null, intent, "startApplicationDetailsActivity");
2030    }
2031
2032    void startApplicationUninstallActivity(ApplicationInfo appInfo) {
2033        if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) == 0) {
2034            // System applications cannot be installed. For now, show a toast explaining that.
2035            // We may give them the option of disabling apps this way.
2036            int messageId = R.string.uninstall_system_app_text;
2037            Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
2038        } else {
2039            String packageName = appInfo.componentName.getPackageName();
2040            String className = appInfo.componentName.getClassName();
2041            Intent intent = new Intent(
2042                    Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
2043            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
2044                    Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
2045            startActivity(intent);
2046        }
2047    }
2048
2049    boolean startActivity(View v, Intent intent, Object tag) {
2050        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2051
2052        try {
2053            // Only launch using the new animation if the shortcut has not opted out (this is a
2054            // private contract between launcher and may be ignored in the future).
2055            boolean useLaunchAnimation = (v != null) &&
2056                    !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
2057            if (useLaunchAnimation) {
2058                ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
2059                        v.getMeasuredWidth(), v.getMeasuredHeight());
2060
2061                startActivity(intent, opts.toBundle());
2062            } else {
2063                startActivity(intent);
2064            }
2065            return true;
2066        } catch (SecurityException e) {
2067            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
2068            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
2069                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
2070                    "or use the exported attribute for this activity. "
2071                    + "tag="+ tag + " intent=" + intent, e);
2072        }
2073        return false;
2074    }
2075
2076    boolean startActivitySafely(View v, Intent intent, Object tag) {
2077        boolean success = false;
2078        try {
2079            success = startActivity(v, intent, tag);
2080        } catch (ActivityNotFoundException e) {
2081            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
2082            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
2083        }
2084        return success;
2085    }
2086
2087    void startActivityForResultSafely(Intent intent, int requestCode) {
2088        try {
2089            startActivityForResult(intent, requestCode);
2090        } catch (ActivityNotFoundException e) {
2091            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
2092        } catch (SecurityException e) {
2093            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
2094            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
2095                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
2096                    "or use the exported attribute for this activity.", e);
2097        }
2098    }
2099
2100    private void handleFolderClick(FolderIcon folderIcon) {
2101        final FolderInfo info = folderIcon.getFolderInfo();
2102        Folder openFolder = mWorkspace.getFolderForTag(info);
2103
2104        // If the folder info reports that the associated folder is open, then verify that
2105        // it is actually opened. There have been a few instances where this gets out of sync.
2106        if (info.opened && openFolder == null) {
2107            Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
2108                    + info.screen + " (" + info.cellX + ", " + info.cellY + ")");
2109            info.opened = false;
2110        }
2111
2112        if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
2113            // Close any open folder
2114            closeFolder();
2115            // Open the requested folder
2116            openFolder(folderIcon);
2117        } else {
2118            // Find the open folder...
2119            int folderScreen;
2120            if (openFolder != null) {
2121                folderScreen = mWorkspace.getPageForView(openFolder);
2122                // .. and close it
2123                closeFolder(openFolder);
2124                if (folderScreen != mWorkspace.getCurrentPage()) {
2125                    // Close any folder open on the current screen
2126                    closeFolder();
2127                    // Pull the folder onto this screen
2128                    openFolder(folderIcon);
2129                }
2130            }
2131        }
2132    }
2133
2134    /**
2135     * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
2136     * in the DragLayer in the exact absolute location of the original FolderIcon.
2137     */
2138    private void copyFolderIconToImage(FolderIcon fi) {
2139        final int width = fi.getMeasuredWidth();
2140        final int height = fi.getMeasuredHeight();
2141
2142        // Lazy load ImageView, Bitmap and Canvas
2143        if (mFolderIconImageView == null) {
2144            mFolderIconImageView = new ImageView(this);
2145        }
2146        if (mFolderIconBitmap == null || mFolderIconBitmap.getWidth() != width ||
2147                mFolderIconBitmap.getHeight() != height) {
2148            mFolderIconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
2149            mFolderIconCanvas = new Canvas(mFolderIconBitmap);
2150        }
2151
2152        DragLayer.LayoutParams lp;
2153        if (mFolderIconImageView.getLayoutParams() instanceof DragLayer.LayoutParams) {
2154            lp = (DragLayer.LayoutParams) mFolderIconImageView.getLayoutParams();
2155        } else {
2156            lp = new DragLayer.LayoutParams(width, height);
2157        }
2158
2159        // The layout from which the folder is being opened may be scaled, adjust the starting
2160        // view size by this scale factor.
2161        float scale = mDragLayer.getDescendantRectRelativeToSelf(fi, mRectForFolderAnimation);
2162        lp.customPosition = true;
2163        lp.x = mRectForFolderAnimation.left;
2164        lp.y = mRectForFolderAnimation.top;
2165        lp.width = (int) (scale * width);
2166        lp.height = (int) (scale * height);
2167
2168        mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
2169        fi.draw(mFolderIconCanvas);
2170        mFolderIconImageView.setImageBitmap(mFolderIconBitmap);
2171        if (fi.getFolder() != null) {
2172            mFolderIconImageView.setPivotX(fi.getFolder().getPivotXForIconAnimation());
2173            mFolderIconImageView.setPivotY(fi.getFolder().getPivotYForIconAnimation());
2174        }
2175        // Just in case this image view is still in the drag layer from a previous animation,
2176        // we remove it and re-add it.
2177        if (mDragLayer.indexOfChild(mFolderIconImageView) != -1) {
2178            mDragLayer.removeView(mFolderIconImageView);
2179        }
2180        mDragLayer.addView(mFolderIconImageView, lp);
2181        if (fi.getFolder() != null) {
2182            fi.getFolder().bringToFront();
2183        }
2184    }
2185
2186    private void growAndFadeOutFolderIcon(FolderIcon fi) {
2187        if (fi == null) return;
2188        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
2189        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f);
2190        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f);
2191
2192        FolderInfo info = (FolderInfo) fi.getTag();
2193        if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2194            CellLayout cl = (CellLayout) fi.getParent().getParent();
2195            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) fi.getLayoutParams();
2196            cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
2197        }
2198
2199        // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
2200        copyFolderIconToImage(fi);
2201        fi.setVisibility(View.INVISIBLE);
2202
2203        ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
2204                scaleX, scaleY);
2205        oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
2206        oa.start();
2207    }
2208
2209    private void shrinkAndFadeInFolderIcon(final FolderIcon fi) {
2210        if (fi == null) return;
2211        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
2212        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
2213        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
2214
2215        final CellLayout cl = (CellLayout) fi.getParent().getParent();
2216
2217        // We remove and re-draw the FolderIcon in-case it has changed
2218        mDragLayer.removeView(mFolderIconImageView);
2219        copyFolderIconToImage(fi);
2220        ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
2221                scaleX, scaleY);
2222        oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
2223        oa.addListener(new AnimatorListenerAdapter() {
2224            @Override
2225            public void onAnimationEnd(Animator animation) {
2226                if (cl != null) {
2227                    cl.clearFolderLeaveBehind();
2228                    // Remove the ImageView copy of the FolderIcon and make the original visible.
2229                    mDragLayer.removeView(mFolderIconImageView);
2230                    fi.setVisibility(View.VISIBLE);
2231                }
2232            }
2233        });
2234        oa.start();
2235    }
2236
2237    /**
2238     * Opens the user folder described by the specified tag. The opening of the folder
2239     * is animated relative to the specified View. If the View is null, no animation
2240     * is played.
2241     *
2242     * @param folderInfo The FolderInfo describing the folder to open.
2243     */
2244    public void openFolder(FolderIcon folderIcon) {
2245        Folder folder = folderIcon.getFolder();
2246        FolderInfo info = folder.mInfo;
2247
2248        info.opened = true;
2249
2250        // Just verify that the folder hasn't already been added to the DragLayer.
2251        // There was a one-off crash where the folder had a parent already.
2252        if (folder.getParent() == null) {
2253            mDragLayer.addView(folder);
2254            mDragController.addDropTarget((DropTarget) folder);
2255        } else {
2256            Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
2257                    folder.getParent() + ").");
2258        }
2259        folder.animateOpen();
2260        growAndFadeOutFolderIcon(folderIcon);
2261    }
2262
2263    public void closeFolder() {
2264        Folder folder = mWorkspace.getOpenFolder();
2265        if (folder != null) {
2266            if (folder.isEditingName()) {
2267                folder.dismissEditingName();
2268            }
2269            closeFolder(folder);
2270
2271            // Dismiss the folder cling
2272            dismissFolderCling(null);
2273        }
2274    }
2275
2276    void closeFolder(Folder folder) {
2277        folder.getInfo().opened = false;
2278
2279        ViewGroup parent = (ViewGroup) folder.getParent().getParent();
2280        if (parent != null) {
2281            FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
2282            shrinkAndFadeInFolderIcon(fi);
2283        }
2284        folder.animateClosed();
2285    }
2286
2287    public boolean onLongClick(View v) {
2288        if (!isDraggingEnabled()) return false;
2289        if (isWorkspaceLocked()) return false;
2290        if (mState != State.WORKSPACE) return false;
2291
2292        if (!(v instanceof CellLayout)) {
2293            v = (View) v.getParent().getParent();
2294        }
2295
2296        resetAddInfo();
2297        CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag();
2298        // This happens when long clicking an item with the dpad/trackball
2299        if (longClickCellInfo == null) {
2300            return true;
2301        }
2302
2303        // The hotseat touch handling does not go through Workspace, and we always allow long press
2304        // on hotseat items.
2305        final View itemUnderLongClick = longClickCellInfo.cell;
2306        boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress();
2307        if (allowLongPress && !mDragController.isDragging()) {
2308            if (itemUnderLongClick == null) {
2309                // User long pressed on empty space
2310                mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
2311                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
2312                startWallpaper();
2313            } else {
2314                if (!(itemUnderLongClick instanceof Folder)) {
2315                    // User long pressed on an item
2316                    mWorkspace.startDrag(longClickCellInfo);
2317                }
2318            }
2319        }
2320        return true;
2321    }
2322
2323    boolean isHotseatLayout(View layout) {
2324        return mHotseat != null && layout != null &&
2325                (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
2326    }
2327    Hotseat getHotseat() {
2328        return mHotseat;
2329    }
2330    SearchDropTargetBar getSearchBar() {
2331        return mSearchDropTargetBar;
2332    }
2333
2334    /**
2335     * Returns the CellLayout of the specified container at the specified screen.
2336     */
2337    CellLayout getCellLayout(long container, int screen) {
2338        if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2339            if (mHotseat != null) {
2340                return mHotseat.getLayout();
2341            } else {
2342                return null;
2343            }
2344        } else {
2345            return (CellLayout) mWorkspace.getChildAt(screen);
2346        }
2347    }
2348
2349    Workspace getWorkspace() {
2350        return mWorkspace;
2351    }
2352
2353    // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
2354    public boolean isAllAppsVisible() {
2355        return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
2356    }
2357
2358    public boolean isAllAppsButtonRank(int rank) {
2359        return mHotseat.isAllAppsButtonRank(rank);
2360    }
2361
2362    /**
2363     * Helper method for the cameraZoomIn/cameraZoomOut animations
2364     * @param view The view being animated
2365     * @param state The state that we are moving in or out of (eg. APPS_CUSTOMIZE)
2366     * @param scaleFactor The scale factor used for the zoom
2367     */
2368    private void setPivotsForZoom(View view, float scaleFactor) {
2369        view.setPivotX(view.getWidth() / 2.0f);
2370        view.setPivotY(view.getHeight() / 2.0f);
2371    }
2372
2373    void disableWallpaperIfInAllApps() {
2374        // Only disable it if we are in all apps
2375        if (isAllAppsVisible()) {
2376            if (mAppsCustomizeTabHost != null &&
2377                    !mAppsCustomizeTabHost.isTransitioning()) {
2378                updateWallpaperVisibility(false);
2379            }
2380        }
2381    }
2382
2383    void updateWallpaperVisibility(boolean visible) {
2384        int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
2385        int curflags = getWindow().getAttributes().flags
2386                & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
2387        if (wpflags != curflags) {
2388            getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
2389        }
2390    }
2391
2392    private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
2393        if (v instanceof LauncherTransitionable) {
2394            ((LauncherTransitionable) v).onLauncherTransitionPrepare(this, animated, toWorkspace);
2395        }
2396    }
2397
2398    private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
2399        if (v instanceof LauncherTransitionable) {
2400            ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace);
2401        }
2402
2403        // Update the workspace transition step as well
2404        dispatchOnLauncherTransitionStep(v, 0f);
2405    }
2406
2407    private void dispatchOnLauncherTransitionStep(View v, float t) {
2408        if (v instanceof LauncherTransitionable) {
2409            ((LauncherTransitionable) v).onLauncherTransitionStep(this, t);
2410        }
2411    }
2412
2413    private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
2414        if (v instanceof LauncherTransitionable) {
2415            ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace);
2416        }
2417
2418        // Update the workspace transition step as well
2419        dispatchOnLauncherTransitionStep(v, 1f);
2420    }
2421
2422    /**
2423     * Things to test when changing the following seven functions.
2424     *   - Home from workspace
2425     *          - from center screen
2426     *          - from other screens
2427     *   - Home from all apps
2428     *          - from center screen
2429     *          - from other screens
2430     *   - Back from all apps
2431     *          - from center screen
2432     *          - from other screens
2433     *   - Launch app from workspace and quit
2434     *          - with back
2435     *          - with home
2436     *   - Launch app from all apps and quit
2437     *          - with back
2438     *          - with home
2439     *   - Go to a screen that's not the default, then all
2440     *     apps, and launch and app, and go back
2441     *          - with back
2442     *          -with home
2443     *   - On workspace, long press power and go back
2444     *          - with back
2445     *          - with home
2446     *   - On all apps, long press power and go back
2447     *          - with back
2448     *          - with home
2449     *   - On workspace, power off
2450     *   - On all apps, power off
2451     *   - Launch an app and turn off the screen while in that app
2452     *          - Go back with home key
2453     *          - Go back with back key  TODO: make this not go to workspace
2454     *          - From all apps
2455     *          - From workspace
2456     *   - Enter and exit car mode (becuase it causes an extra configuration changed)
2457     *          - From all apps
2458     *          - From the center workspace
2459     *          - From another workspace
2460     */
2461
2462    /**
2463     * Zoom the camera out from the workspace to reveal 'toView'.
2464     * Assumes that the view to show is anchored at either the very top or very bottom
2465     * of the screen.
2466     */
2467    private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) {
2468        if (mStateAnimation != null) {
2469            mStateAnimation.cancel();
2470            mStateAnimation = null;
2471        }
2472        final Resources res = getResources();
2473
2474        final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
2475        final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
2476        final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
2477        final View fromView = mWorkspace;
2478        final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
2479        final int startDelay =
2480                res.getInteger(R.integer.config_workspaceAppsCustomizeAnimationStagger);
2481
2482        setPivotsForZoom(toView, scale);
2483
2484        // Shrink workspaces away if going to AppsCustomize from workspace
2485        Animator workspaceAnim =
2486                mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
2487
2488        if (animated) {
2489            toView.setScaleX(scale);
2490            toView.setScaleY(scale);
2491            final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView);
2492            scaleAnim.
2493                scaleX(1f).scaleY(1f).
2494                setDuration(duration).
2495                setInterpolator(new Workspace.ZoomOutInterpolator());
2496
2497            toView.setVisibility(View.VISIBLE);
2498            toView.setAlpha(0f);
2499            final ObjectAnimator alphaAnim = ObjectAnimator
2500                .ofFloat(toView, "alpha", 0f, 1f)
2501                .setDuration(fadeDuration);
2502            alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
2503            alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
2504                @Override
2505                public void onAnimationUpdate(ValueAnimator animation) {
2506                    if (animation == null) {
2507                        throw new RuntimeException("animation is null");
2508                    }
2509                    float t = (Float) animation.getAnimatedValue();
2510                    dispatchOnLauncherTransitionStep(fromView, t);
2511                    dispatchOnLauncherTransitionStep(toView, t);
2512                }
2513            });
2514
2515            // toView should appear right at the end of the workspace shrink
2516            // animation
2517            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
2518            mStateAnimation.play(scaleAnim).after(startDelay);
2519            mStateAnimation.play(alphaAnim).after(startDelay);
2520
2521            mStateAnimation.addListener(new AnimatorListenerAdapter() {
2522                boolean animationCancelled = false;
2523
2524                @Override
2525                public void onAnimationStart(Animator animation) {
2526                    updateWallpaperVisibility(true);
2527                    // Prepare the position
2528                    toView.setTranslationX(0.0f);
2529                    toView.setTranslationY(0.0f);
2530                    toView.setVisibility(View.VISIBLE);
2531                    toView.bringToFront();
2532                }
2533                @Override
2534                public void onAnimationEnd(Animator animation) {
2535                    dispatchOnLauncherTransitionEnd(fromView, animated, false);
2536                    dispatchOnLauncherTransitionEnd(toView, animated, false);
2537
2538                    if (mWorkspace != null && !springLoaded && !LauncherApplication.isScreenLarge()) {
2539                        // Hide the workspace scrollbar
2540                        mWorkspace.hideScrollingIndicator(true);
2541                        hideDockDivider();
2542                    }
2543                    if (!animationCancelled) {
2544                        updateWallpaperVisibility(false);
2545                    }
2546
2547                    // Hide the search bar
2548                    if (mSearchDropTargetBar != null) {
2549                        mSearchDropTargetBar.hideSearchBar(false);
2550                    }
2551                }
2552
2553                @Override
2554                public void onAnimationCancel(Animator animation) {
2555                    animationCancelled = true;
2556                }
2557            });
2558
2559            if (workspaceAnim != null) {
2560                mStateAnimation.play(workspaceAnim);
2561            }
2562
2563            boolean delayAnim = false;
2564            final ViewTreeObserver observer;
2565
2566            dispatchOnLauncherTransitionPrepare(fromView, animated, false);
2567            dispatchOnLauncherTransitionPrepare(toView, animated, false);
2568
2569            // If any of the objects being animated haven't been measured/laid out
2570            // yet, delay the animation until we get a layout pass
2571            if ((((LauncherTransitionable) toView).getContent().getMeasuredWidth() == 0) ||
2572                    (mWorkspace.getMeasuredWidth() == 0) ||
2573                    (toView.getMeasuredWidth() == 0)) {
2574                observer = mWorkspace.getViewTreeObserver();
2575                delayAnim = true;
2576            } else {
2577                observer = null;
2578            }
2579
2580            final AnimatorSet stateAnimation = mStateAnimation;
2581            final Runnable startAnimRunnable = new Runnable() {
2582                public void run() {
2583                    // Check that mStateAnimation hasn't changed while
2584                    // we waited for a layout/draw pass
2585                    if (mStateAnimation != stateAnimation)
2586                        return;
2587                    setPivotsForZoom(toView, scale);
2588                    dispatchOnLauncherTransitionStart(fromView, animated, false);
2589                    dispatchOnLauncherTransitionStart(toView, animated, false);
2590                    toView.post(new Runnable() {
2591                        public void run() {
2592                            // Check that mStateAnimation hasn't changed while
2593                            // we waited for a layout/draw pass
2594                            if (mStateAnimation != stateAnimation)
2595                                return;
2596                            mStateAnimation.start();
2597                        }
2598                    });
2599                }
2600            };
2601            if (delayAnim) {
2602                final OnGlobalLayoutListener delayedStart = new OnGlobalLayoutListener() {
2603                    public void onGlobalLayout() {
2604                        toView.post(startAnimRunnable);
2605                        observer.removeOnGlobalLayoutListener(this);
2606                    }
2607                };
2608                observer.addOnGlobalLayoutListener(delayedStart);
2609            } else {
2610                startAnimRunnable.run();
2611            }
2612        } else {
2613            toView.setTranslationX(0.0f);
2614            toView.setTranslationY(0.0f);
2615            toView.setScaleX(1.0f);
2616            toView.setScaleY(1.0f);
2617            toView.setVisibility(View.VISIBLE);
2618            toView.bringToFront();
2619
2620            if (!springLoaded && !LauncherApplication.isScreenLarge()) {
2621                // Hide the workspace scrollbar
2622                mWorkspace.hideScrollingIndicator(true);
2623                hideDockDivider();
2624
2625                // Hide the search bar
2626                if (mSearchDropTargetBar != null) {
2627                    mSearchDropTargetBar.hideSearchBar(false);
2628                }
2629            }
2630            dispatchOnLauncherTransitionPrepare(fromView, animated, false);
2631            dispatchOnLauncherTransitionStart(fromView, animated, false);
2632            dispatchOnLauncherTransitionEnd(fromView, animated, false);
2633            dispatchOnLauncherTransitionPrepare(toView, animated, false);
2634            dispatchOnLauncherTransitionStart(toView, animated, false);
2635            dispatchOnLauncherTransitionEnd(toView, animated, false);
2636            updateWallpaperVisibility(false);
2637        }
2638    }
2639
2640    /**
2641     * Zoom the camera back into the workspace, hiding 'fromView'.
2642     * This is the opposite of showAppsCustomizeHelper.
2643     * @param animated If true, the transition will be animated.
2644     */
2645    private void hideAppsCustomizeHelper(State toState, final boolean animated,
2646            final boolean springLoaded, final Runnable onCompleteRunnable) {
2647
2648        if (mStateAnimation != null) {
2649            mStateAnimation.cancel();
2650            mStateAnimation = null;
2651        }
2652        Resources res = getResources();
2653
2654        final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
2655        final int fadeOutDuration =
2656                res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
2657        final float scaleFactor = (float)
2658                res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
2659        final View fromView = mAppsCustomizeTabHost;
2660        final View toView = mWorkspace;
2661        Animator workspaceAnim = null;
2662
2663        if (toState == State.WORKSPACE) {
2664            int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
2665            workspaceAnim = mWorkspace.getChangeStateAnimation(
2666                    Workspace.State.NORMAL, animated, stagger);
2667        } else if (toState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
2668            workspaceAnim = mWorkspace.getChangeStateAnimation(
2669                    Workspace.State.SPRING_LOADED, animated);
2670        }
2671
2672        setPivotsForZoom(fromView, scaleFactor);
2673        updateWallpaperVisibility(true);
2674        showHotseat(animated);
2675        if (animated) {
2676            final LauncherViewPropertyAnimator scaleAnim =
2677                    new LauncherViewPropertyAnimator(fromView);
2678            scaleAnim.
2679                scaleX(scaleFactor).scaleY(scaleFactor).
2680                setDuration(duration).
2681                setInterpolator(new Workspace.ZoomInInterpolator());
2682
2683            final ObjectAnimator alphaAnim = ObjectAnimator
2684                .ofFloat(fromView, "alpha", 1f, 0f)
2685                .setDuration(fadeOutDuration);
2686            alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
2687            alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
2688                @Override
2689                public void onAnimationUpdate(ValueAnimator animation) {
2690                    float t = 1f - (Float) animation.getAnimatedValue();
2691                    dispatchOnLauncherTransitionStep(fromView, t);
2692                    dispatchOnLauncherTransitionStep(toView, t);
2693                }
2694            });
2695
2696            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
2697
2698            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
2699            dispatchOnLauncherTransitionPrepare(toView, animated, true);
2700
2701            mStateAnimation.addListener(new AnimatorListenerAdapter() {
2702                @Override
2703                public void onAnimationEnd(Animator animation) {
2704                    updateWallpaperVisibility(true);
2705                    fromView.setVisibility(View.GONE);
2706                    dispatchOnLauncherTransitionEnd(fromView, animated, true);
2707                    dispatchOnLauncherTransitionEnd(toView, animated, true);
2708                    if (mWorkspace != null) {
2709                        mWorkspace.hideScrollingIndicator(false);
2710                    }
2711                    if (onCompleteRunnable != null) {
2712                        onCompleteRunnable.run();
2713                    }
2714                }
2715            });
2716
2717            mStateAnimation.playTogether(scaleAnim, alphaAnim);
2718            if (workspaceAnim != null) {
2719                mStateAnimation.play(workspaceAnim);
2720            }
2721            dispatchOnLauncherTransitionStart(fromView, animated, true);
2722            dispatchOnLauncherTransitionStart(toView, animated, true);
2723            final Animator stateAnimation = mStateAnimation;
2724            mWorkspace.post(new Runnable() {
2725                public void run() {
2726                    if (stateAnimation != mStateAnimation)
2727                        return;
2728                    mStateAnimation.start();
2729                }
2730            });
2731        } else {
2732            fromView.setVisibility(View.GONE);
2733            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
2734            dispatchOnLauncherTransitionStart(fromView, animated, true);
2735            dispatchOnLauncherTransitionEnd(fromView, animated, true);
2736            dispatchOnLauncherTransitionPrepare(toView, animated, true);
2737            dispatchOnLauncherTransitionStart(toView, animated, true);
2738            dispatchOnLauncherTransitionEnd(toView, animated, true);
2739            mWorkspace.hideScrollingIndicator(false);
2740        }
2741    }
2742
2743    @Override
2744    public void onTrimMemory(int level) {
2745        super.onTrimMemory(level);
2746        if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
2747            mAppsCustomizeTabHost.onTrimMemory();
2748        }
2749    }
2750
2751    @Override
2752    public void onWindowFocusChanged(boolean hasFocus) {
2753        if (!hasFocus) {
2754            // When another window occludes launcher (like the notification shade, or recents),
2755            // ensure that we enable the wallpaper flag so that transitions are done correctly.
2756            updateWallpaperVisibility(true);
2757        } else {
2758            // When launcher has focus again, disable the wallpaper if we are in AllApps
2759            mWorkspace.postDelayed(new Runnable() {
2760                @Override
2761                public void run() {
2762                    disableWallpaperIfInAllApps();
2763                }
2764            }, 500);
2765        }
2766    }
2767
2768    void showWorkspace(boolean animated) {
2769        showWorkspace(animated, null);
2770    }
2771
2772    void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
2773        if (mState != State.WORKSPACE) {
2774            boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED);
2775            mWorkspace.setVisibility(View.VISIBLE);
2776            hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);
2777
2778            // Show the search bar (only animate if we were showing the drop target bar in spring
2779            // loaded mode)
2780            if (mSearchDropTargetBar != null) {
2781                mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode);
2782            }
2783
2784            // We only need to animate in the dock divider if we're going from spring loaded mode
2785            showDockDivider(animated && wasInSpringLoadedMode);
2786
2787            // Set focus to the AppsCustomize button
2788            if (mAllAppsButton != null) {
2789                mAllAppsButton.requestFocus();
2790            }
2791        }
2792
2793        mWorkspace.flashScrollingIndicator(animated);
2794
2795        // Change the state *after* we've called all the transition code
2796        mState = State.WORKSPACE;
2797
2798        // Resume the auto-advance of widgets
2799        mUserPresent = true;
2800        updateRunning();
2801
2802        // Send an accessibility event to announce the context change
2803        getWindow().getDecorView()
2804                .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
2805    }
2806
2807    void showAllApps(boolean animated) {
2808        if (mState != State.WORKSPACE) return;
2809
2810        showAppsCustomizeHelper(animated, false);
2811        mAppsCustomizeTabHost.requestFocus();
2812
2813        // Change the state *after* we've called all the transition code
2814        mState = State.APPS_CUSTOMIZE;
2815
2816        // Pause the auto-advance of widgets until we are out of AllApps
2817        mUserPresent = false;
2818        updateRunning();
2819        closeFolder();
2820
2821        // Send an accessibility event to announce the context change
2822        getWindow().getDecorView()
2823                .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
2824    }
2825
2826    void enterSpringLoadedDragMode() {
2827        if (isAllAppsVisible()) {
2828            hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true, null);
2829            hideDockDivider();
2830            mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
2831        }
2832    }
2833
2834    void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay,
2835            final Runnable onCompleteRunnable) {
2836        if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
2837
2838        mHandler.postDelayed(new Runnable() {
2839            @Override
2840            public void run() {
2841                if (successfulDrop) {
2842                    // Before we show workspace, hide all apps again because
2843                    // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
2844                    // clean up our state transition functions
2845                    mAppsCustomizeTabHost.setVisibility(View.GONE);
2846                    showWorkspace(true, onCompleteRunnable);
2847                } else {
2848                    exitSpringLoadedDragMode();
2849                }
2850            }
2851        }, (extendedDelay ?
2852                EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT :
2853                EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT));
2854    }
2855
2856    void exitSpringLoadedDragMode() {
2857        if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
2858            final boolean animated = true;
2859            final boolean springLoaded = true;
2860            showAppsCustomizeHelper(animated, springLoaded);
2861            mState = State.APPS_CUSTOMIZE;
2862        }
2863        // Otherwise, we are not in spring loaded mode, so don't do anything.
2864    }
2865
2866    void hideDockDivider() {
2867        if (mQsbDivider != null && mDockDivider != null) {
2868            mQsbDivider.setVisibility(View.INVISIBLE);
2869            mDockDivider.setVisibility(View.INVISIBLE);
2870        }
2871    }
2872
2873    void showDockDivider(boolean animated) {
2874        if (mQsbDivider != null && mDockDivider != null) {
2875            mQsbDivider.setVisibility(View.VISIBLE);
2876            mDockDivider.setVisibility(View.VISIBLE);
2877            if (mDividerAnimator != null) {
2878                mDividerAnimator.cancel();
2879                mQsbDivider.setAlpha(1f);
2880                mDockDivider.setAlpha(1f);
2881                mDividerAnimator = null;
2882            }
2883            if (animated) {
2884                mDividerAnimator = LauncherAnimUtils.createAnimatorSet();
2885                mDividerAnimator.playTogether(LauncherAnimUtils.ofFloat(mQsbDivider, "alpha", 1f),
2886                        LauncherAnimUtils.ofFloat(mDockDivider, "alpha", 1f));
2887                int duration = 0;
2888                if (mSearchDropTargetBar != null) {
2889                    duration = mSearchDropTargetBar.getTransitionInDuration();
2890                }
2891                mDividerAnimator.setDuration(duration);
2892                mDividerAnimator.start();
2893            }
2894        }
2895    }
2896
2897    void lockAllApps() {
2898        // TODO
2899    }
2900
2901    void unlockAllApps() {
2902        // TODO
2903    }
2904
2905    /**
2906     * Shows the hotseat area.
2907     */
2908    void showHotseat(boolean animated) {
2909        if (!LauncherApplication.isScreenLarge()) {
2910            if (animated) {
2911                if (mHotseat.getAlpha() != 1f) {
2912                    int duration = 0;
2913                    if (mSearchDropTargetBar != null) {
2914                        duration = mSearchDropTargetBar.getTransitionInDuration();
2915                    }
2916                    mHotseat.animate().alpha(1f).setDuration(duration);
2917                }
2918            } else {
2919                mHotseat.setAlpha(1f);
2920            }
2921        }
2922    }
2923
2924    /**
2925     * Hides the hotseat area.
2926     */
2927    void hideHotseat(boolean animated) {
2928        if (!LauncherApplication.isScreenLarge()) {
2929            if (animated) {
2930                if (mHotseat.getAlpha() != 0f) {
2931                    int duration = 0;
2932                    if (mSearchDropTargetBar != null) {
2933                        duration = mSearchDropTargetBar.getTransitionOutDuration();
2934                    }
2935                    mHotseat.animate().alpha(0f).setDuration(duration);
2936                }
2937            } else {
2938                mHotseat.setAlpha(0f);
2939            }
2940        }
2941    }
2942
2943    /**
2944     * Add an item from all apps or customize onto the given workspace screen.
2945     * If layout is null, add to the current screen.
2946     */
2947    void addExternalItemToScreen(ItemInfo itemInfo, final CellLayout layout) {
2948        if (!mWorkspace.addExternalItemToScreen(itemInfo, layout)) {
2949            showOutOfSpaceMessage(isHotseatLayout(layout));
2950        }
2951    }
2952
2953    /** Maps the current orientation to an index for referencing orientation correct global icons */
2954    private int getCurrentOrientationIndexForGlobalIcons() {
2955        // default - 0, landscape - 1
2956        switch (getResources().getConfiguration().orientation) {
2957        case Configuration.ORIENTATION_LANDSCAPE:
2958            return 1;
2959        default:
2960            return 0;
2961        }
2962    }
2963
2964    private Drawable getExternalPackageToolbarIcon(ComponentName activityName, String resourceName) {
2965        try {
2966            PackageManager packageManager = getPackageManager();
2967            // Look for the toolbar icon specified in the activity meta-data
2968            Bundle metaData = packageManager.getActivityInfo(
2969                    activityName, PackageManager.GET_META_DATA).metaData;
2970            if (metaData != null) {
2971                int iconResId = metaData.getInt(resourceName);
2972                if (iconResId != 0) {
2973                    Resources res = packageManager.getResourcesForActivity(activityName);
2974                    return res.getDrawable(iconResId);
2975                }
2976            }
2977        } catch (NameNotFoundException e) {
2978            // This can happen if the activity defines an invalid drawable
2979            Log.w(TAG, "Failed to load toolbar icon; " + activityName.flattenToShortString() +
2980                    " not found", e);
2981        } catch (Resources.NotFoundException nfe) {
2982            // This can happen if the activity defines an invalid drawable
2983            Log.w(TAG, "Failed to load toolbar icon from " + activityName.flattenToShortString(),
2984                    nfe);
2985        }
2986        return null;
2987    }
2988
2989    // if successful in getting icon, return it; otherwise, set button to use default drawable
2990    private Drawable.ConstantState updateTextButtonWithIconFromExternalActivity(
2991            int buttonId, ComponentName activityName, int fallbackDrawableId,
2992            String toolbarResourceName) {
2993        Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName);
2994        Resources r = getResources();
2995        int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
2996        int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
2997
2998        TextView button = (TextView) findViewById(buttonId);
2999        // If we were unable to find the icon via the meta-data, use a generic one
3000        if (toolbarIcon == null) {
3001            toolbarIcon = r.getDrawable(fallbackDrawableId);
3002            toolbarIcon.setBounds(0, 0, w, h);
3003            if (button != null) {
3004                button.setCompoundDrawables(toolbarIcon, null, null, null);
3005            }
3006            return null;
3007        } else {
3008            toolbarIcon.setBounds(0, 0, w, h);
3009            if (button != null) {
3010                button.setCompoundDrawables(toolbarIcon, null, null, null);
3011            }
3012            return toolbarIcon.getConstantState();
3013        }
3014    }
3015
3016    // if successful in getting icon, return it; otherwise, set button to use default drawable
3017    private Drawable.ConstantState updateButtonWithIconFromExternalActivity(
3018            int buttonId, ComponentName activityName, int fallbackDrawableId,
3019            String toolbarResourceName) {
3020        ImageView button = (ImageView) findViewById(buttonId);
3021        Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName);
3022
3023        if (button != null) {
3024            // If we were unable to find the icon via the meta-data, use a
3025            // generic one
3026            if (toolbarIcon == null) {
3027                button.setImageResource(fallbackDrawableId);
3028            } else {
3029                button.setImageDrawable(toolbarIcon);
3030            }
3031        }
3032
3033        return toolbarIcon != null ? toolbarIcon.getConstantState() : null;
3034
3035    }
3036
3037    private void updateTextButtonWithDrawable(int buttonId, Drawable d) {
3038        TextView button = (TextView) findViewById(buttonId);
3039        button.setCompoundDrawables(d, null, null, null);
3040    }
3041
3042    private void updateButtonWithDrawable(int buttonId, Drawable.ConstantState d) {
3043        ImageView button = (ImageView) findViewById(buttonId);
3044        button.setImageDrawable(d.newDrawable(getResources()));
3045    }
3046
3047    private void invalidatePressedFocusedStates(View container, View button) {
3048        if (container instanceof HolographicLinearLayout) {
3049            HolographicLinearLayout layout = (HolographicLinearLayout) container;
3050            layout.invalidatePressedFocusedStates();
3051        } else if (button instanceof HolographicImageView) {
3052            HolographicImageView view = (HolographicImageView) button;
3053            view.invalidatePressedFocusedStates();
3054        }
3055    }
3056
3057    private boolean updateGlobalSearchIcon() {
3058        final View searchButtonContainer = findViewById(R.id.search_button_container);
3059        final ImageView searchButton = (ImageView) findViewById(R.id.search_button);
3060        final View voiceButtonContainer = findViewById(R.id.voice_button_container);
3061        final View voiceButton = findViewById(R.id.voice_button);
3062        final View voiceButtonProxy = findViewById(R.id.voice_button_proxy);
3063
3064        final SearchManager searchManager =
3065                (SearchManager) getSystemService(Context.SEARCH_SERVICE);
3066        ComponentName activityName = searchManager.getGlobalSearchActivity();
3067        if (activityName != null) {
3068            int coi = getCurrentOrientationIndexForGlobalIcons();
3069            sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
3070                    R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo,
3071                    TOOLBAR_SEARCH_ICON_METADATA_NAME);
3072            if (sGlobalSearchIcon[coi] == null) {
3073                sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
3074                        R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo,
3075                        TOOLBAR_ICON_METADATA_NAME);
3076            }
3077
3078            if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.VISIBLE);
3079            searchButton.setVisibility(View.VISIBLE);
3080            invalidatePressedFocusedStates(searchButtonContainer, searchButton);
3081            return true;
3082        } else {
3083            // We disable both search and voice search when there is no global search provider
3084            if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.GONE);
3085            if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE);
3086            searchButton.setVisibility(View.GONE);
3087            voiceButton.setVisibility(View.GONE);
3088            if (voiceButtonProxy != null) {
3089                voiceButtonProxy.setVisibility(View.GONE);
3090            }
3091            return false;
3092        }
3093    }
3094
3095    private void updateGlobalSearchIcon(Drawable.ConstantState d) {
3096        final View searchButtonContainer = findViewById(R.id.search_button_container);
3097        final View searchButton = (ImageView) findViewById(R.id.search_button);
3098        updateButtonWithDrawable(R.id.search_button, d);
3099        invalidatePressedFocusedStates(searchButtonContainer, searchButton);
3100    }
3101
3102    private boolean updateVoiceSearchIcon(boolean searchVisible) {
3103        final View voiceButtonContainer = findViewById(R.id.voice_button_container);
3104        final View voiceButton = findViewById(R.id.voice_button);
3105        final View voiceButtonProxy = findViewById(R.id.voice_button_proxy);
3106
3107        // We only show/update the voice search icon if the search icon is enabled as well
3108        final SearchManager searchManager =
3109                (SearchManager) getSystemService(Context.SEARCH_SERVICE);
3110        ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
3111
3112        ComponentName activityName = null;
3113        if (globalSearchActivity != null) {
3114            // Check if the global search activity handles voice search
3115            Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
3116            intent.setPackage(globalSearchActivity.getPackageName());
3117            activityName = intent.resolveActivity(getPackageManager());
3118        }
3119
3120        if (activityName == null) {
3121            // Fallback: check if an activity other than the global search activity
3122            // resolves this
3123            Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
3124            activityName = intent.resolveActivity(getPackageManager());
3125        }
3126        if (searchVisible && activityName != null) {
3127            int coi = getCurrentOrientationIndexForGlobalIcons();
3128            sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
3129                    R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo,
3130                    TOOLBAR_VOICE_SEARCH_ICON_METADATA_NAME);
3131            if (sVoiceSearchIcon[coi] == null) {
3132                sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
3133                        R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo,
3134                        TOOLBAR_ICON_METADATA_NAME);
3135            }
3136            if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.VISIBLE);
3137            voiceButton.setVisibility(View.VISIBLE);
3138            if (voiceButtonProxy != null) {
3139                voiceButtonProxy.setVisibility(View.VISIBLE);
3140            }
3141            invalidatePressedFocusedStates(voiceButtonContainer, voiceButton);
3142            return true;
3143        } else {
3144            if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE);
3145            voiceButton.setVisibility(View.GONE);
3146            if (voiceButtonProxy != null) {
3147                voiceButtonProxy.setVisibility(View.GONE);
3148            }
3149            return false;
3150        }
3151    }
3152
3153    private void updateVoiceSearchIcon(Drawable.ConstantState d) {
3154        final View voiceButtonContainer = findViewById(R.id.voice_button_container);
3155        final View voiceButton = findViewById(R.id.voice_button);
3156        updateButtonWithDrawable(R.id.voice_button, d);
3157        invalidatePressedFocusedStates(voiceButtonContainer, voiceButton);
3158    }
3159
3160    /**
3161     * Sets the app market icon
3162     */
3163    private void updateAppMarketIcon() {
3164        final View marketButton = findViewById(R.id.market_button);
3165        Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
3166        // Find the app market activity by resolving an intent.
3167        // (If multiple app markets are installed, it will return the ResolverActivity.)
3168        ComponentName activityName = intent.resolveActivity(getPackageManager());
3169        if (activityName != null) {
3170            int coi = getCurrentOrientationIndexForGlobalIcons();
3171            mAppMarketIntent = intent;
3172            sAppMarketIcon[coi] = updateTextButtonWithIconFromExternalActivity(
3173                    R.id.market_button, activityName, R.drawable.ic_launcher_market_holo,
3174                    TOOLBAR_ICON_METADATA_NAME);
3175            marketButton.setVisibility(View.VISIBLE);
3176        } else {
3177            // We should hide and disable the view so that we don't try and restore the visibility
3178            // of it when we swap between drag & normal states from IconDropTarget subclasses.
3179            marketButton.setVisibility(View.GONE);
3180            marketButton.setEnabled(false);
3181        }
3182    }
3183
3184    private void updateAppMarketIcon(Drawable.ConstantState d) {
3185        // Ensure that the new drawable we are creating has the approprate toolbar icon bounds
3186        Resources r = getResources();
3187        Drawable marketIconDrawable = d.newDrawable(r);
3188        int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
3189        int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
3190        marketIconDrawable.setBounds(0, 0, w, h);
3191
3192        updateTextButtonWithDrawable(R.id.market_button, marketIconDrawable);
3193    }
3194
3195    @Override
3196    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
3197        final boolean result = super.dispatchPopulateAccessibilityEvent(event);
3198        final List<CharSequence> text = event.getText();
3199        text.clear();
3200        // Populate event with a fake title based on the current state.
3201        if (mState == State.APPS_CUSTOMIZE) {
3202            text.add(getString(R.string.all_apps_button_label));
3203        } else {
3204            text.add(getString(R.string.all_apps_home_button_label));
3205        }
3206        return result;
3207    }
3208
3209    /**
3210     * Receives notifications when system dialogs are to be closed.
3211     */
3212    private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
3213        @Override
3214        public void onReceive(Context context, Intent intent) {
3215            closeSystemDialogs();
3216        }
3217    }
3218
3219    /**
3220     * Receives notifications whenever the appwidgets are reset.
3221     */
3222    private class AppWidgetResetObserver extends ContentObserver {
3223        public AppWidgetResetObserver() {
3224            super(new Handler());
3225        }
3226
3227        @Override
3228        public void onChange(boolean selfChange) {
3229            onAppWidgetReset();
3230        }
3231    }
3232
3233    /**
3234     * If the activity is currently paused, signal that we need to re-run the loader
3235     * in onResume.
3236     *
3237     * This needs to be called from incoming places where resources might have been loaded
3238     * while we are paused.  That is becaues the Configuration might be wrong
3239     * when we're not running, and if it comes back to what it was when we
3240     * were paused, we are not restarted.
3241     *
3242     * Implementation of the method from LauncherModel.Callbacks.
3243     *
3244     * @return true if we are currently paused.  The caller might be able to
3245     * skip some work in that case since we will come back again.
3246     */
3247    public boolean setLoadOnResume() {
3248        if (mPaused) {
3249            Log.i(TAG, "setLoadOnResume");
3250            mOnResumeNeedsLoad = true;
3251            return true;
3252        } else {
3253            return false;
3254        }
3255    }
3256
3257    /**
3258     * Implementation of the method from LauncherModel.Callbacks.
3259     */
3260    public int getCurrentWorkspaceScreen() {
3261        if (mWorkspace != null) {
3262            return mWorkspace.getCurrentPage();
3263        } else {
3264            return SCREEN_COUNT / 2;
3265        }
3266    }
3267
3268    /**
3269     * Refreshes the shortcuts shown on the workspace.
3270     *
3271     * Implementation of the method from LauncherModel.Callbacks.
3272     */
3273    public void startBinding() {
3274        final Workspace workspace = mWorkspace;
3275
3276        mNewShortcutAnimatePage = -1;
3277        mNewShortcutAnimateViews.clear();
3278        mWorkspace.clearDropTargets();
3279        int count = workspace.getChildCount();
3280        for (int i = 0; i < count; i++) {
3281            // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
3282            final CellLayout layoutParent = (CellLayout) workspace.getChildAt(i);
3283            layoutParent.removeAllViewsInLayout();
3284        }
3285        mWidgetsToAdvance.clear();
3286        if (mHotseat != null) {
3287            mHotseat.resetLayout();
3288        }
3289    }
3290
3291    /**
3292     * Bind the items start-end from the list.
3293     *
3294     * Implementation of the method from LauncherModel.Callbacks.
3295     */
3296    public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
3297        setLoadOnResume();
3298
3299        // Get the list of added shortcuts and intersect them with the set of shortcuts here
3300        Set<String> newApps = new HashSet<String>();
3301        newApps = mSharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps);
3302
3303        Workspace workspace = mWorkspace;
3304        for (int i = start; i < end; i++) {
3305            final ItemInfo item = shortcuts.get(i);
3306
3307            // Short circuit if we are loading dock items for a configuration which has no dock
3308            if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
3309                    mHotseat == null) {
3310                continue;
3311            }
3312
3313            switch (item.itemType) {
3314                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3315                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
3316                    ShortcutInfo info = (ShortcutInfo) item;
3317                    String uri = info.intent.toUri(0).toString();
3318                    View shortcut = createShortcut(info);
3319                    workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
3320                            item.cellY, 1, 1, false);
3321                    boolean animateIconUp = false;
3322                    synchronized (newApps) {
3323                        if (newApps.contains(uri)) {
3324                            animateIconUp = newApps.remove(uri);
3325                        }
3326                    }
3327                    if (animateIconUp) {
3328                        // Prepare the view to be animated up
3329                        shortcut.setAlpha(0f);
3330                        shortcut.setScaleX(0f);
3331                        shortcut.setScaleY(0f);
3332                        mNewShortcutAnimatePage = item.screen;
3333                        if (!mNewShortcutAnimateViews.contains(shortcut)) {
3334                            mNewShortcutAnimateViews.add(shortcut);
3335                        }
3336                    }
3337                    break;
3338                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
3339                    FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
3340                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
3341                            (FolderInfo) item, mIconCache);
3342                    workspace.addInScreen(newFolder, item.container, item.screen, item.cellX,
3343                            item.cellY, 1, 1, false);
3344                    break;
3345            }
3346        }
3347
3348        workspace.requestLayout();
3349    }
3350
3351    /**
3352     * Implementation of the method from LauncherModel.Callbacks.
3353     */
3354    public void bindFolders(HashMap<Long, FolderInfo> folders) {
3355        setLoadOnResume();
3356        sFolders.clear();
3357        sFolders.putAll(folders);
3358    }
3359
3360    /**
3361     * Add the views for a widget to the workspace.
3362     *
3363     * Implementation of the method from LauncherModel.Callbacks.
3364     */
3365    public void bindAppWidget(LauncherAppWidgetInfo item) {
3366        setLoadOnResume();
3367
3368        final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
3369        if (DEBUG_WIDGETS) {
3370            Log.d(TAG, "bindAppWidget: " + item);
3371        }
3372        final Workspace workspace = mWorkspace;
3373
3374        final int appWidgetId = item.appWidgetId;
3375        final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
3376        if (DEBUG_WIDGETS) {
3377            Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
3378        }
3379
3380        item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
3381
3382        item.hostView.setTag(item);
3383        item.onBindAppWidget(this);
3384
3385        workspace.addInScreen(item.hostView, item.container, item.screen, item.cellX,
3386                item.cellY, item.spanX, item.spanY, false);
3387        addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
3388
3389        workspace.requestLayout();
3390
3391        if (DEBUG_WIDGETS) {
3392            Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
3393                    + (SystemClock.uptimeMillis()-start) + "ms");
3394        }
3395    }
3396
3397    public void onPageBoundSynchronously(int page) {
3398        mSynchronouslyBoundPages.add(page);
3399    }
3400
3401    /**
3402     * Callback saying that there aren't any more items to bind.
3403     *
3404     * Implementation of the method from LauncherModel.Callbacks.
3405     */
3406    public void finishBindingItems() {
3407        setLoadOnResume();
3408
3409        if (mSavedState != null) {
3410            if (!mWorkspace.hasFocus()) {
3411                mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
3412            }
3413            mSavedState = null;
3414        }
3415
3416        mWorkspace.restoreInstanceStateForRemainingPages();
3417
3418        // If we received the result of any pending adds while the loader was running (e.g. the
3419        // widget configuration forced an orientation change), process them now.
3420        for (int i = 0; i < sPendingAddList.size(); i++) {
3421            completeAdd(sPendingAddList.get(i));
3422        }
3423        sPendingAddList.clear();
3424
3425        // Update the market app icon as necessary (the other icons will be managed in response to
3426        // package changes in bindSearchablesChanged()
3427        updateAppMarketIcon();
3428
3429        // Animate up any icons as necessary
3430        if (mVisible || mWorkspaceLoading) {
3431            Runnable newAppsRunnable = new Runnable() {
3432                @Override
3433                public void run() {
3434                    runNewAppsAnimation(false);
3435                }
3436            };
3437
3438            boolean willSnapPage = mNewShortcutAnimatePage > -1 &&
3439                    mNewShortcutAnimatePage != mWorkspace.getCurrentPage();
3440            if (canRunNewAppsAnimation()) {
3441                // If the user has not interacted recently, then either snap to the new page to show
3442                // the new-apps animation or just run them if they are to appear on the current page
3443                if (willSnapPage) {
3444                    mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable);
3445                } else {
3446                    runNewAppsAnimation(false);
3447                }
3448            } else {
3449                // If the user has interacted recently, then just add the items in place if they
3450                // are on another page (or just normally if they are added to the current page)
3451                runNewAppsAnimation(willSnapPage);
3452            }
3453        }
3454
3455        mWorkspaceLoading = false;
3456    }
3457
3458    private boolean canRunNewAppsAnimation() {
3459        long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
3460        return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
3461    }
3462
3463    /**
3464     * Runs a new animation that scales up icons that were added while Launcher was in the
3465     * background.
3466     *
3467     * @param immediate whether to run the animation or show the results immediately
3468     */
3469    private void runNewAppsAnimation(boolean immediate) {
3470        AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
3471        Collection<Animator> bounceAnims = new ArrayList<Animator>();
3472
3473        // Order these new views spatially so that they animate in order
3474        Collections.sort(mNewShortcutAnimateViews, new Comparator<View>() {
3475            @Override
3476            public int compare(View a, View b) {
3477                CellLayout.LayoutParams alp = (CellLayout.LayoutParams) a.getLayoutParams();
3478                CellLayout.LayoutParams blp = (CellLayout.LayoutParams) b.getLayoutParams();
3479                int cellCountX = LauncherModel.getCellCountX();
3480                return (alp.cellY * cellCountX + alp.cellX) - (blp.cellY * cellCountX + blp.cellX);
3481            }
3482        });
3483
3484        // Animate each of the views in place (or show them immediately if requested)
3485        if (immediate) {
3486            for (View v : mNewShortcutAnimateViews) {
3487                v.setAlpha(1f);
3488                v.setScaleX(1f);
3489                v.setScaleY(1f);
3490            }
3491        } else {
3492            for (int i = 0; i < mNewShortcutAnimateViews.size(); ++i) {
3493                View v = mNewShortcutAnimateViews.get(i);
3494                ValueAnimator bounceAnim = LauncherAnimUtils.ofPropertyValuesHolder(v,
3495                        PropertyValuesHolder.ofFloat("alpha", 1f),
3496                        PropertyValuesHolder.ofFloat("scaleX", 1f),
3497                        PropertyValuesHolder.ofFloat("scaleY", 1f));
3498                bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
3499                bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
3500                bounceAnim.setInterpolator(new SmoothPagedView.OvershootInterpolator());
3501                bounceAnims.add(bounceAnim);
3502            }
3503            anim.playTogether(bounceAnims);
3504            anim.addListener(new AnimatorListenerAdapter() {
3505                @Override
3506                public void onAnimationEnd(Animator animation) {
3507                    if (mWorkspace != null) {
3508                        mWorkspace.postDelayed(mBuildLayersRunnable, 500);
3509                    }
3510                }
3511            });
3512            anim.start();
3513        }
3514
3515        // Clean up
3516        mNewShortcutAnimatePage = -1;
3517        mNewShortcutAnimateViews.clear();
3518        new Thread("clearNewAppsThread") {
3519            public void run() {
3520                mSharedPrefs.edit()
3521                            .putInt(InstallShortcutReceiver.NEW_APPS_PAGE_KEY, -1)
3522                            .putStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, null)
3523                            .commit();
3524            }
3525        }.start();
3526    }
3527
3528    @Override
3529    public void bindSearchablesChanged() {
3530        boolean searchVisible = updateGlobalSearchIcon();
3531        boolean voiceVisible = updateVoiceSearchIcon(searchVisible);
3532        if (mSearchDropTargetBar != null) {
3533            mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
3534        }
3535    }
3536
3537    /**
3538     * Add the icons for all apps.
3539     *
3540     * Implementation of the method from LauncherModel.Callbacks.
3541     */
3542    public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {
3543        Runnable setAllAppsRunnable = new Runnable() {
3544            public void run() {
3545                if (mAppsCustomizeContent != null) {
3546                    mAppsCustomizeContent.setApps(apps);
3547                }
3548            }
3549        };
3550
3551        // Remove the progress bar entirely; we could also make it GONE
3552        // but better to remove it since we know it's not going to be used
3553        View progressBar = mAppsCustomizeTabHost.
3554            findViewById(R.id.apps_customize_progress_bar);
3555        if (progressBar != null) {
3556            ((ViewGroup)progressBar.getParent()).removeView(progressBar);
3557
3558            // We just post the call to setApps so the user sees the progress bar
3559            // disappear-- otherwise, it just looks like the progress bar froze
3560            // which doesn't look great
3561            mAppsCustomizeTabHost.post(setAllAppsRunnable);
3562        } else {
3563            // If we did not initialize the spinner in onCreate, then we can directly set the
3564            // list of applications without waiting for any progress bars views to be hidden.
3565            setAllAppsRunnable.run();
3566        }
3567    }
3568
3569    /**
3570     * A package was installed.
3571     *
3572     * Implementation of the method from LauncherModel.Callbacks.
3573     */
3574    public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
3575        setLoadOnResume();
3576
3577        if (mAppsCustomizeContent != null) {
3578            mAppsCustomizeContent.addApps(apps);
3579        }
3580    }
3581
3582    /**
3583     * A package was updated.
3584     *
3585     * Implementation of the method from LauncherModel.Callbacks.
3586     */
3587    public void bindAppsUpdated(ArrayList<ApplicationInfo> apps) {
3588        setLoadOnResume();
3589        if (mWorkspace != null) {
3590            mWorkspace.updateShortcuts(apps);
3591        }
3592
3593        if (mAppsCustomizeContent != null) {
3594            mAppsCustomizeContent.updateApps(apps);
3595        }
3596    }
3597
3598    /**
3599     * A package was uninstalled.
3600     *
3601     * Implementation of the method from LauncherModel.Callbacks.
3602     */
3603    public void bindAppsRemoved(ArrayList<String> packageNames, boolean permanent) {
3604        if (permanent) {
3605            mWorkspace.removeItems(packageNames);
3606        }
3607
3608        if (mAppsCustomizeContent != null) {
3609            mAppsCustomizeContent.removeApps(packageNames);
3610        }
3611
3612        // Notify the drag controller
3613        mDragController.onAppsRemoved(packageNames, this);
3614    }
3615
3616    /**
3617     * A number of packages were updated.
3618     */
3619    public void bindPackagesUpdated() {
3620        if (mAppsCustomizeContent != null) {
3621            mAppsCustomizeContent.onPackagesUpdated();
3622        }
3623    }
3624
3625    private int mapConfigurationOriActivityInfoOri(int configOri) {
3626        final Display d = getWindowManager().getDefaultDisplay();
3627        int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
3628        switch (d.getRotation()) {
3629        case Surface.ROTATION_0:
3630        case Surface.ROTATION_180:
3631            // We are currently in the same basic orientation as the natural orientation
3632            naturalOri = configOri;
3633            break;
3634        case Surface.ROTATION_90:
3635        case Surface.ROTATION_270:
3636            // We are currently in the other basic orientation to the natural orientation
3637            naturalOri = (configOri == Configuration.ORIENTATION_LANDSCAPE) ?
3638                    Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
3639            break;
3640        }
3641
3642        int[] oriMap = {
3643                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
3644                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
3645                ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
3646                ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
3647        };
3648        // Since the map starts at portrait, we need to offset if this device's natural orientation
3649        // is landscape.
3650        int indexOffset = 0;
3651        if (naturalOri == Configuration.ORIENTATION_LANDSCAPE) {
3652            indexOffset = 1;
3653        }
3654        return oriMap[(d.getRotation() + indexOffset) % 4];
3655    }
3656
3657    public boolean isRotationEnabled() {
3658        boolean forceEnableRotation = doesFileExist(FORCE_ENABLE_ROTATION_PROPERTY);
3659        boolean enableRotation = forceEnableRotation ||
3660                getResources().getBoolean(R.bool.allow_rotation);
3661        return enableRotation;
3662    }
3663    public void lockScreenOrientation() {
3664        if (isRotationEnabled()) {
3665            setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
3666                    .getConfiguration().orientation));
3667        }
3668    }
3669    public void unlockScreenOrientation(boolean immediate) {
3670        if (isRotationEnabled()) {
3671            if (immediate) {
3672                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
3673            } else {
3674                mHandler.postDelayed(new Runnable() {
3675                    public void run() {
3676                        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
3677                    }
3678                }, mRestoreScreenOrientationDelay);
3679            }
3680        }
3681    }
3682
3683    /* Cling related */
3684    private boolean isClingsEnabled() {
3685        // disable clings when running in a test harness
3686        if(ActivityManager.isRunningInTestHarness()) return false;
3687
3688        return true;
3689    }
3690
3691    private Cling initCling(int clingId, int[] positionData, boolean animate, int delay) {
3692        final Cling cling = (Cling) findViewById(clingId);
3693        if (cling != null) {
3694            cling.init(this, positionData);
3695            cling.setVisibility(View.VISIBLE);
3696            cling.setLayerType(View.LAYER_TYPE_HARDWARE, null);
3697            if (animate) {
3698                cling.buildLayer();
3699                cling.setAlpha(0f);
3700                cling.animate()
3701                    .alpha(1f)
3702                    .setInterpolator(new AccelerateInterpolator())
3703                    .setDuration(SHOW_CLING_DURATION)
3704                    .setStartDelay(delay)
3705                    .start();
3706            } else {
3707                cling.setAlpha(1f);
3708            }
3709            cling.setFocusableInTouchMode(true);
3710            cling.post(new Runnable() {
3711                public void run() {
3712                    cling.setFocusable(true);
3713                    cling.requestFocus();
3714                }
3715            });
3716            mHideFromAccessibilityHelper.setImportantForAccessibilityToNo(
3717                    mDragLayer, clingId == R.id.all_apps_cling);
3718        }
3719        return cling;
3720    }
3721
3722    private void dismissCling(final Cling cling, final String flag, int duration) {
3723        if (cling != null && cling.getVisibility() == View.VISIBLE) {
3724            ObjectAnimator anim = LauncherAnimUtils.ofFloat(cling, "alpha", 0f);
3725            anim.setDuration(duration);
3726            anim.addListener(new AnimatorListenerAdapter() {
3727                public void onAnimationEnd(Animator animation) {
3728                    cling.setVisibility(View.GONE);
3729                    cling.cleanup();
3730                    // We should update the shared preferences on a background thread
3731                    new Thread("dismissClingThread") {
3732                        public void run() {
3733                            SharedPreferences.Editor editor = mSharedPrefs.edit();
3734                            editor.putBoolean(flag, true);
3735                            editor.commit();
3736                        }
3737                    }.start();
3738                };
3739            });
3740            anim.start();
3741            mHideFromAccessibilityHelper.restoreImportantForAccessibility(mDragLayer);
3742        }
3743    }
3744
3745    private void removeCling(int id) {
3746        final View cling = findViewById(id);
3747        if (cling != null) {
3748            final ViewGroup parent = (ViewGroup) cling.getParent();
3749            parent.post(new Runnable() {
3750                @Override
3751                public void run() {
3752                    parent.removeView(cling);
3753                }
3754            });
3755            mHideFromAccessibilityHelper.restoreImportantForAccessibility(mDragLayer);
3756        }
3757    }
3758
3759    private boolean skipCustomClingIfNoAccounts() {
3760        Cling cling = (Cling) findViewById(R.id.workspace_cling);
3761        boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
3762        if (customCling) {
3763            AccountManager am = AccountManager.get(this);
3764            Account[] accounts = am.getAccountsByType("com.google");
3765            return accounts.length == 0;
3766        }
3767        return false;
3768    }
3769
3770    public void showFirstRunWorkspaceCling() {
3771        // Enable the clings only if they have not been dismissed before
3772        if (isClingsEnabled() &&
3773                !mSharedPrefs.getBoolean(Cling.WORKSPACE_CLING_DISMISSED_KEY, false) &&
3774                !skipCustomClingIfNoAccounts() ) {
3775            // If we're not using the default workspace layout, replace workspace cling
3776            // with a custom workspace cling (usually specified in an overlay)
3777            // For now, only do this on tablets
3778            if (mSharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 &&
3779                    getResources().getBoolean(R.bool.config_useCustomClings)) {
3780                // Use a custom cling
3781                View cling = findViewById(R.id.workspace_cling);
3782                ViewGroup clingParent = (ViewGroup) cling.getParent();
3783                int clingIndex = clingParent.indexOfChild(cling);
3784                clingParent.removeViewAt(clingIndex);
3785                View customCling = mInflater.inflate(R.layout.custom_workspace_cling, clingParent, false);
3786                clingParent.addView(customCling, clingIndex);
3787                customCling.setId(R.id.workspace_cling);
3788            }
3789            initCling(R.id.workspace_cling, null, false, 0);
3790        } else {
3791            removeCling(R.id.workspace_cling);
3792        }
3793    }
3794    public void showFirstRunAllAppsCling(int[] position) {
3795        // Enable the clings only if they have not been dismissed before
3796        if (isClingsEnabled() &&
3797                !mSharedPrefs.getBoolean(Cling.ALLAPPS_CLING_DISMISSED_KEY, false)) {
3798            initCling(R.id.all_apps_cling, position, true, 0);
3799        } else {
3800            removeCling(R.id.all_apps_cling);
3801        }
3802    }
3803    public Cling showFirstRunFoldersCling() {
3804        // Enable the clings only if they have not been dismissed before
3805        if (isClingsEnabled() &&
3806                !mSharedPrefs.getBoolean(Cling.FOLDER_CLING_DISMISSED_KEY, false)) {
3807            return initCling(R.id.folder_cling, null, true, 0);
3808        } else {
3809            removeCling(R.id.folder_cling);
3810            return null;
3811        }
3812    }
3813    public boolean isFolderClingVisible() {
3814        Cling cling = (Cling) findViewById(R.id.folder_cling);
3815        if (cling != null) {
3816            return cling.getVisibility() == View.VISIBLE;
3817        }
3818        return false;
3819    }
3820    public void dismissWorkspaceCling(View v) {
3821        Cling cling = (Cling) findViewById(R.id.workspace_cling);
3822        dismissCling(cling, Cling.WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
3823    }
3824    public void dismissAllAppsCling(View v) {
3825        Cling cling = (Cling) findViewById(R.id.all_apps_cling);
3826        dismissCling(cling, Cling.ALLAPPS_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
3827    }
3828    public void dismissFolderCling(View v) {
3829        Cling cling = (Cling) findViewById(R.id.folder_cling);
3830        dismissCling(cling, Cling.FOLDER_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
3831    }
3832
3833    /**
3834     * Prints out out state for debugging.
3835     */
3836    public void dumpState() {
3837        Log.d(TAG, "BEGIN launcher2 dump state for launcher " + this);
3838        Log.d(TAG, "mSavedState=" + mSavedState);
3839        Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
3840        Log.d(TAG, "mRestoring=" + mRestoring);
3841        Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
3842        Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
3843        Log.d(TAG, "sFolders.size=" + sFolders.size());
3844        mModel.dumpState();
3845
3846        if (mAppsCustomizeContent != null) {
3847            mAppsCustomizeContent.dumpState();
3848        }
3849        Log.d(TAG, "END launcher2 dump state");
3850    }
3851
3852    @Override
3853    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
3854        super.dump(prefix, fd, writer, args);
3855        writer.println(" ");
3856        writer.println("Debug logs: ");
3857        for (int i = 0; i < sDumpLogs.size(); i++) {
3858            writer.println("  " + sDumpLogs.get(i));
3859        }
3860    }
3861
3862    public static void dumpDebugLogsToConsole() {
3863        Log.d(TAG, "");
3864        Log.d(TAG, "*********************");
3865        Log.d(TAG, "Launcher debug logs: ");
3866        for (int i = 0; i < sDumpLogs.size(); i++) {
3867            Log.d(TAG, "  " + sDumpLogs.get(i));
3868        }
3869        Log.d(TAG, "*********************");
3870        Log.d(TAG, "");
3871    }
3872}
3873
3874interface LauncherTransitionable {
3875    View getContent();
3876    void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);
3877    void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
3878    void onLauncherTransitionStep(Launcher l, float t);
3879    void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
3880}
3881