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