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