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