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.LauncherApps;
     47 import android.content.pm.PackageManager;
     48 import android.content.pm.PackageManager.NameNotFoundException;
     49 import android.content.res.Configuration;
     50 import android.content.res.Resources;
     51 import android.database.ContentObserver;
     52 import android.graphics.Bitmap;
     53 import android.graphics.Canvas;
     54 import android.graphics.PorterDuff;
     55 import android.graphics.Rect;
     56 import android.graphics.drawable.Drawable;
     57 import android.net.Uri;
     58 import android.os.AsyncTask;
     59 import android.os.Bundle;
     60 import android.os.Environment;
     61 import android.os.Handler;
     62 import android.os.Message;
     63 import android.os.StrictMode;
     64 import android.os.SystemClock;
     65 import android.os.UserHandle;
     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,
   1086                 android.os.Process.myUserHandle(), this);
   1087 
   1088         if (info != null) {
   1089             // Necessary flags are added when the activity is launched via
   1090             // LauncherApps
   1091             info.setActivity(data);
   1092             info.container = ItemInfo.NO_ID;
   1093             mWorkspace.addApplicationShortcut(info, layout, container, screen, cellXY[0], cellXY[1],
   1094                     isWorkspaceLocked(), cellX, cellY);
   1095         } else {
   1096             Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
   1097         }
   1098     }
   1099 
   1100     /**
   1101      * Add a shortcut to the workspace.
   1102      *
   1103      * @param data The intent describing the shortcut.
   1104      * @param cellInfo The position on screen where to create the shortcut.
   1105      */
   1106     private void completeAddShortcut(Intent data, long container, int screen, int cellX,
   1107             int cellY) {
   1108         int[] cellXY = mTmpAddItemCellCoordinates;
   1109         int[] touchXY = mPendingAddInfo.dropPos;
   1110         CellLayout layout = getCellLayout(container, screen);
   1111 
   1112         boolean foundCellSpan = false;
   1113 
   1114         ShortcutInfo info = mModel.infoFromShortcutIntent(this, data, null);
   1115         if (info == null) {
   1116             return;
   1117         }
   1118         final View view = createShortcut(info);
   1119 
   1120         // First we check if we already know the exact location where we want to add this item.
   1121         if (cellX >= 0 && cellY >= 0) {
   1122             cellXY[0] = cellX;
   1123             cellXY[1] = cellY;
   1124             foundCellSpan = true;
   1125 
   1126             // If appropriate, either create a folder or add to an existing folder
   1127             if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
   1128                     true, null,null)) {
   1129                 return;
   1130             }
   1131             DragObject dragObject = new DragObject();
   1132             dragObject.dragInfo = info;
   1133             if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
   1134                     true)) {
   1135                 return;
   1136             }
   1137         } else if (touchXY != null) {
   1138             // when dragging and dropping, just find the closest free spot
   1139             int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY);
   1140             foundCellSpan = (result != null);
   1141         } else {
   1142             foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
   1143         }
   1144 
   1145         if (!foundCellSpan) {
   1146             showOutOfSpaceMessage(isHotseatLayout(layout));
   1147             return;
   1148         }
   1149 
   1150         LauncherModel.addItemToDatabase(this, info, container, screen, cellXY[0], cellXY[1], false);
   1151 
   1152         if (!mRestoring) {
   1153             mWorkspace.addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1,
   1154                     isWorkspaceLocked());
   1155         }
   1156     }
   1157 
   1158     static int[] getSpanForWidget(Context context, ComponentName component, int minWidth,
   1159             int minHeight) {
   1160         Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null);
   1161         // We want to account for the extra amount of padding that we are adding to the widget
   1162         // to ensure that it gets the full amount of space that it has requested
   1163         int requiredWidth = minWidth + padding.left + padding.right;
   1164         int requiredHeight = minHeight + padding.top + padding.bottom;
   1165         return CellLayout.rectToCell(context.getResources(), requiredWidth, requiredHeight, null);
   1166     }
   1167 
   1168     static int[] getSpanForWidget(Context context, AppWidgetProviderInfo info) {
   1169         return getSpanForWidget(context, info.provider, info.minWidth, info.minHeight);
   1170     }
   1171 
   1172     static int[] getMinSpanForWidget(Context context, AppWidgetProviderInfo info) {
   1173         return getSpanForWidget(context, info.provider, info.minResizeWidth, info.minResizeHeight);
   1174     }
   1175 
   1176     static int[] getSpanForWidget(Context context, PendingAddWidgetInfo info) {
   1177         return getSpanForWidget(context, info.componentName, info.info.minWidth,
   1178                 info.info.minHeight);
   1179     }
   1180 
   1181     static int[] getMinSpanForWidget(Context context, PendingAddWidgetInfo info) {
   1182         return getSpanForWidget(context, info.componentName, info.info.minResizeWidth,
   1183                 info.info.minResizeHeight);
   1184     }
   1185 
   1186     /**
   1187      * Add a widget to the workspace.
   1188      *
   1189      * @param appWidgetId The app widget id
   1190      * @param cellInfo The position on screen where to create the widget.
   1191      */
   1192     private void completeAddAppWidget(final int appWidgetId, long container, int screen,
   1193             AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) {
   1194         if (appWidgetInfo == null) {
   1195             appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   1196         }
   1197 
   1198         // Calculate the grid spans needed to fit this widget
   1199         CellLayout layout = getCellLayout(container, screen);
   1200 
   1201         int[] minSpanXY = getMinSpanForWidget(this, appWidgetInfo);
   1202         int[] spanXY = getSpanForWidget(this, appWidgetInfo);
   1203 
   1204         // Try finding open space on Launcher screen
   1205         // We have saved the position to which the widget was dragged-- this really only matters
   1206         // if we are placing widgets on a "spring-loaded" screen
   1207         int[] cellXY = mTmpAddItemCellCoordinates;
   1208         int[] touchXY = mPendingAddInfo.dropPos;
   1209         int[] finalSpan = new int[2];
   1210         boolean foundCellSpan = false;
   1211         if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) {
   1212             cellXY[0] = mPendingAddInfo.cellX;
   1213             cellXY[1] = mPendingAddInfo.cellY;
   1214             spanXY[0] = mPendingAddInfo.spanX;
   1215             spanXY[1] = mPendingAddInfo.spanY;
   1216             foundCellSpan = true;
   1217         } else if (touchXY != null) {
   1218             // when dragging and dropping, just find the closest free spot
   1219             int[] result = layout.findNearestVacantArea(
   1220                     touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0],
   1221                     spanXY[1], cellXY, finalSpan);
   1222             spanXY[0] = finalSpan[0];
   1223             spanXY[1] = finalSpan[1];
   1224             foundCellSpan = (result != null);
   1225         } else {
   1226             foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]);
   1227         }
   1228 
   1229         if (!foundCellSpan) {
   1230             if (appWidgetId != -1) {
   1231                 // Deleting an app widget ID is a void call but writes to disk before returning
   1232                 // to the caller...
   1233                 new Thread("deleteAppWidgetId") {
   1234                     public void run() {
   1235                         mAppWidgetHost.deleteAppWidgetId(appWidgetId);
   1236                     }
   1237                 }.start();
   1238             }
   1239             showOutOfSpaceMessage(isHotseatLayout(layout));
   1240             return;
   1241         }
   1242 
   1243         // Build Launcher-specific widget info and save to database
   1244         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId,
   1245                 appWidgetInfo.provider);
   1246         launcherInfo.spanX = spanXY[0];
   1247         launcherInfo.spanY = spanXY[1];
   1248         launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
   1249         launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
   1250         launcherInfo.user = appWidgetInfo.getProfile();
   1251 
   1252         LauncherModel.addItemToDatabase(this, launcherInfo,
   1253                 container, screen, cellXY[0], cellXY[1], false);
   1254 
   1255         if (!mRestoring) {
   1256             if (hostView == null) {
   1257                 // Perform actual inflation because we're live
   1258                 launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   1259                 launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
   1260             } else {
   1261                 // The AppWidgetHostView has already been inflated and instantiated
   1262                 launcherInfo.hostView = hostView;
   1263             }
   1264 
   1265             launcherInfo.hostView.setTag(launcherInfo);
   1266             launcherInfo.hostView.setVisibility(View.VISIBLE);
   1267             launcherInfo.notifyWidgetSizeChanged(this);
   1268 
   1269             mWorkspace.addInScreen(launcherInfo.hostView, container, screen, cellXY[0], cellXY[1],
   1270                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
   1271 
   1272             addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
   1273         }
   1274         resetAddInfo();
   1275     }
   1276 
   1277     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
   1278         @Override
   1279         public void onReceive(Context context, Intent intent) {
   1280             final String action = intent.getAction();
   1281             if (Intent.ACTION_SCREEN_OFF.equals(action)) {
   1282                 mUserPresent = false;
   1283                 mDragLayer.clearAllResizeFrames();
   1284                 updateRunning();
   1285 
   1286                 // Reset AllApps to its initial state only if we are not in the middle of
   1287                 // processing a multi-step drop
   1288                 if (mAppsCustomizeTabHost != null && mPendingAddInfo.container == ItemInfo.NO_ID) {
   1289                     mAppsCustomizeTabHost.reset();
   1290                     showWorkspace(false);
   1291                 }
   1292             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
   1293                 mUserPresent = true;
   1294                 updateRunning();
   1295             } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
   1296                     || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
   1297                 getModel().forceReload();
   1298             }
   1299         }
   1300     };
   1301 
   1302     @Override
   1303     public void onAttachedToWindow() {
   1304         super.onAttachedToWindow();
   1305 
   1306         // Listen for broadcasts related to user-presence
   1307         final IntentFilter filter = new IntentFilter();
   1308         filter.addAction(Intent.ACTION_SCREEN_OFF);
   1309         filter.addAction(Intent.ACTION_USER_PRESENT);
   1310         filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
   1311         filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
   1312         registerReceiver(mReceiver, filter);
   1313         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
   1314         mAttached = true;
   1315         mVisible = true;
   1316     }
   1317 
   1318     @Override
   1319     public void onDetachedFromWindow() {
   1320         super.onDetachedFromWindow();
   1321         mVisible = false;
   1322 
   1323         if (mAttached) {
   1324             unregisterReceiver(mReceiver);
   1325             mAttached = false;
   1326         }
   1327         updateRunning();
   1328     }
   1329 
   1330     public void onWindowVisibilityChanged(int visibility) {
   1331         mVisible = visibility == View.VISIBLE;
   1332         updateRunning();
   1333         // The following code used to be in onResume, but it turns out onResume is called when
   1334         // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
   1335         // is a more appropriate event to handle
   1336         if (mVisible) {
   1337             mAppsCustomizeTabHost.onWindowVisible();
   1338             if (!mWorkspaceLoading) {
   1339                 final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
   1340                 // We want to let Launcher draw itself at least once before we force it to build
   1341                 // layers on all the workspace pages, so that transitioning to Launcher from other
   1342                 // apps is nice and speedy.
   1343                 observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
   1344                     private boolean mStarted = false;
   1345                     public void onDraw() {
   1346                         if (mStarted) return;
   1347                         mStarted = true;
   1348                         // We delay the layer building a bit in order to give
   1349                         // other message processing a time to run.  In particular
   1350                         // this avoids a delay in hiding the IME if it was
   1351                         // currently shown, because doing that may involve
   1352                         // some communication back with the app.
   1353                         mWorkspace.postDelayed(mBuildLayersRunnable, 500);
   1354                         final ViewTreeObserver.OnDrawListener listener = this;
   1355                         mWorkspace.post(new Runnable() {
   1356                                 public void run() {
   1357                                     if (mWorkspace != null &&
   1358                                             mWorkspace.getViewTreeObserver() != null) {
   1359                                         mWorkspace.getViewTreeObserver().
   1360                                                 removeOnDrawListener(listener);
   1361                                     }
   1362                                 }
   1363                             });
   1364                         return;
   1365                     }
   1366                 });
   1367             }
   1368             // When Launcher comes back to foreground, a different Activity might be responsible for
   1369             // the app market intent, so refresh the icon
   1370             updateAppMarketIcon();
   1371             clearTypedText();
   1372         }
   1373     }
   1374 
   1375     private void sendAdvanceMessage(long delay) {
   1376         mHandler.removeMessages(ADVANCE_MSG);
   1377         Message msg = mHandler.obtainMessage(ADVANCE_MSG);
   1378         mHandler.sendMessageDelayed(msg, delay);
   1379         mAutoAdvanceSentTime = System.currentTimeMillis();
   1380     }
   1381 
   1382     private void updateRunning() {
   1383         boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty();
   1384         if (autoAdvanceRunning != mAutoAdvanceRunning) {
   1385             mAutoAdvanceRunning = autoAdvanceRunning;
   1386             if (autoAdvanceRunning) {
   1387                 long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft;
   1388                 sendAdvanceMessage(delay);
   1389             } else {
   1390                 if (!mWidgetsToAdvance.isEmpty()) {
   1391                     mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval -
   1392                             (System.currentTimeMillis() - mAutoAdvanceSentTime));
   1393                 }
   1394                 mHandler.removeMessages(ADVANCE_MSG);
   1395                 mHandler.removeMessages(0); // Remove messages sent using postDelayed()
   1396             }
   1397         }
   1398     }
   1399 
   1400     private final Handler mHandler = new Handler() {
   1401         @Override
   1402         public void handleMessage(Message msg) {
   1403             if (msg.what == ADVANCE_MSG) {
   1404                 int i = 0;
   1405                 for (View key: mWidgetsToAdvance.keySet()) {
   1406                     final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId);
   1407                     final int delay = mAdvanceStagger * i;
   1408                     if (v instanceof Advanceable) {
   1409                        postDelayed(new Runnable() {
   1410                            public void run() {
   1411                                ((Advanceable) v).advance();
   1412                            }
   1413                        }, delay);
   1414                     }
   1415                     i++;
   1416                 }
   1417                 sendAdvanceMessage(mAdvanceInterval);
   1418             }
   1419         }
   1420     };
   1421 
   1422     void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) {
   1423         if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return;
   1424         View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId);
   1425         if (v instanceof Advanceable) {
   1426             mWidgetsToAdvance.put(hostView, appWidgetInfo);
   1427             ((Advanceable) v).fyiWillBeAdvancedByHostKThx();
   1428             updateRunning();
   1429         }
   1430     }
   1431 
   1432     void removeWidgetToAutoAdvance(View hostView) {
   1433         if (mWidgetsToAdvance.containsKey(hostView)) {
   1434             mWidgetsToAdvance.remove(hostView);
   1435             updateRunning();
   1436         }
   1437     }
   1438 
   1439     public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
   1440         removeWidgetToAutoAdvance(launcherInfo.hostView);
   1441         launcherInfo.hostView = null;
   1442     }
   1443 
   1444     void showOutOfSpaceMessage(boolean isHotseatLayout) {
   1445         int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
   1446         Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
   1447     }
   1448 
   1449     public LauncherAppWidgetHost getAppWidgetHost() {
   1450         return mAppWidgetHost;
   1451     }
   1452 
   1453     public LauncherModel getModel() {
   1454         return mModel;
   1455     }
   1456 
   1457     void closeSystemDialogs() {
   1458         getWindow().closeAllPanels();
   1459 
   1460         // Whatever we were doing is hereby canceled.
   1461         mWaitingForResult = false;
   1462     }
   1463 
   1464     @Override
   1465     protected void onNewIntent(Intent intent) {
   1466         long startTime = 0;
   1467         if (DEBUG_RESUME_TIME) {
   1468             startTime = System.currentTimeMillis();
   1469         }
   1470         super.onNewIntent(intent);
   1471 
   1472         // Close the menu
   1473         if (Intent.ACTION_MAIN.equals(intent.getAction())) {
   1474             // also will cancel mWaitingForResult.
   1475             closeSystemDialogs();
   1476 
   1477             final boolean alreadyOnHome =
   1478                     ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
   1479                         != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
   1480 
   1481             Runnable processIntent = new Runnable() {
   1482                 public void run() {
   1483                     if (mWorkspace == null) {
   1484                         // Can be cases where mWorkspace is null, this prevents a NPE
   1485                         return;
   1486                     }
   1487                     Folder openFolder = mWorkspace.getOpenFolder();
   1488                     // In all these cases, only animate if we're already on home
   1489                     mWorkspace.exitWidgetResizeMode();
   1490                     if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
   1491                             openFolder == null) {
   1492                         mWorkspace.moveToDefaultScreen(true);
   1493                     }
   1494 
   1495                     closeFolder();
   1496                     exitSpringLoadedDragMode();
   1497 
   1498                     // If we are already on home, then just animate back to the workspace,
   1499                     // otherwise, just wait until onResume to set the state back to Workspace
   1500                     if (alreadyOnHome) {
   1501                         showWorkspace(true);
   1502                     } else {
   1503                         mOnResumeState = State.WORKSPACE;
   1504                     }
   1505 
   1506                     final View v = getWindow().peekDecorView();
   1507                     if (v != null && v.getWindowToken() != null) {
   1508                         InputMethodManager imm = (InputMethodManager)getSystemService(
   1509                                 INPUT_METHOD_SERVICE);
   1510                         imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
   1511                     }
   1512 
   1513                     // Reset AllApps to its initial state
   1514                     if (!alreadyOnHome && mAppsCustomizeTabHost != null) {
   1515                         mAppsCustomizeTabHost.reset();
   1516                     }
   1517                 }
   1518             };
   1519 
   1520             if (alreadyOnHome && !mWorkspace.hasWindowFocus()) {
   1521                 // Delay processing of the intent to allow the status bar animation to finish
   1522                 // first in order to avoid janky animations.
   1523                 mWorkspace.postDelayed(processIntent, 350);
   1524             } else {
   1525                 // Process the intent immediately.
   1526                 processIntent.run();
   1527             }
   1528 
   1529         }
   1530         if (DEBUG_RESUME_TIME) {
   1531             Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime));
   1532         }
   1533     }
   1534 
   1535     @Override
   1536     public void onRestoreInstanceState(Bundle state) {
   1537         super.onRestoreInstanceState(state);
   1538         for (int page: mSynchronouslyBoundPages) {
   1539             mWorkspace.restoreInstanceStateForChild(page);
   1540         }
   1541     }
   1542 
   1543     @Override
   1544     protected void onSaveInstanceState(Bundle outState) {
   1545         outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage());
   1546         super.onSaveInstanceState(outState);
   1547 
   1548         outState.putInt(RUNTIME_STATE, mState.ordinal());
   1549         // We close any open folder since it will not be re-opened, and we need to make sure
   1550         // this state is reflected.
   1551         closeFolder();
   1552 
   1553         if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screen > -1 &&
   1554                 mWaitingForResult) {
   1555             outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container);
   1556             outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screen);
   1557             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX);
   1558             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
   1559             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
   1560             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY);
   1561             outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
   1562             outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
   1563         }
   1564 
   1565         if (mFolderInfo != null && mWaitingForResult) {
   1566             outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
   1567             outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
   1568         }
   1569 
   1570         // Save the current AppsCustomize tab
   1571         if (mAppsCustomizeTabHost != null) {
   1572             String currentTabTag = mAppsCustomizeTabHost.getCurrentTabTag();
   1573             if (currentTabTag != null) {
   1574                 outState.putString("apps_customize_currentTab", currentTabTag);
   1575             }
   1576             int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex();
   1577             outState.putInt("apps_customize_currentIndex", currentIndex);
   1578         }
   1579     }
   1580 
   1581     @Override
   1582     public void onDestroy() {
   1583         super.onDestroy();
   1584 
   1585         // Remove all pending runnables
   1586         mHandler.removeMessages(ADVANCE_MSG);
   1587         mHandler.removeMessages(0);
   1588         mWorkspace.removeCallbacks(mBuildLayersRunnable);
   1589 
   1590         // Stop callbacks from LauncherModel
   1591         LauncherApplication app = ((LauncherApplication) getApplication());
   1592         mModel.stopLoader();
   1593         app.setLauncher(null);
   1594 
   1595         try {
   1596             mAppWidgetHost.stopListening();
   1597         } catch (NullPointerException ex) {
   1598             Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
   1599         }
   1600         mAppWidgetHost = null;
   1601 
   1602         mWidgetsToAdvance.clear();
   1603 
   1604         TextKeyListener.getInstance().release();
   1605 
   1606         // Disconnect any of the callbacks and drawables associated with ItemInfos on the workspace
   1607         // to prevent leaking Launcher activities on orientation change.
   1608         if (mModel != null) {
   1609             mModel.unbindItemInfosAndClearQueuedBindRunnables();
   1610         }
   1611 
   1612         getContentResolver().unregisterContentObserver(mWidgetObserver);
   1613         unregisterReceiver(mCloseSystemDialogsReceiver);
   1614 
   1615         mDragLayer.clearAllResizeFrames();
   1616         ((ViewGroup) mWorkspace.getParent()).removeAllViews();
   1617         mWorkspace.removeAllViews();
   1618         mWorkspace = null;
   1619         mDragController = null;
   1620 
   1621         LauncherAnimUtils.onDestroyActivity();
   1622     }
   1623 
   1624     public DragController getDragController() {
   1625         return mDragController;
   1626     }
   1627 
   1628     @Override
   1629     public void startActivityForResult(Intent intent, int requestCode) {
   1630         if (requestCode >= 0) mWaitingForResult = true;
   1631         super.startActivityForResult(intent, requestCode);
   1632     }
   1633 
   1634     /**
   1635      * Indicates that we want global search for this activity by setting the globalSearch
   1636      * argument for {@link #startSearch} to true.
   1637      */
   1638     @Override
   1639     public void startSearch(String initialQuery, boolean selectInitialQuery,
   1640             Bundle appSearchData, boolean globalSearch) {
   1641 
   1642         showWorkspace(true);
   1643 
   1644         if (initialQuery == null) {
   1645             // Use any text typed in the launcher as the initial query
   1646             initialQuery = getTypedText();
   1647         }
   1648         if (appSearchData == null) {
   1649             appSearchData = new Bundle();
   1650             appSearchData.putString(Search.SOURCE, "launcher-search");
   1651         }
   1652         Rect sourceBounds = new Rect();
   1653         if (mSearchDropTargetBar != null) {
   1654             sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
   1655         }
   1656 
   1657         startGlobalSearch(initialQuery, selectInitialQuery,
   1658             appSearchData, sourceBounds);
   1659     }
   1660 
   1661     /**
   1662      * Starts the global search activity. This code is a copied from SearchManager
   1663      */
   1664     public void startGlobalSearch(String initialQuery,
   1665             boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
   1666         final SearchManager searchManager =
   1667             (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   1668         ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
   1669         if (globalSearchActivity == null) {
   1670             Log.w(TAG, "No global search activity found.");
   1671             return;
   1672         }
   1673         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
   1674         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1675         intent.setComponent(globalSearchActivity);
   1676         // Make sure that we have a Bundle to put source in
   1677         if (appSearchData == null) {
   1678             appSearchData = new Bundle();
   1679         } else {
   1680             appSearchData = new Bundle(appSearchData);
   1681         }
   1682         // Set source to package name of app that starts global search, if not set already.
   1683         if (!appSearchData.containsKey("source")) {
   1684             appSearchData.putString("source", getPackageName());
   1685         }
   1686         intent.putExtra(SearchManager.APP_DATA, appSearchData);
   1687         if (!TextUtils.isEmpty(initialQuery)) {
   1688             intent.putExtra(SearchManager.QUERY, initialQuery);
   1689         }
   1690         if (selectInitialQuery) {
   1691             intent.putExtra(SearchManager.EXTRA_SELECT_QUERY, selectInitialQuery);
   1692         }
   1693         intent.setSourceBounds(sourceBounds);
   1694         try {
   1695             startActivity(intent);
   1696         } catch (ActivityNotFoundException ex) {
   1697             Log.e(TAG, "Global search activity not found: " + globalSearchActivity);
   1698         }
   1699     }
   1700 
   1701     @Override
   1702     public boolean onCreateOptionsMenu(Menu menu) {
   1703         if (isWorkspaceLocked()) {
   1704             return false;
   1705         }
   1706 
   1707         super.onCreateOptionsMenu(menu);
   1708 
   1709         Intent manageApps = new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS);
   1710         manageApps.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1711                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   1712         Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
   1713         settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1714                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
   1715         String helpUrl = getString(R.string.help_url);
   1716         Intent help = new Intent(Intent.ACTION_VIEW, Uri.parse(helpUrl));
   1717         help.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1718                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   1719 
   1720         menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
   1721             .setIcon(android.R.drawable.ic_menu_gallery)
   1722             .setAlphabeticShortcut('W');
   1723         menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)
   1724             .setIcon(android.R.drawable.ic_menu_manage)
   1725             .setIntent(manageApps)
   1726             .setAlphabeticShortcut('M');
   1727         menu.add(0, MENU_SYSTEM_SETTINGS, 0, R.string.menu_settings)
   1728             .setIcon(android.R.drawable.ic_menu_preferences)
   1729             .setIntent(settings)
   1730             .setAlphabeticShortcut('P');
   1731         if (!helpUrl.isEmpty()) {
   1732             menu.add(0, MENU_HELP, 0, R.string.menu_help)
   1733                 .setIcon(android.R.drawable.ic_menu_help)
   1734                 .setIntent(help)
   1735                 .setAlphabeticShortcut('H');
   1736         }
   1737         return true;
   1738     }
   1739 
   1740     @Override
   1741     public boolean onPrepareOptionsMenu(Menu menu) {
   1742         super.onPrepareOptionsMenu(menu);
   1743 
   1744         if (mAppsCustomizeTabHost.isTransitioning()) {
   1745             return false;
   1746         }
   1747         boolean allAppsVisible = (mAppsCustomizeTabHost.getVisibility() == View.VISIBLE);
   1748         menu.setGroupVisible(MENU_GROUP_WALLPAPER, !allAppsVisible);
   1749 
   1750         return true;
   1751     }
   1752 
   1753     @Override
   1754     public boolean onOptionsItemSelected(MenuItem item) {
   1755         switch (item.getItemId()) {
   1756         case MENU_WALLPAPER_SETTINGS:
   1757             startWallpaper();
   1758             return true;
   1759         }
   1760 
   1761         return super.onOptionsItemSelected(item);
   1762     }
   1763 
   1764     @Override
   1765     public boolean onSearchRequested() {
   1766         startSearch(null, false, null, true);
   1767         // Use a custom animation for launching search
   1768         return true;
   1769     }
   1770 
   1771     public boolean isWorkspaceLocked() {
   1772         return mWorkspaceLoading || mWaitingForResult;
   1773     }
   1774 
   1775     private void resetAddInfo() {
   1776         mPendingAddInfo.container = ItemInfo.NO_ID;
   1777         mPendingAddInfo.screen = -1;
   1778         mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
   1779         mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
   1780         mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
   1781         mPendingAddInfo.dropPos = null;
   1782     }
   1783 
   1784     void addAppWidgetImpl(final int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
   1785             AppWidgetProviderInfo appWidgetInfo) {
   1786         if (appWidgetInfo.configure != null) {
   1787             mPendingAddWidgetInfo = appWidgetInfo;
   1788             mPendingAddWidgetId = appWidgetId;
   1789 
   1790             // Launch over to configure widget, if needed
   1791             startAppWidgetConfigureActivitySafely(appWidgetId);
   1792         } else {
   1793             // Otherwise just add it
   1794             completeAddAppWidget(appWidgetId, info.container, info.screen, boundWidget,
   1795                     appWidgetInfo);
   1796             // Exit spring loaded mode if necessary after adding the widget
   1797             exitSpringLoadedDragModeDelayed(true, false, null);
   1798         }
   1799     }
   1800 
   1801     /**
   1802      * Process a shortcut drop.
   1803      *
   1804      * @param componentName The name of the component
   1805      * @param screen The screen where it should be added
   1806      * @param cell The cell it should be added to, optional
   1807      * @param position The location on the screen where it was dropped, optional
   1808      */
   1809     void processShortcutFromDrop(ComponentName componentName, long container, int screen,
   1810             int[] cell, int[] loc) {
   1811         resetAddInfo();
   1812         mPendingAddInfo.container = container;
   1813         mPendingAddInfo.screen = screen;
   1814         mPendingAddInfo.dropPos = loc;
   1815 
   1816         if (cell != null) {
   1817             mPendingAddInfo.cellX = cell[0];
   1818             mPendingAddInfo.cellY = cell[1];
   1819         }
   1820 
   1821         Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
   1822         createShortcutIntent.setComponent(componentName);
   1823         processShortcut(createShortcutIntent);
   1824     }
   1825 
   1826     /**
   1827      * Process a widget drop.
   1828      *
   1829      * @param info The PendingAppWidgetInfo of the widget being added.
   1830      * @param screen The screen where it should be added
   1831      * @param cell The cell it should be added to, optional
   1832      * @param position The location on the screen where it was dropped, optional
   1833      */
   1834     void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, int screen,
   1835             int[] cell, int[] span, int[] loc) {
   1836         resetAddInfo();
   1837         mPendingAddInfo.container = info.container = container;
   1838         mPendingAddInfo.screen = info.screen = screen;
   1839         mPendingAddInfo.dropPos = loc;
   1840         mPendingAddInfo.minSpanX = info.minSpanX;
   1841         mPendingAddInfo.minSpanY = info.minSpanY;
   1842 
   1843         if (cell != null) {
   1844             mPendingAddInfo.cellX = cell[0];
   1845             mPendingAddInfo.cellY = cell[1];
   1846         }
   1847         if (span != null) {
   1848             mPendingAddInfo.spanX = span[0];
   1849             mPendingAddInfo.spanY = span[1];
   1850         }
   1851 
   1852         AppWidgetHostView hostView = info.boundWidget;
   1853         int appWidgetId;
   1854         if (hostView != null) {
   1855             appWidgetId = hostView.getAppWidgetId();
   1856             addAppWidgetImpl(appWidgetId, info, hostView, info.info);
   1857         } else {
   1858             // In this case, we either need to start an activity to get permission to bind
   1859             // the widget, or we need to start an activity to configure the widget, or both.
   1860             appWidgetId = getAppWidgetHost().allocateAppWidgetId();
   1861 
   1862             boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
   1863                     info.info.getProfile(), info.componentName, info.bindOptions);
   1864             if (success) {
   1865                 addAppWidgetImpl(appWidgetId, info, null, info.info);
   1866             } else {
   1867                 mPendingAddWidgetInfo = info.info;
   1868                 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
   1869                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   1870                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
   1871                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE,
   1872                         info.info.getProfile());
   1873                 // TODO: we need to make sure that this accounts for the options bundle.
   1874                 // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
   1875                 startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
   1876             }
   1877         }
   1878     }
   1879 
   1880     void processShortcut(Intent intent) {
   1881         // Handle case where user selected "Applications"
   1882         String applicationName = getResources().getString(R.string.group_applications);
   1883         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
   1884 
   1885         if (applicationName != null && applicationName.equals(shortcutName)) {
   1886             Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
   1887             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
   1888 
   1889             Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
   1890             pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
   1891             pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
   1892             startActivityForResultSafely(pickIntent, REQUEST_PICK_APPLICATION);
   1893         } else {
   1894             startActivityForResultSafely(intent, REQUEST_CREATE_SHORTCUT);
   1895         }
   1896     }
   1897 
   1898     void processWallpaper(Intent intent) {
   1899         startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
   1900     }
   1901 
   1902     FolderIcon addFolder(CellLayout layout, long container, final int screen, int cellX,
   1903             int cellY) {
   1904         final FolderInfo folderInfo = new FolderInfo();
   1905         folderInfo.title = getText(R.string.folder_name);
   1906 
   1907         // Update the model
   1908         LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screen, cellX, cellY,
   1909                 false);
   1910         sFolders.put(folderInfo.id, folderInfo);
   1911 
   1912         // Create the view
   1913         FolderIcon newFolder =
   1914             FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
   1915         mWorkspace.addInScreen(newFolder, container, screen, cellX, cellY, 1, 1,
   1916                 isWorkspaceLocked());
   1917         return newFolder;
   1918     }
   1919 
   1920     void removeFolder(FolderInfo folder) {
   1921         sFolders.remove(folder.id);
   1922     }
   1923 
   1924     private void startWallpaper() {
   1925         showWorkspace(true);
   1926         final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
   1927         Intent chooser = Intent.createChooser(pickWallpaper,
   1928                 getText(R.string.chooser_wallpaper));
   1929         // NOTE: Adds a configure option to the chooser if the wallpaper supports it
   1930         //       Removed in Eclair MR1
   1931 //        WallpaperManager wm = (WallpaperManager)
   1932 //                getSystemService(Context.WALLPAPER_SERVICE);
   1933 //        WallpaperInfo wi = wm.getWallpaperInfo();
   1934 //        if (wi != null && wi.getSettingsActivity() != null) {
   1935 //            LabeledIntent li = new LabeledIntent(getPackageName(),
   1936 //                    R.string.configure_wallpaper, 0);
   1937 //            li.setClassName(wi.getPackageName(), wi.getSettingsActivity());
   1938 //            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li });
   1939 //        }
   1940         startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
   1941     }
   1942 
   1943     /**
   1944      * Registers various content observers. The current implementation registers
   1945      * only a favorites observer to keep track of the favorites applications.
   1946      */
   1947     private void registerContentObservers() {
   1948         ContentResolver resolver = getContentResolver();
   1949         resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
   1950                 true, mWidgetObserver);
   1951     }
   1952 
   1953     @Override
   1954     public boolean dispatchKeyEvent(KeyEvent event) {
   1955         if (event.getAction() == KeyEvent.ACTION_DOWN) {
   1956             switch (event.getKeyCode()) {
   1957                 case KeyEvent.KEYCODE_HOME:
   1958                     return true;
   1959                 case KeyEvent.KEYCODE_VOLUME_DOWN:
   1960                     if (isPropertyEnabled(DUMP_STATE_PROPERTY)) {
   1961                         dumpState();
   1962                         return true;
   1963                     }
   1964                     break;
   1965             }
   1966         } else if (event.getAction() == KeyEvent.ACTION_UP) {
   1967             switch (event.getKeyCode()) {
   1968                 case KeyEvent.KEYCODE_HOME:
   1969                     return true;
   1970             }
   1971         }
   1972 
   1973         return super.dispatchKeyEvent(event);
   1974     }
   1975 
   1976     @Override
   1977     public void onBackPressed() {
   1978         if (isAllAppsVisible()) {
   1979             showWorkspace(true);
   1980         } else if (mWorkspace.getOpenFolder() != null) {
   1981             Folder openFolder = mWorkspace.getOpenFolder();
   1982             if (openFolder.isEditingName()) {
   1983                 openFolder.dismissEditingName();
   1984             } else {
   1985                 closeFolder();
   1986             }
   1987         } else {
   1988             mWorkspace.exitWidgetResizeMode();
   1989 
   1990             // Back button is a no-op here, but give at least some feedback for the button press
   1991             mWorkspace.showOutlinesTemporarily();
   1992         }
   1993     }
   1994 
   1995     /**
   1996      * Re-listen when widgets are reset.
   1997      */
   1998     private void onAppWidgetReset() {
   1999         if (mAppWidgetHost != null) {
   2000             mAppWidgetHost.startListening();
   2001         }
   2002     }
   2003 
   2004     /**
   2005      * Launches the intent referred by the clicked shortcut.
   2006      *
   2007      * @param v The view representing the clicked shortcut.
   2008      */
   2009     public void onClick(View v) {
   2010         // Make sure that rogue clicks don't get through while allapps is launching, or after the
   2011         // view has detached (it's possible for this to happen if the view is removed mid touch).
   2012         if (v.getWindowToken() == null) {
   2013             return;
   2014         }
   2015 
   2016         if (!mWorkspace.isFinishedSwitchingState()) {
   2017             return;
   2018         }
   2019 
   2020         Object tag = v.getTag();
   2021         if (tag instanceof ShortcutInfo) {
   2022             // Open shortcut
   2023             final Intent intent = ((ShortcutInfo) tag).intent;
   2024             int[] pos = new int[2];
   2025             v.getLocationOnScreen(pos);
   2026             intent.setSourceBounds(new Rect(pos[0], pos[1],
   2027                     pos[0] + v.getWidth(), pos[1] + v.getHeight()));
   2028 
   2029             boolean success = startActivitySafely(v, intent, tag);
   2030 
   2031             if (success && v instanceof BubbleTextView) {
   2032                 mWaitingForResume = (BubbleTextView) v;
   2033                 mWaitingForResume.setStayPressed(true);
   2034             }
   2035         } else if (tag instanceof FolderInfo) {
   2036             if (v instanceof FolderIcon) {
   2037                 FolderIcon fi = (FolderIcon) v;
   2038                 handleFolderClick(fi);
   2039             }
   2040         } else if (v == mAllAppsButton) {
   2041             if (isAllAppsVisible()) {
   2042                 showWorkspace(true);
   2043             } else {
   2044                 onClickAllAppsButton(v);
   2045             }
   2046         }
   2047     }
   2048 
   2049     public boolean onTouch(View v, MotionEvent event) {
   2050         // this is an intercepted event being forwarded from mWorkspace;
   2051         // clicking anywhere on the workspace causes the customization drawer to slide down
   2052         showWorkspace(true);
   2053         return false;
   2054     }
   2055 
   2056     /**
   2057      * Event handler for the search button
   2058      *
   2059      * @param v The view that was clicked.
   2060      */
   2061     public void onClickSearchButton(View v) {
   2062         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2063 
   2064         onSearchRequested();
   2065     }
   2066 
   2067     /**
   2068      * Event handler for the voice button
   2069      *
   2070      * @param v The view that was clicked.
   2071      */
   2072     public void onClickVoiceButton(View v) {
   2073         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2074 
   2075         try {
   2076             final SearchManager searchManager =
   2077                     (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   2078             ComponentName activityName = searchManager.getGlobalSearchActivity();
   2079             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   2080             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2081             if (activityName != null) {
   2082                 intent.setPackage(activityName.getPackageName());
   2083             }
   2084             startActivity(null, intent, "onClickVoiceButton");
   2085         } catch (ActivityNotFoundException e) {
   2086             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   2087             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2088             startActivitySafely(null, intent, "onClickVoiceButton");
   2089         }
   2090     }
   2091 
   2092     /**
   2093      * Event handler for the "grid" button that appears on the home screen, which
   2094      * enters all apps mode.
   2095      *
   2096      * @param v The view that was clicked.
   2097      */
   2098     public void onClickAllAppsButton(View v) {
   2099         showAllApps(true);
   2100     }
   2101 
   2102     public void onTouchDownAllAppsButton(View v) {
   2103         // Provide the same haptic feedback that the system offers for virtual keys.
   2104         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2105     }
   2106 
   2107     public void onClickAppMarketButton(View v) {
   2108         if (mAppMarketIntent != null) {
   2109             startActivitySafely(v, mAppMarketIntent, "app market");
   2110         } else {
   2111             Log.e(TAG, "Invalid app market intent.");
   2112         }
   2113     }
   2114 
   2115     void startApplicationDetailsActivity(ComponentName componentName, UserHandle user) {
   2116         LauncherApps launcherApps = (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE);
   2117         try {
   2118             launcherApps.startAppDetailsActivity(componentName, user, null, null);
   2119         } catch (SecurityException e) {
   2120             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2121             Log.e(TAG, "Launcher does not have permission to launch settings");
   2122         } catch (ActivityNotFoundException e) {
   2123             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2124             Log.e(TAG, "Unable to launch settings");
   2125         }
   2126     }
   2127 
   2128     void startApplicationUninstallActivity(ApplicationInfo appInfo, UserHandle user) {
   2129         if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) == 0) {
   2130             // System applications cannot be installed. For now, show a toast explaining that.
   2131             // We may give them the option of disabling apps this way.
   2132             int messageId = R.string.uninstall_system_app_text;
   2133             Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
   2134         } else {
   2135             String packageName = appInfo.componentName.getPackageName();
   2136             String className = appInfo.componentName.getClassName();
   2137             Intent intent = new Intent(
   2138                     Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
   2139             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
   2140                     Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   2141             if (user != null) {
   2142                 intent.putExtra(Intent.EXTRA_USER, user);
   2143             }
   2144             startActivity(intent);
   2145         }
   2146     }
   2147 
   2148     boolean startActivity(View v, Intent intent, Object tag) {
   2149         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2150 
   2151         try {
   2152             // Only launch using the new animation if the shortcut has not opted out (this is a
   2153             // private contract between launcher and may be ignored in the future).
   2154             boolean useLaunchAnimation = (v != null) &&
   2155                     !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
   2156             UserHandle user = (UserHandle) intent.getParcelableExtra(ApplicationInfo.EXTRA_PROFILE);
   2157             LauncherApps launcherApps = (LauncherApps)
   2158                     this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
   2159             if (useLaunchAnimation) {
   2160                 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
   2161                         v.getMeasuredWidth(), v.getMeasuredHeight());
   2162                 if (user == null || user.equals(android.os.Process.myUserHandle())) {
   2163                     // Could be launching some bookkeeping activity
   2164                     startActivity(intent, opts.toBundle());
   2165                 } else {
   2166                     launcherApps.startMainActivity(intent.getComponent(), user,
   2167                             intent.getSourceBounds(),
   2168                             opts.toBundle());
   2169                 }
   2170             } else {
   2171                 if (user == null || user.equals(android.os.Process.myUserHandle())) {
   2172                     startActivity(intent);
   2173                 } else {
   2174                     launcherApps.startMainActivity(intent.getComponent(), user,
   2175                             intent.getSourceBounds(), null);
   2176                 }
   2177             }
   2178             return true;
   2179         } catch (SecurityException e) {
   2180             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2181             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   2182                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   2183                     "or use the exported attribute for this activity. "
   2184                     + "tag="+ tag + " intent=" + intent, e);
   2185         }
   2186         return false;
   2187     }
   2188 
   2189     boolean startActivitySafely(View v, Intent intent, Object tag) {
   2190         boolean success = false;
   2191         try {
   2192             success = startActivity(v, intent, tag);
   2193         } catch (ActivityNotFoundException e) {
   2194             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2195             Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
   2196         }
   2197         return success;
   2198     }
   2199 
   2200     void startAppWidgetConfigureActivitySafely(int appWidgetId) {
   2201         try {
   2202             mAppWidgetHost.startAppWidgetConfigureActivityForResult(this, appWidgetId, 0,
   2203                     REQUEST_CREATE_APPWIDGET, null);
   2204         } catch (ActivityNotFoundException e) {
   2205             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2206         }
   2207     }
   2208 
   2209     void startActivityForResultSafely(Intent intent, int requestCode) {
   2210         try {
   2211             startActivityForResult(intent, requestCode);
   2212         } catch (ActivityNotFoundException e) {
   2213             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2214         } catch (SecurityException e) {
   2215             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2216             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   2217                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   2218                     "or use the exported attribute for this activity.", e);
   2219         }
   2220     }
   2221 
   2222     private void handleFolderClick(FolderIcon folderIcon) {
   2223         final FolderInfo info = folderIcon.getFolderInfo();
   2224         Folder openFolder = mWorkspace.getFolderForTag(info);
   2225 
   2226         // If the folder info reports that the associated folder is open, then verify that
   2227         // it is actually opened. There have been a few instances where this gets out of sync.
   2228         if (info.opened && openFolder == null) {
   2229             Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
   2230                     + info.screen + " (" + info.cellX + ", " + info.cellY + ")");
   2231             info.opened = false;
   2232         }
   2233 
   2234         if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
   2235             // Close any open folder
   2236             closeFolder();
   2237             // Open the requested folder
   2238             openFolder(folderIcon);
   2239         } else {
   2240             // Find the open folder...
   2241             int folderScreen;
   2242             if (openFolder != null) {
   2243                 folderScreen = mWorkspace.getPageForView(openFolder);
   2244                 // .. and close it
   2245                 closeFolder(openFolder);
   2246                 if (folderScreen != mWorkspace.getCurrentPage()) {
   2247                     // Close any folder open on the current screen
   2248                     closeFolder();
   2249                     // Pull the folder onto this screen
   2250                     openFolder(folderIcon);
   2251                 }
   2252             }
   2253         }
   2254     }
   2255 
   2256     /**
   2257      * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
   2258      * in the DragLayer in the exact absolute location of the original FolderIcon.
   2259      */
   2260     private void copyFolderIconToImage(FolderIcon fi) {
   2261         final int width = fi.getMeasuredWidth();
   2262         final int height = fi.getMeasuredHeight();
   2263 
   2264         // Lazy load ImageView, Bitmap and Canvas
   2265         if (mFolderIconImageView == null) {
   2266             mFolderIconImageView = new ImageView(this);
   2267         }
   2268         if (mFolderIconBitmap == null || mFolderIconBitmap.getWidth() != width ||
   2269                 mFolderIconBitmap.getHeight() != height) {
   2270             mFolderIconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
   2271             mFolderIconCanvas = new Canvas(mFolderIconBitmap);
   2272         }
   2273 
   2274         DragLayer.LayoutParams lp;
   2275         if (mFolderIconImageView.getLayoutParams() instanceof DragLayer.LayoutParams) {
   2276             lp = (DragLayer.LayoutParams) mFolderIconImageView.getLayoutParams();
   2277         } else {
   2278             lp = new DragLayer.LayoutParams(width, height);
   2279         }
   2280 
   2281         // The layout from which the folder is being opened may be scaled, adjust the starting
   2282         // view size by this scale factor.
   2283         float scale = mDragLayer.getDescendantRectRelativeToSelf(fi, mRectForFolderAnimation);
   2284         lp.customPosition = true;
   2285         lp.x = mRectForFolderAnimation.left;
   2286         lp.y = mRectForFolderAnimation.top;
   2287         lp.width = (int) (scale * width);
   2288         lp.height = (int) (scale * height);
   2289 
   2290         mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
   2291         fi.draw(mFolderIconCanvas);
   2292         mFolderIconImageView.setImageBitmap(mFolderIconBitmap);
   2293         if (fi.getFolder() != null) {
   2294             mFolderIconImageView.setPivotX(fi.getFolder().getPivotXForIconAnimation());
   2295             mFolderIconImageView.setPivotY(fi.getFolder().getPivotYForIconAnimation());
   2296         }
   2297         // Just in case this image view is still in the drag layer from a previous animation,
   2298         // we remove it and re-add it.
   2299         if (mDragLayer.indexOfChild(mFolderIconImageView) != -1) {
   2300             mDragLayer.removeView(mFolderIconImageView);
   2301         }
   2302         mDragLayer.addView(mFolderIconImageView, lp);
   2303         if (fi.getFolder() != null) {
   2304             fi.getFolder().bringToFront();
   2305         }
   2306     }
   2307 
   2308     private void growAndFadeOutFolderIcon(FolderIcon fi) {
   2309         if (fi == null) return;
   2310         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
   2311         PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f);
   2312         PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f);
   2313 
   2314         FolderInfo info = (FolderInfo) fi.getTag();
   2315         if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   2316             CellLayout cl = (CellLayout) fi.getParent().getParent();
   2317             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) fi.getLayoutParams();
   2318             cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
   2319         }
   2320 
   2321         // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
   2322         copyFolderIconToImage(fi);
   2323         fi.setVisibility(View.INVISIBLE);
   2324 
   2325         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
   2326                 scaleX, scaleY);
   2327         oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
   2328         oa.start();
   2329     }
   2330 
   2331     private void shrinkAndFadeInFolderIcon(final FolderIcon fi) {
   2332         if (fi == null) return;
   2333         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
   2334         PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
   2335         PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
   2336 
   2337         final CellLayout cl = (CellLayout) fi.getParent().getParent();
   2338 
   2339         // We remove and re-draw the FolderIcon in-case it has changed
   2340         mDragLayer.removeView(mFolderIconImageView);
   2341         copyFolderIconToImage(fi);
   2342         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
   2343                 scaleX, scaleY);
   2344         oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
   2345         oa.addListener(new AnimatorListenerAdapter() {
   2346             @Override
   2347             public void onAnimationEnd(Animator animation) {
   2348                 if (cl != null) {
   2349                     cl.clearFolderLeaveBehind();
   2350                     // Remove the ImageView copy of the FolderIcon and make the original visible.
   2351                     mDragLayer.removeView(mFolderIconImageView);
   2352                     fi.setVisibility(View.VISIBLE);
   2353                 }
   2354             }
   2355         });
   2356         oa.start();
   2357     }
   2358 
   2359     /**
   2360      * Opens the user folder described by the specified tag. The opening of the folder
   2361      * is animated relative to the specified View. If the View is null, no animation
   2362      * is played.
   2363      *
   2364      * @param folderInfo The FolderInfo describing the folder to open.
   2365      */
   2366     public void openFolder(FolderIcon folderIcon) {
   2367         Folder folder = folderIcon.getFolder();
   2368         FolderInfo info = folder.mInfo;
   2369 
   2370         info.opened = true;
   2371 
   2372         // Just verify that the folder hasn't already been added to the DragLayer.
   2373         // There was a one-off crash where the folder had a parent already.
   2374         if (folder.getParent() == null) {
   2375             mDragLayer.addView(folder);
   2376             mDragController.addDropTarget((DropTarget) folder);
   2377         } else {
   2378             Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
   2379                     folder.getParent() + ").");
   2380         }
   2381         folder.animateOpen();
   2382         growAndFadeOutFolderIcon(folderIcon);
   2383 
   2384         // Notify the accessibility manager that this folder "window" has appeared and occluded
   2385         // the workspace items
   2386         folder.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   2387         getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
   2388     }
   2389 
   2390     public void closeFolder() {
   2391         Folder folder = mWorkspace.getOpenFolder();
   2392         if (folder != null) {
   2393             if (folder.isEditingName()) {
   2394                 folder.dismissEditingName();
   2395             }
   2396             closeFolder(folder);
   2397 
   2398             // Dismiss the folder cling
   2399             dismissFolderCling(null);
   2400         }
   2401     }
   2402 
   2403     void closeFolder(Folder folder) {
   2404         folder.getInfo().opened = false;
   2405 
   2406         ViewGroup parent = (ViewGroup) folder.getParent().getParent();
   2407         if (parent != null) {
   2408             FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
   2409             shrinkAndFadeInFolderIcon(fi);
   2410         }
   2411         folder.animateClosed();
   2412 
   2413         // Notify the accessibility manager that this folder "window" has disappeard and no
   2414         // longer occludeds the workspace items
   2415         getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   2416     }
   2417 
   2418     public boolean onLongClick(View v) {
   2419         if (!isDraggingEnabled()) return false;
   2420         if (isWorkspaceLocked()) return false;
   2421         if (mState != State.WORKSPACE) return false;
   2422 
   2423         if (!(v instanceof CellLayout)) {
   2424             v = (View) v.getParent().getParent();
   2425         }
   2426 
   2427         resetAddInfo();
   2428         CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag();
   2429         // This happens when long clicking an item with the dpad/trackball
   2430         if (longClickCellInfo == null) {
   2431             return true;
   2432         }
   2433 
   2434         // The hotseat touch handling does not go through Workspace, and we always allow long press
   2435         // on hotseat items.
   2436         final View itemUnderLongClick = longClickCellInfo.cell;
   2437         boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress();
   2438         if (allowLongPress && !mDragController.isDragging()) {
   2439             if (itemUnderLongClick == null) {
   2440                 // User long pressed on empty space
   2441                 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   2442                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   2443                 startWallpaper();
   2444             } else {
   2445                 if (!(itemUnderLongClick instanceof Folder)) {
   2446                     // User long pressed on an item
   2447                     mWorkspace.startDrag(longClickCellInfo);
   2448                 }
   2449             }
   2450         }
   2451         return true;
   2452     }
   2453 
   2454     boolean isHotseatLayout(View layout) {
   2455         return mHotseat != null && layout != null &&
   2456                 (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
   2457     }
   2458     Hotseat getHotseat() {
   2459         return mHotseat;
   2460     }
   2461     SearchDropTargetBar getSearchBar() {
   2462         return mSearchDropTargetBar;
   2463     }
   2464 
   2465     /**
   2466      * Returns the CellLayout of the specified container at the specified screen.
   2467      */
   2468     CellLayout getCellLayout(long container, int screen) {
   2469         if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   2470             if (mHotseat != null) {
   2471                 return mHotseat.getLayout();
   2472             } else {
   2473                 return null;
   2474             }
   2475         } else {
   2476             return (CellLayout) mWorkspace.getChildAt(screen);
   2477         }
   2478     }
   2479 
   2480     Workspace getWorkspace() {
   2481         return mWorkspace;
   2482     }
   2483 
   2484     // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
   2485     @Override
   2486     public boolean isAllAppsVisible() {
   2487         return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
   2488     }
   2489 
   2490     @Override
   2491     public boolean isAllAppsButtonRank(int rank) {
   2492         return mHotseat.isAllAppsButtonRank(rank);
   2493     }
   2494 
   2495     /**
   2496      * Helper method for the cameraZoomIn/cameraZoomOut animations
   2497      * @param view The view being animated
   2498      * @param scaleFactor The scale factor used for the zoom
   2499      */
   2500     private void setPivotsForZoom(View view, float scaleFactor) {
   2501         view.setPivotX(view.getWidth() / 2.0f);
   2502         view.setPivotY(view.getHeight() / 2.0f);
   2503     }
   2504 
   2505     void disableWallpaperIfInAllApps() {
   2506         // Only disable it if we are in all apps
   2507         if (isAllAppsVisible()) {
   2508             if (mAppsCustomizeTabHost != null &&
   2509                     !mAppsCustomizeTabHost.isTransitioning()) {
   2510                 updateWallpaperVisibility(false);
   2511             }
   2512         }
   2513     }
   2514 
   2515     private void setWorkspaceBackground(boolean workspace) {
   2516         mLauncherView.setBackground(workspace ?
   2517                 mWorkspaceBackgroundDrawable : null);
   2518     }
   2519 
   2520     void updateWallpaperVisibility(boolean visible) {
   2521         int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
   2522         int curflags = getWindow().getAttributes().flags
   2523                 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
   2524         if (wpflags != curflags) {
   2525             getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
   2526         }
   2527         setWorkspaceBackground(visible);
   2528     }
   2529 
   2530     private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
   2531         if (v instanceof LauncherTransitionable) {
   2532             ((LauncherTransitionable) v).onLauncherTransitionPrepare(this, animated, toWorkspace);
   2533         }
   2534     }
   2535 
   2536     private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
   2537         if (v instanceof LauncherTransitionable) {
   2538             ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace);
   2539         }
   2540 
   2541         // Update the workspace transition step as well
   2542         dispatchOnLauncherTransitionStep(v, 0f);
   2543     }
   2544 
   2545     private void dispatchOnLauncherTransitionStep(View v, float t) {
   2546         if (v instanceof LauncherTransitionable) {
   2547             ((LauncherTransitionable) v).onLauncherTransitionStep(this, t);
   2548         }
   2549     }
   2550 
   2551     private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
   2552         if (v instanceof LauncherTransitionable) {
   2553             ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace);
   2554         }
   2555 
   2556         // Update the workspace transition step as well
   2557         dispatchOnLauncherTransitionStep(v, 1f);
   2558     }
   2559 
   2560     /**
   2561      * Things to test when changing the following seven functions.
   2562      *   - Home from workspace
   2563      *          - from center screen
   2564      *          - from other screens
   2565      *   - Home from all apps
   2566      *          - from center screen
   2567      *          - from other screens
   2568      *   - Back from all apps
   2569      *          - from center screen
   2570      *          - from other screens
   2571      *   - Launch app from workspace and quit
   2572      *          - with back
   2573      *          - with home
   2574      *   - Launch app from all apps and quit
   2575      *          - with back
   2576      *          - with home
   2577      *   - Go to a screen that's not the default, then all
   2578      *     apps, and launch and app, and go back
   2579      *          - with back
   2580      *          -with home
   2581      *   - On workspace, long press power and go back
   2582      *          - with back
   2583      *          - with home
   2584      *   - On all apps, long press power and go back
   2585      *          - with back
   2586      *          - with home
   2587      *   - On workspace, power off
   2588      *   - On all apps, power off
   2589      *   - Launch an app and turn off the screen while in that app
   2590      *          - Go back with home key
   2591      *          - Go back with back key  TODO: make this not go to workspace
   2592      *          - From all apps
   2593      *          - From workspace
   2594      *   - Enter and exit car mode (becuase it causes an extra configuration changed)
   2595      *          - From all apps
   2596      *          - From the center workspace
   2597      *          - From another workspace
   2598      */
   2599 
   2600     /**
   2601      * Zoom the camera out from the workspace to reveal 'toView'.
   2602      * Assumes that the view to show is anchored at either the very top or very bottom
   2603      * of the screen.
   2604      */
   2605     private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) {
   2606         if (mStateAnimation != null) {
   2607             mStateAnimation.setDuration(0);
   2608             mStateAnimation.cancel();
   2609             mStateAnimation = null;
   2610         }
   2611         final Resources res = getResources();
   2612 
   2613         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
   2614         final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
   2615         final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
   2616         final View fromView = mWorkspace;
   2617         final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
   2618         final int startDelay =
   2619                 res.getInteger(R.integer.config_workspaceAppsCustomizeAnimationStagger);
   2620 
   2621         setPivotsForZoom(toView, scale);
   2622 
   2623         // Shrink workspaces away if going to AppsCustomize from workspace
   2624         Animator workspaceAnim =
   2625                 mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
   2626 
   2627         if (animated) {
   2628             toView.setScaleX(scale);
   2629             toView.setScaleY(scale);
   2630             final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView);
   2631             scaleAnim.
   2632                 scaleX(1f).scaleY(1f).
   2633                 setDuration(duration).
   2634                 setInterpolator(new Workspace.ZoomOutInterpolator());
   2635 
   2636             toView.setVisibility(View.VISIBLE);
   2637             toView.setAlpha(0f);
   2638             final ObjectAnimator alphaAnim = LauncherAnimUtils
   2639                 .ofFloat(toView, "alpha", 0f, 1f)
   2640                 .setDuration(fadeDuration);
   2641             alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
   2642             alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
   2643                 @Override
   2644                 public void onAnimationUpdate(ValueAnimator animation) {
   2645                     if (animation == null) {
   2646                         throw new RuntimeException("animation is null");
   2647                     }
   2648                     float t = (Float) animation.getAnimatedValue();
   2649                     dispatchOnLauncherTransitionStep(fromView, t);
   2650                     dispatchOnLauncherTransitionStep(toView, t);
   2651                 }
   2652             });
   2653 
   2654             // toView should appear right at the end of the workspace shrink
   2655             // animation
   2656             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
   2657             mStateAnimation.play(scaleAnim).after(startDelay);
   2658             mStateAnimation.play(alphaAnim).after(startDelay);
   2659 
   2660             mStateAnimation.addListener(new AnimatorListenerAdapter() {
   2661                 boolean animationCancelled = false;
   2662 
   2663                 @Override
   2664                 public void onAnimationStart(Animator animation) {
   2665                     updateWallpaperVisibility(true);
   2666                     // Prepare the position
   2667                     toView.setTranslationX(0.0f);
   2668                     toView.setTranslationY(0.0f);
   2669                     toView.setVisibility(View.VISIBLE);
   2670                     toView.bringToFront();
   2671                 }
   2672                 @Override
   2673                 public void onAnimationEnd(Animator animation) {
   2674                     dispatchOnLauncherTransitionEnd(fromView, animated, false);
   2675                     dispatchOnLauncherTransitionEnd(toView, animated, false);
   2676 
   2677                     if (mWorkspace != null && !springLoaded && !LauncherApplication.isScreenLarge()) {
   2678                         // Hide the workspace scrollbar
   2679                         mWorkspace.hideScrollingIndicator(true);
   2680                         hideDockDivider();
   2681                     }
   2682                     if (!animationCancelled) {
   2683                         updateWallpaperVisibility(false);
   2684                     }
   2685 
   2686                     // Hide the search bar
   2687                     if (mSearchDropTargetBar != null) {
   2688                         mSearchDropTargetBar.hideSearchBar(false);
   2689                     }
   2690                 }
   2691 
   2692                 @Override
   2693                 public void onAnimationCancel(Animator animation) {
   2694                     animationCancelled = true;
   2695                 }
   2696             });
   2697 
   2698             if (workspaceAnim != null) {
   2699                 mStateAnimation.play(workspaceAnim);
   2700             }
   2701 
   2702             boolean delayAnim = false;
   2703 
   2704             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
   2705             dispatchOnLauncherTransitionPrepare(toView, animated, false);
   2706 
   2707             // If any of the objects being animated haven't been measured/laid out
   2708             // yet, delay the animation until we get a layout pass
   2709             if ((((LauncherTransitionable) toView).getContent().getMeasuredWidth() == 0) ||
   2710                     (mWorkspace.getMeasuredWidth() == 0) ||
   2711                     (toView.getMeasuredWidth() == 0)) {
   2712                 delayAnim = true;
   2713             }
   2714 
   2715             final AnimatorSet stateAnimation = mStateAnimation;
   2716             final Runnable startAnimRunnable = new Runnable() {
   2717                 public void run() {
   2718                     // Check that mStateAnimation hasn't changed while
   2719                     // we waited for a layout/draw pass
   2720                     if (mStateAnimation != stateAnimation)
   2721                         return;
   2722                     setPivotsForZoom(toView, scale);
   2723                     dispatchOnLauncherTransitionStart(fromView, animated, false);
   2724                     dispatchOnLauncherTransitionStart(toView, animated, false);
   2725                     LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView);
   2726                 }
   2727             };
   2728             if (delayAnim) {
   2729                 final ViewTreeObserver observer = toView.getViewTreeObserver();
   2730                 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
   2731                         public void onGlobalLayout() {
   2732                             startAnimRunnable.run();
   2733                             toView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
   2734                         }
   2735                     });
   2736             } else {
   2737                 startAnimRunnable.run();
   2738             }
   2739         } else {
   2740             toView.setTranslationX(0.0f);
   2741             toView.setTranslationY(0.0f);
   2742             toView.setScaleX(1.0f);
   2743             toView.setScaleY(1.0f);
   2744             toView.setVisibility(View.VISIBLE);
   2745             toView.bringToFront();
   2746 
   2747             if (!springLoaded && !LauncherApplication.isScreenLarge()) {
   2748                 // Hide the workspace scrollbar
   2749                 mWorkspace.hideScrollingIndicator(true);
   2750                 hideDockDivider();
   2751 
   2752                 // Hide the search bar
   2753                 if (mSearchDropTargetBar != null) {
   2754                     mSearchDropTargetBar.hideSearchBar(false);
   2755                 }
   2756             }
   2757             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
   2758             dispatchOnLauncherTransitionStart(fromView, animated, false);
   2759             dispatchOnLauncherTransitionEnd(fromView, animated, false);
   2760             dispatchOnLauncherTransitionPrepare(toView, animated, false);
   2761             dispatchOnLauncherTransitionStart(toView, animated, false);
   2762             dispatchOnLauncherTransitionEnd(toView, animated, false);
   2763             updateWallpaperVisibility(false);
   2764         }
   2765     }
   2766 
   2767     /**
   2768      * Zoom the camera back into the workspace, hiding 'fromView'.
   2769      * This is the opposite of showAppsCustomizeHelper.
   2770      * @param animated If true, the transition will be animated.
   2771      */
   2772     private void hideAppsCustomizeHelper(State toState, final boolean animated,
   2773             final boolean springLoaded, final Runnable onCompleteRunnable) {
   2774 
   2775         if (mStateAnimation != null) {
   2776             mStateAnimation.setDuration(0);
   2777             mStateAnimation.cancel();
   2778             mStateAnimation = null;
   2779         }
   2780         Resources res = getResources();
   2781 
   2782         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
   2783         final int fadeOutDuration =
   2784                 res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
   2785         final float scaleFactor = (float)
   2786                 res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
   2787         final View fromView = mAppsCustomizeTabHost;
   2788         final View toView = mWorkspace;
   2789         Animator workspaceAnim = null;
   2790 
   2791         if (toState == State.WORKSPACE) {
   2792             int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
   2793             workspaceAnim = mWorkspace.getChangeStateAnimation(
   2794                     Workspace.State.NORMAL, animated, stagger);
   2795         } else if (toState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
   2796             workspaceAnim = mWorkspace.getChangeStateAnimation(
   2797                     Workspace.State.SPRING_LOADED, animated);
   2798         }
   2799 
   2800         setPivotsForZoom(fromView, scaleFactor);
   2801         updateWallpaperVisibility(true);
   2802         showHotseat(animated);
   2803         if (animated) {
   2804             final LauncherViewPropertyAnimator scaleAnim =
   2805                     new LauncherViewPropertyAnimator(fromView);
   2806             scaleAnim.
   2807                 scaleX(scaleFactor).scaleY(scaleFactor).
   2808                 setDuration(duration).
   2809                 setInterpolator(new Workspace.ZoomInInterpolator());
   2810 
   2811             final ObjectAnimator alphaAnim = LauncherAnimUtils
   2812                 .ofFloat(fromView, "alpha", 1f, 0f)
   2813                 .setDuration(fadeOutDuration);
   2814             alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
   2815             alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
   2816                 @Override
   2817                 public void onAnimationUpdate(ValueAnimator animation) {
   2818                     float t = 1f - (Float) animation.getAnimatedValue();
   2819                     dispatchOnLauncherTransitionStep(fromView, t);
   2820                     dispatchOnLauncherTransitionStep(toView, t);
   2821                 }
   2822             });
   2823 
   2824             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
   2825 
   2826             dispatchOnLauncherTransitionPrepare(fromView, animated, true);
   2827             dispatchOnLauncherTransitionPrepare(toView, animated, true);
   2828             mAppsCustomizeContent.pauseScrolling();
   2829 
   2830             mStateAnimation.addListener(new AnimatorListenerAdapter() {
   2831                 @Override
   2832                 public void onAnimationEnd(Animator animation) {
   2833                     updateWallpaperVisibility(true);
   2834                     fromView.setVisibility(View.GONE);
   2835                     dispatchOnLauncherTransitionEnd(fromView, animated, true);
   2836                     dispatchOnLauncherTransitionEnd(toView, animated, true);
   2837                     if (mWorkspace != null) {
   2838                         mWorkspace.hideScrollingIndicator(false);
   2839                     }
   2840                     if (onCompleteRunnable != null) {
   2841                         onCompleteRunnable.run();
   2842                     }
   2843                     mAppsCustomizeContent.updateCurrentPageScroll();
   2844                     mAppsCustomizeContent.resumeScrolling();
   2845                 }
   2846             });
   2847 
   2848             mStateAnimation.playTogether(scaleAnim, alphaAnim);
   2849             if (workspaceAnim != null) {
   2850                 mStateAnimation.play(workspaceAnim);
   2851             }
   2852             dispatchOnLauncherTransitionStart(fromView, animated, true);
   2853             dispatchOnLauncherTransitionStart(toView, animated, true);
   2854             LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView);
   2855         } else {
   2856             fromView.setVisibility(View.GONE);
   2857             dispatchOnLauncherTransitionPrepare(fromView, animated, true);
   2858             dispatchOnLauncherTransitionStart(fromView, animated, true);
   2859             dispatchOnLauncherTransitionEnd(fromView, animated, true);
   2860             dispatchOnLauncherTransitionPrepare(toView, animated, true);
   2861             dispatchOnLauncherTransitionStart(toView, animated, true);
   2862             dispatchOnLauncherTransitionEnd(toView, animated, true);
   2863             mWorkspace.hideScrollingIndicator(false);
   2864         }
   2865     }
   2866 
   2867     @Override
   2868     public void onTrimMemory(int level) {
   2869         super.onTrimMemory(level);
   2870         if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
   2871             mAppsCustomizeTabHost.onTrimMemory();
   2872         }
   2873     }
   2874 
   2875     @Override
   2876     public void onWindowFocusChanged(boolean hasFocus) {
   2877         if (!hasFocus) {
   2878             // When another window occludes launcher (like the notification shade, or recents),
   2879             // ensure that we enable the wallpaper flag so that transitions are done correctly.
   2880             updateWallpaperVisibility(true);
   2881         } else {
   2882             // When launcher has focus again, disable the wallpaper if we are in AllApps
   2883             mWorkspace.postDelayed(new Runnable() {
   2884                 @Override
   2885                 public void run() {
   2886                     disableWallpaperIfInAllApps();
   2887                 }
   2888             }, 500);
   2889         }
   2890     }
   2891 
   2892     void showWorkspace(boolean animated) {
   2893         showWorkspace(animated, null);
   2894     }
   2895 
   2896     void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
   2897         if (mState != State.WORKSPACE) {
   2898             boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED);
   2899             mWorkspace.setVisibility(View.VISIBLE);
   2900             hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);
   2901 
   2902             // Show the search bar (only animate if we were showing the drop target bar in spring
   2903             // loaded mode)
   2904             if (mSearchDropTargetBar != null) {
   2905                 mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode);
   2906             }
   2907 
   2908             // We only need to animate in the dock divider if we're going from spring loaded mode
   2909             showDockDivider(animated && wasInSpringLoadedMode);
   2910 
   2911             // Set focus to the AppsCustomize button
   2912             if (mAllAppsButton != null) {
   2913                 mAllAppsButton.requestFocus();
   2914             }
   2915         }
   2916 
   2917         mWorkspace.flashScrollingIndicator(animated);
   2918 
   2919         // Change the state *after* we've called all the transition code
   2920         mState = State.WORKSPACE;
   2921 
   2922         // Resume the auto-advance of widgets
   2923         mUserPresent = true;
   2924         updateRunning();
   2925 
   2926         // Send an accessibility event to announce the context change
   2927         getWindow().getDecorView()
   2928                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   2929     }
   2930 
   2931     void showAllApps(boolean animated) {
   2932         if (mState != State.WORKSPACE) return;
   2933 
   2934         showAppsCustomizeHelper(animated, false);
   2935         mAppsCustomizeTabHost.requestFocus();
   2936 
   2937         // Change the state *after* we've called all the transition code
   2938         mState = State.APPS_CUSTOMIZE;
   2939 
   2940         // Pause the auto-advance of widgets until we are out of AllApps
   2941         mUserPresent = false;
   2942         updateRunning();
   2943         closeFolder();
   2944 
   2945         // Send an accessibility event to announce the context change
   2946         getWindow().getDecorView()
   2947                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   2948     }
   2949 
   2950     void enterSpringLoadedDragMode() {
   2951         if (isAllAppsVisible()) {
   2952             hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true, null);
   2953             hideDockDivider();
   2954             mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
   2955         }
   2956     }
   2957 
   2958     void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay,
   2959             final Runnable onCompleteRunnable) {
   2960         if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
   2961 
   2962         mHandler.postDelayed(new Runnable() {
   2963             @Override
   2964             public void run() {
   2965                 if (successfulDrop) {
   2966                     // Before we show workspace, hide all apps again because
   2967                     // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
   2968                     // clean up our state transition functions
   2969                     mAppsCustomizeTabHost.setVisibility(View.GONE);
   2970                     showWorkspace(true, onCompleteRunnable);
   2971                 } else {
   2972                     exitSpringLoadedDragMode();
   2973                 }
   2974             }
   2975         }, (extendedDelay ?
   2976                 EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT :
   2977                 EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT));
   2978     }
   2979 
   2980     void exitSpringLoadedDragMode() {
   2981         if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
   2982             final boolean animated = true;
   2983             final boolean springLoaded = true;
   2984             showAppsCustomizeHelper(animated, springLoaded);
   2985             mState = State.APPS_CUSTOMIZE;
   2986         }
   2987         // Otherwise, we are not in spring loaded mode, so don't do anything.
   2988     }
   2989 
   2990     void hideDockDivider() {
   2991         if (mQsbDivider != null && mDockDivider != null) {
   2992             mQsbDivider.setVisibility(View.INVISIBLE);
   2993             mDockDivider.setVisibility(View.INVISIBLE);
   2994         }
   2995     }
   2996 
   2997     void showDockDivider(boolean animated) {
   2998         if (mQsbDivider != null && mDockDivider != null) {
   2999             mQsbDivider.setVisibility(View.VISIBLE);
   3000             mDockDivider.setVisibility(View.VISIBLE);
   3001             if (mDividerAnimator != null) {
   3002                 mDividerAnimator.cancel();
   3003                 mQsbDivider.setAlpha(1f);
   3004                 mDockDivider.setAlpha(1f);
   3005                 mDividerAnimator = null;
   3006             }
   3007             if (animated) {
   3008                 mDividerAnimator = LauncherAnimUtils.createAnimatorSet();
   3009                 mDividerAnimator.playTogether(LauncherAnimUtils.ofFloat(mQsbDivider, "alpha", 1f),
   3010                         LauncherAnimUtils.ofFloat(mDockDivider, "alpha", 1f));
   3011                 int duration = 0;
   3012                 if (mSearchDropTargetBar != null) {
   3013                     duration = mSearchDropTargetBar.getTransitionInDuration();
   3014                 }
   3015                 mDividerAnimator.setDuration(duration);
   3016                 mDividerAnimator.start();
   3017             }
   3018         }
   3019     }
   3020 
   3021     void lockAllApps() {
   3022         // TODO
   3023     }
   3024 
   3025     void unlockAllApps() {
   3026         // TODO
   3027     }
   3028 
   3029     /**
   3030      * Shows the hotseat area.
   3031      */
   3032     void showHotseat(boolean animated) {
   3033         if (!LauncherApplication.isScreenLarge()) {
   3034             if (animated) {
   3035                 if (mHotseat.getAlpha() != 1f) {
   3036                     int duration = 0;
   3037                     if (mSearchDropTargetBar != null) {
   3038                         duration = mSearchDropTargetBar.getTransitionInDuration();
   3039                     }
   3040                     mHotseat.animate().alpha(1f).setDuration(duration);
   3041                 }
   3042             } else {
   3043                 mHotseat.setAlpha(1f);
   3044             }
   3045         }
   3046     }
   3047 
   3048     /**
   3049      * Hides the hotseat area.
   3050      */
   3051     void hideHotseat(boolean animated) {
   3052         if (!LauncherApplication.isScreenLarge()) {
   3053             if (animated) {
   3054                 if (mHotseat.getAlpha() != 0f) {
   3055                     int duration = 0;
   3056                     if (mSearchDropTargetBar != null) {
   3057                         duration = mSearchDropTargetBar.getTransitionOutDuration();
   3058                     }
   3059                     mHotseat.animate().alpha(0f).setDuration(duration);
   3060                 }
   3061             } else {
   3062                 mHotseat.setAlpha(0f);
   3063             }
   3064         }
   3065     }
   3066 
   3067     /**
   3068      * Add an item from all apps or customize onto the given workspace screen.
   3069      * If layout is null, add to the current screen.
   3070      */
   3071     void addExternalItemToScreen(ItemInfo itemInfo, final CellLayout layout) {
   3072         if (!mWorkspace.addExternalItemToScreen(itemInfo, layout)) {
   3073             showOutOfSpaceMessage(isHotseatLayout(layout));
   3074         }
   3075     }
   3076 
   3077     /** Maps the current orientation to an index for referencing orientation correct global icons */
   3078     private int getCurrentOrientationIndexForGlobalIcons() {
   3079         // default - 0, landscape - 1
   3080         switch (getResources().getConfiguration().orientation) {
   3081         case Configuration.ORIENTATION_LANDSCAPE:
   3082             return 1;
   3083         default:
   3084             return 0;
   3085         }
   3086     }
   3087 
   3088     private Drawable getExternalPackageToolbarIcon(ComponentName activityName, String resourceName) {
   3089         try {
   3090             PackageManager packageManager = getPackageManager();
   3091             // Look for the toolbar icon specified in the activity meta-data
   3092             Bundle metaData = packageManager.getActivityInfo(
   3093                     activityName, PackageManager.GET_META_DATA).metaData;
   3094             if (metaData != null) {
   3095                 int iconResId = metaData.getInt(resourceName);
   3096                 if (iconResId != 0) {
   3097                     Resources res = packageManager.getResourcesForActivity(activityName);
   3098                     return res.getDrawable(iconResId);
   3099                 }
   3100             }
   3101         } catch (NameNotFoundException e) {
   3102             // This can happen if the activity defines an invalid drawable
   3103             Log.w(TAG, "Failed to load toolbar icon; " + activityName.flattenToShortString() +
   3104                     " not found", e);
   3105         } catch (Resources.NotFoundException nfe) {
   3106             // This can happen if the activity defines an invalid drawable
   3107             Log.w(TAG, "Failed to load toolbar icon from " + activityName.flattenToShortString(),
   3108                     nfe);
   3109         }
   3110         return null;
   3111     }
   3112 
   3113     // if successful in getting icon, return it; otherwise, set button to use default drawable
   3114     private Drawable.ConstantState updateTextButtonWithIconFromExternalActivity(
   3115             int buttonId, ComponentName activityName, int fallbackDrawableId,
   3116             String toolbarResourceName) {
   3117         Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName);
   3118         Resources r = getResources();
   3119         int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
   3120         int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
   3121 
   3122         TextView button = (TextView) findViewById(buttonId);
   3123         // If we were unable to find the icon via the meta-data, use a generic one
   3124         if (toolbarIcon == null) {
   3125             toolbarIcon = r.getDrawable(fallbackDrawableId);
   3126             toolbarIcon.setBounds(0, 0, w, h);
   3127             if (button != null) {
   3128                 button.setCompoundDrawables(toolbarIcon, null, null, null);
   3129             }
   3130             return null;
   3131         } else {
   3132             toolbarIcon.setBounds(0, 0, w, h);
   3133             if (button != null) {
   3134                 button.setCompoundDrawables(toolbarIcon, null, null, null);
   3135             }
   3136             return toolbarIcon.getConstantState();
   3137         }
   3138     }
   3139 
   3140     // if successful in getting icon, return it; otherwise, set button to use default drawable
   3141     private Drawable.ConstantState updateButtonWithIconFromExternalActivity(
   3142             int buttonId, ComponentName activityName, int fallbackDrawableId,
   3143             String toolbarResourceName) {
   3144         ImageView button = (ImageView) findViewById(buttonId);
   3145         Drawable toolbarIcon = getExternalPackageToolbarIcon(activityName, toolbarResourceName);
   3146 
   3147         if (button != null) {
   3148             // If we were unable to find the icon via the meta-data, use a
   3149             // generic one
   3150             if (toolbarIcon == null) {
   3151                 button.setImageResource(fallbackDrawableId);
   3152             } else {
   3153                 button.setImageDrawable(toolbarIcon);
   3154             }
   3155         }
   3156 
   3157         return toolbarIcon != null ? toolbarIcon.getConstantState() : null;
   3158 
   3159     }
   3160 
   3161     private void updateTextButtonWithDrawable(int buttonId, Drawable d) {
   3162         TextView button = (TextView) findViewById(buttonId);
   3163         button.setCompoundDrawables(d, null, null, null);
   3164     }
   3165 
   3166     private void updateButtonWithDrawable(int buttonId, Drawable.ConstantState d) {
   3167         ImageView button = (ImageView) findViewById(buttonId);
   3168         button.setImageDrawable(d.newDrawable(getResources()));
   3169     }
   3170 
   3171     private void invalidatePressedFocusedStates(View container, View button) {
   3172         if (container instanceof HolographicLinearLayout) {
   3173             HolographicLinearLayout layout = (HolographicLinearLayout) container;
   3174             layout.invalidatePressedFocusedStates();
   3175         } else if (button instanceof HolographicImageView) {
   3176             HolographicImageView view = (HolographicImageView) button;
   3177             view.invalidatePressedFocusedStates();
   3178         }
   3179     }
   3180 
   3181     private boolean updateGlobalSearchIcon() {
   3182         final View searchButtonContainer = findViewById(R.id.search_button_container);
   3183         final ImageView searchButton = (ImageView) findViewById(R.id.search_button);
   3184         final View voiceButtonContainer = findViewById(R.id.voice_button_container);
   3185         final View voiceButton = findViewById(R.id.voice_button);
   3186         final View voiceButtonProxy = findViewById(R.id.voice_button_proxy);
   3187 
   3188         final SearchManager searchManager =
   3189                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   3190         ComponentName activityName = searchManager.getGlobalSearchActivity();
   3191         if (activityName != null) {
   3192             int coi = getCurrentOrientationIndexForGlobalIcons();
   3193             sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   3194                     R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo,
   3195                     TOOLBAR_SEARCH_ICON_METADATA_NAME);
   3196             if (sGlobalSearchIcon[coi] == null) {
   3197                 sGlobalSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   3198                         R.id.search_button, activityName, R.drawable.ic_home_search_normal_holo,
   3199                         TOOLBAR_ICON_METADATA_NAME);
   3200             }
   3201 
   3202             if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.VISIBLE);
   3203             searchButton.setVisibility(View.VISIBLE);
   3204             invalidatePressedFocusedStates(searchButtonContainer, searchButton);
   3205             return true;
   3206         } else {
   3207             // We disable both search and voice search when there is no global search provider
   3208             if (searchButtonContainer != null) searchButtonContainer.setVisibility(View.GONE);
   3209             if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE);
   3210             searchButton.setVisibility(View.GONE);
   3211             voiceButton.setVisibility(View.GONE);
   3212             if (voiceButtonProxy != null) {
   3213                 voiceButtonProxy.setVisibility(View.GONE);
   3214             }
   3215             return false;
   3216         }
   3217     }
   3218 
   3219     private void updateGlobalSearchIcon(Drawable.ConstantState d) {
   3220         final View searchButtonContainer = findViewById(R.id.search_button_container);
   3221         final View searchButton = (ImageView) findViewById(R.id.search_button);
   3222         updateButtonWithDrawable(R.id.search_button, d);
   3223         invalidatePressedFocusedStates(searchButtonContainer, searchButton);
   3224     }
   3225 
   3226     private boolean updateVoiceSearchIcon(boolean searchVisible) {
   3227         final View voiceButtonContainer = findViewById(R.id.voice_button_container);
   3228         final View voiceButton = findViewById(R.id.voice_button);
   3229         final View voiceButtonProxy = findViewById(R.id.voice_button_proxy);
   3230 
   3231         // We only show/update the voice search icon if the search icon is enabled as well
   3232         final SearchManager searchManager =
   3233                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   3234         ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
   3235 
   3236         ComponentName activityName = null;
   3237         if (globalSearchActivity != null) {
   3238             // Check if the global search activity handles voice search
   3239             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   3240             intent.setPackage(globalSearchActivity.getPackageName());
   3241             activityName = intent.resolveActivity(getPackageManager());
   3242         }
   3243 
   3244         if (activityName == null) {
   3245             // Fallback: check if an activity other than the global search activity
   3246             // resolves this
   3247             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
   3248             activityName = intent.resolveActivity(getPackageManager());
   3249         }
   3250         if (searchVisible && activityName != null) {
   3251             int coi = getCurrentOrientationIndexForGlobalIcons();
   3252             sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   3253                     R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo,
   3254                     TOOLBAR_VOICE_SEARCH_ICON_METADATA_NAME);
   3255             if (sVoiceSearchIcon[coi] == null) {
   3256                 sVoiceSearchIcon[coi] = updateButtonWithIconFromExternalActivity(
   3257                         R.id.voice_button, activityName, R.drawable.ic_home_voice_search_holo,
   3258                         TOOLBAR_ICON_METADATA_NAME);
   3259             }
   3260             if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.VISIBLE);
   3261             voiceButton.setVisibility(View.VISIBLE);
   3262             if (voiceButtonProxy != null) {
   3263                 voiceButtonProxy.setVisibility(View.VISIBLE);
   3264             }
   3265             invalidatePressedFocusedStates(voiceButtonContainer, voiceButton);
   3266             return true;
   3267         } else {
   3268             if (voiceButtonContainer != null) voiceButtonContainer.setVisibility(View.GONE);
   3269             voiceButton.setVisibility(View.GONE);
   3270             if (voiceButtonProxy != null) {
   3271                 voiceButtonProxy.setVisibility(View.GONE);
   3272             }
   3273             return false;
   3274         }
   3275     }
   3276 
   3277     private void updateVoiceSearchIcon(Drawable.ConstantState d) {
   3278         final View voiceButtonContainer = findViewById(R.id.voice_button_container);
   3279         final View voiceButton = findViewById(R.id.voice_button);
   3280         updateButtonWithDrawable(R.id.voice_button, d);
   3281         invalidatePressedFocusedStates(voiceButtonContainer, voiceButton);
   3282     }
   3283 
   3284     /**
   3285      * Sets the app market icon
   3286      */
   3287     private void updateAppMarketIcon() {
   3288         final View marketButton = findViewById(R.id.market_button);
   3289         Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
   3290         // Find the app market activity by resolving an intent.
   3291         // (If multiple app markets are installed, it will return the ResolverActivity.)
   3292         ComponentName activityName = intent.resolveActivity(getPackageManager());
   3293         if (activityName != null) {
   3294             int coi = getCurrentOrientationIndexForGlobalIcons();
   3295             mAppMarketIntent = intent;
   3296             sAppMarketIcon[coi] = updateTextButtonWithIconFromExternalActivity(
   3297                     R.id.market_button, activityName, R.drawable.ic_launcher_market_holo,
   3298                     TOOLBAR_ICON_METADATA_NAME);
   3299             marketButton.setVisibility(View.VISIBLE);
   3300         } else {
   3301             // We should hide and disable the view so that we don't try and restore the visibility
   3302             // of it when we swap between drag & normal states from IconDropTarget subclasses.
   3303             marketButton.setVisibility(View.GONE);
   3304             marketButton.setEnabled(false);
   3305         }
   3306     }
   3307 
   3308     private void updateAppMarketIcon(Drawable.ConstantState d) {
   3309         // Ensure that the new drawable we are creating has the approprate toolbar icon bounds
   3310         Resources r = getResources();
   3311         Drawable marketIconDrawable = d.newDrawable(r);
   3312         int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
   3313         int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
   3314         marketIconDrawable.setBounds(0, 0, w, h);
   3315 
   3316         updateTextButtonWithDrawable(R.id.market_button, marketIconDrawable);
   3317     }
   3318 
   3319     @Override
   3320     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
   3321         final boolean result = super.dispatchPopulateAccessibilityEvent(event);
   3322         final List<CharSequence> text = event.getText();
   3323         text.clear();
   3324         // Populate event with a fake title based on the current state.
   3325         if (mState == State.APPS_CUSTOMIZE) {
   3326             text.add(getString(R.string.all_apps_button_label));
   3327         } else {
   3328             text.add(getString(R.string.all_apps_home_button_label));
   3329         }
   3330         return result;
   3331     }
   3332 
   3333     /**
   3334      * Receives notifications when system dialogs are to be closed.
   3335      */
   3336     private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
   3337         @Override
   3338         public void onReceive(Context context, Intent intent) {
   3339             closeSystemDialogs();
   3340         }
   3341     }
   3342 
   3343     /**
   3344      * Receives notifications whenever the appwidgets are reset.
   3345      */
   3346     private class AppWidgetResetObserver extends ContentObserver {
   3347         public AppWidgetResetObserver() {
   3348             super(new Handler());
   3349         }
   3350 
   3351         @Override
   3352         public void onChange(boolean selfChange) {
   3353             onAppWidgetReset();
   3354         }
   3355     }
   3356 
   3357     /**
   3358      * If the activity is currently paused, signal that we need to run the passed Runnable
   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     private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) {
   3372         if (mPaused) {
   3373             Log.i(TAG, "Deferring update until onResume");
   3374             if (deletePreviousRunnables) {
   3375                 while (mOnResumeCallbacks.remove(run)) {
   3376                 }
   3377             }
   3378             mOnResumeCallbacks.add(run);
   3379             return true;
   3380         } else {
   3381             return false;
   3382         }
   3383     }
   3384 
   3385     private boolean waitUntilResume(Runnable run) {
   3386         return waitUntilResume(run, false);
   3387     }
   3388 
   3389     /**
   3390      * If the activity is currently paused, signal that we need to re-run the loader
   3391      * in onResume.
   3392      *
   3393      * This needs to be called from incoming places where resources might have been loaded
   3394      * while we are paused.  That is becaues the Configuration might be wrong
   3395      * when we're not running, and if it comes back to what it was when we
   3396      * were paused, we are not restarted.
   3397      *
   3398      * Implementation of the method from LauncherModel.Callbacks.
   3399      *
   3400      * @return true if we are currently paused.  The caller might be able to
   3401      * skip some work in that case since we will come back again.
   3402      */
   3403     public boolean setLoadOnResume() {
   3404         if (mPaused) {
   3405             Log.i(TAG, "setLoadOnResume");
   3406             mOnResumeNeedsLoad = true;
   3407             return true;
   3408         } else {
   3409             return false;
   3410         }
   3411     }
   3412 
   3413     /**
   3414      * Implementation of the method from LauncherModel.Callbacks.
   3415      */
   3416     public int getCurrentWorkspaceScreen() {
   3417         if (mWorkspace != null) {
   3418             return mWorkspace.getCurrentPage();
   3419         } else {
   3420             return SCREEN_COUNT / 2;
   3421         }
   3422     }
   3423 
   3424     /**
   3425      * Refreshes the shortcuts shown on the workspace.
   3426      *
   3427      * Implementation of the method from LauncherModel.Callbacks.
   3428      */
   3429     public void startBinding() {
   3430         // If we're starting binding all over again, clear any bind calls we'd postponed in
   3431         // the past (see waitUntilResume) -- we don't need them since we're starting binding
   3432         // from scratch again
   3433         mOnResumeCallbacks.clear();
   3434 
   3435         final Workspace workspace = mWorkspace;
   3436         mNewShortcutAnimatePage = -1;
   3437         mNewShortcutAnimateViews.clear();
   3438         mWorkspace.clearDropTargets();
   3439         int count = workspace.getChildCount();
   3440         for (int i = 0; i < count; i++) {
   3441             // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
   3442             final CellLayout layoutParent = (CellLayout) workspace.getChildAt(i);
   3443             layoutParent.removeAllViewsInLayout();
   3444         }
   3445         mWidgetsToAdvance.clear();
   3446         if (mHotseat != null) {
   3447             mHotseat.resetLayout();
   3448         }
   3449     }
   3450 
   3451     /**
   3452      * Bind the items start-end from the list.
   3453      *
   3454      * Implementation of the method from LauncherModel.Callbacks.
   3455      */
   3456     public void bindItems(final ArrayList<ItemInfo> shortcuts, final int start, final int end) {
   3457         if (waitUntilResume(new Runnable() {
   3458                 public void run() {
   3459                     bindItems(shortcuts, start, end);
   3460                 }
   3461             })) {
   3462             return;
   3463         }
   3464 
   3465         // Get the list of added shortcuts and intersect them with the set of shortcuts here
   3466         Set<String> newApps = new HashSet<String>();
   3467         newApps = mSharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps);
   3468 
   3469         Workspace workspace = mWorkspace;
   3470         for (int i = start; i < end; i++) {
   3471             final ItemInfo item = shortcuts.get(i);
   3472 
   3473             // Short circuit if we are loading dock items for a configuration which has no dock
   3474             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
   3475                     mHotseat == null) {
   3476                 continue;
   3477             }
   3478 
   3479             switch (item.itemType) {
   3480                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   3481                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   3482                     ShortcutInfo info = (ShortcutInfo) item;
   3483                     String uri = info.intent.toUri(0).toString();
   3484                     View shortcut = createShortcut(info);
   3485                     workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
   3486                             item.cellY, 1, 1, false);
   3487                     boolean animateIconUp = false;
   3488                     synchronized (newApps) {
   3489                         if (newApps.contains(uri)) {
   3490                             animateIconUp = newApps.remove(uri);
   3491                         }
   3492                     }
   3493                     if (animateIconUp) {
   3494                         // Prepare the view to be animated up
   3495                         shortcut.setAlpha(0f);
   3496                         shortcut.setScaleX(0f);
   3497                         shortcut.setScaleY(0f);
   3498                         mNewShortcutAnimatePage = item.screen;
   3499                         if (!mNewShortcutAnimateViews.contains(shortcut)) {
   3500                             mNewShortcutAnimateViews.add(shortcut);
   3501                         }
   3502                     }
   3503                     break;
   3504                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
   3505                     FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
   3506                             (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
   3507                             (FolderInfo) item, mIconCache);
   3508                     workspace.addInScreen(newFolder, item.container, item.screen, item.cellX,
   3509                             item.cellY, 1, 1, false);
   3510                     break;
   3511             }
   3512         }
   3513 
   3514         workspace.requestLayout();
   3515     }
   3516 
   3517     /**
   3518      * Implementation of the method from LauncherModel.Callbacks.
   3519      */
   3520     public void bindFolders(final HashMap<Long, FolderInfo> folders) {
   3521         if (waitUntilResume(new Runnable() {
   3522                 public void run() {
   3523                     bindFolders(folders);
   3524                 }
   3525             })) {
   3526             return;
   3527         }
   3528         sFolders.clear();
   3529         sFolders.putAll(folders);
   3530     }
   3531 
   3532     /**
   3533      * Add the views for a widget to the workspace.
   3534      *
   3535      * Implementation of the method from LauncherModel.Callbacks.
   3536      */
   3537     public void bindAppWidget(final LauncherAppWidgetInfo item) {
   3538         if (waitUntilResume(new Runnable() {
   3539                 public void run() {
   3540                     bindAppWidget(item);
   3541                 }
   3542             })) {
   3543             return;
   3544         }
   3545 
   3546         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
   3547         if (DEBUG_WIDGETS) {
   3548             Log.d(TAG, "bindAppWidget: " + item);
   3549         }
   3550         final Workspace workspace = mWorkspace;
   3551 
   3552         final int appWidgetId = item.appWidgetId;
   3553         final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   3554         if (DEBUG_WIDGETS) {
   3555             Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
   3556         }
   3557 
   3558         item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   3559 
   3560         item.hostView.setTag(item);
   3561         item.onBindAppWidget(this);
   3562 
   3563         workspace.addInScreen(item.hostView, item.container, item.screen, item.cellX,
   3564                 item.cellY, item.spanX, item.spanY, false);
   3565         addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
   3566 
   3567         workspace.requestLayout();
   3568 
   3569         if (DEBUG_WIDGETS) {
   3570             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
   3571                     + (SystemClock.uptimeMillis()-start) + "ms");
   3572         }
   3573     }
   3574 
   3575     public void onPageBoundSynchronously(int page) {
   3576         mSynchronouslyBoundPages.add(page);
   3577     }
   3578 
   3579     /**
   3580      * Callback saying that there aren't any more items to bind.
   3581      *
   3582      * Implementation of the method from LauncherModel.Callbacks.
   3583      */
   3584     public void finishBindingItems() {
   3585         if (waitUntilResume(new Runnable() {
   3586                 public void run() {
   3587                     finishBindingItems();
   3588                 }
   3589             })) {
   3590             return;
   3591         }
   3592         if (mSavedState != null) {
   3593             if (!mWorkspace.hasFocus()) {
   3594                 mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
   3595             }
   3596             mSavedState = null;
   3597         }
   3598 
   3599         mWorkspace.restoreInstanceStateForRemainingPages();
   3600 
   3601         // If we received the result of any pending adds while the loader was running (e.g. the
   3602         // widget configuration forced an orientation change), process them now.
   3603         for (int i = 0; i < sPendingAddList.size(); i++) {
   3604             completeAdd(sPendingAddList.get(i));
   3605         }
   3606         sPendingAddList.clear();
   3607 
   3608         // Update the market app icon as necessary (the other icons will be managed in response to
   3609         // package changes in bindSearchablesChanged()
   3610         updateAppMarketIcon();
   3611 
   3612         // Animate up any icons as necessary
   3613         if (mVisible || mWorkspaceLoading) {
   3614             Runnable newAppsRunnable = new Runnable() {
   3615                 @Override
   3616                 public void run() {
   3617                     runNewAppsAnimation(false);
   3618                 }
   3619             };
   3620 
   3621             boolean willSnapPage = mNewShortcutAnimatePage > -1 &&
   3622                     mNewShortcutAnimatePage != mWorkspace.getCurrentPage();
   3623             if (canRunNewAppsAnimation()) {
   3624                 // If the user has not interacted recently, then either snap to the new page to show
   3625                 // the new-apps animation or just run them if they are to appear on the current page
   3626                 if (willSnapPage) {
   3627                     mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable);
   3628                 } else {
   3629                     runNewAppsAnimation(false);
   3630                 }
   3631             } else {
   3632                 // If the user has interacted recently, then just add the items in place if they
   3633                 // are on another page (or just normally if they are added to the current page)
   3634                 runNewAppsAnimation(willSnapPage);
   3635             }
   3636         }
   3637 
   3638         mWorkspaceLoading = false;
   3639     }
   3640 
   3641     private boolean canRunNewAppsAnimation() {
   3642         long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
   3643         return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
   3644     }
   3645 
   3646     /**
   3647      * Runs a new animation that scales up icons that were added while Launcher was in the
   3648      * background.
   3649      *
   3650      * @param immediate whether to run the animation or show the results immediately
   3651      */
   3652     private void runNewAppsAnimation(boolean immediate) {
   3653         AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
   3654         Collection<Animator> bounceAnims = new ArrayList<Animator>();
   3655 
   3656         // Order these new views spatially so that they animate in order
   3657         Collections.sort(mNewShortcutAnimateViews, new Comparator<View>() {
   3658             @Override
   3659             public int compare(View a, View b) {
   3660                 CellLayout.LayoutParams alp = (CellLayout.LayoutParams) a.getLayoutParams();
   3661                 CellLayout.LayoutParams blp = (CellLayout.LayoutParams) b.getLayoutParams();
   3662                 int cellCountX = LauncherModel.getCellCountX();
   3663                 return (alp.cellY * cellCountX + alp.cellX) - (blp.cellY * cellCountX + blp.cellX);
   3664             }
   3665         });
   3666 
   3667         // Animate each of the views in place (or show them immediately if requested)
   3668         if (immediate) {
   3669             for (View v : mNewShortcutAnimateViews) {
   3670                 v.setAlpha(1f);
   3671                 v.setScaleX(1f);
   3672                 v.setScaleY(1f);
   3673             }
   3674         } else {
   3675             for (int i = 0; i < mNewShortcutAnimateViews.size(); ++i) {
   3676                 View v = mNewShortcutAnimateViews.get(i);
   3677                 ValueAnimator bounceAnim = LauncherAnimUtils.ofPropertyValuesHolder(v,
   3678                         PropertyValuesHolder.ofFloat("alpha", 1f),
   3679                         PropertyValuesHolder.ofFloat("scaleX", 1f),
   3680                         PropertyValuesHolder.ofFloat("scaleY", 1f));
   3681                 bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
   3682                 bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
   3683                 bounceAnim.setInterpolator(new SmoothPagedView.OvershootInterpolator());
   3684                 bounceAnims.add(bounceAnim);
   3685             }
   3686             anim.playTogether(bounceAnims);
   3687             anim.addListener(new AnimatorListenerAdapter() {
   3688                 @Override
   3689                 public void onAnimationEnd(Animator animation) {
   3690                     if (mWorkspace != null) {
   3691                         mWorkspace.postDelayed(mBuildLayersRunnable, 500);
   3692                     }
   3693                 }
   3694             });
   3695             anim.start();
   3696         }
   3697 
   3698         // Clean up
   3699         mNewShortcutAnimatePage = -1;
   3700         mNewShortcutAnimateViews.clear();
   3701         new Thread("clearNewAppsThread") {
   3702             public void run() {
   3703                 mSharedPrefs.edit()
   3704                             .putInt(InstallShortcutReceiver.NEW_APPS_PAGE_KEY, -1)
   3705                             .putStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, null)
   3706                             .commit();
   3707             }
   3708         }.start();
   3709     }
   3710 
   3711     @Override
   3712     public void bindSearchablesChanged() {
   3713         boolean searchVisible = updateGlobalSearchIcon();
   3714         boolean voiceVisible = updateVoiceSearchIcon(searchVisible);
   3715         if (mSearchDropTargetBar != null) {
   3716             mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
   3717         }
   3718     }
   3719 
   3720     /**
   3721      * Add the icons for all apps.
   3722      *
   3723      * Implementation of the method from LauncherModel.Callbacks.
   3724      */
   3725     public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {
   3726         Runnable setAllAppsRunnable = new Runnable() {
   3727             public void run() {
   3728                 if (mAppsCustomizeContent != null) {
   3729                     mAppsCustomizeContent.setApps(apps);
   3730                 }
   3731             }
   3732         };
   3733 
   3734         // Remove the progress bar entirely; we could also make it GONE
   3735         // but better to remove it since we know it's not going to be used
   3736         View progressBar = mAppsCustomizeTabHost.
   3737             findViewById(R.id.apps_customize_progress_bar);
   3738         if (progressBar != null) {
   3739             ((ViewGroup)progressBar.getParent()).removeView(progressBar);
   3740 
   3741             // We just post the call to setApps so the user sees the progress bar
   3742             // disappear-- otherwise, it just looks like the progress bar froze
   3743             // which doesn't look great
   3744             mAppsCustomizeTabHost.post(setAllAppsRunnable);
   3745         } else {
   3746             // If we did not initialize the spinner in onCreate, then we can directly set the
   3747             // list of applications without waiting for any progress bars views to be hidden.
   3748             setAllAppsRunnable.run();
   3749         }
   3750     }
   3751 
   3752     /**
   3753      * A package was installed.
   3754      *
   3755      * Implementation of the method from LauncherModel.Callbacks.
   3756      */
   3757     public void bindAppsAdded(final ArrayList<ApplicationInfo> apps) {
   3758         if (waitUntilResume(new Runnable() {
   3759                 public void run() {
   3760                     bindAppsAdded(apps);
   3761                 }
   3762             })) {
   3763             return;
   3764         }
   3765 
   3766 
   3767         if (mAppsCustomizeContent != null) {
   3768             mAppsCustomizeContent.addApps(apps);
   3769         }
   3770     }
   3771 
   3772     /**
   3773      * A package was updated.
   3774      *
   3775      * Implementation of the method from LauncherModel.Callbacks.
   3776      */
   3777     public void bindAppsUpdated(final ArrayList<ApplicationInfo> apps) {
   3778         if (waitUntilResume(new Runnable() {
   3779                 public void run() {
   3780                     bindAppsUpdated(apps);
   3781                 }
   3782             })) {
   3783             return;
   3784         }
   3785 
   3786         if (mWorkspace != null) {
   3787             mWorkspace.updateShortcuts(apps);
   3788         }
   3789 
   3790         if (mAppsCustomizeContent != null) {
   3791             mAppsCustomizeContent.updateApps(apps);
   3792         }
   3793     }
   3794 
   3795     /**
   3796      * A package was uninstalled.  We take both the super set of packageNames
   3797      * in addition to specific applications to remove, the reason being that
   3798      * this can be called when a package is updated as well.  In that scenario,
   3799      * we only remove specific components from the workspace, where as
   3800      * package-removal should clear all items by package name.
   3801      *
   3802      * Implementation of the method from LauncherModel.Callbacks.
   3803      */
   3804     public void bindComponentsRemoved(final ArrayList<String> packageNames,
   3805                                       final ArrayList<ApplicationInfo> appInfos,
   3806             final boolean matchPackageNamesOnly, final UserHandle user) {
   3807         if (waitUntilResume(new Runnable() {
   3808             public void run() {
   3809                 bindComponentsRemoved(packageNames, appInfos, matchPackageNamesOnly, user);
   3810             }
   3811         })) {
   3812             return;
   3813         }
   3814 
   3815         if (matchPackageNamesOnly) {
   3816             mWorkspace.removeItemsByPackageName(packageNames, user);
   3817         } else {
   3818             mWorkspace.removeItemsByApplicationInfo(appInfos, user);
   3819         }
   3820 
   3821         if (mAppsCustomizeContent != null) {
   3822             mAppsCustomizeContent.removeApps(appInfos);
   3823         }
   3824 
   3825         // Notify the drag controller
   3826         mDragController.onAppsRemoved(appInfos, this);
   3827     }
   3828 
   3829     /**
   3830      * A number of packages were updated.
   3831      */
   3832 
   3833     private ArrayList<Object> mWidgetsAndShortcuts;
   3834     private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
   3835             public void run() {
   3836                 bindPackagesUpdated(mWidgetsAndShortcuts);
   3837                 mWidgetsAndShortcuts = null;
   3838             }
   3839         };
   3840 
   3841     public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) {
   3842         if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
   3843             mWidgetsAndShortcuts = widgetsAndShortcuts;
   3844             return;
   3845         }
   3846 
   3847         if (mAppsCustomizeContent != null) {
   3848             mAppsCustomizeContent.onPackagesUpdated(widgetsAndShortcuts);
   3849         }
   3850     }
   3851 
   3852     private int mapConfigurationOriActivityInfoOri(int configOri) {
   3853         final Display d = getWindowManager().getDefaultDisplay();
   3854         int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
   3855         switch (d.getRotation()) {
   3856         case Surface.ROTATION_0:
   3857         case Surface.ROTATION_180:
   3858             // We are currently in the same basic orientation as the natural orientation
   3859             naturalOri = configOri;
   3860             break;
   3861         case Surface.ROTATION_90:
   3862         case Surface.ROTATION_270:
   3863             // We are currently in the other basic orientation to the natural orientation
   3864             naturalOri = (configOri == Configuration.ORIENTATION_LANDSCAPE) ?
   3865                     Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
   3866             break;
   3867         }
   3868 
   3869         int[] oriMap = {
   3870                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
   3871                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
   3872                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
   3873                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
   3874         };
   3875         // Since the map starts at portrait, we need to offset if this device's natural orientation
   3876         // is landscape.
   3877         int indexOffset = 0;
   3878         if (naturalOri == Configuration.ORIENTATION_LANDSCAPE) {
   3879             indexOffset = 1;
   3880         }
   3881         return oriMap[(d.getRotation() + indexOffset) % 4];
   3882     }
   3883 
   3884     public boolean isRotationEnabled() {
   3885         boolean enableRotation = sForceEnableRotation ||
   3886                 getResources().getBoolean(R.bool.allow_rotation);
   3887         return enableRotation;
   3888     }
   3889     public void lockScreenOrientation() {
   3890         if (isRotationEnabled()) {
   3891             setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
   3892                     .getConfiguration().orientation));
   3893         }
   3894     }
   3895     public void unlockScreenOrientation(boolean immediate) {
   3896         if (isRotationEnabled()) {
   3897             if (immediate) {
   3898                 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   3899             } else {
   3900                 mHandler.postDelayed(new Runnable() {
   3901                     public void run() {
   3902                         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   3903                     }
   3904                 }, mRestoreScreenOrientationDelay);
   3905             }
   3906         }
   3907     }
   3908 
   3909     /* Cling related */
   3910     private boolean isClingsEnabled() {
   3911         // disable clings when running in a test harness
   3912         if(ActivityManager.isRunningInTestHarness()) return false;
   3913 
   3914         // Restricted secondary users (child mode) will potentially have very few apps
   3915         // seeded when they start up for the first time. Clings won't work well with that
   3916         boolean supportsLimitedUsers =
   3917                 android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
   3918         Account[] accounts = AccountManager.get(this).getAccounts();
   3919         if (supportsLimitedUsers && accounts.length == 0) {
   3920             UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
   3921             Bundle restrictions = um.getUserRestrictions();
   3922             if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
   3923                return false;
   3924             }
   3925         }
   3926         // Check if the system has requested skipping of first-use hints.
   3927         if (Settings.Secure.getInt(getContentResolver(),
   3928                 Settings.Secure.SKIP_FIRST_USE_HINTS, 0) == 1) {
   3929             return false;
   3930         }
   3931         return true;
   3932     }
   3933 
   3934     private Cling initCling(int clingId, int[] positionData, boolean animate, int delay) {
   3935         final Cling cling = (Cling) findViewById(clingId);
   3936         if (cling != null) {
   3937             cling.init(this, positionData);
   3938             cling.setVisibility(View.VISIBLE);
   3939             cling.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3940             if (animate) {
   3941                 cling.buildLayer();
   3942                 cling.setAlpha(0f);
   3943                 cling.animate()
   3944                     .alpha(1f)
   3945                     .setInterpolator(new AccelerateInterpolator())
   3946                     .setDuration(SHOW_CLING_DURATION)
   3947                     .setStartDelay(delay)
   3948                     .start();
   3949             } else {
   3950                 cling.setAlpha(1f);
   3951             }
   3952             cling.setFocusableInTouchMode(true);
   3953             cling.post(new Runnable() {
   3954                 public void run() {
   3955                     cling.setFocusable(true);
   3956                     cling.requestFocus();
   3957                 }
   3958             });
   3959             mHideFromAccessibilityHelper.setImportantForAccessibilityToNo(
   3960                     mDragLayer, clingId == R.id.all_apps_cling);
   3961         }
   3962         return cling;
   3963     }
   3964 
   3965     private void dismissCling(final Cling cling, final String flag, int duration) {
   3966         // To catch cases where siblings of top-level views are made invisible, just check whether
   3967         // the cling is directly set to GONE before dismissing it.
   3968         if (cling != null && cling.getVisibility() != View.GONE) {
   3969             ObjectAnimator anim = LauncherAnimUtils.ofFloat(cling, "alpha", 0f);
   3970             anim.setDuration(duration);
   3971             anim.addListener(new AnimatorListenerAdapter() {
   3972                 public void onAnimationEnd(Animator animation) {
   3973                     cling.setVisibility(View.GONE);
   3974                     cling.cleanup();
   3975                     // We should update the shared preferences on a background thread
   3976                     new Thread("dismissClingThread") {
   3977                         public void run() {
   3978                             SharedPreferences.Editor editor = mSharedPrefs.edit();
   3979                             editor.putBoolean(flag, true);
   3980                             editor.commit();
   3981                         }
   3982                     }.start();
   3983                 };
   3984             });
   3985             anim.start();
   3986             mHideFromAccessibilityHelper.restoreImportantForAccessibility(mDragLayer);
   3987         }
   3988     }
   3989 
   3990     private void removeCling(int id) {
   3991         final View cling = findViewById(id);
   3992         if (cling != null) {
   3993             final ViewGroup parent = (ViewGroup) cling.getParent();
   3994             parent.post(new Runnable() {
   3995                 @Override
   3996                 public void run() {
   3997                     parent.removeView(cling);
   3998                 }
   3999             });
   4000             mHideFromAccessibilityHelper.restoreImportantForAccessibility(mDragLayer);
   4001         }
   4002     }
   4003 
   4004     private boolean skipCustomClingIfNoAccounts() {
   4005         Cling cling = (Cling) findViewById(R.id.workspace_cling);
   4006         boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
   4007         if (customCling) {
   4008             AccountManager am = AccountManager.get(this);
   4009             Account[] accounts = am.getAccountsByType("com.google");
   4010             return accounts.length == 0;
   4011         }
   4012         return false;
   4013     }
   4014 
   4015     public void showFirstRunWorkspaceCling() {
   4016         // Enable the clings only if they have not been dismissed before
   4017         if (isClingsEnabled() &&
   4018                 !mSharedPrefs.getBoolean(Cling.WORKSPACE_CLING_DISMISSED_KEY, false) &&
   4019                 !skipCustomClingIfNoAccounts() ) {
   4020             // If we're not using the default workspace layout, replace workspace cling
   4021             // with a custom workspace cling (usually specified in an overlay)
   4022             // For now, only do this on tablets
   4023             if (mSharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 &&
   4024                     getResources().getBoolean(R.bool.config_useCustomClings)) {
   4025                 // Use a custom cling
   4026                 View cling = findViewById(R.id.workspace_cling);
   4027                 ViewGroup clingParent = (ViewGroup) cling.getParent();
   4028                 int clingIndex = clingParent.indexOfChild(cling);
   4029                 clingParent.removeViewAt(clingIndex);
   4030                 View customCling = mInflater.inflate(R.layout.custom_workspace_cling, clingParent, false);
   4031                 clingParent.addView(customCling, clingIndex);
   4032                 customCling.setId(R.id.workspace_cling);
   4033             }
   4034             initCling(R.id.workspace_cling, null, false, 0);
   4035         } else {
   4036             removeCling(R.id.workspace_cling);
   4037         }
   4038     }
   4039     public void showFirstRunAllAppsCling(int[] position) {
   4040         // Enable the clings only if they have not been dismissed before
   4041         if (isClingsEnabled() &&
   4042                 !mSharedPrefs.getBoolean(Cling.ALLAPPS_CLING_DISMISSED_KEY, false)) {
   4043             initCling(R.id.all_apps_cling, position, true, 0);
   4044         } else {
   4045             removeCling(R.id.all_apps_cling);
   4046         }
   4047     }
   4048     public Cling showFirstRunFoldersCling() {
   4049         // Enable the clings only if they have not been dismissed before
   4050         if (isClingsEnabled() &&
   4051                 !mSharedPrefs.getBoolean(Cling.FOLDER_CLING_DISMISSED_KEY, false)) {
   4052             return initCling(R.id.folder_cling, null, true, 0);
   4053         } else {
   4054             removeCling(R.id.folder_cling);
   4055             return null;
   4056         }
   4057     }
   4058     public boolean isFolderClingVisible() {
   4059         Cling cling = (Cling) findViewById(R.id.folder_cling);
   4060         if (cling != null) {
   4061             return cling.getVisibility() == View.VISIBLE;
   4062         }
   4063         return false;
   4064     }
   4065     public void dismissWorkspaceCling(View v) {
   4066         Cling cling = (Cling) findViewById(R.id.workspace_cling);
   4067         dismissCling(cling, Cling.WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
   4068     }
   4069     public void dismissAllAppsCling(View v) {
   4070         Cling cling = (Cling) findViewById(R.id.all_apps_cling);
   4071         dismissCling(cling, Cling.ALLAPPS_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
   4072     }
   4073     public void dismissFolderCling(View v) {
   4074         Cling cling = (Cling) findViewById(R.id.folder_cling);
   4075         dismissCling(cling, Cling.FOLDER_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
   4076     }
   4077 
   4078     /**
   4079      * Prints out out state for debugging.
   4080      */
   4081     public void dumpState() {
   4082         Log.d(TAG, "BEGIN launcher2 dump state for launcher " + this);
   4083         Log.d(TAG, "mSavedState=" + mSavedState);
   4084         Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
   4085         Log.d(TAG, "mRestoring=" + mRestoring);
   4086         Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
   4087         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
   4088         Log.d(TAG, "sFolders.size=" + sFolders.size());
   4089         mModel.dumpState();
   4090 
   4091         if (mAppsCustomizeContent != null) {
   4092             mAppsCustomizeContent.dumpState();
   4093         }
   4094         Log.d(TAG, "END launcher2 dump state");
   4095     }
   4096 
   4097     @Override
   4098     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
   4099         super.dump(prefix, fd, writer, args);
   4100         writer.println(" ");
   4101         writer.println("Debug logs: ");
   4102         for (int i = 0; i < sDumpLogs.size(); i++) {
   4103             writer.println("  " + sDumpLogs.get(i));
   4104         }
   4105     }
   4106 
   4107     public static void dumpDebugLogsToConsole() {
   4108         Log.d(TAG, "");
   4109         Log.d(TAG, "*********************");
   4110         Log.d(TAG, "Launcher debug logs: ");
   4111         for (int i = 0; i < sDumpLogs.size(); i++) {
   4112             Log.d(TAG, "  " + sDumpLogs.get(i));
   4113         }
   4114         Log.d(TAG, "*********************");
   4115         Log.d(TAG, "");
   4116     }
   4117 }
   4118 
   4119 interface LauncherTransitionable {
   4120     View getContent();
   4121     void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);
   4122     void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
   4123     void onLauncherTransitionStep(Launcher l, float t);
   4124     void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
   4125 }
   4126