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