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