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