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