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.animation.Animator;
     21 import android.animation.AnimatorListenerAdapter;
     22 import android.animation.AnimatorSet;
     23 import android.animation.ObjectAnimator;
     24 import android.animation.PropertyValuesHolder;
     25 import android.animation.TimeInterpolator;
     26 import android.animation.ValueAnimator;
     27 import android.annotation.TargetApi;
     28 import android.app.Activity;
     29 import android.app.ActivityManager;
     30 import android.app.ActivityOptions;
     31 import android.app.AlertDialog;
     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.DialogInterface;
     43 import android.content.Intent;
     44 import android.content.IntentFilter;
     45 import android.content.SharedPreferences;
     46 import android.content.pm.ActivityInfo;
     47 import android.content.pm.ApplicationInfo;
     48 import android.content.pm.PackageManager;
     49 import android.content.pm.PackageManager.NameNotFoundException;
     50 import android.content.res.Configuration;
     51 import android.content.res.Resources;
     52 import android.database.ContentObserver;
     53 import android.database.sqlite.SQLiteDatabase;
     54 import android.graphics.Bitmap;
     55 import android.graphics.Canvas;
     56 import android.graphics.Color;
     57 import android.graphics.PorterDuff;
     58 import android.graphics.Rect;
     59 import android.graphics.drawable.Drawable;
     60 import android.net.Uri;
     61 import android.os.AsyncTask;
     62 import android.os.Build;
     63 import android.os.Bundle;
     64 import android.os.Environment;
     65 import android.os.Handler;
     66 import android.os.Message;
     67 import android.os.StrictMode;
     68 import android.os.SystemClock;
     69 import android.text.Selection;
     70 import android.text.SpannableStringBuilder;
     71 import android.text.TextUtils;
     72 import android.text.method.TextKeyListener;
     73 import android.util.Log;
     74 import android.view.Display;
     75 import android.view.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.ViewAnimationUtils;
     86 import android.view.ViewGroup;
     87 import android.view.ViewStub;
     88 import android.view.ViewTreeObserver;
     89 import android.view.Window;
     90 import android.view.WindowManager;
     91 import android.view.accessibility.AccessibilityEvent;
     92 import android.view.animation.AccelerateInterpolator;
     93 import android.view.animation.DecelerateInterpolator;
     94 import android.view.inputmethod.InputMethodManager;
     95 import android.widget.Advanceable;
     96 import android.widget.FrameLayout;
     97 import android.widget.ImageView;
     98 import android.widget.Toast;
     99 
    100 import com.android.launcher3.DropTarget.DragObject;
    101 import com.android.launcher3.PagedView.PageSwitchListener;
    102 import com.android.launcher3.compat.AppWidgetManagerCompat;
    103 import com.android.launcher3.compat.LauncherActivityInfoCompat;
    104 import com.android.launcher3.compat.LauncherAppsCompat;
    105 import com.android.launcher3.compat.PackageInstallerCompat;
    106 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
    107 import com.android.launcher3.compat.UserHandleCompat;
    108 import com.android.launcher3.compat.UserManagerCompat;
    109 
    110 import java.io.DataInputStream;
    111 import java.io.DataOutputStream;
    112 import java.io.File;
    113 import java.io.FileDescriptor;
    114 import java.io.FileNotFoundException;
    115 import java.io.FileOutputStream;
    116 import java.io.IOException;
    117 import java.io.PrintWriter;
    118 import java.lang.reflect.Field;
    119 import java.lang.reflect.InvocationTargetException;
    120 import java.lang.reflect.Method;
    121 import java.text.DateFormat;
    122 import java.util.ArrayList;
    123 import java.util.Collection;
    124 import java.util.Date;
    125 import java.util.HashMap;
    126 import java.util.HashSet;
    127 import java.util.List;
    128 import java.util.concurrent.atomic.AtomicInteger;
    129 
    130 /**
    131  * Default launcher application.
    132  */
    133 public class Launcher extends Activity
    134         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
    135                    View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
    136     static final String TAG = "Launcher";
    137     static final boolean LOGD = false;
    138 
    139     static final boolean PROFILE_STARTUP = false;
    140     static final boolean DEBUG_WIDGETS = false;
    141     static final boolean DEBUG_STRICT_MODE = false;
    142     static final boolean DEBUG_RESUME_TIME = false;
    143     static final boolean DEBUG_DUMP_LOG = false;
    144 
    145     static final boolean ENABLE_DEBUG_INTENTS = false; // allow DebugIntents to run
    146 
    147     private static final int REQUEST_CREATE_SHORTCUT = 1;
    148     private static final int REQUEST_CREATE_APPWIDGET = 5;
    149     private static final int REQUEST_PICK_SHORTCUT = 7;
    150     private static final int REQUEST_PICK_APPWIDGET = 9;
    151     private static final int REQUEST_PICK_WALLPAPER = 10;
    152 
    153     private static final int REQUEST_BIND_APPWIDGET = 11;
    154     private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
    155 
    156     /**
    157      * IntentStarter uses request codes starting with this. This must be greater than all activity
    158      * request codes used internally.
    159      */
    160     protected static final int REQUEST_LAST = 100;
    161 
    162     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
    163 
    164     static final int SCREEN_COUNT = 5;
    165     static final int DEFAULT_SCREEN = 2;
    166 
    167     // To turn on these properties, type
    168     // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
    169     static final String DUMP_STATE_PROPERTY = "launcher_dump_state";
    170     static final String DISABLE_ALL_APPS_PROPERTY = "launcher_noallapps";
    171 
    172     // The Intent extra that defines whether to ignore the launch animation
    173     static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
    174             "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
    175 
    176     // Type: int
    177     private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
    178     // Type: int
    179     private static final String RUNTIME_STATE = "launcher.state";
    180     // Type: int
    181     private static final String RUNTIME_STATE_PENDING_ADD_CONTAINER = "launcher.add_container";
    182     // Type: int
    183     private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
    184     // Type: int
    185     private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x";
    186     // Type: int
    187     private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y";
    188     // Type: boolean
    189     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
    190     // Type: long
    191     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
    192     // Type: int
    193     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x";
    194     // Type: int
    195     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y";
    196     // Type: parcelable
    197     private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
    198     // Type: parcelable
    199     private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_ID = "launcher.add_widget_id";
    200     // Type: int[]
    201     private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids";
    202 
    203     static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
    204     static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
    205 
    206     static final String FIRST_LOAD_COMPLETE = "launcher.first_load_complete";
    207     static final String ACTION_FIRST_LOAD_COMPLETE =
    208             "com.android.launcher3.action.FIRST_LOAD_COMPLETE";
    209 
    210     public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem";
    211     public static final boolean SHOW_WEIGHT_WATCHER_DEFAULT = false;
    212 
    213     private static final String QSB_WIDGET_ID = "qsb_widget_id";
    214     private static final String QSB_WIDGET_PROVIDER = "qsb_widget_provider";
    215 
    216     public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data";
    217 
    218     /** The different states that Launcher can be in. */
    219     private enum State { NONE, WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED };
    220     private State mState = State.WORKSPACE;
    221     private AnimatorSet mStateAnimation;
    222 
    223     private boolean mIsSafeModeEnabled;
    224 
    225     LauncherOverlayCallbacks mLauncherOverlayCallbacks = new LauncherOverlayCallbacksImpl();
    226     LauncherOverlay mLauncherOverlay;
    227     InsettableFrameLayout mLauncherOverlayContainer;
    228 
    229     static final int APPWIDGET_HOST_ID = 1024;
    230     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
    231     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
    232     private static final int ACTIVITY_START_DELAY = 1000;
    233 
    234     private static final Object sLock = new Object();
    235     private static int sScreen = DEFAULT_SCREEN;
    236 
    237     private HashMap<Integer, Integer> mItemIdToViewId = new HashMap<Integer, Integer>();
    238     private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
    239 
    240     // How long to wait before the new-shortcut animation automatically pans the workspace
    241     private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
    242     private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
    243     private static int NEW_APPS_ANIMATION_DELAY = 500;
    244     private static final int SINGLE_FRAME_DELAY = 16;
    245 
    246     private final BroadcastReceiver mCloseSystemDialogsReceiver
    247             = new CloseSystemDialogsIntentReceiver();
    248     private final ContentObserver mWidgetObserver = new AppWidgetResetObserver();
    249 
    250     private LayoutInflater mInflater;
    251 
    252     private Workspace mWorkspace;
    253     private View mLauncherView;
    254     private View mPageIndicators;
    255     private DragLayer mDragLayer;
    256     private DragController mDragController;
    257     private View mWeightWatcher;
    258 
    259     private AppWidgetManagerCompat mAppWidgetManager;
    260     private LauncherAppWidgetHost mAppWidgetHost;
    261 
    262     private ItemInfo mPendingAddInfo = new ItemInfo();
    263     private AppWidgetProviderInfo mPendingAddWidgetInfo;
    264     private int mPendingAddWidgetId = -1;
    265 
    266     private int[] mTmpAddItemCellCoordinates = new int[2];
    267 
    268     private FolderInfo mFolderInfo;
    269 
    270     private Hotseat mHotseat;
    271     private ViewGroup mOverviewPanel;
    272 
    273     private View mAllAppsButton;
    274 
    275     private SearchDropTargetBar mSearchDropTargetBar;
    276     private AppsCustomizeTabHost mAppsCustomizeTabHost;
    277     private AppsCustomizePagedView mAppsCustomizeContent;
    278     private boolean mAutoAdvanceRunning = false;
    279     private AppWidgetHostView mQsb;
    280 
    281     private Bundle mSavedState;
    282     // We set the state in both onCreate and then onNewIntent in some cases, which causes both
    283     // scroll issues (because the workspace may not have been measured yet) and extra work.
    284     // Instead, just save the state that we need to restore Launcher to, and commit it in onResume.
    285     private State mOnResumeState = State.NONE;
    286 
    287     private SpannableStringBuilder mDefaultKeySsb = null;
    288 
    289     private boolean mWorkspaceLoading = true;
    290 
    291     private boolean mPaused = true;
    292     private boolean mRestoring;
    293     private boolean mWaitingForResult;
    294     private boolean mOnResumeNeedsLoad;
    295 
    296     private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
    297     private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
    298 
    299     private Bundle mSavedInstanceState;
    300 
    301     private LauncherModel mModel;
    302     private IconCache mIconCache;
    303     private boolean mUserPresent = true;
    304     private boolean mVisible = false;
    305     private boolean mHasFocus = false;
    306     private boolean mAttached = false;
    307 
    308     private static LocaleConfiguration sLocaleConfiguration = null;
    309 
    310     private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
    311 
    312     private View.OnTouchListener mHapticFeedbackTouchListener;
    313 
    314     // Related to the auto-advancing of widgets
    315     private final int ADVANCE_MSG = 1;
    316     private final int mAdvanceInterval = 20000;
    317     private final int mAdvanceStagger = 250;
    318     private long mAutoAdvanceSentTime;
    319     private long mAutoAdvanceTimeLeft = -1;
    320     private HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance =
    321         new HashMap<View, AppWidgetProviderInfo>();
    322 
    323     // Determines how long to wait after a rotation before restoring the screen orientation to
    324     // match the sensor state.
    325     private final int mRestoreScreenOrientationDelay = 500;
    326 
    327     private Drawable mWorkspaceBackgroundDrawable;
    328 
    329     private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
    330     private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false;
    331 
    332     static final ArrayList<String> sDumpLogs = new ArrayList<String>();
    333     static Date sDateStamp = new Date();
    334     static DateFormat sDateFormat =
    335             DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
    336     static long sRunStart = System.currentTimeMillis();
    337     static final String CORRUPTION_EMAIL_SENT_KEY = "corruptionEmailSent";
    338 
    339     // We only want to get the SharedPreferences once since it does an FS stat each time we get
    340     // it from the context.
    341     private SharedPreferences mSharedPrefs;
    342 
    343     private static ArrayList<ComponentName> mIntentsOnWorkspaceFromUpgradePath = null;
    344 
    345     // Holds the page that we need to animate to, and the icon views that we need to animate up
    346     // when we scroll to that page on resume.
    347     private ImageView mFolderIconImageView;
    348     private Bitmap mFolderIconBitmap;
    349     private Canvas mFolderIconCanvas;
    350     private Rect mRectForFolderAnimation = new Rect();
    351 
    352     private BubbleTextView mWaitingForResume;
    353 
    354     private Runnable mBuildLayersRunnable = new Runnable() {
    355         public void run() {
    356             if (mWorkspace != null) {
    357                 mWorkspace.buildPageHardwareLayers();
    358             }
    359         }
    360     };
    361 
    362     private static PendingAddArguments sPendingAddItem;
    363 
    364     private static class PendingAddArguments {
    365         int requestCode;
    366         Intent intent;
    367         long container;
    368         long screenId;
    369         int cellX;
    370         int cellY;
    371         int appWidgetId;
    372     }
    373 
    374     private Stats mStats;
    375 
    376     FocusIndicatorView mFocusHandler;
    377 
    378     @Override
    379     protected void onCreate(Bundle savedInstanceState) {
    380         if (DEBUG_STRICT_MODE) {
    381             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    382                     .detectDiskReads()
    383                     .detectDiskWrites()
    384                     .detectNetwork()   // or .detectAll() for all detectable problems
    385                     .penaltyLog()
    386                     .build());
    387             StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    388                     .detectLeakedSqlLiteObjects()
    389                     .detectLeakedClosableObjects()
    390                     .penaltyLog()
    391                     .penaltyDeath()
    392                     .build());
    393         }
    394 
    395         if (mLauncherCallbacks != null) {
    396             mLauncherCallbacks.preOnCreate();
    397         }
    398 
    399         super.onCreate(savedInstanceState);
    400 
    401         LauncherAppState.setApplicationContext(getApplicationContext());
    402         LauncherAppState app = LauncherAppState.getInstance();
    403         LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this);
    404 
    405         // Lazy-initialize the dynamic grid
    406         DeviceProfile grid = app.initDynamicGrid(this);
    407 
    408         // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
    409         mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
    410                 Context.MODE_PRIVATE);
    411         mIsSafeModeEnabled = getPackageManager().isSafeMode();
    412         mModel = app.setLauncher(this);
    413         mIconCache = app.getIconCache();
    414         mIconCache.flushInvalidIcons(grid);
    415         mDragController = new DragController(this);
    416         mInflater = getLayoutInflater();
    417 
    418         mStats = new Stats(this);
    419 
    420         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
    421 
    422         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
    423         mAppWidgetHost.startListening();
    424 
    425         // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
    426         // this also ensures that any synchronous binding below doesn't re-trigger another
    427         // LauncherModel load.
    428         mPaused = false;
    429 
    430         if (PROFILE_STARTUP) {
    431             android.os.Debug.startMethodTracing(
    432                     Environment.getExternalStorageDirectory() + "/launcher");
    433         }
    434 
    435         checkForLocaleChange();
    436         setContentView(R.layout.launcher);
    437 
    438         setupViews();
    439         grid.layout(this);
    440 
    441         registerContentObservers();
    442 
    443         lockAllApps();
    444 
    445         mSavedState = savedInstanceState;
    446         restoreState(mSavedState);
    447 
    448         if (PROFILE_STARTUP) {
    449             android.os.Debug.stopMethodTracing();
    450         }
    451 
    452         if (!mRestoring) {
    453             if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
    454                 // If the user leaves launcher, then we should just load items asynchronously when
    455                 // they return.
    456                 mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
    457             } else {
    458                 // We only load the page synchronously if the user rotates (or triggers a
    459                 // configuration change) while launcher is in the foreground
    460                 mModel.startLoader(true, mWorkspace.getRestorePage());
    461             }
    462         }
    463 
    464         // For handling default keys
    465         mDefaultKeySsb = new SpannableStringBuilder();
    466         Selection.setSelection(mDefaultKeySsb, 0);
    467 
    468         IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    469         registerReceiver(mCloseSystemDialogsReceiver, filter);
    470 
    471         // On large interfaces, we want the screen to auto-rotate based on the current orientation
    472         unlockScreenOrientation(true);
    473 
    474         if (mLauncherCallbacks != null) {
    475             mLauncherCallbacks.onCreate(savedInstanceState);
    476             if (mLauncherCallbacks.hasLauncherOverlay()) {
    477                 ViewStub stub = (ViewStub) findViewById(R.id.launcher_overlay_stub);
    478                 mLauncherOverlayContainer = (InsettableFrameLayout) stub.inflate();
    479                 mLauncherOverlay = mLauncherCallbacks.setLauncherOverlayView(
    480                         mLauncherOverlayContainer, mLauncherOverlayCallbacks);
    481                 mWorkspace.setLauncherOverlay(mLauncherOverlay);
    482             }
    483         }
    484 
    485         if (shouldShowIntroScreen()) {
    486             showIntroScreen();
    487         } else {
    488             showFirstRunActivity();
    489             showFirstRunClings();
    490         }
    491     }
    492 
    493     private LauncherCallbacks mLauncherCallbacks;
    494 
    495     public void onPostCreate(Bundle savedInstanceState) {
    496         super.onPostCreate(savedInstanceState);
    497         if (mLauncherCallbacks != null) {
    498             mLauncherCallbacks.onPostCreate(savedInstanceState);
    499         }
    500     }
    501 
    502     public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
    503         mLauncherCallbacks = callbacks;
    504         return true;
    505     }
    506 
    507     @Override
    508     public void onLauncherProviderChange() {
    509         if (mLauncherCallbacks != null) {
    510             mLauncherCallbacks.onLauncherProviderChange();
    511         }
    512     }
    513 
    514     /** To be overridden by subclasses to hint to Launcher that we have custom content */
    515     protected boolean hasCustomContentToLeft() {
    516         if (mLauncherCallbacks != null) {
    517             return mLauncherCallbacks.hasCustomContentToLeft();
    518         }
    519         return false;
    520     }
    521 
    522     /**
    523      * To be overridden by subclasses to populate the custom content container and call
    524      * {@link #addToCustomContentPage}. This will only be invoked if
    525      * {@link #hasCustomContentToLeft()} is {@code true}.
    526      */
    527     protected void populateCustomContentContainer() {
    528         if (mLauncherCallbacks != null) {
    529             mLauncherCallbacks.populateCustomContentContainer();
    530         }
    531     }
    532 
    533     /**
    534      * Invoked by subclasses to signal a change to the {@link #addCustomContentToLeft} value to
    535      * ensure the custom content page is added or removed if necessary.
    536      */
    537     protected void invalidateHasCustomContentToLeft() {
    538         if (mWorkspace == null || mWorkspace.getScreenOrder().isEmpty()) {
    539             // Not bound yet, wait for bindScreens to be called.
    540             return;
    541         }
    542 
    543         if (!mWorkspace.hasCustomContent() && hasCustomContentToLeft()) {
    544             // Create the custom content page and call the subclass to populate it.
    545             mWorkspace.createCustomContentContainer();
    546             populateCustomContentContainer();
    547         } else if (mWorkspace.hasCustomContent() && !hasCustomContentToLeft()) {
    548             mWorkspace.removeCustomContentPage();
    549         }
    550     }
    551 
    552     private void checkForLocaleChange() {
    553         if (sLocaleConfiguration == null) {
    554             new AsyncTask<Void, Void, LocaleConfiguration>() {
    555                 @Override
    556                 protected LocaleConfiguration doInBackground(Void... unused) {
    557                     LocaleConfiguration localeConfiguration = new LocaleConfiguration();
    558                     readConfiguration(Launcher.this, localeConfiguration);
    559                     return localeConfiguration;
    560                 }
    561 
    562                 @Override
    563                 protected void onPostExecute(LocaleConfiguration result) {
    564                     sLocaleConfiguration = result;
    565                     checkForLocaleChange();  // recursive, but now with a locale configuration
    566                 }
    567             }.execute();
    568             return;
    569         }
    570 
    571         final Configuration configuration = getResources().getConfiguration();
    572 
    573         final String previousLocale = sLocaleConfiguration.locale;
    574         final String locale = configuration.locale.toString();
    575 
    576         final int previousMcc = sLocaleConfiguration.mcc;
    577         final int mcc = configuration.mcc;
    578 
    579         final int previousMnc = sLocaleConfiguration.mnc;
    580         final int mnc = configuration.mnc;
    581 
    582         boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
    583 
    584         if (localeChanged) {
    585             sLocaleConfiguration.locale = locale;
    586             sLocaleConfiguration.mcc = mcc;
    587             sLocaleConfiguration.mnc = mnc;
    588 
    589             mIconCache.flush();
    590 
    591             final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
    592             new AsyncTask<Void, Void, Void>() {
    593                 public Void doInBackground(Void ... args) {
    594                     writeConfiguration(Launcher.this, localeConfiguration);
    595                     return null;
    596                 }
    597             }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
    598         }
    599     }
    600 
    601     private static class LocaleConfiguration {
    602         public String locale;
    603         public int mcc = -1;
    604         public int mnc = -1;
    605     }
    606 
    607     private static void readConfiguration(Context context, LocaleConfiguration configuration) {
    608         DataInputStream in = null;
    609         try {
    610             in = new DataInputStream(context.openFileInput(LauncherFiles.LAUNCHER_PREFERENCES));
    611             configuration.locale = in.readUTF();
    612             configuration.mcc = in.readInt();
    613             configuration.mnc = in.readInt();
    614         } catch (FileNotFoundException e) {
    615             // Ignore
    616         } catch (IOException e) {
    617             // Ignore
    618         } finally {
    619             if (in != null) {
    620                 try {
    621                     in.close();
    622                 } catch (IOException e) {
    623                     // Ignore
    624                 }
    625             }
    626         }
    627     }
    628 
    629     private static void writeConfiguration(Context context, LocaleConfiguration configuration) {
    630         DataOutputStream out = null;
    631         try {
    632             out = new DataOutputStream(context.openFileOutput(
    633                     LauncherFiles.LAUNCHER_PREFERENCES, MODE_PRIVATE));
    634             out.writeUTF(configuration.locale);
    635             out.writeInt(configuration.mcc);
    636             out.writeInt(configuration.mnc);
    637             out.flush();
    638         } catch (FileNotFoundException e) {
    639             // Ignore
    640         } catch (IOException e) {
    641             //noinspection ResultOfMethodCallIgnored
    642             context.getFileStreamPath(LauncherFiles.LAUNCHER_PREFERENCES).delete();
    643         } finally {
    644             if (out != null) {
    645                 try {
    646                     out.close();
    647                 } catch (IOException e) {
    648                     // Ignore
    649                 }
    650             }
    651         }
    652     }
    653 
    654     public Stats getStats() {
    655         return mStats;
    656     }
    657 
    658     public LayoutInflater getInflater() {
    659         return mInflater;
    660     }
    661 
    662     boolean isDraggingEnabled() {
    663         // We prevent dragging when we are loading the workspace as it is possible to pick up a view
    664         // that is subsequently removed from the workspace in startBinding().
    665         return !mModel.isLoadingWorkspace();
    666     }
    667 
    668     static int getScreen() {
    669         synchronized (sLock) {
    670             return sScreen;
    671         }
    672     }
    673 
    674     static void setScreen(int screen) {
    675         synchronized (sLock) {
    676             sScreen = screen;
    677         }
    678     }
    679 
    680     public static int generateViewId() {
    681         if (Build.VERSION.SDK_INT >= 17) {
    682             return View.generateViewId();
    683         } else {
    684             // View.generateViewId() is not available. The following fallback logic is a copy
    685             // of its implementation.
    686             for (;;) {
    687                 final int result = sNextGeneratedId.get();
    688                 // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
    689                 int newValue = result + 1;
    690                 if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
    691                 if (sNextGeneratedId.compareAndSet(result, newValue)) {
    692                     return result;
    693                 }
    694             }
    695         }
    696     }
    697 
    698     public int getViewIdForItem(ItemInfo info) {
    699         // This cast is safe given the > 2B range for int.
    700         int itemId = (int) info.id;
    701         if (mItemIdToViewId.containsKey(itemId)) {
    702             return mItemIdToViewId.get(itemId);
    703         }
    704         int viewId = generateViewId();
    705         mItemIdToViewId.put(itemId, viewId);
    706         return viewId;
    707     }
    708 
    709     /**
    710      * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
    711      * a configuration step, this allows the proper animations to run after other transitions.
    712      */
    713     private long completeAdd(PendingAddArguments args) {
    714         long screenId = args.screenId;
    715         if (args.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
    716             // When the screen id represents an actual screen (as opposed to a rank) we make sure
    717             // that the drop page actually exists.
    718             screenId = ensurePendingDropLayoutExists(args.screenId);
    719         }
    720 
    721         switch (args.requestCode) {
    722             case REQUEST_CREATE_SHORTCUT:
    723                 completeAddShortcut(args.intent, args.container, screenId, args.cellX,
    724                         args.cellY);
    725                 break;
    726             case REQUEST_CREATE_APPWIDGET:
    727                 completeAddAppWidget(args.appWidgetId, args.container, screenId, null, null);
    728                 break;
    729             case REQUEST_RECONFIGURE_APPWIDGET:
    730                 completeRestoreAppWidget(args.appWidgetId);
    731                 break;
    732         }
    733         // Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
    734         // if you turned the screen off and then back while in All Apps, Launcher would not
    735         // return to the workspace. Clearing mAddInfo.container here fixes this issue
    736         resetAddInfo();
    737         return screenId;
    738     }
    739 
    740     private void handleActivityResult(
    741             final int requestCode, final int resultCode, final Intent data) {
    742         // Reset the startActivity waiting flag
    743         setWaitingForResult(false);
    744         final int pendingAddWidgetId = mPendingAddWidgetId;
    745         mPendingAddWidgetId = -1;
    746 
    747         Runnable exitSpringLoaded = new Runnable() {
    748             @Override
    749             public void run() {
    750                 exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
    751                         EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
    752             }
    753         };
    754 
    755         if (requestCode == REQUEST_BIND_APPWIDGET) {
    756             final int appWidgetId = data != null ?
    757                     data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
    758             if (resultCode == RESULT_CANCELED) {
    759                 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
    760                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
    761                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    762             } else if (resultCode == RESULT_OK) {
    763                 addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
    764                         mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
    765             }
    766             return;
    767         } else if (requestCode == REQUEST_PICK_WALLPAPER) {
    768             if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) {
    769                 mWorkspace.exitOverviewMode(false);
    770             }
    771             return;
    772         }
    773 
    774         boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
    775                 requestCode == REQUEST_CREATE_APPWIDGET);
    776 
    777         final boolean workspaceLocked = isWorkspaceLocked();
    778         // We have special handling for widgets
    779         if (isWidgetDrop) {
    780             final int appWidgetId;
    781             int widgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
    782                     : -1;
    783             if (widgetId < 0) {
    784                 appWidgetId = pendingAddWidgetId;
    785             } else {
    786                 appWidgetId = widgetId;
    787             }
    788 
    789             final int result;
    790             if (appWidgetId < 0 || resultCode == RESULT_CANCELED) {
    791                 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " +
    792                         "returned from the widget configuration activity.");
    793                 result = RESULT_CANCELED;
    794                 completeTwoStageWidgetDrop(result, appWidgetId);
    795                 final Runnable onComplete = new Runnable() {
    796                     @Override
    797                     public void run() {
    798                         exitSpringLoadedDragModeDelayed(false, 0, null);
    799                     }
    800                 };
    801                 if (workspaceLocked) {
    802                     // No need to remove the empty screen if we're mid-binding, as the
    803                     // the bind will not add the empty screen.
    804                     mWorkspace.postDelayed(onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
    805                 } else {
    806                     mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
    807                             ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    808                 }
    809             } else {
    810                 if (!workspaceLocked) {
    811                     if (mPendingAddInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
    812                         // When the screen id represents an actual screen (as opposed to a rank)
    813                         // we make sure that the drop page actually exists.
    814                         mPendingAddInfo.screenId =
    815                                 ensurePendingDropLayoutExists(mPendingAddInfo.screenId);
    816                     }
    817                     final CellLayout dropLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
    818 
    819                     dropLayout.setDropPending(true);
    820                     final Runnable onComplete = new Runnable() {
    821                         @Override
    822                         public void run() {
    823                             completeTwoStageWidgetDrop(resultCode, appWidgetId);
    824                             dropLayout.setDropPending(false);
    825                         }
    826                     };
    827                     mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
    828                             ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    829                 } else {
    830                     PendingAddArguments args = preparePendingAddArgs(requestCode, data, appWidgetId,
    831                             mPendingAddInfo);
    832                     sPendingAddItem = args;
    833                 }
    834             }
    835             return;
    836         }
    837 
    838         if (requestCode == REQUEST_RECONFIGURE_APPWIDGET) {
    839             if (resultCode == RESULT_OK) {
    840                 // Update the widget view.
    841                 PendingAddArguments args = preparePendingAddArgs(requestCode, data,
    842                         pendingAddWidgetId, mPendingAddInfo);
    843                 if (workspaceLocked) {
    844                     sPendingAddItem = args;
    845                 } else {
    846                     completeAdd(args);
    847                 }
    848             }
    849             // Leave the widget in the pending state if the user canceled the configure.
    850             return;
    851         }
    852 
    853         // The pattern used here is that a user PICKs a specific application,
    854         // which, depending on the target, might need to CREATE the actual target.
    855 
    856         // For example, the user would PICK_SHORTCUT for "Music playlist", and we
    857         // launch over to the Music app to actually CREATE_SHORTCUT.
    858         if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
    859             final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1,
    860                     mPendingAddInfo);
    861             if (isWorkspaceLocked()) {
    862                 sPendingAddItem = args;
    863             } else {
    864                 completeAdd(args);
    865                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
    866                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    867             }
    868         } else if (resultCode == RESULT_CANCELED) {
    869             mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
    870                     ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
    871         }
    872         mDragLayer.clearAnimatedView();
    873 
    874     }
    875 
    876     @Override
    877     protected void onActivityResult(
    878             final int requestCode, final int resultCode, final Intent data) {
    879         handleActivityResult(requestCode, resultCode, data);
    880         if (mLauncherCallbacks != null) {
    881             mLauncherCallbacks.onActivityResult(requestCode, resultCode, data);
    882         }
    883     }
    884 
    885     private PendingAddArguments preparePendingAddArgs(int requestCode, Intent data, int
    886             appWidgetId, ItemInfo info) {
    887         PendingAddArguments args = new PendingAddArguments();
    888         args.requestCode = requestCode;
    889         args.intent = data;
    890         args.container = info.container;
    891         args.screenId = info.screenId;
    892         args.cellX = info.cellX;
    893         args.cellY = info.cellY;
    894         args.appWidgetId = appWidgetId;
    895         return args;
    896     }
    897 
    898     /**
    899      * Check to see if a given screen id exists. If not, create it at the end, return the new id.
    900      *
    901      * @param screenId the screen id to check
    902      * @return the new screen, or screenId if it exists
    903      */
    904     private long ensurePendingDropLayoutExists(long screenId) {
    905         CellLayout dropLayout =
    906                 (CellLayout) mWorkspace.getScreenWithId(screenId);
    907         if (dropLayout == null) {
    908             // it's possible that the add screen was removed because it was
    909             // empty and a re-bind occurred
    910             mWorkspace.addExtraEmptyScreen();
    911             return mWorkspace.commitExtraEmptyScreen();
    912         } else {
    913             return screenId;
    914         }
    915     }
    916 
    917     private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
    918         CellLayout cellLayout =
    919                 (CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
    920         Runnable onCompleteRunnable = null;
    921         int animationType = 0;
    922 
    923         AppWidgetHostView boundWidget = null;
    924         if (resultCode == RESULT_OK) {
    925             animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
    926             final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
    927                     mPendingAddWidgetInfo);
    928             boundWidget = layout;
    929             onCompleteRunnable = new Runnable() {
    930                 @Override
    931                 public void run() {
    932                     completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
    933                             mPendingAddInfo.screenId, layout, null);
    934                     exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
    935                             EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
    936                 }
    937             };
    938         } else if (resultCode == RESULT_CANCELED) {
    939             mAppWidgetHost.deleteAppWidgetId(appWidgetId);
    940             animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
    941         }
    942         if (mDragLayer.getAnimatedView() != null) {
    943             mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout,
    944                     (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
    945                     animationType, boundWidget, true);
    946         } else if (onCompleteRunnable != null) {
    947             // The animated view may be null in the case of a rotation during widget configuration
    948             onCompleteRunnable.run();
    949         }
    950     }
    951 
    952     @Override
    953     protected void onStop() {
    954         super.onStop();
    955         FirstFrameAnimatorHelper.setIsVisible(false);
    956 
    957         if (mLauncherCallbacks != null) {
    958             mLauncherCallbacks.onStop();
    959         }
    960     }
    961 
    962     @Override
    963     protected void onStart() {
    964         super.onStart();
    965         FirstFrameAnimatorHelper.setIsVisible(true);
    966 
    967         if (mLauncherCallbacks != null) {
    968             mLauncherCallbacks.onStart();
    969         }
    970     }
    971 
    972     @Override
    973     protected void onResume() {
    974         long startTime = 0;
    975         if (DEBUG_RESUME_TIME) {
    976             startTime = System.currentTimeMillis();
    977             Log.v(TAG, "Launcher.onResume()");
    978         }
    979 
    980         if (mLauncherCallbacks != null) {
    981             mLauncherCallbacks.preOnResume();
    982         }
    983 
    984         super.onResume();
    985 
    986         // Restore the previous launcher state
    987         if (mOnResumeState == State.WORKSPACE) {
    988             showWorkspace(false);
    989         } else if (mOnResumeState == State.APPS_CUSTOMIZE) {
    990             showAllApps(false, mAppsCustomizeContent.getContentType(), false);
    991         }
    992         mOnResumeState = State.NONE;
    993 
    994         // Background was set to gradient in onPause(), restore to black if in all apps.
    995         setWorkspaceBackground(mState == State.WORKSPACE);
    996 
    997         mPaused = false;
    998         if (mRestoring || mOnResumeNeedsLoad) {
    999             setWorkspaceLoading(true);
   1000             mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
   1001             mRestoring = false;
   1002             mOnResumeNeedsLoad = false;
   1003         }
   1004         if (mBindOnResumeCallbacks.size() > 0) {
   1005             // We might have postponed some bind calls until onResume (see waitUntilResume) --
   1006             // execute them here
   1007             long startTimeCallbacks = 0;
   1008             if (DEBUG_RESUME_TIME) {
   1009                 startTimeCallbacks = System.currentTimeMillis();
   1010             }
   1011 
   1012             if (mAppsCustomizeContent != null) {
   1013                 mAppsCustomizeContent.setBulkBind(true);
   1014             }
   1015             for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
   1016                 mBindOnResumeCallbacks.get(i).run();
   1017             }
   1018             if (mAppsCustomizeContent != null) {
   1019                 mAppsCustomizeContent.setBulkBind(false);
   1020             }
   1021             mBindOnResumeCallbacks.clear();
   1022             if (DEBUG_RESUME_TIME) {
   1023                 Log.d(TAG, "Time spent processing callbacks in onResume: " +
   1024                     (System.currentTimeMillis() - startTimeCallbacks));
   1025             }
   1026         }
   1027         if (mOnResumeCallbacks.size() > 0) {
   1028             for (int i = 0; i < mOnResumeCallbacks.size(); i++) {
   1029                 mOnResumeCallbacks.get(i).run();
   1030             }
   1031             mOnResumeCallbacks.clear();
   1032         }
   1033 
   1034         // Reset the pressed state of icons that were locked in the press state while activities
   1035         // were launching
   1036         if (mWaitingForResume != null) {
   1037             // Resets the previous workspace icon press state
   1038             mWaitingForResume.setStayPressed(false);
   1039         }
   1040 
   1041         // It is possible that widgets can receive updates while launcher is not in the foreground.
   1042         // Consequently, the widgets will be inflated in the orientation of the foreground activity
   1043         // (framework issue). On resuming, we ensure that any widgets are inflated for the current
   1044         // orientation.
   1045         getWorkspace().reinflateWidgetsIfNecessary();
   1046 
   1047         // Process any items that were added while Launcher was away.
   1048         InstallShortcutReceiver.disableAndFlushInstallQueue(this);
   1049 
   1050         if (DEBUG_RESUME_TIME) {
   1051             Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime));
   1052         }
   1053 
   1054         if (mWorkspace.getCustomContentCallbacks() != null) {
   1055             // If we are resuming and the custom content is the current page, we call onShow().
   1056             // It is also poassible that onShow will instead be called slightly after first layout
   1057             // if PagedView#setRestorePage was set to the custom content page in onCreate().
   1058             if (mWorkspace.isOnOrMovingToCustomContent()) {
   1059                 mWorkspace.getCustomContentCallbacks().onShow(true);
   1060             }
   1061         }
   1062         mWorkspace.updateInteractionForState();
   1063         mWorkspace.onResume();
   1064 
   1065         PackageInstallerCompat.getInstance(this).onResume();
   1066 
   1067         if (mLauncherCallbacks != null) {
   1068             mLauncherCallbacks.onResume();
   1069         }
   1070     }
   1071 
   1072     @Override
   1073     protected void onPause() {
   1074         // Ensure that items added to Launcher are queued until Launcher returns
   1075         InstallShortcutReceiver.enableInstallQueue();
   1076         PackageInstallerCompat.getInstance(this).onPause();
   1077 
   1078         super.onPause();
   1079         mPaused = true;
   1080         mDragController.cancelDrag();
   1081         mDragController.resetLastGestureUpTime();
   1082 
   1083         // We call onHide() aggressively. The custom content callbacks should be able to
   1084         // debounce excess onHide calls.
   1085         if (mWorkspace.getCustomContentCallbacks() != null) {
   1086             mWorkspace.getCustomContentCallbacks().onHide();
   1087         }
   1088 
   1089         if (mLauncherCallbacks != null) {
   1090             mLauncherCallbacks.onPause();
   1091         }
   1092     }
   1093 
   1094     public interface CustomContentCallbacks {
   1095         // Custom content is completely shown. {@code fromResume} indicates whether this was caused
   1096         // by a onResume or by scrolling otherwise.
   1097         public void onShow(boolean fromResume);
   1098 
   1099         // Custom content is completely hidden
   1100         public void onHide();
   1101 
   1102         // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
   1103         public void onScrollProgressChanged(float progress);
   1104 
   1105         // Indicates whether the user is allowed to scroll away from the custom content.
   1106         boolean isScrollingAllowed();
   1107     }
   1108 
   1109     public interface LauncherOverlay {
   1110 
   1111         /**
   1112          * Touch interaction leading to overscroll has begun
   1113          */
   1114         public void onScrollInteractionBegin();
   1115 
   1116         /**
   1117          * Touch interaction related to overscroll has ended
   1118          */
   1119         public void onScrollInteractionEnd();
   1120 
   1121         /**
   1122          * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
   1123          * screen (or in the case of RTL, the rightmost screen).
   1124          */
   1125         public void onScrollChange(int progress, boolean rtl);
   1126 
   1127         /**
   1128          * Screen has stopped scrolling
   1129          */
   1130         public void onScrollSettled();
   1131 
   1132         /**
   1133          * This method can be called by the Launcher in order to force the LauncherOverlay
   1134          * to exit fully immersive mode.
   1135          */
   1136         public void forceExitFullImmersion();
   1137     }
   1138 
   1139     public interface LauncherOverlayCallbacks {
   1140         /**
   1141          * This method indicates whether a call to {@link #enterFullImmersion()} will succeed,
   1142          * however it doesn't modify any state within the launcher.
   1143          */
   1144         public boolean canEnterFullImmersion();
   1145 
   1146         /**
   1147          * Should be called to tell Launcher that the LauncherOverlay will take over interaction,
   1148          * eg. by occupying the full screen and handling all touch events.
   1149          *
   1150          * @return true if Launcher allows the LauncherOverlay to become fully immersive. In this
   1151          *          case, Launcher will modify any necessary state and assumes the overlay is
   1152          *          handling all interaction. If false, the LauncherOverlay should cancel any
   1153          *
   1154          */
   1155         public boolean enterFullImmersion();
   1156 
   1157         /**
   1158          * Must be called when exiting fully immersive mode. Indicates to Launcher that it has
   1159          * full control over UI and state.
   1160          */
   1161         public void exitFullImmersion();
   1162     }
   1163 
   1164     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
   1165 
   1166         @Override
   1167         public boolean canEnterFullImmersion() {
   1168             return mState == State.WORKSPACE;
   1169         }
   1170 
   1171         @Override
   1172         public boolean enterFullImmersion() {
   1173             if (mState == State.WORKSPACE) {
   1174                 // When fully immersed, disregard any touches which fall through.
   1175                 mDragLayer.setBlockTouch(true);
   1176                 return true;
   1177             }
   1178             return false;
   1179         }
   1180 
   1181         @Override
   1182         public void exitFullImmersion() {
   1183             mDragLayer.setBlockTouch(false);
   1184         }
   1185     }
   1186 
   1187     protected boolean hasSettings() {
   1188         if (mLauncherCallbacks != null) {
   1189             return mLauncherCallbacks.hasSettings();
   1190         }
   1191         return false;
   1192     }
   1193 
   1194 
   1195     public void addToCustomContentPage(View customContent,
   1196             CustomContentCallbacks callbacks, String description) {
   1197         mWorkspace.addToCustomContentPage(customContent, callbacks, description);
   1198     }
   1199 
   1200     // The custom content needs to offset its content to account for the QSB
   1201     public int getTopOffsetForCustomContent() {
   1202         return mWorkspace.getPaddingTop();
   1203     }
   1204 
   1205     @Override
   1206     public Object onRetainNonConfigurationInstance() {
   1207         // Flag the loader to stop early before switching
   1208         if (mModel.isCurrentCallbacks(this)) {
   1209             mModel.stopLoader();
   1210         }
   1211         if (mAppsCustomizeContent != null) {
   1212             mAppsCustomizeContent.surrender();
   1213         }
   1214         return Boolean.TRUE;
   1215     }
   1216 
   1217     // We can't hide the IME if it was forced open.  So don't bother
   1218     @Override
   1219     public void onWindowFocusChanged(boolean hasFocus) {
   1220         super.onWindowFocusChanged(hasFocus);
   1221         mHasFocus = hasFocus;
   1222 
   1223         if (mLauncherCallbacks != null) {
   1224             mLauncherCallbacks.onWindowFocusChanged(hasFocus);
   1225         }
   1226     }
   1227 
   1228     private boolean acceptFilter() {
   1229         final InputMethodManager inputManager = (InputMethodManager)
   1230                 getSystemService(Context.INPUT_METHOD_SERVICE);
   1231         return !inputManager.isFullscreenMode();
   1232     }
   1233 
   1234     @Override
   1235     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1236         final int uniChar = event.getUnicodeChar();
   1237         final boolean handled = super.onKeyDown(keyCode, event);
   1238         final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar);
   1239         if (!handled && acceptFilter() && isKeyNotWhitespace) {
   1240             boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
   1241                     keyCode, event);
   1242             if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
   1243                 // something usable has been typed - start a search
   1244                 // the typed text will be retrieved and cleared by
   1245                 // showSearchDialog()
   1246                 // If there are multiple keystrokes before the search dialog takes focus,
   1247                 // onSearchRequested() will be called for every keystroke,
   1248                 // but it is idempotent, so it's fine.
   1249                 return onSearchRequested();
   1250             }
   1251         }
   1252 
   1253         // Eat the long press event so the keyboard doesn't come up.
   1254         if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
   1255             return true;
   1256         }
   1257 
   1258         return handled;
   1259     }
   1260 
   1261     private String getTypedText() {
   1262         return mDefaultKeySsb.toString();
   1263     }
   1264 
   1265     private void clearTypedText() {
   1266         mDefaultKeySsb.clear();
   1267         mDefaultKeySsb.clearSpans();
   1268         Selection.setSelection(mDefaultKeySsb, 0);
   1269     }
   1270 
   1271     /**
   1272      * Given the integer (ordinal) value of a State enum instance, convert it to a variable of type
   1273      * State
   1274      */
   1275     private static State intToState(int stateOrdinal) {
   1276         State state = State.WORKSPACE;
   1277         final State[] stateValues = State.values();
   1278         for (int i = 0; i < stateValues.length; i++) {
   1279             if (stateValues[i].ordinal() == stateOrdinal) {
   1280                 state = stateValues[i];
   1281                 break;
   1282             }
   1283         }
   1284         return state;
   1285     }
   1286 
   1287     /**
   1288      * Restores the previous state, if it exists.
   1289      *
   1290      * @param savedState The previous state.
   1291      */
   1292     @SuppressWarnings("unchecked")
   1293     private void restoreState(Bundle savedState) {
   1294         if (savedState == null) {
   1295             return;
   1296         }
   1297 
   1298         State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()));
   1299         if (state == State.APPS_CUSTOMIZE) {
   1300             mOnResumeState = State.APPS_CUSTOMIZE;
   1301         }
   1302 
   1303         int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN,
   1304                 PagedView.INVALID_RESTORE_PAGE);
   1305         if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
   1306             mWorkspace.setRestorePage(currentScreen);
   1307         }
   1308 
   1309         final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
   1310         final long pendingAddScreen = savedState.getLong(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
   1311 
   1312         if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
   1313             mPendingAddInfo.container = pendingAddContainer;
   1314             mPendingAddInfo.screenId = pendingAddScreen;
   1315             mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
   1316             mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
   1317             mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
   1318             mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
   1319             mPendingAddWidgetInfo = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
   1320             mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID);
   1321             setWaitingForResult(true);
   1322             mRestoring = true;
   1323         }
   1324 
   1325         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
   1326         if (renameFolder) {
   1327             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
   1328             mFolderInfo = mModel.getFolderById(this, sFolders, id);
   1329             mRestoring = true;
   1330         }
   1331 
   1332         // Restore the AppsCustomize tab
   1333         if (mAppsCustomizeTabHost != null) {
   1334             String curTab = savedState.getString("apps_customize_currentTab");
   1335             if (curTab != null) {
   1336                 mAppsCustomizeTabHost.setContentTypeImmediate(
   1337                         mAppsCustomizeTabHost.getContentTypeForTabTag(curTab));
   1338                 mAppsCustomizeContent.loadAssociatedPages(
   1339                         mAppsCustomizeContent.getCurrentPage());
   1340             }
   1341 
   1342             int currentIndex = savedState.getInt("apps_customize_currentIndex");
   1343             mAppsCustomizeContent.restorePageForIndex(currentIndex);
   1344         }
   1345         mItemIdToViewId = (HashMap<Integer, Integer>)
   1346                 savedState.getSerializable(RUNTIME_STATE_VIEW_IDS);
   1347     }
   1348 
   1349     /**
   1350      * Finds all the views we need and configure them properly.
   1351      */
   1352     private void setupViews() {
   1353         final DragController dragController = mDragController;
   1354 
   1355         mLauncherView = findViewById(R.id.launcher);
   1356         mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
   1357         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
   1358         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
   1359         mWorkspace.setPageSwitchListener(this);
   1360         mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
   1361 
   1362         mLauncherView.setSystemUiVisibility(
   1363                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
   1364         mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg);
   1365 
   1366         // Setup the drag layer
   1367         mDragLayer.setup(this, dragController);
   1368 
   1369         // Setup the hotseat
   1370         mHotseat = (Hotseat) findViewById(R.id.hotseat);
   1371         if (mHotseat != null) {
   1372             mHotseat.setup(this);
   1373             mHotseat.setOnLongClickListener(this);
   1374         }
   1375 
   1376         mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
   1377         View widgetButton = findViewById(R.id.widget_button);
   1378         widgetButton.setOnClickListener(new OnClickListener() {
   1379             @Override
   1380             public void onClick(View arg0) {
   1381                 if (!mWorkspace.isSwitchingState()) {
   1382                     onClickAddWidgetButton(arg0);
   1383                 }
   1384             }
   1385         });
   1386         widgetButton.setOnTouchListener(getHapticFeedbackTouchListener());
   1387 
   1388         View wallpaperButton = findViewById(R.id.wallpaper_button);
   1389         wallpaperButton.setOnClickListener(new OnClickListener() {
   1390             @Override
   1391             public void onClick(View arg0) {
   1392                 if (!mWorkspace.isSwitchingState()) {
   1393                     onClickWallpaperPicker(arg0);
   1394                 }
   1395             }
   1396         });
   1397         wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
   1398 
   1399         View settingsButton = findViewById(R.id.settings_button);
   1400         if (hasSettings()) {
   1401             settingsButton.setOnClickListener(new OnClickListener() {
   1402                 @Override
   1403                 public void onClick(View arg0) {
   1404                     if (!mWorkspace.isSwitchingState()) {
   1405                         onClickSettingsButton(arg0);
   1406                     }
   1407                 }
   1408             });
   1409             settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
   1410         } else {
   1411             settingsButton.setVisibility(View.GONE);
   1412         }
   1413 
   1414         mOverviewPanel.setAlpha(0f);
   1415 
   1416         // Setup the workspace
   1417         mWorkspace.setHapticFeedbackEnabled(false);
   1418         mWorkspace.setOnLongClickListener(this);
   1419         mWorkspace.setup(dragController);
   1420         dragController.addDragListener(mWorkspace);
   1421 
   1422         // Get the search/delete bar
   1423         mSearchDropTargetBar = (SearchDropTargetBar)
   1424                 mDragLayer.findViewById(R.id.search_drop_target_bar);
   1425 
   1426         // Setup AppsCustomize
   1427         mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane);
   1428         mAppsCustomizeContent = (AppsCustomizePagedView)
   1429                 mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
   1430         mAppsCustomizeContent.setup(this, dragController);
   1431 
   1432         // Setup the drag controller (drop targets have to be added in reverse order in priority)
   1433         dragController.setDragScoller(mWorkspace);
   1434         dragController.setScrollView(mDragLayer);
   1435         dragController.setMoveTarget(mWorkspace);
   1436         dragController.addDropTarget(mWorkspace);
   1437         if (mSearchDropTargetBar != null) {
   1438             mSearchDropTargetBar.setup(this, dragController);
   1439             mSearchDropTargetBar.setQsbSearchBar(getQsbBar());
   1440         }
   1441 
   1442         if (getResources().getBoolean(R.bool.debug_memory_enabled)) {
   1443             Log.v(TAG, "adding WeightWatcher");
   1444             mWeightWatcher = new WeightWatcher(this);
   1445             mWeightWatcher.setAlpha(0.5f);
   1446             ((FrameLayout) mLauncherView).addView(mWeightWatcher,
   1447                     new FrameLayout.LayoutParams(
   1448                             FrameLayout.LayoutParams.MATCH_PARENT,
   1449                             FrameLayout.LayoutParams.WRAP_CONTENT,
   1450                             Gravity.BOTTOM)
   1451             );
   1452 
   1453             boolean show = shouldShowWeightWatcher();
   1454             mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
   1455         }
   1456     }
   1457 
   1458     /**
   1459      * Sets the all apps button. This method is called from {@link Hotseat}.
   1460      */
   1461     public void setAllAppsButton(View allAppsButton) {
   1462         mAllAppsButton = allAppsButton;
   1463     }
   1464 
   1465     public View getAllAppsButton() {
   1466         return mAllAppsButton;
   1467     }
   1468 
   1469     /**
   1470      * Creates a view representing a shortcut.
   1471      *
   1472      * @param info The data structure describing the shortcut.
   1473      *
   1474      * @return A View inflated from R.layout.application.
   1475      */
   1476     View createShortcut(ShortcutInfo info) {
   1477         return createShortcut(R.layout.application,
   1478                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
   1479     }
   1480 
   1481     /**
   1482      * Creates a view representing a shortcut inflated from the specified resource.
   1483      *
   1484      * @param layoutResId The id of the XML layout used to create the shortcut.
   1485      * @param parent The group the shortcut belongs to.
   1486      * @param info The data structure describing the shortcut.
   1487      *
   1488      * @return A View inflated from layoutResId.
   1489      */
   1490     View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
   1491         BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
   1492         favorite.applyFromShortcutInfo(info, mIconCache, true);
   1493         favorite.setOnClickListener(this);
   1494         favorite.setOnFocusChangeListener(mFocusHandler);
   1495         return favorite;
   1496     }
   1497 
   1498     /**
   1499      * Add a shortcut to the workspace.
   1500      *
   1501      * @param data The intent describing the shortcut.
   1502      * @param cellInfo The position on screen where to create the shortcut.
   1503      */
   1504     private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
   1505             int cellY) {
   1506         int[] cellXY = mTmpAddItemCellCoordinates;
   1507         int[] touchXY = mPendingAddInfo.dropPos;
   1508         CellLayout layout = getCellLayout(container, screenId);
   1509 
   1510         boolean foundCellSpan = false;
   1511 
   1512         ShortcutInfo info = mModel.infoFromShortcutIntent(this, data);
   1513         if (info == null) {
   1514             return;
   1515         }
   1516         final View view = createShortcut(info);
   1517 
   1518         // First we check if we already know the exact location where we want to add this item.
   1519         if (cellX >= 0 && cellY >= 0) {
   1520             cellXY[0] = cellX;
   1521             cellXY[1] = cellY;
   1522             foundCellSpan = true;
   1523 
   1524             // If appropriate, either create a folder or add to an existing folder
   1525             if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
   1526                     true, null,null)) {
   1527                 return;
   1528             }
   1529             DragObject dragObject = new DragObject();
   1530             dragObject.dragInfo = info;
   1531             if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
   1532                     true)) {
   1533                 return;
   1534             }
   1535         } else if (touchXY != null) {
   1536             // when dragging and dropping, just find the closest free spot
   1537             int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY);
   1538             foundCellSpan = (result != null);
   1539         } else {
   1540             foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
   1541         }
   1542 
   1543         if (!foundCellSpan) {
   1544             showOutOfSpaceMessage(isHotseatLayout(layout));
   1545             return;
   1546         }
   1547 
   1548         LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1], false);
   1549 
   1550         if (!mRestoring) {
   1551             mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
   1552                     isWorkspaceLocked());
   1553         }
   1554     }
   1555 
   1556     static int[] getSpanForWidget(Context context, ComponentName component, int minWidth,
   1557             int minHeight) {
   1558         Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null);
   1559         // We want to account for the extra amount of padding that we are adding to the widget
   1560         // to ensure that it gets the full amount of space that it has requested
   1561         int requiredWidth = minWidth + padding.left + padding.right;
   1562         int requiredHeight = minHeight + padding.top + padding.bottom;
   1563         return CellLayout.rectToCell(requiredWidth, requiredHeight, null);
   1564     }
   1565 
   1566     static int[] getSpanForWidget(Context context, AppWidgetProviderInfo info) {
   1567         return getSpanForWidget(context, info.provider, info.minWidth, info.minHeight);
   1568     }
   1569 
   1570     static int[] getMinSpanForWidget(Context context, AppWidgetProviderInfo info) {
   1571         return getSpanForWidget(context, info.provider, info.minResizeWidth, info.minResizeHeight);
   1572     }
   1573 
   1574     static int[] getSpanForWidget(Context context, PendingAddWidgetInfo info) {
   1575         return getSpanForWidget(context, info.componentName, info.minWidth, info.minHeight);
   1576     }
   1577 
   1578     static int[] getMinSpanForWidget(Context context, PendingAddWidgetInfo info) {
   1579         return getSpanForWidget(context, info.componentName, info.minResizeWidth,
   1580                 info.minResizeHeight);
   1581     }
   1582 
   1583     /**
   1584      * Add a widget to the workspace.
   1585      *
   1586      * @param appWidgetId The app widget id
   1587      * @param cellInfo The position on screen where to create the widget.
   1588      */
   1589     private void completeAddAppWidget(final int appWidgetId, long container, long screenId,
   1590             AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) {
   1591         if (appWidgetInfo == null) {
   1592             appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   1593         }
   1594 
   1595         // Calculate the grid spans needed to fit this widget
   1596         CellLayout layout = getCellLayout(container, screenId);
   1597 
   1598         int[] minSpanXY = getMinSpanForWidget(this, appWidgetInfo);
   1599         int[] spanXY = getSpanForWidget(this, appWidgetInfo);
   1600 
   1601         // Try finding open space on Launcher screen
   1602         // We have saved the position to which the widget was dragged-- this really only matters
   1603         // if we are placing widgets on a "spring-loaded" screen
   1604         int[] cellXY = mTmpAddItemCellCoordinates;
   1605         int[] touchXY = mPendingAddInfo.dropPos;
   1606         int[] finalSpan = new int[2];
   1607         boolean foundCellSpan = false;
   1608         if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) {
   1609             cellXY[0] = mPendingAddInfo.cellX;
   1610             cellXY[1] = mPendingAddInfo.cellY;
   1611             spanXY[0] = mPendingAddInfo.spanX;
   1612             spanXY[1] = mPendingAddInfo.spanY;
   1613             foundCellSpan = true;
   1614         } else if (touchXY != null) {
   1615             // when dragging and dropping, just find the closest free spot
   1616             int[] result = layout.findNearestVacantArea(
   1617                     touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0],
   1618                     spanXY[1], cellXY, finalSpan);
   1619             spanXY[0] = finalSpan[0];
   1620             spanXY[1] = finalSpan[1];
   1621             foundCellSpan = (result != null);
   1622         } else {
   1623             foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]);
   1624         }
   1625 
   1626         if (!foundCellSpan) {
   1627             if (appWidgetId != -1) {
   1628                 // Deleting an app widget ID is a void call but writes to disk before returning
   1629                 // to the caller...
   1630                 new AsyncTask<Void, Void, Void>() {
   1631                     public Void doInBackground(Void ... args) {
   1632                         mAppWidgetHost.deleteAppWidgetId(appWidgetId);
   1633                         return null;
   1634                     }
   1635                 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
   1636             }
   1637             showOutOfSpaceMessage(isHotseatLayout(layout));
   1638             return;
   1639         }
   1640 
   1641         // Build Launcher-specific widget info and save to database
   1642         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId,
   1643                 appWidgetInfo.provider);
   1644         launcherInfo.spanX = spanXY[0];
   1645         launcherInfo.spanY = spanXY[1];
   1646         launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
   1647         launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
   1648         launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
   1649 
   1650         LauncherModel.addItemToDatabase(this, launcherInfo,
   1651                 container, screenId, cellXY[0], cellXY[1], false);
   1652 
   1653         if (!mRestoring) {
   1654             if (hostView == null) {
   1655                 // Perform actual inflation because we're live
   1656                 launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   1657                 launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
   1658             } else {
   1659                 // The AppWidgetHostView has already been inflated and instantiated
   1660                 launcherInfo.hostView = hostView;
   1661             }
   1662 
   1663             launcherInfo.hostView.setTag(launcherInfo);
   1664             launcherInfo.hostView.setVisibility(View.VISIBLE);
   1665             launcherInfo.notifyWidgetSizeChanged(this);
   1666 
   1667             mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, cellXY[0], cellXY[1],
   1668                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
   1669 
   1670             addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
   1671         }
   1672         resetAddInfo();
   1673     }
   1674 
   1675     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
   1676         @Override
   1677         public void onReceive(Context context, Intent intent) {
   1678             final String action = intent.getAction();
   1679             if (Intent.ACTION_SCREEN_OFF.equals(action)) {
   1680                 mUserPresent = false;
   1681                 mDragLayer.clearAllResizeFrames();
   1682                 updateRunning();
   1683 
   1684                 // Reset AllApps to its initial state only if we are not in the middle of
   1685                 // processing a multi-step drop
   1686                 if (mAppsCustomizeTabHost != null && mPendingAddInfo.container == ItemInfo.NO_ID) {
   1687                     showWorkspace(false);
   1688                 }
   1689             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
   1690                 mUserPresent = true;
   1691                 updateRunning();
   1692             } else if (ENABLE_DEBUG_INTENTS && DebugIntents.DELETE_DATABASE.equals(action)) {
   1693                 mModel.resetLoadedState(false, true);
   1694                 mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
   1695                         LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
   1696             } else if (ENABLE_DEBUG_INTENTS && DebugIntents.MIGRATE_DATABASE.equals(action)) {
   1697                 mModel.resetLoadedState(false, true);
   1698                 mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
   1699                         LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
   1700                                 | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
   1701             } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
   1702                     || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
   1703                 getModel().forceReload();
   1704             }
   1705         }
   1706     };
   1707 
   1708     @Override
   1709     public void onAttachedToWindow() {
   1710         super.onAttachedToWindow();
   1711 
   1712         // Listen for broadcasts related to user-presence
   1713         final IntentFilter filter = new IntentFilter();
   1714         filter.addAction(Intent.ACTION_SCREEN_OFF);
   1715         filter.addAction(Intent.ACTION_USER_PRESENT);
   1716         // For handling managed profiles
   1717         filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
   1718         filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
   1719         if (ENABLE_DEBUG_INTENTS) {
   1720             filter.addAction(DebugIntents.DELETE_DATABASE);
   1721             filter.addAction(DebugIntents.MIGRATE_DATABASE);
   1722         }
   1723         registerReceiver(mReceiver, filter);
   1724         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
   1725         setupTransparentSystemBarsForLmp();
   1726         mAttached = true;
   1727         mVisible = true;
   1728     }
   1729 
   1730     /**
   1731      * Sets up transparent navigation and status bars in LMP.
   1732      * This method is a no-op for other platform versions.
   1733      */
   1734     @TargetApi(19)
   1735     private void setupTransparentSystemBarsForLmp() {
   1736         // TODO(sansid): use the APIs directly when compiling against L sdk.
   1737         // Currently we use reflection to access the flags and the API to set the transparency
   1738         // on the System bars.
   1739         if (Utilities.isLmpOrAbove()) {
   1740             try {
   1741                 getWindow().getAttributes().systemUiVisibility |=
   1742                         (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
   1743                                 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
   1744                                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
   1745                 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
   1746                         | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
   1747                 Field drawsSysBackgroundsField = WindowManager.LayoutParams.class.getField(
   1748                         "FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS");
   1749                 getWindow().addFlags(drawsSysBackgroundsField.getInt(null));
   1750 
   1751                 Method setStatusBarColorMethod =
   1752                         Window.class.getDeclaredMethod("setStatusBarColor", int.class);
   1753                 Method setNavigationBarColorMethod =
   1754                         Window.class.getDeclaredMethod("setNavigationBarColor", int.class);
   1755                 setStatusBarColorMethod.invoke(getWindow(), Color.TRANSPARENT);
   1756                 setNavigationBarColorMethod.invoke(getWindow(), Color.TRANSPARENT);
   1757             } catch (NoSuchFieldException e) {
   1758                 Log.w(TAG, "NoSuchFieldException while setting up transparent bars");
   1759             } catch (NoSuchMethodException ex) {
   1760                 Log.w(TAG, "NoSuchMethodException while setting up transparent bars");
   1761             } catch (IllegalAccessException e) {
   1762                 Log.w(TAG, "IllegalAccessException while setting up transparent bars");
   1763             } catch (IllegalArgumentException e) {
   1764                 Log.w(TAG, "IllegalArgumentException while setting up transparent bars");
   1765             } catch (InvocationTargetException e) {
   1766                 Log.w(TAG, "InvocationTargetException while setting up transparent bars");
   1767             } finally {}
   1768         }
   1769     }
   1770 
   1771     @Override
   1772     public void onDetachedFromWindow() {
   1773         super.onDetachedFromWindow();
   1774         mVisible = false;
   1775 
   1776         if (mAttached) {
   1777             unregisterReceiver(mReceiver);
   1778             mAttached = false;
   1779         }
   1780         updateRunning();
   1781     }
   1782 
   1783     public void onWindowVisibilityChanged(int visibility) {
   1784         mVisible = visibility == View.VISIBLE;
   1785         updateRunning();
   1786         // The following code used to be in onResume, but it turns out onResume is called when
   1787         // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
   1788         // is a more appropriate event to handle
   1789         if (mVisible) {
   1790             mAppsCustomizeTabHost.onWindowVisible();
   1791             if (!mWorkspaceLoading) {
   1792                 final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
   1793                 // We want to let Launcher draw itself at least once before we force it to build
   1794                 // layers on all the workspace pages, so that transitioning to Launcher from other
   1795                 // apps is nice and speedy.
   1796                 observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
   1797                     private boolean mStarted = false;
   1798                     public void onDraw() {
   1799                         if (mStarted) return;
   1800                         mStarted = true;
   1801                         // We delay the layer building a bit in order to give
   1802                         // other message processing a time to run.  In particular
   1803                         // this avoids a delay in hiding the IME if it was
   1804                         // currently shown, because doing that may involve
   1805                         // some communication back with the app.
   1806                         mWorkspace.postDelayed(mBuildLayersRunnable, 500);
   1807                         final ViewTreeObserver.OnDrawListener listener = this;
   1808                         mWorkspace.post(new Runnable() {
   1809                                 public void run() {
   1810                                     if (mWorkspace != null &&
   1811                                             mWorkspace.getViewTreeObserver() != null) {
   1812                                         mWorkspace.getViewTreeObserver().
   1813                                                 removeOnDrawListener(listener);
   1814                                     }
   1815                                 }
   1816                             });
   1817                         return;
   1818                     }
   1819                 });
   1820             }
   1821             clearTypedText();
   1822         }
   1823     }
   1824 
   1825     private void sendAdvanceMessage(long delay) {
   1826         mHandler.removeMessages(ADVANCE_MSG);
   1827         Message msg = mHandler.obtainMessage(ADVANCE_MSG);
   1828         mHandler.sendMessageDelayed(msg, delay);
   1829         mAutoAdvanceSentTime = System.currentTimeMillis();
   1830     }
   1831 
   1832     private void updateRunning() {
   1833         boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty();
   1834         if (autoAdvanceRunning != mAutoAdvanceRunning) {
   1835             mAutoAdvanceRunning = autoAdvanceRunning;
   1836             if (autoAdvanceRunning) {
   1837                 long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft;
   1838                 sendAdvanceMessage(delay);
   1839             } else {
   1840                 if (!mWidgetsToAdvance.isEmpty()) {
   1841                     mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval -
   1842                             (System.currentTimeMillis() - mAutoAdvanceSentTime));
   1843                 }
   1844                 mHandler.removeMessages(ADVANCE_MSG);
   1845                 mHandler.removeMessages(0); // Remove messages sent using postDelayed()
   1846             }
   1847         }
   1848     }
   1849 
   1850     private final Handler mHandler = new Handler() {
   1851         @Override
   1852         public void handleMessage(Message msg) {
   1853             if (msg.what == ADVANCE_MSG) {
   1854                 int i = 0;
   1855                 for (View key: mWidgetsToAdvance.keySet()) {
   1856                     final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId);
   1857                     final int delay = mAdvanceStagger * i;
   1858                     if (v instanceof Advanceable) {
   1859                        postDelayed(new Runnable() {
   1860                            public void run() {
   1861                                ((Advanceable) v).advance();
   1862                            }
   1863                        }, delay);
   1864                     }
   1865                     i++;
   1866                 }
   1867                 sendAdvanceMessage(mAdvanceInterval);
   1868             }
   1869         }
   1870     };
   1871 
   1872     void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) {
   1873         if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return;
   1874         View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId);
   1875         if (v instanceof Advanceable) {
   1876             mWidgetsToAdvance.put(hostView, appWidgetInfo);
   1877             ((Advanceable) v).fyiWillBeAdvancedByHostKThx();
   1878             updateRunning();
   1879         }
   1880     }
   1881 
   1882     void removeWidgetToAutoAdvance(View hostView) {
   1883         if (mWidgetsToAdvance.containsKey(hostView)) {
   1884             mWidgetsToAdvance.remove(hostView);
   1885             updateRunning();
   1886         }
   1887     }
   1888 
   1889     public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
   1890         removeWidgetToAutoAdvance(launcherInfo.hostView);
   1891         launcherInfo.hostView = null;
   1892     }
   1893 
   1894     void showOutOfSpaceMessage(boolean isHotseatLayout) {
   1895         int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
   1896         Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
   1897     }
   1898 
   1899     public DragLayer getDragLayer() {
   1900         return mDragLayer;
   1901     }
   1902 
   1903     public Workspace getWorkspace() {
   1904         return mWorkspace;
   1905     }
   1906 
   1907     public Hotseat getHotseat() {
   1908         return mHotseat;
   1909     }
   1910 
   1911     public ViewGroup getOverviewPanel() {
   1912         return mOverviewPanel;
   1913     }
   1914 
   1915     public SearchDropTargetBar getSearchBar() {
   1916         return mSearchDropTargetBar;
   1917     }
   1918 
   1919     public LauncherAppWidgetHost getAppWidgetHost() {
   1920         return mAppWidgetHost;
   1921     }
   1922 
   1923     public LauncherModel getModel() {
   1924         return mModel;
   1925     }
   1926 
   1927     protected SharedPreferences getSharedPrefs() {
   1928         return mSharedPrefs;
   1929     }
   1930 
   1931     public void closeSystemDialogs() {
   1932         getWindow().closeAllPanels();
   1933 
   1934         // Whatever we were doing is hereby canceled.
   1935         setWaitingForResult(false);
   1936     }
   1937 
   1938     @Override
   1939     protected void onNewIntent(Intent intent) {
   1940         long startTime = 0;
   1941         if (DEBUG_RESUME_TIME) {
   1942             startTime = System.currentTimeMillis();
   1943         }
   1944         super.onNewIntent(intent);
   1945 
   1946         // Close the menu
   1947         if (Intent.ACTION_MAIN.equals(intent.getAction())) {
   1948             // also will cancel mWaitingForResult.
   1949             closeSystemDialogs();
   1950 
   1951             final boolean alreadyOnHome = mHasFocus && ((intent.getFlags() &
   1952                     Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
   1953                     != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
   1954 
   1955             if (mWorkspace == null) {
   1956                 // Can be cases where mWorkspace is null, this prevents a NPE
   1957                 return;
   1958             }
   1959             Folder openFolder = mWorkspace.getOpenFolder();
   1960             // In all these cases, only animate if we're already on home
   1961             mWorkspace.exitWidgetResizeMode();
   1962 
   1963             boolean moveToDefaultScreen = mLauncherCallbacks != null ?
   1964                     mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
   1965             if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
   1966                     openFolder == null && moveToDefaultScreen) {
   1967                 mWorkspace.moveToDefaultScreen(true);
   1968             }
   1969 
   1970             closeFolder();
   1971             exitSpringLoadedDragMode();
   1972 
   1973             // If we are already on home, then just animate back to the workspace,
   1974             // otherwise, just wait until onResume to set the state back to Workspace
   1975             if (alreadyOnHome) {
   1976                 showWorkspace(true);
   1977             } else {
   1978                 mOnResumeState = State.WORKSPACE;
   1979             }
   1980 
   1981             final View v = getWindow().peekDecorView();
   1982             if (v != null && v.getWindowToken() != null) {
   1983                 InputMethodManager imm = (InputMethodManager)getSystemService(
   1984                         INPUT_METHOD_SERVICE);
   1985                 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
   1986             }
   1987 
   1988             // Reset the apps customize page
   1989             if (!alreadyOnHome && mAppsCustomizeTabHost != null) {
   1990                 mAppsCustomizeTabHost.reset();
   1991             }
   1992 
   1993             if (mLauncherCallbacks != null) {
   1994                 mLauncherCallbacks.onHomeIntent();
   1995             }
   1996         }
   1997 
   1998         if (DEBUG_RESUME_TIME) {
   1999             Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime));
   2000         }
   2001 
   2002         if (mLauncherCallbacks != null) {
   2003             mLauncherCallbacks.onNewIntent(intent);
   2004         }
   2005     }
   2006 
   2007     @Override
   2008     public void onRestoreInstanceState(Bundle state) {
   2009         super.onRestoreInstanceState(state);
   2010         for (int page: mSynchronouslyBoundPages) {
   2011             mWorkspace.restoreInstanceStateForChild(page);
   2012         }
   2013     }
   2014 
   2015     @Override
   2016     protected void onSaveInstanceState(Bundle outState) {
   2017         if (mWorkspace.getChildCount() > 0) {
   2018             outState.putInt(RUNTIME_STATE_CURRENT_SCREEN,
   2019                     mWorkspace.getCurrentPageOffsetFromCustomContent());
   2020         }
   2021         super.onSaveInstanceState(outState);
   2022 
   2023         outState.putInt(RUNTIME_STATE, mState.ordinal());
   2024         // We close any open folder since it will not be re-opened, and we need to make sure
   2025         // this state is reflected.
   2026         closeFolder();
   2027 
   2028         if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
   2029                 mWaitingForResult) {
   2030             outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container);
   2031             outState.putLong(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screenId);
   2032             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX);
   2033             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
   2034             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
   2035             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY);
   2036             outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
   2037             outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
   2038         }
   2039 
   2040         if (mFolderInfo != null && mWaitingForResult) {
   2041             outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
   2042             outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
   2043         }
   2044 
   2045         // Save the current AppsCustomize tab
   2046         if (mAppsCustomizeTabHost != null) {
   2047             AppsCustomizePagedView.ContentType type = mAppsCustomizeContent.getContentType();
   2048             String currentTabTag = mAppsCustomizeTabHost.getTabTagForContentType(type);
   2049             if (currentTabTag != null) {
   2050                 outState.putString("apps_customize_currentTab", currentTabTag);
   2051             }
   2052             int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex();
   2053             outState.putInt("apps_customize_currentIndex", currentIndex);
   2054         }
   2055         outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId);
   2056 
   2057         if (mLauncherCallbacks != null) {
   2058             mLauncherCallbacks.onSaveInstanceState(outState);
   2059         }
   2060     }
   2061 
   2062     @Override
   2063     public void onDestroy() {
   2064         super.onDestroy();
   2065 
   2066         // Remove all pending runnables
   2067         mHandler.removeMessages(ADVANCE_MSG);
   2068         mHandler.removeMessages(0);
   2069         mWorkspace.removeCallbacks(mBuildLayersRunnable);
   2070 
   2071         // Stop callbacks from LauncherModel
   2072         LauncherAppState app = (LauncherAppState.getInstance());
   2073 
   2074         // It's possible to receive onDestroy after a new Launcher activity has
   2075         // been created. In this case, don't interfere with the new Launcher.
   2076         if (mModel.isCurrentCallbacks(this)) {
   2077             mModel.stopLoader();
   2078             app.setLauncher(null);
   2079         }
   2080 
   2081         try {
   2082             mAppWidgetHost.stopListening();
   2083         } catch (NullPointerException ex) {
   2084             Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
   2085         }
   2086         mAppWidgetHost = null;
   2087 
   2088         mWidgetsToAdvance.clear();
   2089 
   2090         TextKeyListener.getInstance().release();
   2091 
   2092         // Disconnect any of the callbacks and drawables associated with ItemInfos on the workspace
   2093         // to prevent leaking Launcher activities on orientation change.
   2094         if (mModel != null) {
   2095             mModel.unbindItemInfosAndClearQueuedBindRunnables();
   2096         }
   2097 
   2098         getContentResolver().unregisterContentObserver(mWidgetObserver);
   2099         unregisterReceiver(mCloseSystemDialogsReceiver);
   2100 
   2101         mDragLayer.clearAllResizeFrames();
   2102         ((ViewGroup) mWorkspace.getParent()).removeAllViews();
   2103         mWorkspace.removeAllWorkspaceScreens();
   2104         mWorkspace = null;
   2105         mDragController = null;
   2106 
   2107         LauncherAnimUtils.onDestroyActivity();
   2108 
   2109         if (mLauncherCallbacks != null) {
   2110             mLauncherCallbacks.onDestroy();
   2111         }
   2112     }
   2113 
   2114     public DragController getDragController() {
   2115         return mDragController;
   2116     }
   2117 
   2118     @Override
   2119     public void startActivityForResult(Intent intent, int requestCode) {
   2120         if (requestCode >= 0) {
   2121             setWaitingForResult(true);
   2122         }
   2123         super.startActivityForResult(intent, requestCode);
   2124     }
   2125 
   2126     /**
   2127      * Indicates that we want global search for this activity by setting the globalSearch
   2128      * argument for {@link #startSearch} to true.
   2129      */
   2130     @Override
   2131     public void startSearch(String initialQuery, boolean selectInitialQuery,
   2132             Bundle appSearchData, boolean globalSearch) {
   2133 
   2134         showWorkspace(true);
   2135 
   2136         if (initialQuery == null) {
   2137             // Use any text typed in the launcher as the initial query
   2138             initialQuery = getTypedText();
   2139         }
   2140         if (appSearchData == null) {
   2141             appSearchData = new Bundle();
   2142             appSearchData.putString("source", "launcher-search");
   2143         }
   2144         Rect sourceBounds = new Rect();
   2145         if (mSearchDropTargetBar != null) {
   2146             sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
   2147         }
   2148 
   2149         boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery,
   2150                 appSearchData, sourceBounds);
   2151         if (clearTextImmediately) {
   2152             clearTypedText();
   2153         }
   2154     }
   2155 
   2156     /**
   2157      * Start a text search.
   2158      *
   2159      * @return {@code true} if the search will start immediately, so any further keypresses
   2160      * will be handled directly by the search UI. {@code false} if {@link Launcher} should continue
   2161      * to buffer keypresses.
   2162      */
   2163     public boolean startSearch(String initialQuery,
   2164             boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
   2165         if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) {
   2166             return mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData,
   2167                     sourceBounds);
   2168         }
   2169 
   2170         startGlobalSearch(initialQuery, selectInitialQuery,
   2171                 appSearchData, sourceBounds);
   2172         return false;
   2173     }
   2174 
   2175     /**
   2176      * Starts the global search activity. This code is a copied from SearchManager
   2177      */
   2178     private void startGlobalSearch(String initialQuery,
   2179             boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
   2180         final SearchManager searchManager =
   2181             (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   2182         ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
   2183         if (globalSearchActivity == null) {
   2184             Log.w(TAG, "No global search activity found.");
   2185             return;
   2186         }
   2187         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
   2188         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2189         intent.setComponent(globalSearchActivity);
   2190         // Make sure that we have a Bundle to put source in
   2191         if (appSearchData == null) {
   2192             appSearchData = new Bundle();
   2193         } else {
   2194             appSearchData = new Bundle(appSearchData);
   2195         }
   2196         // Set source to package name of app that starts global search if not set already.
   2197         if (!appSearchData.containsKey("source")) {
   2198             appSearchData.putString("source", getPackageName());
   2199         }
   2200         intent.putExtra(SearchManager.APP_DATA, appSearchData);
   2201         if (!TextUtils.isEmpty(initialQuery)) {
   2202             intent.putExtra(SearchManager.QUERY, initialQuery);
   2203         }
   2204         if (selectInitialQuery) {
   2205             intent.putExtra(SearchManager.EXTRA_SELECT_QUERY, selectInitialQuery);
   2206         }
   2207         intent.setSourceBounds(sourceBounds);
   2208         try {
   2209             startActivity(intent);
   2210         } catch (ActivityNotFoundException ex) {
   2211             Log.e(TAG, "Global search activity not found: " + globalSearchActivity);
   2212         }
   2213     }
   2214 
   2215     public boolean isOnCustomContent() {
   2216         return mWorkspace.isOnOrMovingToCustomContent();
   2217     }
   2218 
   2219     @Override
   2220     public boolean onPrepareOptionsMenu(Menu menu) {
   2221         super.onPrepareOptionsMenu(menu);
   2222         if (!isOnCustomContent()) {
   2223             // Close any open folders
   2224             closeFolder();
   2225             // Stop resizing any widgets
   2226             mWorkspace.exitWidgetResizeMode();
   2227             if (!mWorkspace.isInOverviewMode()) {
   2228                 // Show the overview mode
   2229                 showOverviewMode(true);
   2230             } else {
   2231                 showWorkspace(true);
   2232             }
   2233         }
   2234         if (mLauncherCallbacks != null) {
   2235             return mLauncherCallbacks.onPrepareOptionsMenu(menu);
   2236         }
   2237 
   2238         return false;
   2239     }
   2240 
   2241     @Override
   2242     public boolean onSearchRequested() {
   2243         startSearch(null, false, null, true);
   2244         // Use a custom animation for launching search
   2245         return true;
   2246     }
   2247 
   2248     public boolean isWorkspaceLocked() {
   2249         return mWorkspaceLoading || mWaitingForResult;
   2250     }
   2251 
   2252     public boolean isWorkspaceLoading() {
   2253         return mWorkspaceLoading;
   2254     }
   2255 
   2256     private void setWorkspaceLoading(boolean value) {
   2257         boolean isLocked = isWorkspaceLocked();
   2258         mWorkspaceLoading = value;
   2259         if (isLocked != isWorkspaceLocked()) {
   2260             onWorkspaceLockedChanged();
   2261         }
   2262     }
   2263 
   2264     private void setWaitingForResult(boolean value) {
   2265         boolean isLocked = isWorkspaceLocked();
   2266         mWaitingForResult = value;
   2267         if (isLocked != isWorkspaceLocked()) {
   2268             onWorkspaceLockedChanged();
   2269         }
   2270     }
   2271 
   2272     protected void onWorkspaceLockedChanged() {
   2273         if (mLauncherCallbacks != null) {
   2274             mLauncherCallbacks.onWorkspaceLockedChanged();
   2275         }
   2276     }
   2277 
   2278     private void resetAddInfo() {
   2279         mPendingAddInfo.container = ItemInfo.NO_ID;
   2280         mPendingAddInfo.screenId = -1;
   2281         mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
   2282         mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
   2283         mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
   2284         mPendingAddInfo.dropPos = null;
   2285     }
   2286 
   2287     void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
   2288             final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo) {
   2289         addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
   2290     }
   2291 
   2292     void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
   2293             final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo, int
   2294             delay) {
   2295         if (appWidgetInfo.configure != null) {
   2296             mPendingAddWidgetInfo = appWidgetInfo;
   2297             mPendingAddWidgetId = appWidgetId;
   2298 
   2299             // Launch over to configure widget, if needed
   2300             mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
   2301                     mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
   2302 
   2303         } else {
   2304             // Otherwise just add it
   2305             Runnable onComplete = new Runnable() {
   2306                 @Override
   2307                 public void run() {
   2308                     // Exit spring loaded mode if necessary after adding the widget
   2309                     exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT,
   2310                             null);
   2311                 }
   2312             };
   2313             completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
   2314                     appWidgetInfo);
   2315             mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
   2316         }
   2317     }
   2318 
   2319     protected void moveToCustomContentScreen(boolean animate) {
   2320         // Close any folders that may be open.
   2321         closeFolder();
   2322         mWorkspace.moveToCustomContentScreen(animate);
   2323     }
   2324     /**
   2325      * Process a shortcut drop.
   2326      *
   2327      * @param componentName The name of the component
   2328      * @param screenId The ID of the screen where it should be added
   2329      * @param cell The cell it should be added to, optional
   2330      * @param position The location on the screen where it was dropped, optional
   2331      */
   2332     void processShortcutFromDrop(ComponentName componentName, long container, long screenId,
   2333             int[] cell, int[] loc) {
   2334         resetAddInfo();
   2335         mPendingAddInfo.container = container;
   2336         mPendingAddInfo.screenId = screenId;
   2337         mPendingAddInfo.dropPos = loc;
   2338 
   2339         if (cell != null) {
   2340             mPendingAddInfo.cellX = cell[0];
   2341             mPendingAddInfo.cellY = cell[1];
   2342         }
   2343 
   2344         Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
   2345         createShortcutIntent.setComponent(componentName);
   2346         processShortcut(createShortcutIntent);
   2347     }
   2348 
   2349     /**
   2350      * Process a widget drop.
   2351      *
   2352      * @param info The PendingAppWidgetInfo of the widget being added.
   2353      * @param screenId The ID of the screen where it should be added
   2354      * @param cell The cell it should be added to, optional
   2355      * @param position The location on the screen where it was dropped, optional
   2356      */
   2357     void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId,
   2358             int[] cell, int[] span, int[] loc) {
   2359         resetAddInfo();
   2360         mPendingAddInfo.container = info.container = container;
   2361         mPendingAddInfo.screenId = info.screenId = screenId;
   2362         mPendingAddInfo.dropPos = loc;
   2363         mPendingAddInfo.minSpanX = info.minSpanX;
   2364         mPendingAddInfo.minSpanY = info.minSpanY;
   2365 
   2366         if (cell != null) {
   2367             mPendingAddInfo.cellX = cell[0];
   2368             mPendingAddInfo.cellY = cell[1];
   2369         }
   2370         if (span != null) {
   2371             mPendingAddInfo.spanX = span[0];
   2372             mPendingAddInfo.spanY = span[1];
   2373         }
   2374 
   2375         AppWidgetHostView hostView = info.boundWidget;
   2376         int appWidgetId;
   2377         if (hostView != null) {
   2378             appWidgetId = hostView.getAppWidgetId();
   2379             addAppWidgetImpl(appWidgetId, info, hostView, info.info);
   2380         } else {
   2381             // In this case, we either need to start an activity to get permission to bind
   2382             // the widget, or we need to start an activity to configure the widget, or both.
   2383             appWidgetId = getAppWidgetHost().allocateAppWidgetId();
   2384             Bundle options = info.bindOptions;
   2385 
   2386             boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
   2387                     appWidgetId, info.info, options);
   2388             if (success) {
   2389                 addAppWidgetImpl(appWidgetId, info, null, info.info);
   2390             } else {
   2391                 mPendingAddWidgetInfo = info.info;
   2392                 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
   2393                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   2394                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
   2395                 mAppWidgetManager.getUser(mPendingAddWidgetInfo)
   2396                     .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
   2397                 // TODO: we need to make sure that this accounts for the options bundle.
   2398                 // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
   2399                 startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
   2400             }
   2401         }
   2402     }
   2403 
   2404     void processShortcut(Intent intent) {
   2405         Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
   2406     }
   2407 
   2408     void processWallpaper(Intent intent) {
   2409         startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
   2410     }
   2411 
   2412     FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
   2413             int cellY) {
   2414         final FolderInfo folderInfo = new FolderInfo();
   2415         folderInfo.title = getText(R.string.folder_name);
   2416 
   2417         // Update the model
   2418         LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId, cellX, cellY,
   2419                 false);
   2420         sFolders.put(folderInfo.id, folderInfo);
   2421 
   2422         // Create the view
   2423         FolderIcon newFolder =
   2424             FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
   2425         mWorkspace.addInScreen(newFolder, container, screenId, cellX, cellY, 1, 1,
   2426                 isWorkspaceLocked());
   2427         // Force measure the new folder icon
   2428         CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder);
   2429         parent.getShortcutsAndWidgets().measureChild(newFolder);
   2430         return newFolder;
   2431     }
   2432 
   2433     void removeFolder(FolderInfo folder) {
   2434         sFolders.remove(folder.id);
   2435     }
   2436 
   2437     protected ComponentName getWallpaperPickerComponent() {
   2438         if (mLauncherCallbacks != null) {
   2439             return mLauncherCallbacks.getWallpaperPickerComponent();
   2440         }
   2441         return new ComponentName(getPackageName(), LauncherWallpaperPickerActivity.class.getName());
   2442     }
   2443 
   2444     /**
   2445      * Registers various content observers. The current implementation registers
   2446      * only a favorites observer to keep track of the favorites applications.
   2447      */
   2448     private void registerContentObservers() {
   2449         ContentResolver resolver = getContentResolver();
   2450         resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
   2451                 true, mWidgetObserver);
   2452     }
   2453 
   2454     @Override
   2455     public boolean dispatchKeyEvent(KeyEvent event) {
   2456         if (event.getAction() == KeyEvent.ACTION_DOWN) {
   2457             switch (event.getKeyCode()) {
   2458                 case KeyEvent.KEYCODE_HOME:
   2459                     return true;
   2460                 case KeyEvent.KEYCODE_VOLUME_DOWN:
   2461                     if (Utilities.isPropertyEnabled(DUMP_STATE_PROPERTY)) {
   2462                         dumpState();
   2463                         return true;
   2464                     }
   2465                     break;
   2466             }
   2467         } else if (event.getAction() == KeyEvent.ACTION_UP) {
   2468             switch (event.getKeyCode()) {
   2469                 case KeyEvent.KEYCODE_HOME:
   2470                     return true;
   2471             }
   2472         }
   2473 
   2474         return super.dispatchKeyEvent(event);
   2475     }
   2476 
   2477     @Override
   2478     public void onBackPressed() {
   2479         if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) {
   2480             return;
   2481         }
   2482 
   2483         if (isAllAppsVisible()) {
   2484             if (mAppsCustomizeContent.getContentType() ==
   2485                     AppsCustomizePagedView.ContentType.Applications) {
   2486                 showWorkspace(true);
   2487             } else {
   2488                 showOverviewMode(true);
   2489             }
   2490         } else if (mWorkspace.isInOverviewMode()) {
   2491             mWorkspace.exitOverviewMode(true);
   2492         } else if (mWorkspace.getOpenFolder() != null) {
   2493             Folder openFolder = mWorkspace.getOpenFolder();
   2494             if (openFolder.isEditingName()) {
   2495                 openFolder.dismissEditingName();
   2496             } else {
   2497                 closeFolder();
   2498             }
   2499         } else {
   2500             mWorkspace.exitWidgetResizeMode();
   2501 
   2502             // Back button is a no-op here, but give at least some feedback for the button press
   2503             mWorkspace.showOutlinesTemporarily();
   2504         }
   2505     }
   2506 
   2507     /**
   2508      * Re-listen when widgets are reset.
   2509      */
   2510     private void onAppWidgetReset() {
   2511         if (mAppWidgetHost != null) {
   2512             mAppWidgetHost.startListening();
   2513         }
   2514     }
   2515 
   2516     /**
   2517      * Launches the intent referred by the clicked shortcut.
   2518      *
   2519      * @param v The view representing the clicked shortcut.
   2520      */
   2521     public void onClick(View v) {
   2522         // Make sure that rogue clicks don't get through while allapps is launching, or after the
   2523         // view has detached (it's possible for this to happen if the view is removed mid touch).
   2524         if (v.getWindowToken() == null) {
   2525             return;
   2526         }
   2527 
   2528         if (!mWorkspace.isFinishedSwitchingState()) {
   2529             return;
   2530         }
   2531 
   2532         if (v instanceof Workspace) {
   2533             if (mWorkspace.isInOverviewMode()) {
   2534                 mWorkspace.exitOverviewMode(true);
   2535             }
   2536             return;
   2537         }
   2538 
   2539         if (v instanceof CellLayout) {
   2540             if (mWorkspace.isInOverviewMode()) {
   2541                 mWorkspace.exitOverviewMode(mWorkspace.indexOfChild(v), true);
   2542             }
   2543         }
   2544 
   2545         Object tag = v.getTag();
   2546         if (tag instanceof ShortcutInfo) {
   2547             onClickAppShortcut(v);
   2548         } else if (tag instanceof FolderInfo) {
   2549             if (v instanceof FolderIcon) {
   2550                 onClickFolderIcon(v);
   2551             }
   2552         } else if (v == mAllAppsButton) {
   2553             onClickAllAppsButton(v);
   2554         } else if (tag instanceof AppInfo) {
   2555             startAppShortcutOrInfoActivity(v);
   2556         } else if (tag instanceof LauncherAppWidgetInfo) {
   2557             if (v instanceof PendingAppWidgetHostView) {
   2558                 onClickPendingWidget((PendingAppWidgetHostView) v);
   2559             }
   2560         }
   2561     }
   2562 
   2563     public void onClickPagedViewIcon(View v) {
   2564         startAppShortcutOrInfoActivity(v);
   2565         if (mLauncherCallbacks != null) {
   2566             mLauncherCallbacks.onClickPagedViewIcon(v);
   2567         }
   2568     }
   2569 
   2570     public boolean onTouch(View v, MotionEvent event) {
   2571         return false;
   2572     }
   2573 
   2574     /**
   2575      * Event handler for the app widget view which has not fully restored.
   2576      */
   2577     public void onClickPendingWidget(final PendingAppWidgetHostView v) {
   2578         if (mIsSafeModeEnabled) {
   2579             Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
   2580             return;
   2581         }
   2582 
   2583         final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
   2584         if (v.isReadyForClickSetup()) {
   2585             int widgetId = info.appWidgetId;
   2586             AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
   2587             if (appWidgetInfo != null) {
   2588                 mPendingAddWidgetInfo = appWidgetInfo;
   2589                 mPendingAddInfo.copyFrom(info);
   2590                 mPendingAddWidgetId = widgetId;
   2591 
   2592                 AppWidgetManagerCompat.getInstance(this).startConfigActivity(appWidgetInfo,
   2593                         info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
   2594             }
   2595         } else if (info.installProgress < 0) {
   2596             // The install has not been queued
   2597             final String packageName = info.providerName.getPackageName();
   2598             showBrokenAppInstallDialog(packageName,
   2599                 new DialogInterface.OnClickListener() {
   2600                     public void onClick(DialogInterface dialog, int id) {
   2601                         startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
   2602                     }
   2603                 });
   2604         } else {
   2605             // Download has started.
   2606             final String packageName = info.providerName.getPackageName();
   2607             startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
   2608         }
   2609     }
   2610 
   2611     /**
   2612      * Event handler for the "grid" button that appears on the home screen, which
   2613      * enters all apps mode.
   2614      *
   2615      * @param v The view that was clicked.
   2616      */
   2617     protected void onClickAllAppsButton(View v) {
   2618         if (LOGD) Log.d(TAG, "onClickAllAppsButton");
   2619         if (isAllAppsVisible()) {
   2620             showWorkspace(true);
   2621         } else {
   2622             showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
   2623         }
   2624         if (mLauncherCallbacks != null) {
   2625             mLauncherCallbacks.onClickAllAppsButton(v);
   2626         }
   2627     }
   2628 
   2629     private void showBrokenAppInstallDialog(final String packageName,
   2630             DialogInterface.OnClickListener onSearchClickListener) {
   2631         new AlertDialog.Builder(this)
   2632             .setTitle(R.string.abandoned_promises_title)
   2633             .setMessage(R.string.abandoned_promise_explanation)
   2634             .setPositiveButton(R.string.abandoned_search, onSearchClickListener)
   2635             .setNeutralButton(R.string.abandoned_clean_this,
   2636                 new DialogInterface.OnClickListener() {
   2637                     public void onClick(DialogInterface dialog, int id) {
   2638                         final UserHandleCompat user = UserHandleCompat.myUserHandle();
   2639                         mWorkspace.removeAbandonedPromise(packageName, user);
   2640                     }
   2641                 })
   2642             .create().show();
   2643         return;
   2644     }
   2645 
   2646     /**
   2647      * Event handler for an app shortcut click.
   2648      *
   2649      * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
   2650      */
   2651     protected void onClickAppShortcut(final View v) {
   2652         if (LOGD) Log.d(TAG, "onClickAppShortcut");
   2653         Object tag = v.getTag();
   2654         if (!(tag instanceof ShortcutInfo)) {
   2655             throw new IllegalArgumentException("Input must be a Shortcut");
   2656         }
   2657 
   2658         // Open shortcut
   2659         final ShortcutInfo shortcut = (ShortcutInfo) tag;
   2660 
   2661         if (shortcut.isDisabled != 0) {
   2662             int error = R.string.activity_not_available;
   2663             if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
   2664                 error = R.string.safemode_shortcut_error;
   2665             }
   2666             Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
   2667             return;
   2668         }
   2669 
   2670         final Intent intent = shortcut.intent;
   2671 
   2672         // Check for special shortcuts
   2673         if (intent.getComponent() != null) {
   2674             final String shortcutClass = intent.getComponent().getClassName();
   2675 
   2676             if (shortcutClass.equals(MemoryDumpActivity.class.getName())) {
   2677                 MemoryDumpActivity.startDump(this);
   2678                 return;
   2679             } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) {
   2680                 toggleShowWeightWatcher();
   2681                 return;
   2682             }
   2683         }
   2684 
   2685         // Check for abandoned promise
   2686         if ((v instanceof BubbleTextView)
   2687                 && shortcut.isPromise()
   2688                 && !shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE)) {
   2689             showBrokenAppInstallDialog(
   2690                     shortcut.getTargetComponent().getPackageName(),
   2691                     new DialogInterface.OnClickListener() {
   2692                         public void onClick(DialogInterface dialog, int id) {
   2693                             startAppShortcutOrInfoActivity(v);
   2694                         }
   2695                     });
   2696             return;
   2697         }
   2698 
   2699         // Start activities
   2700         startAppShortcutOrInfoActivity(v);
   2701 
   2702         if (mLauncherCallbacks != null) {
   2703             mLauncherCallbacks.onClickAppShortcut(v);
   2704         }
   2705     }
   2706 
   2707     private void startAppShortcutOrInfoActivity(View v) {
   2708         Object tag = v.getTag();
   2709         final ShortcutInfo shortcut;
   2710         final Intent intent;
   2711         if (tag instanceof ShortcutInfo) {
   2712             shortcut = (ShortcutInfo) tag;
   2713             intent = shortcut.intent;
   2714             int[] pos = new int[2];
   2715             v.getLocationOnScreen(pos);
   2716             intent.setSourceBounds(new Rect(pos[0], pos[1],
   2717                     pos[0] + v.getWidth(), pos[1] + v.getHeight()));
   2718 
   2719         } else if (tag instanceof AppInfo) {
   2720             shortcut = null;
   2721             intent = ((AppInfo) tag).intent;
   2722         } else {
   2723             throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
   2724         }
   2725 
   2726         boolean success = startActivitySafely(v, intent, tag);
   2727         mStats.recordLaunch(intent, shortcut);
   2728 
   2729         if (success && v instanceof BubbleTextView) {
   2730             mWaitingForResume = (BubbleTextView) v;
   2731             mWaitingForResume.setStayPressed(true);
   2732         }
   2733     }
   2734 
   2735     /**
   2736      * Event handler for a folder icon click.
   2737      *
   2738      * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
   2739      */
   2740     protected void onClickFolderIcon(View v) {
   2741         if (LOGD) Log.d(TAG, "onClickFolder");
   2742         if (!(v instanceof FolderIcon)){
   2743             throw new IllegalArgumentException("Input must be a FolderIcon");
   2744         }
   2745 
   2746         FolderIcon folderIcon = (FolderIcon) v;
   2747         final FolderInfo info = folderIcon.getFolderInfo();
   2748         Folder openFolder = mWorkspace.getFolderForTag(info);
   2749 
   2750         // If the folder info reports that the associated folder is open, then verify that
   2751         // it is actually opened. There have been a few instances where this gets out of sync.
   2752         if (info.opened && openFolder == null) {
   2753             Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
   2754                     + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
   2755             info.opened = false;
   2756         }
   2757 
   2758         if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
   2759             // Close any open folder
   2760             closeFolder();
   2761             // Open the requested folder
   2762             openFolder(folderIcon);
   2763         } else {
   2764             // Find the open folder...
   2765             int folderScreen;
   2766             if (openFolder != null) {
   2767                 folderScreen = mWorkspace.getPageForView(openFolder);
   2768                 // .. and close it
   2769                 closeFolder(openFolder);
   2770                 if (folderScreen != mWorkspace.getCurrentPage()) {
   2771                     // Close any folder open on the current screen
   2772                     closeFolder();
   2773                     // Pull the folder onto this screen
   2774                     openFolder(folderIcon);
   2775                 }
   2776             }
   2777         }
   2778 
   2779         if (mLauncherCallbacks != null) {
   2780             mLauncherCallbacks.onClickFolderIcon(v);
   2781         }
   2782     }
   2783 
   2784     /**
   2785      * Event handler for the (Add) Widgets button that appears after a long press
   2786      * on the home screen.
   2787      */
   2788     protected void onClickAddWidgetButton(View view) {
   2789         if (LOGD) Log.d(TAG, "onClickAddWidgetButton");
   2790         if (mIsSafeModeEnabled) {
   2791             Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
   2792         } else {
   2793             showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
   2794             if (mLauncherCallbacks != null) {
   2795                 mLauncherCallbacks.onClickAddWidgetButton(view);
   2796             }
   2797         }
   2798     }
   2799 
   2800     /**
   2801      * Event handler for the wallpaper picker button that appears after a long press
   2802      * on the home screen.
   2803      */
   2804     protected void onClickWallpaperPicker(View v) {
   2805         if (LOGD) Log.d(TAG, "onClickWallpaperPicker");
   2806         final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
   2807         pickWallpaper.setComponent(getWallpaperPickerComponent());
   2808         startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER);
   2809 
   2810         if (mLauncherCallbacks != null) {
   2811             mLauncherCallbacks.onClickWallpaperPicker(v);
   2812         }
   2813     }
   2814 
   2815     /**
   2816      * Event handler for a click on the settings button that appears after a long press
   2817      * on the home screen.
   2818      */
   2819     protected void onClickSettingsButton(View v) {
   2820         if (LOGD) Log.d(TAG, "onClickSettingsButton");
   2821         if (mLauncherCallbacks != null) {
   2822             mLauncherCallbacks.onClickSettingsButton(v);
   2823         }
   2824     }
   2825 
   2826     public void onTouchDownAllAppsButton(View v) {
   2827         // Provide the same haptic feedback that the system offers for virtual keys.
   2828         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2829     }
   2830 
   2831     public void performHapticFeedbackOnTouchDown(View v) {
   2832         // Provide the same haptic feedback that the system offers for virtual keys.
   2833         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2834     }
   2835 
   2836     public View.OnTouchListener getHapticFeedbackTouchListener() {
   2837         if (mHapticFeedbackTouchListener == null) {
   2838             mHapticFeedbackTouchListener = new View.OnTouchListener() {
   2839                 @Override
   2840                 public boolean onTouch(View v, MotionEvent event) {
   2841                     if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
   2842                         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
   2843                     }
   2844                     return false;
   2845                 }
   2846             };
   2847         }
   2848         return mHapticFeedbackTouchListener;
   2849     }
   2850 
   2851     public void onDragStarted(View view) {
   2852         if (isOnCustomContent()) {
   2853             // Custom content screen doesn't participate in drag and drop. If on custom
   2854             // content screen, move to default.
   2855             moveWorkspaceToDefaultScreen();
   2856         }
   2857 
   2858         if (mLauncherCallbacks != null) {
   2859             mLauncherCallbacks.onDragStarted(view);
   2860         }
   2861     }
   2862 
   2863     /**
   2864      * Called when the user stops interacting with the launcher.
   2865      * This implies that the user is now on the homescreen and is not doing housekeeping.
   2866      */
   2867     protected void onInteractionEnd() {
   2868         if (mLauncherCallbacks != null) {
   2869             mLauncherCallbacks.onInteractionEnd();
   2870         }
   2871     }
   2872 
   2873     /**
   2874      * Called when the user starts interacting with the launcher.
   2875      * The possible interactions are:
   2876      *  - open all apps
   2877      *  - reorder an app shortcut, or a widget
   2878      *  - open the overview mode.
   2879      * This is a good time to stop doing things that only make sense
   2880      * when the user is on the homescreen and not doing housekeeping.
   2881      */
   2882     protected void onInteractionBegin() {
   2883         if (mLauncherCallbacks != null) {
   2884             mLauncherCallbacks.onInteractionBegin();
   2885         }
   2886     }
   2887 
   2888     void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) {
   2889         try {
   2890             LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
   2891             launcherApps.showAppDetailsForProfile(componentName, user);
   2892         } catch (SecurityException e) {
   2893             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2894             Log.e(TAG, "Launcher does not have permission to launch settings");
   2895         } catch (ActivityNotFoundException e) {
   2896             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2897             Log.e(TAG, "Unable to launch settings");
   2898         }
   2899     }
   2900 
   2901     // returns true if the activity was started
   2902     boolean startApplicationUninstallActivity(ComponentName componentName, int flags,
   2903             UserHandleCompat user) {
   2904         if ((flags & AppInfo.DOWNLOADED_FLAG) == 0) {
   2905             // System applications cannot be installed. For now, show a toast explaining that.
   2906             // We may give them the option of disabling apps this way.
   2907             int messageId = R.string.uninstall_system_app_text;
   2908             Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
   2909             return false;
   2910         } else {
   2911             String packageName = componentName.getPackageName();
   2912             String className = componentName.getClassName();
   2913             Intent intent = new Intent(
   2914                     Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
   2915             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
   2916                     Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   2917             if (user != null) {
   2918                 user.addToIntent(intent, Intent.EXTRA_USER);
   2919             }
   2920             startActivity(intent);
   2921             return true;
   2922         }
   2923     }
   2924 
   2925     boolean startActivity(View v, Intent intent, Object tag) {
   2926         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2927         try {
   2928             // Only launch using the new animation if the shortcut has not opted out (this is a
   2929             // private contract between launcher and may be ignored in the future).
   2930             boolean useLaunchAnimation = (v != null) &&
   2931                     !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
   2932             LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
   2933             UserManagerCompat userManager = UserManagerCompat.getInstance(this);
   2934 
   2935             UserHandleCompat user = null;
   2936             if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
   2937                 long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
   2938                 user = userManager.getUserForSerialNumber(serialNumber);
   2939             }
   2940 
   2941             Bundle optsBundle = null;
   2942             if (useLaunchAnimation) {
   2943                 ActivityOptions opts = Utilities.isLmpOrAbove() ?
   2944                         ActivityOptions.makeCustomAnimation(this, R.anim.task_open_enter, R.anim.no_anim) :
   2945                         ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
   2946                 optsBundle = opts.toBundle();
   2947             }
   2948 
   2949             if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
   2950                 // Could be launching some bookkeeping activity
   2951                 startActivity(intent, optsBundle);
   2952             } else {
   2953                 // TODO Component can be null when shortcuts are supported for secondary user
   2954                 launcherApps.startActivityForProfile(intent.getComponent(), user,
   2955                         intent.getSourceBounds(), optsBundle);
   2956             }
   2957             return true;
   2958         } catch (SecurityException e) {
   2959             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2960             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   2961                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   2962                     "or use the exported attribute for this activity. "
   2963                     + "tag="+ tag + " intent=" + intent, e);
   2964         }
   2965         return false;
   2966     }
   2967 
   2968     boolean startActivitySafely(View v, Intent intent, Object tag) {
   2969         boolean success = false;
   2970         if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
   2971             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
   2972             return false;
   2973         }
   2974         try {
   2975             success = startActivity(v, intent, tag);
   2976         } catch (ActivityNotFoundException e) {
   2977             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   2978             Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
   2979         }
   2980         return success;
   2981     }
   2982 
   2983     /**
   2984      * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
   2985      * in the DragLayer in the exact absolute location of the original FolderIcon.
   2986      */
   2987     private void copyFolderIconToImage(FolderIcon fi) {
   2988         final int width = fi.getMeasuredWidth();
   2989         final int height = fi.getMeasuredHeight();
   2990 
   2991         // Lazy load ImageView, Bitmap and Canvas
   2992         if (mFolderIconImageView == null) {
   2993             mFolderIconImageView = new ImageView(this);
   2994         }
   2995         if (mFolderIconBitmap == null || mFolderIconBitmap.getWidth() != width ||
   2996                 mFolderIconBitmap.getHeight() != height) {
   2997             mFolderIconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
   2998             mFolderIconCanvas = new Canvas(mFolderIconBitmap);
   2999         }
   3000 
   3001         DragLayer.LayoutParams lp;
   3002         if (mFolderIconImageView.getLayoutParams() instanceof DragLayer.LayoutParams) {
   3003             lp = (DragLayer.LayoutParams) mFolderIconImageView.getLayoutParams();
   3004         } else {
   3005             lp = new DragLayer.LayoutParams(width, height);
   3006         }
   3007 
   3008         // The layout from which the folder is being opened may be scaled, adjust the starting
   3009         // view size by this scale factor.
   3010         float scale = mDragLayer.getDescendantRectRelativeToSelf(fi, mRectForFolderAnimation);
   3011         lp.customPosition = true;
   3012         lp.x = mRectForFolderAnimation.left;
   3013         lp.y = mRectForFolderAnimation.top;
   3014         lp.width = (int) (scale * width);
   3015         lp.height = (int) (scale * height);
   3016 
   3017         mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
   3018         fi.draw(mFolderIconCanvas);
   3019         mFolderIconImageView.setImageBitmap(mFolderIconBitmap);
   3020         if (fi.getFolder() != null) {
   3021             mFolderIconImageView.setPivotX(fi.getFolder().getPivotXForIconAnimation());
   3022             mFolderIconImageView.setPivotY(fi.getFolder().getPivotYForIconAnimation());
   3023         }
   3024         // Just in case this image view is still in the drag layer from a previous animation,
   3025         // we remove it and re-add it.
   3026         if (mDragLayer.indexOfChild(mFolderIconImageView) != -1) {
   3027             mDragLayer.removeView(mFolderIconImageView);
   3028         }
   3029         mDragLayer.addView(mFolderIconImageView, lp);
   3030         if (fi.getFolder() != null) {
   3031             fi.getFolder().bringToFront();
   3032         }
   3033     }
   3034 
   3035     private void growAndFadeOutFolderIcon(FolderIcon fi) {
   3036         if (fi == null) return;
   3037         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
   3038         PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f);
   3039         PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f);
   3040 
   3041         FolderInfo info = (FolderInfo) fi.getTag();
   3042         if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   3043             CellLayout cl = (CellLayout) fi.getParent().getParent();
   3044             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) fi.getLayoutParams();
   3045             cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
   3046         }
   3047 
   3048         // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
   3049         copyFolderIconToImage(fi);
   3050         fi.setVisibility(View.INVISIBLE);
   3051 
   3052         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
   3053                 scaleX, scaleY);
   3054         if (Utilities.isLmpOrAbove()) {
   3055             oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
   3056         }
   3057         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
   3058         oa.start();
   3059     }
   3060 
   3061     private void shrinkAndFadeInFolderIcon(final FolderIcon fi) {
   3062         if (fi == null) return;
   3063         PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
   3064         PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
   3065         PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
   3066 
   3067         final CellLayout cl = (CellLayout) fi.getParent().getParent();
   3068 
   3069         // We remove and re-draw the FolderIcon in-case it has changed
   3070         mDragLayer.removeView(mFolderIconImageView);
   3071         copyFolderIconToImage(fi);
   3072         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
   3073                 scaleX, scaleY);
   3074         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
   3075         oa.addListener(new AnimatorListenerAdapter() {
   3076             @Override
   3077             public void onAnimationEnd(Animator animation) {
   3078                 if (cl != null) {
   3079                     cl.clearFolderLeaveBehind();
   3080                     // Remove the ImageView copy of the FolderIcon and make the original visible.
   3081                     mDragLayer.removeView(mFolderIconImageView);
   3082                     fi.setVisibility(View.VISIBLE);
   3083                 }
   3084             }
   3085         });
   3086         oa.start();
   3087     }
   3088 
   3089     /**
   3090      * Opens the user folder described by the specified tag. The opening of the folder
   3091      * is animated relative to the specified View. If the View is null, no animation
   3092      * is played.
   3093      *
   3094      * @param folderInfo The FolderInfo describing the folder to open.
   3095      */
   3096     public void openFolder(FolderIcon folderIcon) {
   3097         Folder folder = folderIcon.getFolder();
   3098         FolderInfo info = folder.mInfo;
   3099 
   3100         info.opened = true;
   3101 
   3102         // Just verify that the folder hasn't already been added to the DragLayer.
   3103         // There was a one-off crash where the folder had a parent already.
   3104         if (folder.getParent() == null) {
   3105             mDragLayer.addView(folder);
   3106             mDragController.addDropTarget((DropTarget) folder);
   3107         } else {
   3108             Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
   3109                     folder.getParent() + ").");
   3110         }
   3111         folder.animateOpen();
   3112         growAndFadeOutFolderIcon(folderIcon);
   3113 
   3114         // Notify the accessibility manager that this folder "window" has appeared and occluded
   3115         // the workspace items
   3116         folder.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3117         getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
   3118     }
   3119 
   3120     public void closeFolder() {
   3121         Folder folder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
   3122         if (folder != null) {
   3123             if (folder.isEditingName()) {
   3124                 folder.dismissEditingName();
   3125             }
   3126             closeFolder(folder);
   3127         }
   3128     }
   3129 
   3130     void closeFolder(Folder folder) {
   3131         folder.getInfo().opened = false;
   3132 
   3133         ViewGroup parent = (ViewGroup) folder.getParent().getParent();
   3134         if (parent != null) {
   3135             FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
   3136             shrinkAndFadeInFolderIcon(fi);
   3137         }
   3138         folder.animateClosed();
   3139 
   3140         // Notify the accessibility manager that this folder "window" has disappeard and no
   3141         // longer occludeds the workspace items
   3142         getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3143     }
   3144 
   3145     public boolean onLongClick(View v) {
   3146         if (!isDraggingEnabled()) return false;
   3147         if (isWorkspaceLocked()) return false;
   3148         if (mState != State.WORKSPACE) return false;
   3149 
   3150         if (v instanceof Workspace) {
   3151             if (!mWorkspace.isInOverviewMode()) {
   3152                 if (mWorkspace.enterOverviewMode()) {
   3153                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   3154                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   3155                     return true;
   3156                 } else {
   3157                     return false;
   3158                 }
   3159             } else {
   3160                 return false;
   3161             }
   3162         }
   3163 
   3164         CellLayout.CellInfo longClickCellInfo = null;
   3165         View itemUnderLongClick = null;
   3166         if (v.getTag() instanceof ItemInfo) {
   3167             ItemInfo info = (ItemInfo) v.getTag();
   3168             longClickCellInfo = new CellLayout.CellInfo(v, info);;
   3169             itemUnderLongClick = longClickCellInfo.cell;
   3170             resetAddInfo();
   3171         }
   3172 
   3173         // The hotseat touch handling does not go through Workspace, and we always allow long press
   3174         // on hotseat items.
   3175         final boolean inHotseat = isHotseatLayout(v);
   3176         boolean allowLongPress = inHotseat || mWorkspace.allowLongPress();
   3177         if (allowLongPress && !mDragController.isDragging()) {
   3178             if (itemUnderLongClick == null) {
   3179                 // User long pressed on empty space
   3180                 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   3181                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   3182                 if (mWorkspace.isInOverviewMode()) {
   3183                     mWorkspace.startReordering(v);
   3184                 } else {
   3185                     mWorkspace.enterOverviewMode();
   3186                 }
   3187             } else {
   3188                 final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank(
   3189                         mHotseat.getOrderInHotseat(
   3190                                 longClickCellInfo.cellX,
   3191                                 longClickCellInfo.cellY));
   3192                 if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
   3193                     // User long pressed on an item
   3194                     mWorkspace.startDrag(longClickCellInfo);
   3195                 }
   3196             }
   3197         }
   3198         return true;
   3199     }
   3200 
   3201     boolean isHotseatLayout(View layout) {
   3202         return mHotseat != null && layout != null &&
   3203                 (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
   3204     }
   3205 
   3206     /**
   3207      * Returns the CellLayout of the specified container at the specified screen.
   3208      */
   3209     CellLayout getCellLayout(long container, long screenId) {
   3210         if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   3211             if (mHotseat != null) {
   3212                 return mHotseat.getLayout();
   3213             } else {
   3214                 return null;
   3215             }
   3216         } else {
   3217             return (CellLayout) mWorkspace.getScreenWithId(screenId);
   3218         }
   3219     }
   3220 
   3221     public boolean isAllAppsVisible() {
   3222         return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
   3223     }
   3224 
   3225     private void setWorkspaceBackground(boolean workspace) {
   3226         mLauncherView.setBackground(workspace ?
   3227                 mWorkspaceBackgroundDrawable : null);
   3228     }
   3229 
   3230     protected void changeWallpaperVisiblity(boolean visible) {
   3231         int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
   3232         int curflags = getWindow().getAttributes().flags
   3233                 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
   3234         if (wpflags != curflags) {
   3235             getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
   3236         }
   3237         setWorkspaceBackground(visible);
   3238     }
   3239 
   3240     private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
   3241         if (v instanceof LauncherTransitionable) {
   3242             ((LauncherTransitionable) v).onLauncherTransitionPrepare(this, animated, toWorkspace);
   3243         }
   3244     }
   3245 
   3246     private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
   3247         if (v instanceof LauncherTransitionable) {
   3248             ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace);
   3249         }
   3250 
   3251         // Update the workspace transition step as well
   3252         dispatchOnLauncherTransitionStep(v, 0f);
   3253     }
   3254 
   3255     private void dispatchOnLauncherTransitionStep(View v, float t) {
   3256         if (v instanceof LauncherTransitionable) {
   3257             ((LauncherTransitionable) v).onLauncherTransitionStep(this, t);
   3258         }
   3259     }
   3260 
   3261     private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
   3262         if (v instanceof LauncherTransitionable) {
   3263             ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace);
   3264         }
   3265 
   3266         // Update the workspace transition step as well
   3267         dispatchOnLauncherTransitionStep(v, 1f);
   3268     }
   3269 
   3270     /**
   3271      * Things to test when changing the following seven functions.
   3272      *   - Home from workspace
   3273      *          - from center screen
   3274      *          - from other screens
   3275      *   - Home from all apps
   3276      *          - from center screen
   3277      *          - from other screens
   3278      *   - Back from all apps
   3279      *          - from center screen
   3280      *          - from other screens
   3281      *   - Launch app from workspace and quit
   3282      *          - with back
   3283      *          - with home
   3284      *   - Launch app from all apps and quit
   3285      *          - with back
   3286      *          - with home
   3287      *   - Go to a screen that's not the default, then all
   3288      *     apps, and launch and app, and go back
   3289      *          - with back
   3290      *          -with home
   3291      *   - On workspace, long press power and go back
   3292      *          - with back
   3293      *          - with home
   3294      *   - On all apps, long press power and go back
   3295      *          - with back
   3296      *          - with home
   3297      *   - On workspace, power off
   3298      *   - On all apps, power off
   3299      *   - Launch an app and turn off the screen while in that app
   3300      *          - Go back with home key
   3301      *          - Go back with back key  TODO: make this not go to workspace
   3302      *          - From all apps
   3303      *          - From workspace
   3304      *   - Enter and exit car mode (becuase it causes an extra configuration changed)
   3305      *          - From all apps
   3306      *          - From the center workspace
   3307      *          - From another workspace
   3308      */
   3309 
   3310     /**
   3311      * Zoom the camera out from the workspace to reveal 'toView'.
   3312      * Assumes that the view to show is anchored at either the very top or very bottom
   3313      * of the screen.
   3314      */
   3315     private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) {
   3316         AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType();
   3317         showAppsCustomizeHelper(animated, springLoaded, contentType);
   3318     }
   3319 
   3320     private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded,
   3321                                          final AppsCustomizePagedView.ContentType contentType) {
   3322         if (mStateAnimation != null) {
   3323             mStateAnimation.setDuration(0);
   3324             mStateAnimation.cancel();
   3325             mStateAnimation = null;
   3326         }
   3327 
   3328         boolean material = Utilities.isLmpOrAbove();
   3329 
   3330         final Resources res = getResources();
   3331 
   3332         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
   3333         final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
   3334         final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
   3335         final int itemsAlphaStagger =
   3336                 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
   3337 
   3338         final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
   3339         final View fromView = mWorkspace;
   3340         final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
   3341 
   3342         final ArrayList<View> layerViews = new ArrayList<View>();
   3343 
   3344         Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ?
   3345                 Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN;
   3346         Animator workspaceAnim =
   3347                 mWorkspace.getChangeStateAnimation(workspaceState, animated, layerViews);
   3348         if (!LauncherAppState.isDisableAllApps()
   3349                 || contentType == AppsCustomizePagedView.ContentType.Widgets) {
   3350             // Set the content type for the all apps/widgets space
   3351             mAppsCustomizeTabHost.setContentTypeImmediate(contentType);
   3352         }
   3353 
   3354         // If for some reason our views aren't initialized, don't animate
   3355         boolean initialized = getAllAppsButton() != null;
   3356 
   3357         if (animated && initialized) {
   3358             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
   3359             final AppsCustomizePagedView content = (AppsCustomizePagedView)
   3360                     toView.findViewById(R.id.apps_customize_pane_content);
   3361 
   3362             final View page = content.getPageAt(content.getCurrentPage());
   3363             final View revealView = toView.findViewById(R.id.fake_page);
   3364 
   3365             final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets;
   3366             if (isWidgetTray) {
   3367                 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
   3368             } else {
   3369                 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
   3370             }
   3371 
   3372             // Hide the real page background, and swap in the fake one
   3373             content.setPageBackgroundsVisible(false);
   3374             revealView.setVisibility(View.VISIBLE);
   3375             // We need to hide this view as the animation start will be posted.
   3376             revealView.setAlpha(0);
   3377 
   3378             int width = revealView.getMeasuredWidth();
   3379             int height = revealView.getMeasuredHeight();
   3380             float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
   3381 
   3382             revealView.setTranslationY(0);
   3383             revealView.setTranslationX(0);
   3384 
   3385             // Get the y delta between the center of the page and the center of the all apps button
   3386             int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
   3387                     getAllAppsButton(), null);
   3388 
   3389             float alpha = 0;
   3390             float xDrift = 0;
   3391             float yDrift = 0;
   3392             if (material) {
   3393                 alpha = isWidgetTray ? 0.3f : 1f;
   3394                 yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
   3395                 xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
   3396             } else {
   3397                 yDrift = 2 * height / 3;
   3398                 xDrift = 0;
   3399             }
   3400             final float initAlpha = alpha;
   3401 
   3402             revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3403             layerViews.add(revealView);
   3404             PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f);
   3405             PropertyValuesHolder panelDriftY =
   3406                     PropertyValuesHolder.ofFloat("translationY", yDrift, 0);
   3407             PropertyValuesHolder panelDriftX =
   3408                     PropertyValuesHolder.ofFloat("translationX", xDrift, 0);
   3409 
   3410             ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
   3411                     panelAlpha, panelDriftY, panelDriftX);
   3412 
   3413             panelAlphaAndDrift.setDuration(revealDuration);
   3414             panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
   3415 
   3416             mStateAnimation.play(panelAlphaAndDrift);
   3417 
   3418             if (page != null) {
   3419                 page.setVisibility(View.VISIBLE);
   3420                 page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3421                 layerViews.add(page);
   3422 
   3423                 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0);
   3424                 page.setTranslationY(yDrift);
   3425                 pageDrift.setDuration(revealDuration);
   3426                 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
   3427                 pageDrift.setStartDelay(itemsAlphaStagger);
   3428                 mStateAnimation.play(pageDrift);
   3429 
   3430                 page.setAlpha(0f);
   3431                 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, "alpha", 0f, 1f);
   3432                 itemsAlpha.setDuration(revealDuration);
   3433                 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
   3434                 itemsAlpha.setStartDelay(itemsAlphaStagger);
   3435                 mStateAnimation.play(itemsAlpha);
   3436             }
   3437 
   3438             View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator);
   3439             pageIndicators.setAlpha(0.01f);
   3440             ObjectAnimator indicatorsAlpha =
   3441                     ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f);
   3442             indicatorsAlpha.setDuration(revealDuration);
   3443             mStateAnimation.play(indicatorsAlpha);
   3444 
   3445             if (material) {
   3446                 final View allApps = getAllAppsButton();
   3447                 int allAppsButtonSize = LauncherAppState.getInstance().
   3448                         getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
   3449                 float startRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
   3450                 Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2,
   3451                                 height / 2, startRadius, revealRadius);
   3452                 reveal.setDuration(revealDuration);
   3453                 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
   3454 
   3455                 reveal.addListener(new AnimatorListenerAdapter() {
   3456                     public void onAnimationStart(Animator animation) {
   3457                         if (!isWidgetTray) {
   3458                             allApps.setVisibility(View.INVISIBLE);
   3459                         }
   3460                     }
   3461                     public void onAnimationEnd(Animator animation) {
   3462                         if (!isWidgetTray) {
   3463                             allApps.setVisibility(View.VISIBLE);
   3464                         }
   3465                     }
   3466                 });
   3467                 mStateAnimation.play(reveal);
   3468             }
   3469 
   3470             mStateAnimation.addListener(new AnimatorListenerAdapter() {
   3471                 @Override
   3472                 public void onAnimationEnd(Animator animation) {
   3473                     dispatchOnLauncherTransitionEnd(fromView, animated, false);
   3474                     dispatchOnLauncherTransitionEnd(toView, animated, false);
   3475 
   3476                     revealView.setVisibility(View.INVISIBLE);
   3477                     revealView.setLayerType(View.LAYER_TYPE_NONE, null);
   3478                     if (page != null) {
   3479                         page.setLayerType(View.LAYER_TYPE_NONE, null);
   3480                     }
   3481                     content.setPageBackgroundsVisible(true);
   3482 
   3483                     // Hide the search bar
   3484                     if (mSearchDropTargetBar != null) {
   3485                         mSearchDropTargetBar.hideSearchBar(false);
   3486                     }
   3487 
   3488                     // This can hold unnecessary references to views.
   3489                     mStateAnimation = null;
   3490                 }
   3491 
   3492             });
   3493 
   3494             if (workspaceAnim != null) {
   3495                 mStateAnimation.play(workspaceAnim);
   3496             }
   3497 
   3498             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
   3499             dispatchOnLauncherTransitionPrepare(toView, animated, false);
   3500             final AnimatorSet stateAnimation = mStateAnimation;
   3501             final Runnable startAnimRunnable = new Runnable() {
   3502                 public void run() {
   3503                     // Check that mStateAnimation hasn't changed while
   3504                     // we waited for a layout/draw pass
   3505                     if (mStateAnimation != stateAnimation)
   3506                         return;
   3507                     dispatchOnLauncherTransitionStart(fromView, animated, false);
   3508                     dispatchOnLauncherTransitionStart(toView, animated, false);
   3509 
   3510                     revealView.setAlpha(initAlpha);
   3511                     if (Utilities.isLmpOrAbove()) {
   3512                         for (int i = 0; i < layerViews.size(); i++) {
   3513                             View v = layerViews.get(i);
   3514                             if (v != null) {
   3515                                 if (Utilities.isViewAttachedToWindow(v)) v.buildLayer();
   3516                             }
   3517                         }
   3518                     }
   3519                     mStateAnimation.start();
   3520                 }
   3521             };
   3522             toView.bringToFront();
   3523             toView.setVisibility(View.VISIBLE);
   3524             toView.post(startAnimRunnable);
   3525         } else {
   3526             toView.setTranslationX(0.0f);
   3527             toView.setTranslationY(0.0f);
   3528             toView.setScaleX(1.0f);
   3529             toView.setScaleY(1.0f);
   3530             toView.setVisibility(View.VISIBLE);
   3531             toView.bringToFront();
   3532 
   3533             if (!springLoaded && !LauncherAppState.getInstance().isScreenLarge()) {
   3534                 // Hide the search bar
   3535                 if (mSearchDropTargetBar != null) {
   3536                     mSearchDropTargetBar.hideSearchBar(false);
   3537                 }
   3538             }
   3539             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
   3540             dispatchOnLauncherTransitionStart(fromView, animated, false);
   3541             dispatchOnLauncherTransitionEnd(fromView, animated, false);
   3542             dispatchOnLauncherTransitionPrepare(toView, animated, false);
   3543             dispatchOnLauncherTransitionStart(toView, animated, false);
   3544             dispatchOnLauncherTransitionEnd(toView, animated, false);
   3545         }
   3546     }
   3547 
   3548     /**
   3549      * Zoom the camera back into the workspace, hiding 'fromView'.
   3550      * This is the opposite of showAppsCustomizeHelper.
   3551      * @param animated If true, the transition will be animated.
   3552      */
   3553     private void hideAppsCustomizeHelper(Workspace.State toState, final boolean animated,
   3554             final boolean springLoaded, final Runnable onCompleteRunnable) {
   3555 
   3556         if (mStateAnimation != null) {
   3557             mStateAnimation.setDuration(0);
   3558             mStateAnimation.cancel();
   3559             mStateAnimation = null;
   3560         }
   3561 
   3562         boolean material = Utilities.isLmpOrAbove();
   3563         Resources res = getResources();
   3564 
   3565         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
   3566         final int fadeOutDuration = res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
   3567         final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime);
   3568         final int itemsAlphaStagger =
   3569                 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
   3570 
   3571         final float scaleFactor = (float)
   3572                 res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
   3573         final View fromView = mAppsCustomizeTabHost;
   3574         final View toView = mWorkspace;
   3575         Animator workspaceAnim = null;
   3576         final ArrayList<View> layerViews = new ArrayList<View>();
   3577 
   3578         if (toState == Workspace.State.NORMAL) {
   3579             workspaceAnim = mWorkspace.getChangeStateAnimation(
   3580                     toState, animated, layerViews);
   3581         } else if (toState == Workspace.State.SPRING_LOADED ||
   3582                 toState == Workspace.State.OVERVIEW) {
   3583             workspaceAnim = mWorkspace.getChangeStateAnimation(
   3584                     toState, animated, layerViews);
   3585         }
   3586 
   3587         // If for some reason our views aren't initialized, don't animate
   3588         boolean initialized = getAllAppsButton() != null;
   3589 
   3590         if (animated && initialized) {
   3591             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
   3592             if (workspaceAnim != null) {
   3593                 mStateAnimation.play(workspaceAnim);
   3594             }
   3595 
   3596             final AppsCustomizePagedView content = (AppsCustomizePagedView)
   3597                     fromView.findViewById(R.id.apps_customize_pane_content);
   3598 
   3599             final View page = content.getPageAt(content.getNextPage());
   3600 
   3601             // We need to hide side pages of the Apps / Widget tray to avoid some ugly edge cases
   3602             int count = content.getChildCount();
   3603             for (int i = 0; i < count; i++) {
   3604                 View child = content.getChildAt(i);
   3605                 if (child != page) {
   3606                     child.setVisibility(View.INVISIBLE);
   3607                 }
   3608             }
   3609             final View revealView = fromView.findViewById(R.id.fake_page);
   3610 
   3611             // hideAppsCustomizeHelper is called in some cases when it is already hidden
   3612             // don't perform all these no-op animations. In particularly, this was causing
   3613             // the all-apps button to pop in and out.
   3614             if (fromView.getVisibility() == View.VISIBLE) {
   3615                 AppsCustomizePagedView.ContentType contentType = content.getContentType();
   3616                 final boolean isWidgetTray =
   3617                         contentType == AppsCustomizePagedView.ContentType.Widgets;
   3618 
   3619                 if (isWidgetTray) {
   3620                     revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
   3621                 } else {
   3622                     revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
   3623                 }
   3624 
   3625                 int width = revealView.getMeasuredWidth();
   3626                 int height = revealView.getMeasuredHeight();
   3627                 float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
   3628 
   3629                 // Hide the real page background, and swap in the fake one
   3630                 revealView.setVisibility(View.VISIBLE);
   3631                 content.setPageBackgroundsVisible(false);
   3632 
   3633                 final View allAppsButton = getAllAppsButton();
   3634                 revealView.setTranslationY(0);
   3635                 int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
   3636                         allAppsButton, null);
   3637 
   3638                 float xDrift = 0;
   3639                 float yDrift = 0;
   3640                 if (material) {
   3641                     yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
   3642                     xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
   3643                 } else {
   3644                     yDrift = 2 * height / 3;
   3645                     xDrift = 0;
   3646                 }
   3647 
   3648                 revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3649                 TimeInterpolator decelerateInterpolator = material ?
   3650                         new LogDecelerateInterpolator(100, 0) :
   3651                         new DecelerateInterpolator(1f);
   3652 
   3653                 // The vertical motion of the apps panel should be delayed by one frame
   3654                 // from the conceal animation in order to give the right feel. We correpsondingly
   3655                 // shorten the duration so that the slide and conceal end at the same time.
   3656                 ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
   3657                         0, yDrift);
   3658                 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
   3659                 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
   3660                 panelDriftY.setInterpolator(decelerateInterpolator);
   3661                 mStateAnimation.play(panelDriftY);
   3662 
   3663                 ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
   3664                         0, xDrift);
   3665                 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
   3666                 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
   3667                 panelDriftX.setInterpolator(decelerateInterpolator);
   3668                 mStateAnimation.play(panelDriftX);
   3669 
   3670                 if (isWidgetTray || !material) {
   3671                     float finalAlpha = material ? 0.4f : 0f;
   3672                     revealView.setAlpha(1f);
   3673                     ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
   3674                             1f, finalAlpha);
   3675                     panelAlpha.setDuration(material ? revealDuration : 150);
   3676                     panelAlpha.setInterpolator(decelerateInterpolator);
   3677                     panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
   3678                     mStateAnimation.play(panelAlpha);
   3679                 }
   3680 
   3681                 if (page != null) {
   3682                     page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   3683 
   3684                     ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY",
   3685                             0, yDrift);
   3686                     page.setTranslationY(0);
   3687                     pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
   3688                     pageDrift.setInterpolator(decelerateInterpolator);
   3689                     pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
   3690                     mStateAnimation.play(pageDrift);
   3691 
   3692                     page.setAlpha(1f);
   3693                     ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(page, "alpha", 1f, 0f);
   3694                     itemsAlpha.setDuration(100);
   3695                     itemsAlpha.setInterpolator(decelerateInterpolator);
   3696                     mStateAnimation.play(itemsAlpha);
   3697                 }
   3698 
   3699                 View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator);
   3700                 pageIndicators.setAlpha(1f);
   3701                 ObjectAnimator indicatorsAlpha =
   3702                         LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f);
   3703                 indicatorsAlpha.setDuration(revealDuration);
   3704                 indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
   3705                 mStateAnimation.play(indicatorsAlpha);
   3706 
   3707                 width = revealView.getMeasuredWidth();
   3708 
   3709                 if (material) {
   3710                     if (!isWidgetTray) {
   3711                         allAppsButton.setVisibility(View.INVISIBLE);
   3712                     }
   3713                     int allAppsButtonSize = LauncherAppState.getInstance().
   3714                             getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
   3715                     float finalRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
   3716                     Animator reveal =
   3717                             LauncherAnimUtils.createCircularReveal(revealView, width / 2,
   3718                                     height / 2, revealRadius, finalRadius);
   3719                     reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
   3720                     reveal.setDuration(revealDuration);
   3721                     reveal.setStartDelay(itemsAlphaStagger);
   3722 
   3723                     reveal.addListener(new AnimatorListenerAdapter() {
   3724                         public void onAnimationEnd(Animator animation) {
   3725                             revealView.setVisibility(View.INVISIBLE);
   3726                             if (!isWidgetTray) {
   3727                                 allAppsButton.setVisibility(View.VISIBLE);
   3728                             }
   3729                         }
   3730                     });
   3731 
   3732                     mStateAnimation.play(reveal);
   3733                 }
   3734 
   3735                 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
   3736                 dispatchOnLauncherTransitionPrepare(toView, animated, true);
   3737                 mAppsCustomizeContent.stopScrolling();
   3738             }
   3739 
   3740             mStateAnimation.addListener(new AnimatorListenerAdapter() {
   3741                 @Override
   3742                 public void onAnimationEnd(Animator animation) {
   3743                     fromView.setVisibility(View.GONE);
   3744                     dispatchOnLauncherTransitionEnd(fromView, animated, true);
   3745                     dispatchOnLauncherTransitionEnd(toView, animated, true);
   3746                     if (onCompleteRunnable != null) {
   3747                         onCompleteRunnable.run();
   3748                     }
   3749 
   3750                     revealView.setLayerType(View.LAYER_TYPE_NONE, null);
   3751                     if (page != null) {
   3752                         page.setLayerType(View.LAYER_TYPE_NONE, null);
   3753                     }
   3754                     content.setPageBackgroundsVisible(true);
   3755                     // Unhide side pages
   3756                     int count = content.getChildCount();
   3757                     for (int i = 0; i < count; i++) {
   3758                         View child = content.getChildAt(i);
   3759                         child.setVisibility(View.VISIBLE);
   3760                     }
   3761 
   3762                     // Reset page transforms
   3763                     if (page != null) {
   3764                         page.setTranslationX(0);
   3765                         page.setTranslationY(0);
   3766                         page.setAlpha(1);
   3767                     }
   3768                     content.setCurrentPage(content.getNextPage());
   3769 
   3770                     mAppsCustomizeContent.updateCurrentPageScroll();
   3771 
   3772                     // This can hold unnecessary references to views.
   3773                     mStateAnimation = null;
   3774                 }
   3775             });
   3776 
   3777             final AnimatorSet stateAnimation = mStateAnimation;
   3778             final Runnable startAnimRunnable = new Runnable() {
   3779                 public void run() {
   3780                     // Check that mStateAnimation hasn't changed while
   3781                     // we waited for a layout/draw pass
   3782                     if (mStateAnimation != stateAnimation)
   3783                         return;
   3784                     dispatchOnLauncherTransitionStart(fromView, animated, false);
   3785                     dispatchOnLauncherTransitionStart(toView, animated, false);
   3786 
   3787                     if (Utilities.isLmpOrAbove()) {
   3788                         for (int i = 0; i < layerViews.size(); i++) {
   3789                             View v = layerViews.get(i);
   3790                             if (v != null) {
   3791                                 if (Utilities.isViewAttachedToWindow(v)) v.buildLayer();
   3792                             }
   3793                         }
   3794                     }
   3795                     mStateAnimation.start();
   3796                 }
   3797             };
   3798             fromView.post(startAnimRunnable);
   3799         } else {
   3800             fromView.setVisibility(View.GONE);
   3801             dispatchOnLauncherTransitionPrepare(fromView, animated, true);
   3802             dispatchOnLauncherTransitionStart(fromView, animated, true);
   3803             dispatchOnLauncherTransitionEnd(fromView, animated, true);
   3804             dispatchOnLauncherTransitionPrepare(toView, animated, true);
   3805             dispatchOnLauncherTransitionStart(toView, animated, true);
   3806             dispatchOnLauncherTransitionEnd(toView, animated, true);
   3807         }
   3808     }
   3809 
   3810     @Override
   3811     public void onTrimMemory(int level) {
   3812         super.onTrimMemory(level);
   3813         if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
   3814             // The widget preview db can result in holding onto over
   3815             // 3MB of memory for caching which isn't necessary.
   3816             SQLiteDatabase.releaseMemory();
   3817 
   3818             // This clears all widget bitmaps from the widget tray
   3819             if (mAppsCustomizeTabHost != null) {
   3820                 mAppsCustomizeTabHost.trimMemory();
   3821             }
   3822         }
   3823     }
   3824 
   3825     protected void showWorkspace(boolean animated) {
   3826         showWorkspace(animated, null);
   3827     }
   3828 
   3829     protected void showWorkspace() {
   3830         showWorkspace(true);
   3831     }
   3832 
   3833     void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
   3834         if (mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL) {
   3835             boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
   3836             mWorkspace.setVisibility(View.VISIBLE);
   3837             hideAppsCustomizeHelper(Workspace.State.NORMAL, animated, false, onCompleteRunnable);
   3838 
   3839             // Show the search bar (only animate if we were showing the drop target bar in spring
   3840             // loaded mode)
   3841             if (mSearchDropTargetBar != null) {
   3842                 mSearchDropTargetBar.showSearchBar(animated && wasInSpringLoadedMode);
   3843             }
   3844 
   3845             // Set focus to the AppsCustomize button
   3846             if (mAllAppsButton != null) {
   3847                 mAllAppsButton.requestFocus();
   3848             }
   3849         }
   3850 
   3851         // Change the state *after* we've called all the transition code
   3852         mState = State.WORKSPACE;
   3853 
   3854         // Resume the auto-advance of widgets
   3855         mUserPresent = true;
   3856         updateRunning();
   3857 
   3858         // Send an accessibility event to announce the context change
   3859         getWindow().getDecorView()
   3860                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3861 
   3862         onWorkspaceShown(animated);
   3863     }
   3864 
   3865     void showOverviewMode(boolean animated) {
   3866         mWorkspace.setVisibility(View.VISIBLE);
   3867         hideAppsCustomizeHelper(Workspace.State.OVERVIEW, animated, false, null);
   3868         mState = State.WORKSPACE;
   3869         onWorkspaceShown(animated);
   3870     }
   3871 
   3872     public void onWorkspaceShown(boolean animated) {
   3873     }
   3874 
   3875     void showAllApps(boolean animated, AppsCustomizePagedView.ContentType contentType,
   3876                      boolean resetPageToZero) {
   3877         if (mState != State.WORKSPACE) return;
   3878 
   3879         if (resetPageToZero) {
   3880             mAppsCustomizeTabHost.reset();
   3881         }
   3882         showAppsCustomizeHelper(animated, false, contentType);
   3883         mAppsCustomizeTabHost.post(new Runnable() {
   3884             @Override
   3885             public void run() {
   3886                 // We post this in-case the all apps view isn't yet constructed.
   3887                 mAppsCustomizeTabHost.requestFocus();
   3888             }
   3889         });
   3890 
   3891         // Change the state *after* we've called all the transition code
   3892         mState = State.APPS_CUSTOMIZE;
   3893 
   3894         // Pause the auto-advance of widgets until we are out of AllApps
   3895         mUserPresent = false;
   3896         updateRunning();
   3897         closeFolder();
   3898 
   3899         // Send an accessibility event to announce the context change
   3900         getWindow().getDecorView()
   3901                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3902     }
   3903 
   3904     void enterSpringLoadedDragMode() {
   3905         if (isAllAppsVisible()) {
   3906             hideAppsCustomizeHelper(Workspace.State.SPRING_LOADED, true, true, null);
   3907             mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
   3908         }
   3909     }
   3910 
   3911     void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
   3912             final Runnable onCompleteRunnable) {
   3913         if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
   3914 
   3915         mHandler.postDelayed(new Runnable() {
   3916             @Override
   3917             public void run() {
   3918                 if (successfulDrop) {
   3919                     // Before we show workspace, hide all apps again because
   3920                     // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
   3921                     // clean up our state transition functions
   3922                     mAppsCustomizeTabHost.setVisibility(View.GONE);
   3923                     showWorkspace(true, onCompleteRunnable);
   3924                 } else {
   3925                     exitSpringLoadedDragMode();
   3926                 }
   3927             }
   3928         }, delay);
   3929     }
   3930 
   3931     void exitSpringLoadedDragMode() {
   3932         if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
   3933             final boolean animated = true;
   3934             final boolean springLoaded = true;
   3935             showAppsCustomizeHelper(animated, springLoaded);
   3936             mState = State.APPS_CUSTOMIZE;
   3937         }
   3938         // Otherwise, we are not in spring loaded mode, so don't do anything.
   3939     }
   3940 
   3941     void lockAllApps() {
   3942         // TODO
   3943     }
   3944 
   3945     void unlockAllApps() {
   3946         // TODO
   3947     }
   3948 
   3949     protected void disableVoiceButtonProxy(boolean disable) {
   3950         // NO-OP
   3951     }
   3952 
   3953     public View getQsbBar() {
   3954         if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) {
   3955             return mLauncherCallbacks.getQsbBar();
   3956         }
   3957 
   3958         if (mQsb == null) {
   3959             AppWidgetProviderInfo searchProvider = Utilities.getSearchWidgetProvider(this);
   3960             if (searchProvider == null) {
   3961                 return null;
   3962             }
   3963 
   3964             Bundle opts = new Bundle();
   3965             opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
   3966                     AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
   3967 
   3968             SharedPreferences sp = getSharedPreferences(
   3969                     LauncherAppState.getSharedPreferencesKey(), MODE_PRIVATE);
   3970             int widgetId = sp.getInt(QSB_WIDGET_ID, -1);
   3971             AppWidgetProviderInfo widgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
   3972             if (!searchProvider.provider.flattenToString().equals(
   3973                     sp.getString(QSB_WIDGET_PROVIDER, null))
   3974                     || (widgetInfo == null)
   3975                     || !widgetInfo.provider.equals(searchProvider.provider)) {
   3976                 // A valid widget is not already bound.
   3977                 if (widgetId > -1) {
   3978                     mAppWidgetHost.deleteAppWidgetId(widgetId);
   3979                     widgetId = -1;
   3980                 }
   3981 
   3982                 // Try to bind a new widget
   3983                 widgetId = mAppWidgetHost.allocateAppWidgetId();
   3984 
   3985                 if (!AppWidgetManagerCompat.getInstance(this)
   3986                         .bindAppWidgetIdIfAllowed(widgetId, searchProvider, opts)) {
   3987                     mAppWidgetHost.deleteAppWidgetId(widgetId);
   3988                     widgetId = -1;
   3989                 }
   3990 
   3991                 sp.edit()
   3992                     .putInt(QSB_WIDGET_ID, widgetId)
   3993                     .putString(QSB_WIDGET_PROVIDER, searchProvider.provider.flattenToString())
   3994                     .commit();
   3995             }
   3996 
   3997             if (widgetId != -1) {
   3998                 mQsb = mAppWidgetHost.createView(this, widgetId, searchProvider);
   3999                 mQsb.updateAppWidgetOptions(opts);
   4000                 mQsb.setPadding(0, 0, 0, 0);
   4001                 mSearchDropTargetBar.addView(mQsb);
   4002             }
   4003         }
   4004         return mQsb;
   4005     }
   4006 
   4007     @Override
   4008     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
   4009         final boolean result = super.dispatchPopulateAccessibilityEvent(event);
   4010         final List<CharSequence> text = event.getText();
   4011         text.clear();
   4012         // Populate event with a fake title based on the current state.
   4013         if (mState == State.APPS_CUSTOMIZE) {
   4014             text.add(mAppsCustomizeTabHost.getContentTag());
   4015         } else {
   4016             text.add(getString(R.string.all_apps_home_button_label));
   4017         }
   4018         return result;
   4019     }
   4020 
   4021     /**
   4022      * Receives notifications when system dialogs are to be closed.
   4023      */
   4024     private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
   4025         @Override
   4026         public void onReceive(Context context, Intent intent) {
   4027             closeSystemDialogs();
   4028         }
   4029     }
   4030 
   4031     /**
   4032      * Receives notifications whenever the appwidgets are reset.
   4033      */
   4034     private class AppWidgetResetObserver extends ContentObserver {
   4035         public AppWidgetResetObserver() {
   4036             super(new Handler());
   4037         }
   4038 
   4039         @Override
   4040         public void onChange(boolean selfChange) {
   4041             onAppWidgetReset();
   4042         }
   4043     }
   4044 
   4045     /**
   4046      * If the activity is currently paused, signal that we need to run the passed Runnable
   4047      * in onResume.
   4048      *
   4049      * This needs to be called from incoming places where resources might have been loaded
   4050      * while we are paused.  That is becaues the Configuration might be wrong
   4051      * when we're not running, and if it comes back to what it was when we
   4052      * were paused, we are not restarted.
   4053      *
   4054      * Implementation of the method from LauncherModel.Callbacks.
   4055      *
   4056      * @return true if we are currently paused.  The caller might be able to
   4057      * skip some work in that case since we will come back again.
   4058      */
   4059     private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) {
   4060         if (mPaused) {
   4061             Log.i(TAG, "Deferring update until onResume");
   4062             if (deletePreviousRunnables) {
   4063                 while (mBindOnResumeCallbacks.remove(run)) {
   4064                 }
   4065             }
   4066             mBindOnResumeCallbacks.add(run);
   4067             return true;
   4068         } else {
   4069             return false;
   4070         }
   4071     }
   4072 
   4073     private boolean waitUntilResume(Runnable run) {
   4074         return waitUntilResume(run, false);
   4075     }
   4076 
   4077     public void addOnResumeCallback(Runnable run) {
   4078         mOnResumeCallbacks.add(run);
   4079     }
   4080 
   4081     /**
   4082      * If the activity is currently paused, signal that we need to re-run the loader
   4083      * in onResume.
   4084      *
   4085      * This needs to be called from incoming places where resources might have been loaded
   4086      * while we are paused.  That is becaues the Configuration might be wrong
   4087      * when we're not running, and if it comes back to what it was when we
   4088      * were paused, we are not restarted.
   4089      *
   4090      * Implementation of the method from LauncherModel.Callbacks.
   4091      *
   4092      * @return true if we are currently paused.  The caller might be able to
   4093      * skip some work in that case since we will come back again.
   4094      */
   4095     public boolean setLoadOnResume() {
   4096         if (mPaused) {
   4097             Log.i(TAG, "setLoadOnResume");
   4098             mOnResumeNeedsLoad = true;
   4099             return true;
   4100         } else {
   4101             return false;
   4102         }
   4103     }
   4104 
   4105     /**
   4106      * Implementation of the method from LauncherModel.Callbacks.
   4107      */
   4108     public int getCurrentWorkspaceScreen() {
   4109         if (mWorkspace != null) {
   4110             return mWorkspace.getCurrentPage();
   4111         } else {
   4112             return SCREEN_COUNT / 2;
   4113         }
   4114     }
   4115 
   4116     /**
   4117      * Refreshes the shortcuts shown on the workspace.
   4118      *
   4119      * Implementation of the method from LauncherModel.Callbacks.
   4120      */
   4121     public void startBinding() {
   4122         setWorkspaceLoading(true);
   4123 
   4124         // If we're starting binding all over again, clear any bind calls we'd postponed in
   4125         // the past (see waitUntilResume) -- we don't need them since we're starting binding
   4126         // from scratch again
   4127         mBindOnResumeCallbacks.clear();
   4128 
   4129         // Clear the workspace because it's going to be rebound
   4130         mWorkspace.clearDropTargets();
   4131         mWorkspace.removeAllWorkspaceScreens();
   4132 
   4133         mWidgetsToAdvance.clear();
   4134         if (mHotseat != null) {
   4135             mHotseat.resetLayout();
   4136         }
   4137     }
   4138 
   4139     @Override
   4140     public void bindScreens(ArrayList<Long> orderedScreenIds) {
   4141         bindAddScreens(orderedScreenIds);
   4142 
   4143         // If there are no screens, we need to have an empty screen
   4144         if (orderedScreenIds.size() == 0) {
   4145             mWorkspace.addExtraEmptyScreen();
   4146         }
   4147 
   4148         // Create the custom content page (this call updates mDefaultScreen which calls
   4149         // setCurrentPage() so ensure that all pages are added before calling this).
   4150         if (hasCustomContentToLeft()) {
   4151             mWorkspace.createCustomContentContainer();
   4152             populateCustomContentContainer();
   4153         }
   4154     }
   4155 
   4156     @Override
   4157     public void bindAddScreens(ArrayList<Long> orderedScreenIds) {
   4158         // Log to disk
   4159         Launcher.addDumpLog(TAG, "11683562 - bindAddScreens()", true);
   4160         Launcher.addDumpLog(TAG, "11683562 -   orderedScreenIds: " +
   4161                 TextUtils.join(", ", orderedScreenIds), true);
   4162         int count = orderedScreenIds.size();
   4163         for (int i = 0; i < count; i++) {
   4164             mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i));
   4165         }
   4166     }
   4167 
   4168     private boolean shouldShowWeightWatcher() {
   4169         String spKey = LauncherAppState.getSharedPreferencesKey();
   4170         SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
   4171         boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, SHOW_WEIGHT_WATCHER_DEFAULT);
   4172 
   4173         return show;
   4174     }
   4175 
   4176     private void toggleShowWeightWatcher() {
   4177         String spKey = LauncherAppState.getSharedPreferencesKey();
   4178         SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
   4179         boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, true);
   4180 
   4181         show = !show;
   4182 
   4183         SharedPreferences.Editor editor = sp.edit();
   4184         editor.putBoolean(SHOW_WEIGHT_WATCHER, show);
   4185         editor.commit();
   4186 
   4187         if (mWeightWatcher != null) {
   4188             mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
   4189         }
   4190     }
   4191 
   4192     public void bindAppsAdded(final ArrayList<Long> newScreens,
   4193                               final ArrayList<ItemInfo> addNotAnimated,
   4194                               final ArrayList<ItemInfo> addAnimated,
   4195                               final ArrayList<AppInfo> addedApps) {
   4196         Runnable r = new Runnable() {
   4197             public void run() {
   4198                 bindAppsAdded(newScreens, addNotAnimated, addAnimated, addedApps);
   4199             }
   4200         };
   4201         if (waitUntilResume(r)) {
   4202             return;
   4203         }
   4204 
   4205         // Add the new screens
   4206         if (newScreens != null) {
   4207             bindAddScreens(newScreens);
   4208         }
   4209 
   4210         // We add the items without animation on non-visible pages, and with
   4211         // animations on the new page (which we will try and snap to).
   4212         if (addNotAnimated != null && !addNotAnimated.isEmpty()) {
   4213             bindItems(addNotAnimated, 0,
   4214                     addNotAnimated.size(), false);
   4215         }
   4216         if (addAnimated != null && !addAnimated.isEmpty()) {
   4217             bindItems(addAnimated, 0,
   4218                     addAnimated.size(), true);
   4219         }
   4220 
   4221         // Remove the extra empty screen
   4222         mWorkspace.removeExtraEmptyScreen(false, false);
   4223 
   4224         if (!LauncherAppState.isDisableAllApps() &&
   4225                 addedApps != null && mAppsCustomizeContent != null) {
   4226             mAppsCustomizeContent.addApps(addedApps);
   4227         }
   4228     }
   4229 
   4230     /**
   4231      * Bind the items start-end from the list.
   4232      *
   4233      * Implementation of the method from LauncherModel.Callbacks.
   4234      */
   4235     public void bindItems(final ArrayList<ItemInfo> shortcuts, final int start, final int end,
   4236                           final boolean forceAnimateIcons) {
   4237         Runnable r = new Runnable() {
   4238             public void run() {
   4239                 bindItems(shortcuts, start, end, forceAnimateIcons);
   4240             }
   4241         };
   4242         if (waitUntilResume(r)) {
   4243             return;
   4244         }
   4245 
   4246         // Get the list of added shortcuts and intersect them with the set of shortcuts here
   4247         final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
   4248         final Collection<Animator> bounceAnims = new ArrayList<Animator>();
   4249         final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
   4250         Workspace workspace = mWorkspace;
   4251         long newShortcutsScreenId = -1;
   4252         for (int i = start; i < end; i++) {
   4253             final ItemInfo item = shortcuts.get(i);
   4254 
   4255             // Short circuit if we are loading dock items for a configuration which has no dock
   4256             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
   4257                     mHotseat == null) {
   4258                 continue;
   4259             }
   4260 
   4261             switch (item.itemType) {
   4262                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   4263                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   4264                     ShortcutInfo info = (ShortcutInfo) item;
   4265                     View shortcut = createShortcut(info);
   4266 
   4267                     /*
   4268                      * TODO: FIX collision case
   4269                      */
   4270                     if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
   4271                         CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
   4272                         if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
   4273                             View v = cl.getChildAt(item.cellX, item.cellY);
   4274                             Object tag = v.getTag();
   4275                             String desc = "Collision while binding workspace item: " + item
   4276                                     + ". Collides with " + tag;
   4277                             if (LauncherAppState.isDogfoodBuild()) {
   4278                                 throw (new RuntimeException(desc));
   4279                             } else {
   4280                                 Log.d(TAG, desc);
   4281                             }
   4282                         }
   4283                     }
   4284 
   4285                     workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX,
   4286                             item.cellY, 1, 1);
   4287                     if (animateIcons) {
   4288                         // Animate all the applications up now
   4289                         shortcut.setAlpha(0f);
   4290                         shortcut.setScaleX(0f);
   4291                         shortcut.setScaleY(0f);
   4292                         bounceAnims.add(createNewAppBounceAnimation(shortcut, i));
   4293                         newShortcutsScreenId = item.screenId;
   4294                     }
   4295                     break;
   4296                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
   4297                     FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
   4298                             (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
   4299                             (FolderInfo) item, mIconCache);
   4300                     workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX,
   4301                             item.cellY, 1, 1);
   4302                     break;
   4303                 default:
   4304                     throw new RuntimeException("Invalid Item Type");
   4305             }
   4306         }
   4307 
   4308         if (animateIcons) {
   4309             // Animate to the correct page
   4310             if (newShortcutsScreenId > -1) {
   4311                 long currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
   4312                 final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newShortcutsScreenId);
   4313                 final Runnable startBounceAnimRunnable = new Runnable() {
   4314                     public void run() {
   4315                         anim.playTogether(bounceAnims);
   4316                         anim.start();
   4317                     }
   4318                 };
   4319                 if (newShortcutsScreenId != currentScreenId) {
   4320                     // We post the animation slightly delayed to prevent slowdowns
   4321                     // when we are loading right after we return to launcher.
   4322                     mWorkspace.postDelayed(new Runnable() {
   4323                         public void run() {
   4324                             if (mWorkspace != null) {
   4325                                 mWorkspace.snapToPage(newScreenIndex);
   4326                                 mWorkspace.postDelayed(startBounceAnimRunnable,
   4327                                         NEW_APPS_ANIMATION_DELAY);
   4328                             }
   4329                         }
   4330                     }, NEW_APPS_PAGE_MOVE_DELAY);
   4331                 } else {
   4332                     mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
   4333                 }
   4334             }
   4335         }
   4336         workspace.requestLayout();
   4337     }
   4338 
   4339     /**
   4340      * Implementation of the method from LauncherModel.Callbacks.
   4341      */
   4342     public void bindFolders(final HashMap<Long, FolderInfo> folders) {
   4343         Runnable r = new Runnable() {
   4344             public void run() {
   4345                 bindFolders(folders);
   4346             }
   4347         };
   4348         if (waitUntilResume(r)) {
   4349             return;
   4350         }
   4351         sFolders.clear();
   4352         sFolders.putAll(folders);
   4353     }
   4354 
   4355     /**
   4356      * Add the views for a widget to the workspace.
   4357      *
   4358      * Implementation of the method from LauncherModel.Callbacks.
   4359      */
   4360     public void bindAppWidget(final LauncherAppWidgetInfo item) {
   4361         Runnable r = new Runnable() {
   4362             public void run() {
   4363                 bindAppWidget(item);
   4364             }
   4365         };
   4366         if (waitUntilResume(r)) {
   4367             return;
   4368         }
   4369 
   4370         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
   4371         if (DEBUG_WIDGETS) {
   4372             Log.d(TAG, "bindAppWidget: " + item);
   4373         }
   4374         final Workspace workspace = mWorkspace;
   4375 
   4376         AppWidgetProviderInfo appWidgetInfo;
   4377         if (!mIsSafeModeEnabled
   4378                 && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0)
   4379                 && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) {
   4380 
   4381             appWidgetInfo = mModel.findAppWidgetProviderInfoWithComponent(this, item.providerName);
   4382             if (appWidgetInfo == null) {
   4383                 if (DEBUG_WIDGETS) {
   4384                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
   4385                             + " belongs to component " + item.providerName
   4386                             + ", as the povider is null");
   4387                 }
   4388                 LauncherModel.deleteItemFromDatabase(this, item);
   4389                 return;
   4390             }
   4391             // Note: This assumes that the id remap broadcast is received before this step.
   4392             // If that is not the case, the id remap will be ignored and user may see the
   4393             // click to setup view.
   4394             PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null, null);
   4395             pendingInfo.spanX = item.spanX;
   4396             pendingInfo.spanY = item.spanY;
   4397             pendingInfo.minSpanX = item.minSpanX;
   4398             pendingInfo.minSpanY = item.minSpanY;
   4399             Bundle options =
   4400                     AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
   4401 
   4402             int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
   4403             boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
   4404                     newWidgetId, appWidgetInfo, options);
   4405 
   4406             // TODO consider showing a permission dialog when the widget is clicked.
   4407             if (!success) {
   4408                 mAppWidgetHost.deleteAppWidgetId(newWidgetId);
   4409                 if (DEBUG_WIDGETS) {
   4410                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
   4411                             + " belongs to component " + item.providerName
   4412                             + ", as the launcher is unable to bing a new widget id");
   4413                 }
   4414                 LauncherModel.deleteItemFromDatabase(this, item);
   4415                 return;
   4416             }
   4417 
   4418             item.appWidgetId = newWidgetId;
   4419 
   4420             // If the widget has a configure activity, it is still needs to set it up, otherwise
   4421             // the widget is ready to go.
   4422             item.restoreStatus = (appWidgetInfo.configure == null)
   4423                     ? LauncherAppWidgetInfo.RESTORE_COMPLETED
   4424                     : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
   4425 
   4426             LauncherModel.updateItemInDatabase(this, item);
   4427         }
   4428 
   4429         if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
   4430             final int appWidgetId = item.appWidgetId;
   4431             appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   4432             if (DEBUG_WIDGETS) {
   4433                 Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
   4434             }
   4435 
   4436             item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   4437         } else {
   4438             appWidgetInfo = null;
   4439             PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item,
   4440                     mIsSafeModeEnabled);
   4441             view.updateIcon(mIconCache);
   4442             item.hostView = view;
   4443             item.hostView.updateAppWidget(null);
   4444             item.hostView.setOnClickListener(this);
   4445         }
   4446 
   4447         item.hostView.setTag(item);
   4448         item.onBindAppWidget(this);
   4449 
   4450         workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX,
   4451                 item.cellY, item.spanX, item.spanY, false);
   4452         addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
   4453 
   4454         workspace.requestLayout();
   4455 
   4456         if (DEBUG_WIDGETS) {
   4457             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
   4458                     + (SystemClock.uptimeMillis()-start) + "ms");
   4459         }
   4460     }
   4461 
   4462     /**
   4463      * Restores a pending widget.
   4464      *
   4465      * @param appWidgetId The app widget id
   4466      * @param cellInfo The position on screen where to create the widget.
   4467      */
   4468     private void completeRestoreAppWidget(final int appWidgetId) {
   4469         LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId);
   4470         if ((view == null) || !(view instanceof PendingAppWidgetHostView)) {
   4471             Log.e(TAG, "Widget update called, when the widget no longer exists.");
   4472             return;
   4473         }
   4474 
   4475         LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
   4476         info.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
   4477 
   4478         mWorkspace.reinflateWidgetsIfNecessary();
   4479         LauncherModel.updateItemInDatabase(this, info);
   4480     }
   4481 
   4482     public void onPageBoundSynchronously(int page) {
   4483         mSynchronouslyBoundPages.add(page);
   4484     }
   4485 
   4486     /**
   4487      * Callback saying that there aren't any more items to bind.
   4488      *
   4489      * Implementation of the method from LauncherModel.Callbacks.
   4490      */
   4491     public void finishBindingItems(final boolean upgradePath) {
   4492         Runnable r = new Runnable() {
   4493             public void run() {
   4494                 finishBindingItems(upgradePath);
   4495             }
   4496         };
   4497         if (waitUntilResume(r)) {
   4498             return;
   4499         }
   4500         if (mSavedState != null) {
   4501             if (!mWorkspace.hasFocus()) {
   4502                 mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
   4503             }
   4504             mSavedState = null;
   4505         }
   4506 
   4507         mWorkspace.restoreInstanceStateForRemainingPages();
   4508 
   4509         setWorkspaceLoading(false);
   4510         sendLoadingCompleteBroadcastIfNecessary();
   4511 
   4512         // If we received the result of any pending adds while the loader was running (e.g. the
   4513         // widget configuration forced an orientation change), process them now.
   4514         if (sPendingAddItem != null) {
   4515             final long screenId = completeAdd(sPendingAddItem);
   4516 
   4517             // TODO: this moves the user to the page where the pending item was added. Ideally,
   4518             // the screen would be guaranteed to exist after bind, and the page would be set through
   4519             // the workspace restore process.
   4520             mWorkspace.post(new Runnable() {
   4521                 @Override
   4522                 public void run() {
   4523                     mWorkspace.snapToScreenId(screenId);
   4524                 }
   4525             });
   4526             sPendingAddItem = null;
   4527         }
   4528 
   4529         if (upgradePath) {
   4530             mWorkspace.getUniqueComponents(true, null);
   4531             mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null);
   4532         }
   4533         PackageInstallerCompat.getInstance(this).onFinishBind();
   4534 
   4535         if (mLauncherCallbacks != null) {
   4536             mLauncherCallbacks.finishBindingItems(upgradePath);
   4537         }
   4538     }
   4539 
   4540     private void sendLoadingCompleteBroadcastIfNecessary() {
   4541         if (!mSharedPrefs.getBoolean(FIRST_LOAD_COMPLETE, false)) {
   4542             String permission =
   4543                     getResources().getString(R.string.receive_first_load_broadcast_permission);
   4544             Intent intent = new Intent(ACTION_FIRST_LOAD_COMPLETE);
   4545             sendBroadcast(intent, permission);
   4546             SharedPreferences.Editor editor = mSharedPrefs.edit();
   4547             editor.putBoolean(FIRST_LOAD_COMPLETE, true);
   4548             editor.apply();
   4549         }
   4550     }
   4551 
   4552     public boolean isAllAppsButtonRank(int rank) {
   4553         if (mHotseat != null) {
   4554             return mHotseat.isAllAppsButtonRank(rank);
   4555         }
   4556         return false;
   4557     }
   4558 
   4559     private boolean canRunNewAppsAnimation() {
   4560         long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
   4561         return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
   4562     }
   4563 
   4564     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
   4565         ValueAnimator bounceAnim = LauncherAnimUtils.ofPropertyValuesHolder(v,
   4566                 PropertyValuesHolder.ofFloat("alpha", 1f),
   4567                 PropertyValuesHolder.ofFloat("scaleX", 1f),
   4568                 PropertyValuesHolder.ofFloat("scaleY", 1f));
   4569         bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
   4570         bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
   4571         bounceAnim.setInterpolator(new SmoothPagedView.OvershootInterpolator());
   4572         return bounceAnim;
   4573     }
   4574 
   4575     public boolean useVerticalBarLayout() {
   4576         return LauncherAppState.getInstance().getDynamicGrid().
   4577                 getDeviceProfile().isVerticalBarLayout();
   4578     }
   4579 
   4580     protected Rect getSearchBarBounds() {
   4581         return LauncherAppState.getInstance().getDynamicGrid().
   4582                 getDeviceProfile().getSearchBarBounds();
   4583     }
   4584 
   4585     public void bindSearchablesChanged() {
   4586         if (mSearchDropTargetBar == null) {
   4587             return;
   4588         }
   4589         if (mQsb != null) {
   4590             mSearchDropTargetBar.removeView(mQsb);
   4591             mQsb = null;
   4592         }
   4593         mSearchDropTargetBar.setQsbSearchBar(getQsbBar());
   4594     }
   4595 
   4596     /**
   4597      * Add the icons for all apps.
   4598      *
   4599      * Implementation of the method from LauncherModel.Callbacks.
   4600      */
   4601     public void bindAllApplications(final ArrayList<AppInfo> apps) {
   4602         if (LauncherAppState.isDisableAllApps()) {
   4603             if (mIntentsOnWorkspaceFromUpgradePath != null) {
   4604                 if (LauncherModel.UPGRADE_USE_MORE_APPS_FOLDER) {
   4605                     getHotseat().addAllAppsFolder(mIconCache, apps,
   4606                             mIntentsOnWorkspaceFromUpgradePath, Launcher.this, mWorkspace);
   4607                 }
   4608                 mIntentsOnWorkspaceFromUpgradePath = null;
   4609             }
   4610             if (mAppsCustomizeContent != null) {
   4611                 mAppsCustomizeContent.onPackagesUpdated(
   4612                         LauncherModel.getSortedWidgetsAndShortcuts(this));
   4613             }
   4614         } else {
   4615             if (mAppsCustomizeContent != null) {
   4616                 mAppsCustomizeContent.setApps(apps);
   4617                 mAppsCustomizeContent.onPackagesUpdated(
   4618                         LauncherModel.getSortedWidgetsAndShortcuts(this));
   4619             }
   4620         }
   4621         if (mLauncherCallbacks != null) {
   4622             mLauncherCallbacks.bindAllApplications(apps);
   4623         }
   4624     }
   4625 
   4626     /**
   4627      * A package was updated.
   4628      *
   4629      * Implementation of the method from LauncherModel.Callbacks.
   4630      */
   4631     public void bindAppsUpdated(final ArrayList<AppInfo> apps) {
   4632         Runnable r = new Runnable() {
   4633             public void run() {
   4634                 bindAppsUpdated(apps);
   4635             }
   4636         };
   4637         if (waitUntilResume(r)) {
   4638             return;
   4639         }
   4640 
   4641         if (!LauncherAppState.isDisableAllApps() &&
   4642                 mAppsCustomizeContent != null) {
   4643             mAppsCustomizeContent.updateApps(apps);
   4644         }
   4645     }
   4646 
   4647     @Override
   4648     public void bindWidgetsRestored(final ArrayList<LauncherAppWidgetInfo> widgets) {
   4649         Runnable r = new Runnable() {
   4650             public void run() {
   4651                 bindWidgetsRestored(widgets);
   4652             }
   4653         };
   4654         if (waitUntilResume(r)) {
   4655             return;
   4656         }
   4657         mWorkspace.widgetsRestored(widgets);
   4658     }
   4659 
   4660     /**
   4661      * Some shortcuts were updated in the background.
   4662      *
   4663      * Implementation of the method from LauncherModel.Callbacks.
   4664      */
   4665     @Override
   4666     public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated,
   4667             final ArrayList<ShortcutInfo> removed, final UserHandleCompat user) {
   4668         Runnable r = new Runnable() {
   4669             public void run() {
   4670                 bindShortcutsChanged(updated, removed, user);
   4671             }
   4672         };
   4673         if (waitUntilResume(r)) {
   4674             return;
   4675         }
   4676 
   4677         if (!updated.isEmpty()) {
   4678             mWorkspace.updateShortcuts(updated);
   4679         }
   4680 
   4681         if (!removed.isEmpty()) {
   4682             HashSet<ComponentName> removedComponents = new HashSet<ComponentName>();
   4683             for (ShortcutInfo si : removed) {
   4684                 removedComponents.add(si.getTargetComponent());
   4685             }
   4686             mWorkspace.removeItemsByComponentName(removedComponents, user);
   4687             // Notify the drag controller
   4688             mDragController.onAppsRemoved(new ArrayList<String>(), removedComponents);
   4689         }
   4690     }
   4691 
   4692     /**
   4693      * Update the state of a package, typically related to install state.
   4694      *
   4695      * Implementation of the method from LauncherModel.Callbacks.
   4696      */
   4697     @Override
   4698     public void updatePackageState(ArrayList<PackageInstallInfo> installInfo) {
   4699         if (mWorkspace != null) {
   4700             mWorkspace.updatePackageState(installInfo);
   4701         }
   4702     }
   4703 
   4704     /**
   4705      * Update the label and icon of all the icons in a package
   4706      *
   4707      * Implementation of the method from LauncherModel.Callbacks.
   4708      */
   4709     @Override
   4710     public void updatePackageBadge(String packageName) {
   4711         if (mWorkspace != null) {
   4712             mWorkspace.updatePackageBadge(packageName, UserHandleCompat.myUserHandle());
   4713         }
   4714     }
   4715 
   4716     /**
   4717      * A package was uninstalled.  We take both the super set of packageNames
   4718      * in addition to specific applications to remove, the reason being that
   4719      * this can be called when a package is updated as well.  In that scenario,
   4720      * we only remove specific components from the workspace, where as
   4721      * package-removal should clear all items by package name.
   4722      *
   4723      * @param reason if non-zero, the icons are not permanently removed, rather marked as disabled.
   4724      * Implementation of the method from LauncherModel.Callbacks.
   4725      */
   4726     @Override
   4727     public void bindComponentsRemoved(final ArrayList<String> packageNames,
   4728             final ArrayList<AppInfo> appInfos, final UserHandleCompat user, final int reason) {
   4729         Runnable r = new Runnable() {
   4730             public void run() {
   4731                 bindComponentsRemoved(packageNames, appInfos, user, reason);
   4732             }
   4733         };
   4734         if (waitUntilResume(r)) {
   4735             return;
   4736         }
   4737 
   4738         if (reason == 0) {
   4739             HashSet<ComponentName> removedComponents = new HashSet<ComponentName>();
   4740             for (AppInfo info : appInfos) {
   4741                 removedComponents.add(info.componentName);
   4742             }
   4743             if (!packageNames.isEmpty()) {
   4744                 mWorkspace.removeItemsByPackageName(packageNames, user);
   4745             }
   4746             if (!removedComponents.isEmpty()) {
   4747                 mWorkspace.removeItemsByComponentName(removedComponents, user);
   4748             }
   4749             // Notify the drag controller
   4750             mDragController.onAppsRemoved(packageNames, removedComponents);
   4751 
   4752         } else {
   4753             mWorkspace.disableShortcutsByPackageName(packageNames, user, reason);
   4754         }
   4755 
   4756         // Update AllApps
   4757         if (!LauncherAppState.isDisableAllApps() &&
   4758                 mAppsCustomizeContent != null) {
   4759             mAppsCustomizeContent.removeApps(appInfos);
   4760         }
   4761     }
   4762 
   4763     /**
   4764      * A number of packages were updated.
   4765      */
   4766     private ArrayList<Object> mWidgetsAndShortcuts;
   4767     private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
   4768             public void run() {
   4769                 bindPackagesUpdated(mWidgetsAndShortcuts);
   4770                 mWidgetsAndShortcuts = null;
   4771             }
   4772         };
   4773     public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) {
   4774         if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
   4775             mWidgetsAndShortcuts = widgetsAndShortcuts;
   4776             return;
   4777         }
   4778 
   4779         // Update the widgets pane
   4780         if (mAppsCustomizeContent != null) {
   4781             mAppsCustomizeContent.onPackagesUpdated(widgetsAndShortcuts);
   4782         }
   4783     }
   4784 
   4785     private int mapConfigurationOriActivityInfoOri(int configOri) {
   4786         final Display d = getWindowManager().getDefaultDisplay();
   4787         int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
   4788         switch (d.getRotation()) {
   4789         case Surface.ROTATION_0:
   4790         case Surface.ROTATION_180:
   4791             // We are currently in the same basic orientation as the natural orientation
   4792             naturalOri = configOri;
   4793             break;
   4794         case Surface.ROTATION_90:
   4795         case Surface.ROTATION_270:
   4796             // We are currently in the other basic orientation to the natural orientation
   4797             naturalOri = (configOri == Configuration.ORIENTATION_LANDSCAPE) ?
   4798                     Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
   4799             break;
   4800         }
   4801 
   4802         int[] oriMap = {
   4803                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
   4804                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
   4805                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
   4806                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
   4807         };
   4808         // Since the map starts at portrait, we need to offset if this device's natural orientation
   4809         // is landscape.
   4810         int indexOffset = 0;
   4811         if (naturalOri == Configuration.ORIENTATION_LANDSCAPE) {
   4812             indexOffset = 1;
   4813         }
   4814         return oriMap[(d.getRotation() + indexOffset) % 4];
   4815     }
   4816 
   4817     public void lockScreenOrientation() {
   4818         if (Utilities.isRotationEnabled(this)) {
   4819             setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
   4820                     .getConfiguration().orientation));
   4821         }
   4822     }
   4823     public void unlockScreenOrientation(boolean immediate) {
   4824         if (Utilities.isRotationEnabled(this)) {
   4825             if (immediate) {
   4826                 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   4827             } else {
   4828                 mHandler.postDelayed(new Runnable() {
   4829                     public void run() {
   4830                         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   4831                     }
   4832                 }, mRestoreScreenOrientationDelay);
   4833             }
   4834         }
   4835     }
   4836 
   4837     protected boolean isLauncherPreinstalled() {
   4838         if (mLauncherCallbacks != null) {
   4839             return mLauncherCallbacks.isLauncherPreinstalled();
   4840         }
   4841         PackageManager pm = getPackageManager();
   4842         try {
   4843             ApplicationInfo ai = pm.getApplicationInfo(getComponentName().getPackageName(), 0);
   4844             if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
   4845                 return true;
   4846             } else {
   4847                 return false;
   4848             }
   4849         } catch (NameNotFoundException e) {
   4850             e.printStackTrace();
   4851             return false;
   4852         }
   4853     }
   4854 
   4855     /**
   4856      * This method indicates whether or not we should suggest default wallpaper dimensions
   4857      * when our wallpaper cropper was not yet used to set a wallpaper.
   4858      */
   4859     protected boolean overrideWallpaperDimensions() {
   4860         if (mLauncherCallbacks != null) {
   4861             return mLauncherCallbacks.overrideWallpaperDimensions();
   4862         }
   4863         return true;
   4864     }
   4865 
   4866     /**
   4867      * To be overridden by subclasses to indicate that there is an activity to launch
   4868      * before showing the standard launcher experience.
   4869      */
   4870     protected boolean hasFirstRunActivity() {
   4871         if (mLauncherCallbacks != null) {
   4872             return mLauncherCallbacks.hasFirstRunActivity();
   4873         }
   4874         return false;
   4875     }
   4876 
   4877     /**
   4878      * To be overridden by subclasses to launch any first run activity
   4879      */
   4880     protected Intent getFirstRunActivity() {
   4881         if (mLauncherCallbacks != null) {
   4882             return mLauncherCallbacks.getFirstRunActivity();
   4883         }
   4884         return null;
   4885     }
   4886 
   4887     private boolean shouldRunFirstRunActivity() {
   4888         return !ActivityManager.isRunningInTestHarness() &&
   4889                 !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
   4890     }
   4891 
   4892     protected boolean hasRunFirstRunActivity() {
   4893         return mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
   4894     }
   4895 
   4896     public boolean showFirstRunActivity() {
   4897         if (shouldRunFirstRunActivity() &&
   4898                 hasFirstRunActivity()) {
   4899             Intent firstRunIntent = getFirstRunActivity();
   4900             if (firstRunIntent != null) {
   4901                 startActivity(firstRunIntent);
   4902                 markFirstRunActivityShown();
   4903                 return true;
   4904             }
   4905         }
   4906         return false;
   4907     }
   4908 
   4909     private void markFirstRunActivityShown() {
   4910         SharedPreferences.Editor editor = mSharedPrefs.edit();
   4911         editor.putBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, true);
   4912         editor.apply();
   4913     }
   4914 
   4915     /**
   4916      * To be overridden by subclasses to indicate that there is an in-activity full-screen intro
   4917      * screen that must be displayed and dismissed.
   4918      */
   4919     protected boolean hasDismissableIntroScreen() {
   4920         if (mLauncherCallbacks != null) {
   4921             return mLauncherCallbacks.hasDismissableIntroScreen();
   4922         }
   4923         return false;
   4924     }
   4925 
   4926     /**
   4927      * Full screen intro screen to be shown and dismissed before the launcher can be used.
   4928      */
   4929     protected View getIntroScreen() {
   4930         if (mLauncherCallbacks != null) {
   4931             return mLauncherCallbacks.getIntroScreen();
   4932         }
   4933         return null;
   4934     }
   4935 
   4936     /**
   4937      * To be overriden by subclasses to indicate whether the in-activity intro screen has been
   4938      * dismissed. This method is ignored if #hasDismissableIntroScreen returns false.
   4939      */
   4940     private boolean shouldShowIntroScreen() {
   4941         return hasDismissableIntroScreen() &&
   4942                 !mSharedPrefs.getBoolean(INTRO_SCREEN_DISMISSED, false);
   4943     }
   4944 
   4945     protected void showIntroScreen() {
   4946         View introScreen = getIntroScreen();
   4947         changeWallpaperVisiblity(false);
   4948         if (introScreen != null) {
   4949             mDragLayer.showOverlayView(introScreen);
   4950         }
   4951         if (mLauncherOverlayContainer != null) {
   4952             mLauncherOverlayContainer.setVisibility(View.INVISIBLE);
   4953         }
   4954     }
   4955 
   4956     public void dismissIntroScreen() {
   4957         markIntroScreenDismissed();
   4958         if (showFirstRunActivity()) {
   4959             // We delay hiding the intro view until the first run activity is showing. This
   4960             // avoids a blip.
   4961             mWorkspace.postDelayed(new Runnable() {
   4962                 @Override
   4963                 public void run() {
   4964                     mDragLayer.dismissOverlayView();
   4965                     if (mLauncherOverlayContainer != null) {
   4966                         mLauncherOverlayContainer.setVisibility(View.VISIBLE);
   4967                     }
   4968                     showFirstRunClings();
   4969                 }
   4970             }, ACTIVITY_START_DELAY);
   4971         } else {
   4972             mDragLayer.dismissOverlayView();
   4973             if (mLauncherOverlayContainer != null) {
   4974                 mLauncherOverlayContainer.setVisibility(View.VISIBLE);
   4975             }
   4976             showFirstRunClings();
   4977         }
   4978         changeWallpaperVisiblity(true);
   4979     }
   4980 
   4981     private void markIntroScreenDismissed() {
   4982         SharedPreferences.Editor editor = mSharedPrefs.edit();
   4983         editor.putBoolean(INTRO_SCREEN_DISMISSED, true);
   4984         editor.apply();
   4985     }
   4986 
   4987     private void showFirstRunClings() {
   4988         // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
   4989         // on the device, then we always show the first run cling experience (or if there is no
   4990         // launcher2). Otherwise, we prompt the user upon started for migration
   4991         LauncherClings launcherClings = new LauncherClings(this);
   4992         if (launcherClings.shouldShowFirstRunOrMigrationClings()) {
   4993             if (mModel.canMigrateFromOldLauncherDb(this)) {
   4994                 launcherClings.showMigrationCling();
   4995             } else {
   4996                 launcherClings.showLongPressCling(true);
   4997             }
   4998         }
   4999     }
   5000 
   5001     void showWorkspaceSearchAndHotseat() {
   5002         if (mWorkspace != null) mWorkspace.setAlpha(1f);
   5003         if (mHotseat != null) mHotseat.setAlpha(1f);
   5004         if (mPageIndicators != null) mPageIndicators.setAlpha(1f);
   5005         if (mSearchDropTargetBar != null) mSearchDropTargetBar.showSearchBar(false);
   5006     }
   5007 
   5008     void hideWorkspaceSearchAndHotseat() {
   5009         if (mWorkspace != null) mWorkspace.setAlpha(0f);
   5010         if (mHotseat != null) mHotseat.setAlpha(0f);
   5011         if (mPageIndicators != null) mPageIndicators.setAlpha(0f);
   5012         if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false);
   5013     }
   5014 
   5015     public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
   5016         // Called from search suggestion, not supported in other profiles.
   5017         final UserHandleCompat myUser = UserHandleCompat.myUserHandle();
   5018         LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
   5019         LauncherActivityInfoCompat activityInfo = launcherApps.resolveActivity(appLaunchIntent,
   5020                 myUser);
   5021         if (activityInfo == null) {
   5022             return null;
   5023         }
   5024         return new AppInfo(this, activityInfo, myUser, mIconCache, null);
   5025     }
   5026 
   5027     public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
   5028             Bitmap icon) {
   5029         // Called from search suggestion, not supported in other profiles.
   5030         return createShortcutDragInfo(shortcutIntent, caption, icon,
   5031                 UserHandleCompat.myUserHandle());
   5032     }
   5033 
   5034     public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
   5035             Bitmap icon, UserHandleCompat user) {
   5036         UserManagerCompat userManager = UserManagerCompat.getInstance(this);
   5037         CharSequence contentDescription = userManager.getBadgedLabelForUser(caption, user);
   5038         return new ShortcutInfo(shortcutIntent, caption, contentDescription, icon, user);
   5039     }
   5040 
   5041     protected void moveWorkspaceToDefaultScreen() {
   5042         mWorkspace.moveToDefaultScreen(false);
   5043     }
   5044 
   5045     public void startDrag(View dragView, ItemInfo dragInfo, DragSource source) {
   5046         dragView.setTag(dragInfo);
   5047         mWorkspace.onExternalDragStartedWithItem(dragView);
   5048         mWorkspace.beginExternalDragShared(dragView, source);
   5049     }
   5050 
   5051     @Override
   5052     public void onPageSwitch(View newPage, int newPageIndex) {
   5053         if (mLauncherCallbacks != null) {
   5054             mLauncherCallbacks.onPageSwitch(newPage, newPageIndex);
   5055         }
   5056     }
   5057 
   5058     /**
   5059      * Prints out out state for debugging.
   5060      */
   5061     public void dumpState() {
   5062         Log.d(TAG, "BEGIN launcher3 dump state for launcher " + this);
   5063         Log.d(TAG, "mSavedState=" + mSavedState);
   5064         Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
   5065         Log.d(TAG, "mRestoring=" + mRestoring);
   5066         Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
   5067         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
   5068         Log.d(TAG, "sFolders.size=" + sFolders.size());
   5069         mModel.dumpState();
   5070 
   5071         if (mAppsCustomizeContent != null) {
   5072             mAppsCustomizeContent.dumpState();
   5073         }
   5074         Log.d(TAG, "END launcher3 dump state");
   5075     }
   5076 
   5077     @Override
   5078     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
   5079         super.dump(prefix, fd, writer, args);
   5080         synchronized (sDumpLogs) {
   5081             writer.println(" ");
   5082             writer.println("Debug logs: ");
   5083             for (int i = 0; i < sDumpLogs.size(); i++) {
   5084                 writer.println("  " + sDumpLogs.get(i));
   5085             }
   5086         }
   5087         if (mLauncherCallbacks != null) {
   5088             mLauncherCallbacks.dump(prefix, fd, writer, args);
   5089         }
   5090     }
   5091 
   5092     public static void dumpDebugLogsToConsole() {
   5093         if (DEBUG_DUMP_LOG) {
   5094             synchronized (sDumpLogs) {
   5095                 Log.d(TAG, "");
   5096                 Log.d(TAG, "*********************");
   5097                 Log.d(TAG, "Launcher debug logs: ");
   5098                 for (int i = 0; i < sDumpLogs.size(); i++) {
   5099                     Log.d(TAG, "  " + sDumpLogs.get(i));
   5100                 }
   5101                 Log.d(TAG, "*********************");
   5102                 Log.d(TAG, "");
   5103             }
   5104         }
   5105     }
   5106 
   5107     public static void addDumpLog(String tag, String log, boolean debugLog) {
   5108         addDumpLog(tag, log, null, debugLog);
   5109     }
   5110 
   5111     public static void addDumpLog(String tag, String log, Exception e, boolean debugLog) {
   5112         if (debugLog) {
   5113             if (e != null) {
   5114                 Log.d(tag, log, e);
   5115             } else {
   5116                 Log.d(tag, log);
   5117             }
   5118         }
   5119         if (DEBUG_DUMP_LOG) {
   5120             sDateStamp.setTime(System.currentTimeMillis());
   5121             synchronized (sDumpLogs) {
   5122                 sDumpLogs.add(sDateFormat.format(sDateStamp) + ": " + tag + ", " + log
   5123                     + (e == null ? "" : (", Exception: " + e)));
   5124             }
   5125         }
   5126     }
   5127 
   5128     public void dumpLogsToLocalData() {
   5129         if (DEBUG_DUMP_LOG) {
   5130             new AsyncTask<Void, Void, Void>() {
   5131                 public Void doInBackground(Void ... args) {
   5132                     boolean success = false;
   5133                     sDateStamp.setTime(sRunStart);
   5134                     String FILENAME = sDateStamp.getMonth() + "-"
   5135                             + sDateStamp.getDay() + "_"
   5136                             + sDateStamp.getHours() + "-"
   5137                             + sDateStamp.getMinutes() + "_"
   5138                             + sDateStamp.getSeconds() + ".txt";
   5139 
   5140                     FileOutputStream fos = null;
   5141                     File outFile = null;
   5142                     try {
   5143                         outFile = new File(getFilesDir(), FILENAME);
   5144                         outFile.createNewFile();
   5145                         fos = new FileOutputStream(outFile);
   5146                     } catch (Exception e) {
   5147                         e.printStackTrace();
   5148                     }
   5149                     if (fos != null) {
   5150                         PrintWriter writer = new PrintWriter(fos);
   5151 
   5152                         writer.println(" ");
   5153                         writer.println("Debug logs: ");
   5154                         synchronized (sDumpLogs) {
   5155                             for (int i = 0; i < sDumpLogs.size(); i++) {
   5156                                 writer.println("  " + sDumpLogs.get(i));
   5157                             }
   5158                         }
   5159                         writer.close();
   5160                     }
   5161                     try {
   5162                         if (fos != null) {
   5163                             fos.close();
   5164                             success = true;
   5165                         }
   5166                     } catch (IOException e) {
   5167                         e.printStackTrace();
   5168                     }
   5169                     return null;
   5170                 }
   5171             }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
   5172         }
   5173     }
   5174 }
   5175 
   5176 interface LauncherTransitionable {
   5177     View getContent();
   5178     void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);
   5179     void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
   5180     void onLauncherTransitionStep(Launcher l, float t);
   5181     void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
   5182 }
   5183 
   5184 interface DebugIntents {
   5185     static final String DELETE_DATABASE = "com.android.launcher3.action.DELETE_DATABASE";
   5186     static final String MIGRATE_DATABASE = "com.android.launcher3.action.MIGRATE_DATABASE";
   5187 }
   5188