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