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