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