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