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