OSDN Git Service

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