• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher2;
18 
19 import com.android.common.Search;
20 
21 import android.app.Activity;
22 import android.app.AlertDialog;
23 import android.app.Dialog;
24 import android.app.SearchManager;
25 import android.app.StatusBarManager;
26 import android.app.WallpaperManager;
27 import android.content.ActivityNotFoundException;
28 import android.content.BroadcastReceiver;
29 import android.content.ComponentName;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.Intent;
34 import android.content.Intent.ShortcutIconResource;
35 import android.content.IntentFilter;
36 import android.content.pm.ActivityInfo;
37 import android.content.pm.PackageManager;
38 import android.content.pm.ResolveInfo;
39 import android.content.res.Configuration;
40 import android.content.res.Resources;
41 import android.content.res.TypedArray;
42 import android.database.ContentObserver;
43 import android.graphics.Bitmap;
44 import android.graphics.Rect;
45 import android.graphics.Canvas;
46 import android.graphics.drawable.Drawable;
47 import android.graphics.drawable.ColorDrawable;
48 import android.net.Uri;
49 import android.os.AsyncTask;
50 import android.os.Bundle;
51 import android.os.Handler;
52 import android.os.Parcelable;
53 import android.os.SystemClock;
54 import android.os.SystemProperties;
55 import android.provider.LiveFolders;
56 import android.text.Selection;
57 import android.text.SpannableStringBuilder;
58 import android.text.TextUtils;
59 import android.text.method.TextKeyListener;
60 import android.util.Log;
61 import android.view.Display;
62 import android.view.HapticFeedbackConstants;
63 import android.view.KeyEvent;
64 import android.view.LayoutInflater;
65 import android.view.Menu;
66 import android.view.MenuItem;
67 import android.view.View;
68 import android.view.ViewGroup;
69 import android.view.View.OnLongClickListener;
70 import android.view.inputmethod.InputMethodManager;
71 import android.widget.EditText;
72 import android.widget.TextView;
73 import android.widget.Toast;
74 import android.widget.ImageView;
75 import android.widget.PopupWindow;
76 import android.widget.LinearLayout;
77 import android.appwidget.AppWidgetManager;
78 import android.appwidget.AppWidgetProviderInfo;
79 
80 import java.util.ArrayList;
81 import java.util.List;
82 import java.util.HashMap;
83 import java.io.DataOutputStream;
84 import java.io.FileNotFoundException;
85 import java.io.IOException;
86 import java.io.DataInputStream;
87 
88 import com.android.launcher.R;
89 
90 /**
91  * Default launcher application.
92  */
93 public final class Launcher extends Activity
94         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
95     static final String TAG = "Launcher";
96     static final boolean LOGD = false;
97 
98     static final boolean PROFILE_STARTUP = false;
99     static final boolean DEBUG_WIDGETS = false;
100     static final boolean DEBUG_USER_INTERFACE = false;
101 
102     private static final int WALLPAPER_SCREENS_SPAN = 2;
103 
104     private static final int MENU_GROUP_ADD = 1;
105     private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1;
106 
107     private static final int MENU_ADD = Menu.FIRST + 1;
108     private static final int MENU_MANAGE_APPS = MENU_ADD + 1;
109     private static final int MENU_WALLPAPER_SETTINGS = MENU_MANAGE_APPS + 1;
110     private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
111     private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1;
112     private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
113 
114     private static final int REQUEST_CREATE_SHORTCUT = 1;
115     private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
116     private static final int REQUEST_CREATE_APPWIDGET = 5;
117     private static final int REQUEST_PICK_APPLICATION = 6;
118     private static final int REQUEST_PICK_SHORTCUT = 7;
119     private static final int REQUEST_PICK_LIVE_FOLDER = 8;
120     private static final int REQUEST_PICK_APPWIDGET = 9;
121     private static final int REQUEST_PICK_WALLPAPER = 10;
122 
123     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
124 
125     static final int SCREEN_COUNT = 5;
126     static final int DEFAULT_SCREEN = 2;
127     static final int NUMBER_CELLS_X = 4;
128     static final int NUMBER_CELLS_Y = 4;
129 
130     static final int DIALOG_CREATE_SHORTCUT = 1;
131     static final int DIALOG_RENAME_FOLDER = 2;
132 
133     private static final String PREFERENCES = "launcher.preferences";
134 
135     // Type: int
136     private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
137     // Type: boolean
138     private static final String RUNTIME_STATE_ALL_APPS_FOLDER = "launcher.all_apps_folder";
139     // Type: long
140     private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder";
141     // Type: int
142     private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
143     // Type: int
144     private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX";
145     // Type: int
146     private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY";
147     // Type: int
148     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX";
149     // Type: int
150     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY";
151     // Type: int
152     private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX";
153     // Type: int
154     private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY";
155     // Type: int[]
156     private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells";
157     // Type: boolean
158     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
159     // Type: long
160     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
161 
162     static final int APPWIDGET_HOST_ID = 1024;
163 
164     private static final Object sLock = new Object();
165     private static int sScreen = DEFAULT_SCREEN;
166 
167     private final BroadcastReceiver mCloseSystemDialogsReceiver
168             = new CloseSystemDialogsIntentReceiver();
169     private final ContentObserver mWidgetObserver = new AppWidgetResetObserver();
170 
171     private LayoutInflater mInflater;
172 
173     private DragController mDragController;
174     private Workspace mWorkspace;
175 
176     private AppWidgetManager mAppWidgetManager;
177     private LauncherAppWidgetHost mAppWidgetHost;
178 
179     private CellLayout.CellInfo mAddItemCellInfo;
180     private CellLayout.CellInfo mMenuAddInfo;
181     private final int[] mCellCoordinates = new int[2];
182     private FolderInfo mFolderInfo;
183 
184     private DeleteZone mDeleteZone;
185     private HandleView mHandleView;
186     private AllAppsView mAllAppsGrid;
187 
188     private Bundle mSavedState;
189 
190     private SpannableStringBuilder mDefaultKeySsb = null;
191 
192     private boolean mWorkspaceLoading = true;
193 
194     private boolean mPaused = true;
195     private boolean mRestoring;
196     private boolean mWaitingForResult;
197     private boolean mOnResumeNeedsLoad;
198 
199     private Bundle mSavedInstanceState;
200 
201     private LauncherModel mModel;
202     private IconCache mIconCache;
203 
204     private static LocaleConfiguration sLocaleConfiguration = null;
205 
206     private ArrayList<ItemInfo> mDesktopItems = new ArrayList<ItemInfo>();
207     private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
208 
209     private ImageView mPreviousView;
210     private ImageView mNextView;
211 
212     // Hotseats (quick-launch icons next to AllApps)
213     private static final int NUM_HOTSEATS = 2;
214     private String[] mHotseatConfig = null;
215     private Intent[] mHotseats = null;
216     private Drawable[] mHotseatIcons = null;
217     private CharSequence[] mHotseatLabels = null;
218 
219     @Override
onCreate(Bundle savedInstanceState)220     protected void onCreate(Bundle savedInstanceState) {
221         super.onCreate(savedInstanceState);
222 
223         LauncherApplication app = ((LauncherApplication)getApplication());
224         mModel = app.setLauncher(this);
225         mIconCache = app.getIconCache();
226         mDragController = new DragController(this);
227         mInflater = getLayoutInflater();
228 
229         mAppWidgetManager = AppWidgetManager.getInstance(this);
230         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
231         mAppWidgetHost.startListening();
232 
233         if (PROFILE_STARTUP) {
234             android.os.Debug.startMethodTracing("/sdcard/launcher");
235         }
236 
237         loadHotseats();
238         checkForLocaleChange();
239         setWallpaperDimension();
240 
241         setContentView(R.layout.launcher);
242         setupViews();
243 
244         registerContentObservers();
245 
246         lockAllApps();
247 
248         mSavedState = savedInstanceState;
249         restoreState(mSavedState);
250 
251         if (PROFILE_STARTUP) {
252             android.os.Debug.stopMethodTracing();
253         }
254 
255         if (!mRestoring) {
256             mModel.startLoader(this, true);
257         }
258 
259         // For handling default keys
260         mDefaultKeySsb = new SpannableStringBuilder();
261         Selection.setSelection(mDefaultKeySsb, 0);
262 
263         IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
264         registerReceiver(mCloseSystemDialogsReceiver, filter);
265     }
266 
checkForLocaleChange()267     private void checkForLocaleChange() {
268         if (sLocaleConfiguration == null) {
269             new AsyncTask<Void, Void, LocaleConfiguration>() {
270                 @Override
271                 protected LocaleConfiguration doInBackground(Void... unused) {
272                     LocaleConfiguration localeConfiguration = new LocaleConfiguration();
273                     readConfiguration(Launcher.this, localeConfiguration);
274                     return localeConfiguration;
275                 }
276 
277                 @Override
278                 protected void onPostExecute(LocaleConfiguration result) {
279                     sLocaleConfiguration = result;
280                     checkForLocaleChange();  // recursive, but now with a locale configuration
281                 }
282             }.execute();
283             return;
284         }
285 
286         final Configuration configuration = getResources().getConfiguration();
287 
288         final String previousLocale = sLocaleConfiguration.locale;
289         final String locale = configuration.locale.toString();
290 
291         final int previousMcc = sLocaleConfiguration.mcc;
292         final int mcc = configuration.mcc;
293 
294         final int previousMnc = sLocaleConfiguration.mnc;
295         final int mnc = configuration.mnc;
296 
297         boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
298 
299         if (localeChanged) {
300             sLocaleConfiguration.locale = locale;
301             sLocaleConfiguration.mcc = mcc;
302             sLocaleConfiguration.mnc = mnc;
303 
304             mIconCache.flush();
305             loadHotseats();
306 
307             final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
308             new Thread("WriteLocaleConfiguration") {
309                 public void run() {
310                     writeConfiguration(Launcher.this, localeConfiguration);
311                 }
312             }.start();
313         }
314     }
315 
316     private static class LocaleConfiguration {
317         public String locale;
318         public int mcc = -1;
319         public int mnc = -1;
320     }
321 
readConfiguration(Context context, LocaleConfiguration configuration)322     private static void readConfiguration(Context context, LocaleConfiguration configuration) {
323         DataInputStream in = null;
324         try {
325             in = new DataInputStream(context.openFileInput(PREFERENCES));
326             configuration.locale = in.readUTF();
327             configuration.mcc = in.readInt();
328             configuration.mnc = in.readInt();
329         } catch (FileNotFoundException e) {
330             // Ignore
331         } catch (IOException e) {
332             // Ignore
333         } finally {
334             if (in != null) {
335                 try {
336                     in.close();
337                 } catch (IOException e) {
338                     // Ignore
339                 }
340             }
341         }
342     }
343 
writeConfiguration(Context context, LocaleConfiguration configuration)344     private static void writeConfiguration(Context context, LocaleConfiguration configuration) {
345         DataOutputStream out = null;
346         try {
347             out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE));
348             out.writeUTF(configuration.locale);
349             out.writeInt(configuration.mcc);
350             out.writeInt(configuration.mnc);
351             out.flush();
352         } catch (FileNotFoundException e) {
353             // Ignore
354         } catch (IOException e) {
355             //noinspection ResultOfMethodCallIgnored
356             context.getFileStreamPath(PREFERENCES).delete();
357         } finally {
358             if (out != null) {
359                 try {
360                     out.close();
361                 } catch (IOException e) {
362                     // Ignore
363                 }
364             }
365         }
366     }
367 
getScreen()368     static int getScreen() {
369         synchronized (sLock) {
370             return sScreen;
371         }
372     }
373 
setScreen(int screen)374     static void setScreen(int screen) {
375         synchronized (sLock) {
376             sScreen = screen;
377         }
378     }
379 
setWallpaperDimension()380     private void setWallpaperDimension() {
381         WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE);
382 
383         Display display = getWindowManager().getDefaultDisplay();
384         boolean isPortrait = display.getWidth() < display.getHeight();
385 
386         final int width = isPortrait ? display.getWidth() : display.getHeight();
387         final int height = isPortrait ? display.getHeight() : display.getWidth();
388         wpm.suggestDesiredDimensions(width * WALLPAPER_SCREENS_SPAN, height);
389     }
390 
391     // Note: This doesn't do all the client-id magic that BrowserProvider does
392     // in Browser. (http://b/2425179)
393     private Uri getDefaultBrowserUri() {
394         String url = getString(R.string.default_browser_url);
395         if (url.indexOf("{CID}") != -1) {
396             url = url.replace("{CID}", "android-google");
397         }
398         return Uri.parse(url);
399     }
400 
401     // Load the Intent templates from arrays.xml to populate the hotseats. For
402     // each Intent, if it resolves to a single app, use that as the launch
403     // intent & use that app's label as the contentDescription. Otherwise,
404     // retain the ResolveActivity so the user can pick an app.
405     private void loadHotseats() {
406         if (mHotseatConfig == null) {
407             mHotseatConfig = getResources().getStringArray(R.array.hotseats);
408             if (mHotseatConfig.length > 0) {
409                 mHotseats = new Intent[mHotseatConfig.length];
410                 mHotseatLabels = new CharSequence[mHotseatConfig.length];
411                 mHotseatIcons = new Drawable[mHotseatConfig.length];
412             } else {
413                 mHotseats = null;
414                 mHotseatIcons = null;
415                 mHotseatLabels = null;
416             }
417 
418             TypedArray hotseatIconDrawables = getResources().obtainTypedArray(R.array.hotseat_icons);
419             for (int i=0; i<mHotseatConfig.length; i++) {
420                 // load icon for this slot; currently unrelated to the actual activity
421                 try {
422                     mHotseatIcons[i] = hotseatIconDrawables.getDrawable(i);
423                 } catch (ArrayIndexOutOfBoundsException ex) {
424                     Log.w(TAG, "Missing hotseat_icons array item #" + i);
425                     mHotseatIcons[i] = null;
426                 }
427             }
428             hotseatIconDrawables.recycle();
429         }
430 
431         PackageManager pm = getPackageManager();
432         for (int i=0; i<mHotseatConfig.length; i++) {
433             Intent intent = null;
434             if (mHotseatConfig[i].equals("*BROWSER*")) {
435                 // magic value meaning "launch user's default web browser"
436                 // replace it with a generic web request so we can see if there is indeed a default
437                 String defaultUri = getString(R.string.default_browser_url);
438                 intent = new Intent(
439                         Intent.ACTION_VIEW,
440                         ((defaultUri != null)
441                             ? Uri.parse(defaultUri)
442                             : getDefaultBrowserUri())
443                     ).addCategory(Intent.CATEGORY_BROWSABLE);
444                 // note: if the user launches this without a default set, she
445                 // will always be taken to the default URL above; this is
446                 // unavoidable as we must specify a valid URL in order for the
447                 // chooser to appear, and once the user selects something, that
448                 // URL is unavoidably sent to the chosen app.
449             } else {
450                 try {
451                     intent = Intent.parseUri(mHotseatConfig[i], 0);
452                 } catch (java.net.URISyntaxException ex) {
453                     Log.w(TAG, "Invalid hotseat intent: " + mHotseatConfig[i]);
454                     // bogus; leave intent=null
455                 }
456             }
457 
458             if (intent == null) {
459                 mHotseats[i] = null;
460                 mHotseatLabels[i] = getText(R.string.activity_not_found);
461                 continue;
462             }
463 
464             if (LOGD) {
Log.d(TAG, "loadHotseats: hotseat " + i + " initial intent=[" + intent.toUri(Intent.URI_INTENT_SCHEME) + "]")465                 Log.d(TAG, "loadHotseats: hotseat " + i
466                     + " initial intent=["
467                     + intent.toUri(Intent.URI_INTENT_SCHEME)
468                     + "]");
469             }
470 
471             ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
472             List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
473             if (LOGD) {
Log.d(TAG, "Best match for intent: " + bestMatch)474                 Log.d(TAG, "Best match for intent: " + bestMatch);
Log.d(TAG, "All matches: ")475                 Log.d(TAG, "All matches: ");
476                 for (ResolveInfo ri : allMatches) {
Log.d(TAG, "  --> " + ri)477                     Log.d(TAG, "  --> " + ri);
478                 }
479             }
480             // did this resolve to a single app, or the resolver?
481             if (allMatches.size() == 0 || bestMatch == null) {
482                 // can't find any activity to handle this. let's leave the
483                 // intent as-is and let Launcher show a toast when it fails
484                 // to launch.
485                 mHotseats[i] = intent;
486 
487                 // set accessibility text to "Not installed"
488                 mHotseatLabels[i] = getText(R.string.activity_not_found);
489             } else {
490                 boolean found = false;
491                 for (ResolveInfo ri : allMatches) {
492                     if (bestMatch.activityInfo.name.equals(ri.activityInfo.name)
493                         && bestMatch.activityInfo.applicationInfo.packageName
494                             .equals(ri.activityInfo.applicationInfo.packageName)) {
495                         found = true;
496                         break;
497                     }
498                 }
499 
500                 if (!found) {
Log.d(TAG, "Multiple options, no default yet")501                     if (LOGD) Log.d(TAG, "Multiple options, no default yet");
502                     // the bestMatch is probably the ResolveActivity, meaning the
503                     // user has not yet selected a default
504                     // so: we'll keep the original intent for now
505                     mHotseats[i] = intent;
506 
507                     // set the accessibility text to "Select shortcut"
508                     mHotseatLabels[i] = getText(R.string.title_select_shortcut);
509                 } else {
510                     // we have an app!
511                     // now reconstruct the intent to launch it through the front
512                     // door
513                     ComponentName com = new ComponentName(
514                         bestMatch.activityInfo.applicationInfo.packageName,
515                         bestMatch.activityInfo.name);
516                     mHotseats[i] = new Intent(Intent.ACTION_MAIN).setComponent(com);
517 
518                     // load the app label for accessibility
519                     mHotseatLabels[i] = bestMatch.activityInfo.loadLabel(pm);
520                 }
521             }
522 
523             if (LOGD) {
Log.d(TAG, "loadHotseats: hotseat " + i + " final intent=[" + ((mHotseats[i] == null) ? "null" : mHotseats[i].toUri(Intent.URI_INTENT_SCHEME)) + "] label=[" + mHotseatLabels[i] + "]" )524                 Log.d(TAG, "loadHotseats: hotseat " + i
525                     + " final intent=["
526                     + ((mHotseats[i] == null)
527                         ? "null"
528                         : mHotseats[i].toUri(Intent.URI_INTENT_SCHEME))
529                     + "] label=[" + mHotseatLabels[i]
530                     + "]"
531                     );
532             }
533         }
534     }
535 
536     @Override
onActivityResult(int requestCode, int resultCode, Intent data)537     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
538         mWaitingForResult = false;
539 
540         // The pattern used here is that a user PICKs a specific application,
541         // which, depending on the target, might need to CREATE the actual target.
542 
543         // For example, the user would PICK_SHORTCUT for "Music playlist", and we
544         // launch over to the Music app to actually CREATE_SHORTCUT.
545 
546         if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
547             switch (requestCode) {
548                 case REQUEST_PICK_APPLICATION:
549                     completeAddApplication(this, data, mAddItemCellInfo);
550                     break;
551                 case REQUEST_PICK_SHORTCUT:
552                     processShortcut(data);
553                     break;
554                 case REQUEST_CREATE_SHORTCUT:
555                     completeAddShortcut(data, mAddItemCellInfo);
556                     break;
557                 case REQUEST_PICK_LIVE_FOLDER:
558                     addLiveFolder(data);
559                     break;
560                 case REQUEST_CREATE_LIVE_FOLDER:
561                     completeAddLiveFolder(data, mAddItemCellInfo);
562                     break;
563                 case REQUEST_PICK_APPWIDGET:
564                     addAppWidget(data);
565                     break;
566                 case REQUEST_CREATE_APPWIDGET:
567                     completeAddAppWidget(data, mAddItemCellInfo);
568                     break;
569                 case REQUEST_PICK_WALLPAPER:
570                     // We just wanted the activity result here so we can clear mWaitingForResult
571                     break;
572             }
573         } else if ((requestCode == REQUEST_PICK_APPWIDGET ||
574                 requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED &&
575                 data != null) {
576             // Clean up the appWidgetId if we canceled
577             int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
578             if (appWidgetId != -1) {
579                 mAppWidgetHost.deleteAppWidgetId(appWidgetId);
580             }
581         }
582     }
583 
584     @Override
onResume()585     protected void onResume() {
586         super.onResume();
587         mPaused = false;
588         if (mRestoring || mOnResumeNeedsLoad) {
589             mWorkspaceLoading = true;
590             mModel.startLoader(this, true);
591             mRestoring = false;
592             mOnResumeNeedsLoad = false;
593         }
594     }
595 
596     @Override
onPause()597     protected void onPause() {
598         super.onPause();
599         mPaused = true;
600         dismissPreview(mPreviousView);
601         dismissPreview(mNextView);
602         mDragController.cancelDrag();
603     }
604 
605     @Override
onRetainNonConfigurationInstance()606     public Object onRetainNonConfigurationInstance() {
607         // Flag the loader to stop early before switching
608         mModel.stopLoader();
609         mAllAppsGrid.surrender();
610         return Boolean.TRUE;
611     }
612 
613     // We can't hide the IME if it was forced open.  So don't bother
614     /*
615     @Override
616     public void onWindowFocusChanged(boolean hasFocus) {
617         super.onWindowFocusChanged(hasFocus);
618 
619         if (hasFocus) {
620             final InputMethodManager inputManager = (InputMethodManager)
621                     getSystemService(Context.INPUT_METHOD_SERVICE);
622             WindowManager.LayoutParams lp = getWindow().getAttributes();
623             inputManager.hideSoftInputFromWindow(lp.token, 0, new android.os.ResultReceiver(new
624                         android.os.Handler()) {
625                         protected void onReceiveResult(int resultCode, Bundle resultData) {
626                             Log.d(TAG, "ResultReceiver got resultCode=" + resultCode);
627                         }
628                     });
629             Log.d(TAG, "called hideSoftInputFromWindow from onWindowFocusChanged");
630         }
631     }
632     */
633 
acceptFilter()634     private boolean acceptFilter() {
635         final InputMethodManager inputManager = (InputMethodManager)
636                 getSystemService(Context.INPUT_METHOD_SERVICE);
637         return !inputManager.isFullscreenMode();
638     }
639 
640     @Override
onKeyDown(int keyCode, KeyEvent event)641     public boolean onKeyDown(int keyCode, KeyEvent event) {
642         boolean handled = super.onKeyDown(keyCode, event);
643         if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) {
644             boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
645                     keyCode, event);
646             if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
647                 // something usable has been typed - start a search
648                 // the typed text will be retrieved and cleared by
649                 // showSearchDialog()
650                 // If there are multiple keystrokes before the search dialog takes focus,
651                 // onSearchRequested() will be called for every keystroke,
652                 // but it is idempotent, so it's fine.
653                 return onSearchRequested();
654             }
655         }
656 
657         // Eat the long press event so the keyboard doesn't come up.
658         if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
659             return true;
660         }
661 
662         return handled;
663     }
664 
getTypedText()665     private String getTypedText() {
666         return mDefaultKeySsb.toString();
667     }
668 
clearTypedText()669     private void clearTypedText() {
670         mDefaultKeySsb.clear();
671         mDefaultKeySsb.clearSpans();
672         Selection.setSelection(mDefaultKeySsb, 0);
673     }
674 
675     /**
676      * Restores the previous state, if it exists.
677      *
678      * @param savedState The previous state.
679      */
restoreState(Bundle savedState)680     private void restoreState(Bundle savedState) {
681         if (savedState == null) {
682             return;
683         }
684 
685         final boolean allApps = savedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
686         if (allApps) {
687             showAllApps(false);
688         }
689 
690         final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
691         if (currentScreen > -1) {
692             mWorkspace.setCurrentScreen(currentScreen);
693         }
694 
695         final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
696         if (addScreen > -1) {
697             mAddItemCellInfo = new CellLayout.CellInfo();
698             final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
699             addItemCellInfo.valid = true;
700             addItemCellInfo.screen = addScreen;
701             addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
702             addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
703             addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
704             addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
705             addItemCellInfo.findVacantCellsFromOccupied(
706                     savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS),
707                     savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X),
708                     savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y));
709             mRestoring = true;
710         }
711 
712         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
713         if (renameFolder) {
714             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
715             mFolderInfo = mModel.getFolderById(this, sFolders, id);
716             mRestoring = true;
717         }
718     }
719 
720     /**
721      * Finds all the views we need and configure them properly.
722      */
setupViews()723     private void setupViews() {
724         DragController dragController = mDragController;
725 
726         DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer);
727         dragLayer.setDragController(dragController);
728 
729         mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);
730         mAllAppsGrid.setLauncher(this);
731         mAllAppsGrid.setDragController(dragController);
732         ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
733         // Manage focusability manually since this thing is always visible
734         ((View) mAllAppsGrid).setFocusable(false);
735 
736         mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
737         final Workspace workspace = mWorkspace;
738         workspace.setHapticFeedbackEnabled(false);
739 
740         DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
741         mDeleteZone = deleteZone;
742 
743         mHandleView = (HandleView) findViewById(R.id.all_apps_button);
744         mHandleView.setLauncher(this);
745         mHandleView.setOnClickListener(this);
746         mHandleView.setOnLongClickListener(this);
747 
748         ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left);
749         hotseatLeft.setContentDescription(mHotseatLabels[0]);
750         hotseatLeft.setImageDrawable(mHotseatIcons[0]);
751         ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right);
752         hotseatRight.setContentDescription(mHotseatLabels[1]);
753         hotseatRight.setImageDrawable(mHotseatIcons[1]);
754 
755         mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
756         mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
757 
758         Drawable previous = mPreviousView.getDrawable();
759         Drawable next = mNextView.getDrawable();
760         mWorkspace.setIndicators(previous, next);
761 
762         mPreviousView.setHapticFeedbackEnabled(false);
763         mPreviousView.setOnLongClickListener(this);
764         mNextView.setHapticFeedbackEnabled(false);
765         mNextView.setOnLongClickListener(this);
766 
767         workspace.setOnLongClickListener(this);
768         workspace.setDragController(dragController);
769         workspace.setLauncher(this);
770 
771         deleteZone.setLauncher(this);
772         deleteZone.setDragController(dragController);
773         deleteZone.setHandle(findViewById(R.id.all_apps_button_cluster));
774 
775         dragController.setDragScoller(workspace);
776         dragController.setDragListener(deleteZone);
777         dragController.setScrollView(dragLayer);
778         dragController.setMoveTarget(workspace);
779 
780         // The order here is bottom to top.
781         dragController.addDropTarget(workspace);
782         dragController.addDropTarget(deleteZone);
783     }
784 
785     @SuppressWarnings({"UnusedDeclaration"})
previousScreen(View v)786     public void previousScreen(View v) {
787         if (!isAllAppsVisible()) {
788             mWorkspace.scrollLeft();
789         }
790     }
791 
792     @SuppressWarnings({"UnusedDeclaration"})
nextScreen(View v)793     public void nextScreen(View v) {
794         if (!isAllAppsVisible()) {
795             mWorkspace.scrollRight();
796         }
797     }
798 
799     @SuppressWarnings({"UnusedDeclaration"})
launchHotSeat(View v)800     public void launchHotSeat(View v) {
801         if (isAllAppsVisible()) return;
802 
803         int index = -1;
804         if (v.getId() == R.id.hotseat_left) {
805             index = 0;
806         } else if (v.getId() == R.id.hotseat_right) {
807             index = 1;
808         }
809 
810         // reload these every tap; you never know when they might change
811         loadHotseats();
812         if (index >= 0 && index < mHotseats.length && mHotseats[index] != null) {
813             Intent intent = mHotseats[index];
814             startActivitySafely(
815                 mHotseats[index],
816                 "hotseat"
817             );
818         }
819     }
820 
821     /**
822      * Creates a view representing a shortcut.
823      *
824      * @param info The data structure describing the shortcut.
825      *
826      * @return A View inflated from R.layout.application.
827      */
createShortcut(ShortcutInfo info)828     View createShortcut(ShortcutInfo info) {
829         return createShortcut(R.layout.application,
830                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
831     }
832 
833     /**
834      * Creates a view representing a shortcut inflated from the specified resource.
835      *
836      * @param layoutResId The id of the XML layout used to create the shortcut.
837      * @param parent The group the shortcut belongs to.
838      * @param info The data structure describing the shortcut.
839      *
840      * @return A View inflated from layoutResId.
841      */
createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info)842     View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
843         TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);
844 
845         favorite.setCompoundDrawablesWithIntrinsicBounds(null,
846                 new FastBitmapDrawable(info.getIcon(mIconCache)),
847                 null, null);
848         favorite.setText(info.title);
849         favorite.setTag(info);
850         favorite.setOnClickListener(this);
851 
852         return favorite;
853     }
854 
855     /**
856      * Add an application shortcut to the workspace.
857      *
858      * @param data The intent describing the application.
859      * @param cellInfo The position on screen where to create the shortcut.
860      */
completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo)861     void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) {
862         cellInfo.screen = mWorkspace.getCurrentScreen();
863         if (!findSingleSlot(cellInfo)) return;
864 
865         final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(),
866                 data, context);
867 
868         if (info != null) {
869             info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
870                     Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
871             info.container = ItemInfo.NO_ID;
872             mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked());
873         } else {
874             Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
875         }
876     }
877 
878     /**
879      * Add a shortcut to the workspace.
880      *
881      * @param data The intent describing the shortcut.
882      * @param cellInfo The position on screen where to create the shortcut.
883      */
completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo)884     private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) {
885         cellInfo.screen = mWorkspace.getCurrentScreen();
886         if (!findSingleSlot(cellInfo)) return;
887 
888         final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false);
889 
890         if (!mRestoring) {
891             final View view = createShortcut(info);
892             mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
893                     isWorkspaceLocked());
894         }
895     }
896 
897 
898     /**
899      * Add a widget to the workspace.
900      *
901      * @param data The intent describing the appWidgetId.
902      * @param cellInfo The position on screen where to create the widget.
903      */
completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo)904     private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) {
905         Bundle extras = data.getExtras();
906         int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
907 
908         if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString());
909 
910         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
911 
912         // Calculate the grid spans needed to fit this widget
913         CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
914         int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
915 
916         // Try finding open space on Launcher screen
917         final int[] xy = mCellCoordinates;
918         if (!findSlot(cellInfo, xy, spans[0], spans[1])) {
919             if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId);
920             return;
921         }
922 
923         // Build Launcher-specific widget info and save to database
924         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
925         launcherInfo.spanX = spans[0];
926         launcherInfo.spanY = spans[1];
927 
928         LauncherModel.addItemToDatabase(this, launcherInfo,
929                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
930                 mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
931 
932         if (!mRestoring) {
933             mDesktopItems.add(launcherInfo);
934 
935             // Perform actual inflation because we're live
936             launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
937 
938             launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
939             launcherInfo.hostView.setTag(launcherInfo);
940 
941             mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
942                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
943         }
944     }
945 
removeAppWidget(LauncherAppWidgetInfo launcherInfo)946     public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
947         mDesktopItems.remove(launcherInfo);
948         launcherInfo.hostView = null;
949     }
950 
getAppWidgetHost()951     public LauncherAppWidgetHost getAppWidgetHost() {
952         return mAppWidgetHost;
953     }
954 
closeSystemDialogs()955     void closeSystemDialogs() {
956         getWindow().closeAllPanels();
957 
958         try {
959             dismissDialog(DIALOG_CREATE_SHORTCUT);
960             // Unlock the workspace if the dialog was showing
961         } catch (Exception e) {
962             // An exception is thrown if the dialog is not visible, which is fine
963         }
964 
965         try {
966             dismissDialog(DIALOG_RENAME_FOLDER);
967             // Unlock the workspace if the dialog was showing
968         } catch (Exception e) {
969             // An exception is thrown if the dialog is not visible, which is fine
970         }
971 
972         // Whatever we were doing is hereby canceled.
973         mWaitingForResult = false;
974     }
975 
976     @Override
onNewIntent(Intent intent)977     protected void onNewIntent(Intent intent) {
978         super.onNewIntent(intent);
979 
980         // Close the menu
981         if (Intent.ACTION_MAIN.equals(intent.getAction())) {
982             // also will cancel mWaitingForResult.
983             closeSystemDialogs();
984 
985             boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
986                         != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
987             boolean allAppsVisible = isAllAppsVisible();
988             if (!mWorkspace.isDefaultScreenShowing()) {
989                 mWorkspace.moveToDefaultScreen(alreadyOnHome && !allAppsVisible);
990             }
991             closeAllApps(alreadyOnHome && allAppsVisible);
992 
993             final View v = getWindow().peekDecorView();
994             if (v != null && v.getWindowToken() != null) {
995                 InputMethodManager imm = (InputMethodManager)getSystemService(
996                         INPUT_METHOD_SERVICE);
997                 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
998             }
999         }
1000     }
1001 
1002     @Override
onRestoreInstanceState(Bundle savedInstanceState)1003     protected void onRestoreInstanceState(Bundle savedInstanceState) {
1004         // Do not call super here
1005         mSavedInstanceState = savedInstanceState;
1006     }
1007 
1008     @Override
onSaveInstanceState(Bundle outState)1009     protected void onSaveInstanceState(Bundle outState) {
1010         outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
1011 
1012         final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
1013         if (folders.size() > 0) {
1014             final int count = folders.size();
1015             long[] ids = new long[count];
1016             for (int i = 0; i < count; i++) {
1017                 final FolderInfo info = folders.get(i).getInfo();
1018                 ids[i] = info.id;
1019             }
1020             outState.putLongArray(RUNTIME_STATE_USER_FOLDERS, ids);
1021         } else {
1022             super.onSaveInstanceState(outState);
1023         }
1024 
1025         // TODO should not do this if the drawer is currently closing.
1026         if (isAllAppsVisible()) {
1027             outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true);
1028         }
1029 
1030         if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) {
1031             final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
1032             final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen);
1033 
1034             outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen);
1035             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX);
1036             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY);
1037             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX);
1038             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY);
1039             outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX());
1040             outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY());
1041             outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS,
1042                    layout.getOccupiedCells());
1043         }
1044 
1045         if (mFolderInfo != null && mWaitingForResult) {
1046             outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
1047             outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
1048         }
1049     }
1050 
1051     @Override
onDestroy()1052     public void onDestroy() {
1053         super.onDestroy();
1054 
1055         try {
1056             mAppWidgetHost.stopListening();
1057         } catch (NullPointerException ex) {
1058             Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
1059         }
1060 
1061         TextKeyListener.getInstance().release();
1062 
1063         mModel.stopLoader();
1064 
1065         unbindDesktopItems();
1066 
1067         getContentResolver().unregisterContentObserver(mWidgetObserver);
1068 
1069         dismissPreview(mPreviousView);
1070         dismissPreview(mNextView);
1071 
1072         unregisterReceiver(mCloseSystemDialogsReceiver);
1073     }
1074 
1075     @Override
startActivityForResult(Intent intent, int requestCode)1076     public void startActivityForResult(Intent intent, int requestCode) {
1077         if (requestCode >= 0) mWaitingForResult = true;
1078         super.startActivityForResult(intent, requestCode);
1079     }
1080 
1081     @Override
startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch)1082     public void startSearch(String initialQuery, boolean selectInitialQuery,
1083             Bundle appSearchData, boolean globalSearch) {
1084 
1085         closeAllApps(true);
1086 
1087         if (initialQuery == null) {
1088             // Use any text typed in the launcher as the initial query
1089             initialQuery = getTypedText();
1090             clearTypedText();
1091         }
1092         if (appSearchData == null) {
1093             appSearchData = new Bundle();
1094             appSearchData.putString(Search.SOURCE, "launcher-search");
1095         }
1096 
1097         final SearchManager searchManager =
1098                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
1099         searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
1100             appSearchData, globalSearch);
1101     }
1102 
1103     @Override
onCreateOptionsMenu(Menu menu)1104     public boolean onCreateOptionsMenu(Menu menu) {
1105         if (isWorkspaceLocked()) {
1106             return false;
1107         }
1108 
1109         super.onCreateOptionsMenu(menu);
1110 
1111         menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
1112                 .setIcon(android.R.drawable.ic_menu_add)
1113                 .setAlphabeticShortcut('A');
1114         menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)
1115                 .setIcon(android.R.drawable.ic_menu_manage)
1116                 .setAlphabeticShortcut('M');
1117         menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
1118                  .setIcon(android.R.drawable.ic_menu_gallery)
1119                  .setAlphabeticShortcut('W');
1120         menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
1121                 .setIcon(android.R.drawable.ic_search_category_default)
1122                 .setAlphabeticShortcut(SearchManager.MENU_KEY);
1123         menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications)
1124                 .setIcon(com.android.internal.R.drawable.ic_menu_notifications)
1125                 .setAlphabeticShortcut('N');
1126 
1127         final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
1128         settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1129                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1130 
1131         menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings)
1132                 .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P')
1133                 .setIntent(settings);
1134 
1135         return true;
1136     }
1137 
1138     @Override
onPrepareOptionsMenu(Menu menu)1139     public boolean onPrepareOptionsMenu(Menu menu) {
1140         super.onPrepareOptionsMenu(menu);
1141 
1142         // If all apps is animating, don't show the menu, because we don't know
1143         // which one to show.
1144         if (mAllAppsGrid.isVisible() && !mAllAppsGrid.isOpaque()) {
1145             return false;
1146         }
1147 
1148         // Only show the add and wallpaper options when we're not in all apps.
1149         boolean visible = !mAllAppsGrid.isOpaque();
1150         menu.setGroupVisible(MENU_GROUP_ADD, visible);
1151         menu.setGroupVisible(MENU_GROUP_WALLPAPER, visible);
1152 
1153         // Disable add if the workspace is full.
1154         if (visible) {
1155             mMenuAddInfo = mWorkspace.findAllVacantCells(null);
1156             menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid);
1157         }
1158 
1159         return true;
1160     }
1161 
1162     @Override
onOptionsItemSelected(MenuItem item)1163     public boolean onOptionsItemSelected(MenuItem item) {
1164         switch (item.getItemId()) {
1165             case MENU_ADD:
1166                 addItems();
1167                 return true;
1168             case MENU_MANAGE_APPS:
1169                 manageApps();
1170                 return true;
1171             case MENU_WALLPAPER_SETTINGS:
1172                 startWallpaper();
1173                 return true;
1174             case MENU_SEARCH:
1175                 onSearchRequested();
1176                 return true;
1177             case MENU_NOTIFICATIONS:
1178                 showNotifications();
1179                 return true;
1180         }
1181 
1182         return super.onOptionsItemSelected(item);
1183     }
1184 
1185     /**
1186      * Indicates that we want global search for this activity by setting the globalSearch
1187      * argument for {@link #startSearch} to true.
1188      */
1189 
1190     @Override
onSearchRequested()1191     public boolean onSearchRequested() {
1192         startSearch(null, false, null, true);
1193         return true;
1194     }
1195 
isWorkspaceLocked()1196     public boolean isWorkspaceLocked() {
1197         return mWorkspaceLoading || mWaitingForResult;
1198     }
1199 
addItems()1200     private void addItems() {
1201         closeAllApps(true);
1202         showAddDialog(mMenuAddInfo);
1203     }
1204 
manageApps()1205     private void manageApps() {
1206         startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS));
1207     }
1208 
addAppWidget(Intent data)1209     void addAppWidget(Intent data) {
1210         // TODO: catch bad widget exception when sent
1211         int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
1212         AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
1213 
1214         if (appWidget.configure != null) {
1215             // Launch over to configure widget, if needed
1216             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
1217             intent.setComponent(appWidget.configure);
1218             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1219 
1220             startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
1221         } else {
1222             // Otherwise just add it
1223             onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
1224         }
1225     }
1226 
processShortcut(Intent intent)1227     void processShortcut(Intent intent) {
1228         // Handle case where user selected "Applications"
1229         String applicationName = getResources().getString(R.string.group_applications);
1230         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1231 
1232         if (applicationName != null && applicationName.equals(shortcutName)) {
1233             Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1234             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1235 
1236             Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
1237             pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
1238             pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
1239             startActivityForResultSafely(pickIntent, REQUEST_PICK_APPLICATION);
1240         } else {
1241             startActivityForResultSafely(intent, REQUEST_CREATE_SHORTCUT);
1242         }
1243     }
1244 
addLiveFolder(Intent intent)1245     void addLiveFolder(Intent intent) {
1246         // Handle case where user selected "Folder"
1247         String folderName = getResources().getString(R.string.group_folder);
1248         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1249 
1250         if (folderName != null && folderName.equals(shortcutName)) {
1251             addFolder();
1252         } else {
1253             startActivityForResultSafely(intent, REQUEST_CREATE_LIVE_FOLDER);
1254         }
1255     }
1256 
addFolder()1257     void addFolder() {
1258         UserFolderInfo folderInfo = new UserFolderInfo();
1259         folderInfo.title = getText(R.string.folder_name);
1260 
1261         CellLayout.CellInfo cellInfo = mAddItemCellInfo;
1262         cellInfo.screen = mWorkspace.getCurrentScreen();
1263         if (!findSingleSlot(cellInfo)) return;
1264 
1265         // Update the model
1266         LauncherModel.addItemToDatabase(this, folderInfo,
1267                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
1268                 mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
1269         sFolders.put(folderInfo.id, folderInfo);
1270 
1271         // Create the view
1272         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
1273                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
1274         mWorkspace.addInCurrentScreen(newFolder,
1275                 cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked());
1276     }
1277 
removeFolder(FolderInfo folder)1278     void removeFolder(FolderInfo folder) {
1279         sFolders.remove(folder.id);
1280     }
1281 
completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo)1282     private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) {
1283         cellInfo.screen = mWorkspace.getCurrentScreen();
1284         if (!findSingleSlot(cellInfo)) return;
1285 
1286         final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
1287 
1288         if (!mRestoring) {
1289             final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
1290                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
1291             mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
1292                     isWorkspaceLocked());
1293         }
1294     }
1295 
addLiveFolder(Context context, Intent data, CellLayout.CellInfo cellInfo, boolean notify)1296     static LiveFolderInfo addLiveFolder(Context context, Intent data,
1297             CellLayout.CellInfo cellInfo, boolean notify) {
1298 
1299         Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT);
1300         String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME);
1301 
1302         Drawable icon = null;
1303         Intent.ShortcutIconResource iconResource = null;
1304 
1305         Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON);
1306         if (extra != null && extra instanceof Intent.ShortcutIconResource) {
1307             try {
1308                 iconResource = (Intent.ShortcutIconResource) extra;
1309                 final PackageManager packageManager = context.getPackageManager();
1310                 Resources resources = packageManager.getResourcesForApplication(
1311                         iconResource.packageName);
1312                 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1313                 icon = resources.getDrawable(id);
1314             } catch (Exception e) {
1315                 Log.w(TAG, "Could not load live folder icon: " + extra);
1316             }
1317         }
1318 
1319         if (icon == null) {
1320             icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder);
1321         }
1322 
1323         final LiveFolderInfo info = new LiveFolderInfo();
1324         info.icon = Utilities.createIconBitmap(icon, context);
1325         info.title = name;
1326         info.iconResource = iconResource;
1327         info.uri = data.getData();
1328         info.baseIntent = baseIntent;
1329         info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
1330                 LiveFolders.DISPLAY_MODE_GRID);
1331 
1332         LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1333                 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1334         sFolders.put(info.id, info);
1335 
1336         return info;
1337     }
1338 
findSingleSlot(CellLayout.CellInfo cellInfo)1339     private boolean findSingleSlot(CellLayout.CellInfo cellInfo) {
1340         final int[] xy = new int[2];
1341         if (findSlot(cellInfo, xy, 1, 1)) {
1342             cellInfo.cellX = xy[0];
1343             cellInfo.cellY = xy[1];
1344             return true;
1345         }
1346         return false;
1347     }
1348 
findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY)1349     private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
1350         if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
1351             boolean[] occupied = mSavedState != null ?
1352                     mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null;
1353             cellInfo = mWorkspace.findAllVacantCells(occupied);
1354             if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
1355                 Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
1356                 return false;
1357             }
1358         }
1359         return true;
1360     }
1361 
showNotifications()1362     private void showNotifications() {
1363         final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
1364         if (statusBar != null) {
1365             statusBar.expand();
1366         }
1367     }
1368 
startWallpaper()1369     private void startWallpaper() {
1370         closeAllApps(true);
1371         final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
1372         Intent chooser = Intent.createChooser(pickWallpaper,
1373                 getText(R.string.chooser_wallpaper));
1374         // NOTE: Adds a configure option to the chooser if the wallpaper supports it
1375         //       Removed in Eclair MR1
1376 //        WallpaperManager wm = (WallpaperManager)
1377 //                getSystemService(Context.WALLPAPER_SERVICE);
1378 //        WallpaperInfo wi = wm.getWallpaperInfo();
1379 //        if (wi != null && wi.getSettingsActivity() != null) {
1380 //            LabeledIntent li = new LabeledIntent(getPackageName(),
1381 //                    R.string.configure_wallpaper, 0);
1382 //            li.setClassName(wi.getPackageName(), wi.getSettingsActivity());
1383 //            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li });
1384 //        }
1385         startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
1386     }
1387 
1388     /**
1389      * Registers various content observers. The current implementation registers
1390      * only a favorites observer to keep track of the favorites applications.
1391      */
registerContentObservers()1392     private void registerContentObservers() {
1393         ContentResolver resolver = getContentResolver();
1394         resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
1395                 true, mWidgetObserver);
1396     }
1397 
1398     @Override
dispatchKeyEvent(KeyEvent event)1399     public boolean dispatchKeyEvent(KeyEvent event) {
1400         if (event.getAction() == KeyEvent.ACTION_DOWN) {
1401             switch (event.getKeyCode()) {
1402                 case KeyEvent.KEYCODE_HOME:
1403                     return true;
1404                 case KeyEvent.KEYCODE_VOLUME_DOWN:
1405                     if (SystemProperties.getInt("debug.launcher2.dumpstate", 0) != 0) {
1406                         dumpState();
1407                         return true;
1408                     }
1409                     break;
1410             }
1411         } else if (event.getAction() == KeyEvent.ACTION_UP) {
1412             switch (event.getKeyCode()) {
1413                 case KeyEvent.KEYCODE_HOME:
1414                     return true;
1415             }
1416         }
1417 
1418         return super.dispatchKeyEvent(event);
1419     }
1420 
1421     @Override
onBackPressed()1422     public void onBackPressed() {
1423         if (isAllAppsVisible()) {
1424             closeAllApps(true);
1425         } else {
1426             closeFolder();
1427         }
1428         dismissPreview(mPreviousView);
1429         dismissPreview(mNextView);
1430     }
1431 
closeFolder()1432     private void closeFolder() {
1433         Folder folder = mWorkspace.getOpenFolder();
1434         if (folder != null) {
1435             closeFolder(folder);
1436         }
1437     }
1438 
closeFolder(Folder folder)1439     void closeFolder(Folder folder) {
1440         folder.getInfo().opened = false;
1441         ViewGroup parent = (ViewGroup) folder.getParent();
1442         if (parent != null) {
1443             parent.removeView(folder);
1444             if (folder instanceof DropTarget) {
1445                 // Live folders aren't DropTargets.
1446                 mDragController.removeDropTarget((DropTarget)folder);
1447             }
1448         }
1449         folder.onClose();
1450     }
1451 
1452     /**
1453      * Re-listen when widgets are reset.
1454      */
onAppWidgetReset()1455     private void onAppWidgetReset() {
1456         mAppWidgetHost.startListening();
1457     }
1458 
1459     /**
1460      * Go through the and disconnect any of the callbacks in the drawables and the views or we
1461      * leak the previous Home screen on orientation change.
1462      */
unbindDesktopItems()1463     private void unbindDesktopItems() {
1464         for (ItemInfo item: mDesktopItems) {
1465             item.unbind();
1466         }
1467     }
1468 
1469     /**
1470      * Launches the intent referred by the clicked shortcut.
1471      *
1472      * @param v The view representing the clicked shortcut.
1473      */
onClick(View v)1474     public void onClick(View v) {
1475         Object tag = v.getTag();
1476         if (tag instanceof ShortcutInfo) {
1477             // Open shortcut
1478             final Intent intent = ((ShortcutInfo) tag).intent;
1479             int[] pos = new int[2];
1480             v.getLocationOnScreen(pos);
1481             intent.setSourceBounds(new Rect(pos[0], pos[1],
1482                     pos[0] + v.getWidth(), pos[1] + v.getHeight()));
1483             startActivitySafely(intent, tag);
1484         } else if (tag instanceof FolderInfo) {
1485             handleFolderClick((FolderInfo) tag);
1486         } else if (v == mHandleView) {
1487             if (isAllAppsVisible()) {
1488                 closeAllApps(true);
1489             } else {
1490                 showAllApps(true);
1491             }
1492         }
1493     }
1494 
startActivitySafely(Intent intent, Object tag)1495     void startActivitySafely(Intent intent, Object tag) {
1496         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1497         try {
1498             startActivity(intent);
1499         } catch (ActivityNotFoundException e) {
1500             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1501             Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
1502         } catch (SecurityException e) {
1503             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1504             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
1505                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
1506                     "or use the exported attribute for this activity. "
1507                     + "tag="+ tag + " intent=" + intent, e);
1508         }
1509     }
1510 
startActivityForResultSafely(Intent intent, int requestCode)1511     void startActivityForResultSafely(Intent intent, int requestCode) {
1512         try {
1513             startActivityForResult(intent, requestCode);
1514         } catch (ActivityNotFoundException e) {
1515             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1516         } catch (SecurityException e) {
1517             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1518             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
1519                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
1520                     "or use the exported attribute for this activity.", e);
1521         }
1522     }
1523 
handleFolderClick(FolderInfo folderInfo)1524     private void handleFolderClick(FolderInfo folderInfo) {
1525         if (!folderInfo.opened) {
1526             // Close any open folder
1527             closeFolder();
1528             // Open the requested folder
1529             openFolder(folderInfo);
1530         } else {
1531             // Find the open folder...
1532             Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
1533             int folderScreen;
1534             if (openFolder != null) {
1535                 folderScreen = mWorkspace.getScreenForView(openFolder);
1536                 // .. and close it
1537                 closeFolder(openFolder);
1538                 if (folderScreen != mWorkspace.getCurrentScreen()) {
1539                     // Close any folder open on the current screen
1540                     closeFolder();
1541                     // Pull the folder onto this screen
1542                     openFolder(folderInfo);
1543                 }
1544             }
1545         }
1546     }
1547 
1548     /**
1549      * Opens the user fodler described by the specified tag. The opening of the folder
1550      * is animated relative to the specified View. If the View is null, no animation
1551      * is played.
1552      *
1553      * @param folderInfo The FolderInfo describing the folder to open.
1554      */
openFolder(FolderInfo folderInfo)1555     private void openFolder(FolderInfo folderInfo) {
1556         Folder openFolder;
1557 
1558         if (folderInfo instanceof UserFolderInfo) {
1559             openFolder = UserFolder.fromXml(this);
1560         } else if (folderInfo instanceof LiveFolderInfo) {
1561             openFolder = com.android.launcher2.LiveFolder.fromXml(this, folderInfo);
1562         } else {
1563             return;
1564         }
1565 
1566         openFolder.setDragController(mDragController);
1567         openFolder.setLauncher(this);
1568 
1569         openFolder.bind(folderInfo);
1570         folderInfo.opened = true;
1571 
1572         mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4);
1573         openFolder.onOpen();
1574     }
1575 
onLongClick(View v)1576     public boolean onLongClick(View v) {
1577         switch (v.getId()) {
1578             case R.id.previous_screen:
1579                 if (!isAllAppsVisible()) {
1580                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1581                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1582                     showPreviews(v);
1583                 }
1584                 return true;
1585             case R.id.next_screen:
1586                 if (!isAllAppsVisible()) {
1587                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1588                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1589                     showPreviews(v);
1590                 }
1591                 return true;
1592             case R.id.all_apps_button:
1593                 if (!isAllAppsVisible()) {
1594                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1595                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1596                     showPreviews(v);
1597                 }
1598                 return true;
1599         }
1600 
1601         if (isWorkspaceLocked()) {
1602             return false;
1603         }
1604 
1605         if (!(v instanceof CellLayout)) {
1606             v = (View) v.getParent();
1607         }
1608 
1609         CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
1610 
1611         // This happens when long clicking an item with the dpad/trackball
1612         if (cellInfo == null) {
1613             return true;
1614         }
1615 
1616         if (mWorkspace.allowLongPress()) {
1617             if (cellInfo.cell == null) {
1618                 if (cellInfo.valid) {
1619                     // User long pressed on empty space
1620                     mWorkspace.setAllowLongPress(false);
1621                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1622                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1623                     showAddDialog(cellInfo);
1624                 }
1625             } else {
1626                 if (!(cellInfo.cell instanceof Folder)) {
1627                     // User long pressed on an item
1628                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
1629                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
1630                     mWorkspace.startDrag(cellInfo);
1631                 }
1632             }
1633         }
1634         return true;
1635     }
1636 
1637     @SuppressWarnings({"unchecked"})
dismissPreview(final View v)1638     private void dismissPreview(final View v) {
1639         final PopupWindow window = (PopupWindow) v.getTag();
1640         if (window != null) {
1641             window.setOnDismissListener(new PopupWindow.OnDismissListener() {
1642                 public void onDismiss() {
1643                     ViewGroup group = (ViewGroup) v.getTag(R.id.workspace);
1644                     int count = group.getChildCount();
1645                     for (int i = 0; i < count; i++) {
1646                         ((ImageView) group.getChildAt(i)).setImageDrawable(null);
1647                     }
1648                     ArrayList<Bitmap> bitmaps = (ArrayList<Bitmap>) v.getTag(R.id.icon);
1649                     for (Bitmap bitmap : bitmaps) bitmap.recycle();
1650 
1651                     v.setTag(R.id.workspace, null);
1652                     v.setTag(R.id.icon, null);
1653                     window.setOnDismissListener(null);
1654                 }
1655             });
1656             window.dismiss();
1657         }
1658         v.setTag(null);
1659     }
1660 
showPreviews(View anchor)1661     private void showPreviews(View anchor) {
1662         showPreviews(anchor, 0, mWorkspace.getChildCount());
1663     }
1664 
showPreviews(final View anchor, int start, int end)1665     private void showPreviews(final View anchor, int start, int end) {
1666         final Resources resources = getResources();
1667         final Workspace workspace = mWorkspace;
1668 
1669         CellLayout cell = ((CellLayout) workspace.getChildAt(start));
1670 
1671         float max = workspace.getChildCount();
1672 
1673         final Rect r = new Rect();
1674         resources.getDrawable(R.drawable.preview_background).getPadding(r);
1675         int extraW = (int) ((r.left + r.right) * max);
1676         int extraH = r.top + r.bottom;
1677 
1678         int aW = cell.getWidth() - extraW;
1679         float w = aW / max;
1680 
1681         int width = cell.getWidth();
1682         int height = cell.getHeight();
1683         int x = cell.getLeftPadding();
1684         int y = cell.getTopPadding();
1685         width -= (x + cell.getRightPadding());
1686         height -= (y + cell.getBottomPadding());
1687 
1688         float scale = w / width;
1689 
1690         int count = end - start;
1691 
1692         final float sWidth = width * scale;
1693         float sHeight = height * scale;
1694 
1695         LinearLayout preview = new LinearLayout(this);
1696 
1697         PreviewTouchHandler handler = new PreviewTouchHandler(anchor);
1698         ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>(count);
1699 
1700         for (int i = start; i < end; i++) {
1701             ImageView image = new ImageView(this);
1702             cell = (CellLayout) workspace.getChildAt(i);
1703 
1704             final Bitmap bitmap = Bitmap.createBitmap((int) sWidth, (int) sHeight,
1705                     Bitmap.Config.ARGB_8888);
1706 
1707             final Canvas c = new Canvas(bitmap);
1708             c.scale(scale, scale);
1709             c.translate(-cell.getLeftPadding(), -cell.getTopPadding());
1710             cell.dispatchDraw(c);
1711 
1712             image.setBackgroundDrawable(resources.getDrawable(R.drawable.preview_background));
1713             image.setImageBitmap(bitmap);
1714             image.setTag(i);
1715             image.setOnClickListener(handler);
1716             image.setOnFocusChangeListener(handler);
1717             image.setFocusable(true);
1718             if (i == mWorkspace.getCurrentScreen()) image.requestFocus();
1719 
1720             preview.addView(image,
1721                     LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
1722 
1723             bitmaps.add(bitmap);
1724         }
1725 
1726         final PopupWindow p = new PopupWindow(this);
1727         p.setContentView(preview);
1728         p.setWidth((int) (sWidth * count + extraW));
1729         p.setHeight((int) (sHeight + extraH));
1730         p.setAnimationStyle(R.style.AnimationPreview);
1731         p.setOutsideTouchable(true);
1732         p.setFocusable(true);
1733         p.setBackgroundDrawable(new ColorDrawable(0));
1734         p.showAsDropDown(anchor, 0, 0);
1735 
1736         p.setOnDismissListener(new PopupWindow.OnDismissListener() {
1737             public void onDismiss() {
1738                 dismissPreview(anchor);
1739             }
1740         });
1741 
1742         anchor.setTag(p);
1743         anchor.setTag(R.id.workspace, preview);
1744         anchor.setTag(R.id.icon, bitmaps);
1745     }
1746 
1747     class PreviewTouchHandler implements View.OnClickListener, Runnable, View.OnFocusChangeListener {
1748         private final View mAnchor;
1749 
PreviewTouchHandler(View anchor)1750         public PreviewTouchHandler(View anchor) {
1751             mAnchor = anchor;
1752         }
1753 
onClick(View v)1754         public void onClick(View v) {
1755             mWorkspace.snapToScreen((Integer) v.getTag());
1756             v.post(this);
1757         }
1758 
run()1759         public void run() {
1760             dismissPreview(mAnchor);
1761         }
1762 
onFocusChange(View v, boolean hasFocus)1763         public void onFocusChange(View v, boolean hasFocus) {
1764             if (hasFocus) {
1765                 mWorkspace.snapToScreen((Integer) v.getTag());
1766             }
1767         }
1768     }
1769 
getWorkspace()1770     Workspace getWorkspace() {
1771         return mWorkspace;
1772     }
1773 
1774     @Override
onCreateDialog(int id)1775     protected Dialog onCreateDialog(int id) {
1776         switch (id) {
1777             case DIALOG_CREATE_SHORTCUT:
1778                 return new CreateShortcut().createDialog();
1779             case DIALOG_RENAME_FOLDER:
1780                 return new RenameFolder().createDialog();
1781         }
1782 
1783         return super.onCreateDialog(id);
1784     }
1785 
1786     @Override
onPrepareDialog(int id, Dialog dialog)1787     protected void onPrepareDialog(int id, Dialog dialog) {
1788         switch (id) {
1789             case DIALOG_CREATE_SHORTCUT:
1790                 break;
1791             case DIALOG_RENAME_FOLDER:
1792                 if (mFolderInfo != null) {
1793                     EditText input = (EditText) dialog.findViewById(R.id.folder_name);
1794                     final CharSequence text = mFolderInfo.title;
1795                     input.setText(text);
1796                     input.setSelection(0, text.length());
1797                 }
1798                 break;
1799         }
1800     }
1801 
showRenameDialog(FolderInfo info)1802     void showRenameDialog(FolderInfo info) {
1803         mFolderInfo = info;
1804         mWaitingForResult = true;
1805         showDialog(DIALOG_RENAME_FOLDER);
1806     }
1807 
showAddDialog(CellLayout.CellInfo cellInfo)1808     private void showAddDialog(CellLayout.CellInfo cellInfo) {
1809         mAddItemCellInfo = cellInfo;
1810         mWaitingForResult = true;
1811         showDialog(DIALOG_CREATE_SHORTCUT);
1812     }
1813 
pickShortcut()1814     private void pickShortcut() {
1815         Bundle bundle = new Bundle();
1816 
1817         ArrayList<String> shortcutNames = new ArrayList<String>();
1818         shortcutNames.add(getString(R.string.group_applications));
1819         bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
1820 
1821         ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>();
1822         shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
1823                         R.drawable.ic_launcher_application));
1824         bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
1825 
1826         Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
1827         pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT));
1828         pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_shortcut));
1829         pickIntent.putExtras(bundle);
1830 
1831         startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
1832     }
1833 
1834     private class RenameFolder {
1835         private EditText mInput;
1836 
createDialog()1837         Dialog createDialog() {
1838             final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null);
1839             mInput = (EditText) layout.findViewById(R.id.folder_name);
1840 
1841             AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
1842             builder.setIcon(0);
1843             builder.setTitle(getString(R.string.rename_folder_title));
1844             builder.setCancelable(true);
1845             builder.setOnCancelListener(new Dialog.OnCancelListener() {
1846                 public void onCancel(DialogInterface dialog) {
1847                     cleanup();
1848                 }
1849             });
1850             builder.setNegativeButton(getString(R.string.cancel_action),
1851                 new Dialog.OnClickListener() {
1852                     public void onClick(DialogInterface dialog, int which) {
1853                         cleanup();
1854                     }
1855                 }
1856             );
1857             builder.setPositiveButton(getString(R.string.rename_action),
1858                 new Dialog.OnClickListener() {
1859                     public void onClick(DialogInterface dialog, int which) {
1860                         changeFolderName();
1861                     }
1862                 }
1863             );
1864             builder.setView(layout);
1865 
1866             final AlertDialog dialog = builder.create();
1867             dialog.setOnShowListener(new DialogInterface.OnShowListener() {
1868                 public void onShow(DialogInterface dialog) {
1869                     mWaitingForResult = true;
1870                     mInput.requestFocus();
1871                     InputMethodManager inputManager = (InputMethodManager)
1872                             getSystemService(Context.INPUT_METHOD_SERVICE);
1873                     inputManager.showSoftInput(mInput, 0);
1874                 }
1875             });
1876 
1877             return dialog;
1878         }
1879 
changeFolderName()1880         private void changeFolderName() {
1881             final String name = mInput.getText().toString();
1882             if (!TextUtils.isEmpty(name)) {
1883                 // Make sure we have the right folder info
1884                 mFolderInfo = sFolders.get(mFolderInfo.id);
1885                 mFolderInfo.title = name;
1886                 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
1887 
1888                 if (mWorkspaceLoading) {
1889                     lockAllApps();
1890                     mModel.startLoader(Launcher.this, false);
1891                 } else {
1892                     final FolderIcon folderIcon = (FolderIcon)
1893                             mWorkspace.getViewForTag(mFolderInfo);
1894                     if (folderIcon != null) {
1895                         folderIcon.setText(name);
1896                         getWorkspace().requestLayout();
1897                     } else {
1898                         lockAllApps();
1899                         mWorkspaceLoading = true;
1900                         mModel.startLoader(Launcher.this, false);
1901                     }
1902                 }
1903             }
1904             cleanup();
1905         }
1906 
cleanup()1907         private void cleanup() {
1908             dismissDialog(DIALOG_RENAME_FOLDER);
1909             mWaitingForResult = false;
1910             mFolderInfo = null;
1911         }
1912     }
1913 
1914     // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
isAllAppsVisible()1915     public boolean isAllAppsVisible() {
1916         return (mAllAppsGrid != null) ? mAllAppsGrid.isVisible() : false;
1917     }
1918 
1919     // AllAppsView.Watcher
zoomed(float zoom)1920     public void zoomed(float zoom) {
1921         if (zoom == 1.0f) {
1922             mWorkspace.setVisibility(View.GONE);
1923         }
1924     }
1925 
showAllApps(boolean animated)1926     void showAllApps(boolean animated) {
1927         mAllAppsGrid.zoom(1.0f, animated);
1928 
1929         ((View) mAllAppsGrid).setFocusable(true);
1930         ((View) mAllAppsGrid).requestFocus();
1931 
1932         // TODO: fade these two too
1933         mDeleteZone.setVisibility(View.GONE);
1934     }
1935 
1936     /**
1937      * Things to test when changing this code.
1938      *   - Home from workspace
1939      *          - from center screen
1940      *          - from other screens
1941      *   - Home from all apps
1942      *          - from center screen
1943      *          - from other screens
1944      *   - Back from all apps
1945      *          - from center screen
1946      *          - from other screens
1947      *   - Launch app from workspace and quit
1948      *          - with back
1949      *          - with home
1950      *   - Launch app from all apps and quit
1951      *          - with back
1952      *          - with home
1953      *   - Go to a screen that's not the default, then all
1954      *     apps, and launch and app, and go back
1955      *          - with back
1956      *          -with home
1957      *   - On workspace, long press power and go back
1958      *          - with back
1959      *          - with home
1960      *   - On all apps, long press power and go back
1961      *          - with back
1962      *          - with home
1963      *   - On workspace, power off
1964      *   - On all apps, power off
1965      *   - Launch an app and turn off the screen while in that app
1966      *          - Go back with home key
1967      *          - Go back with back key  TODO: make this not go to workspace
1968      *          - From all apps
1969      *          - From workspace
1970      *   - Enter and exit car mode (becuase it causes an extra configuration changed)
1971      *          - From all apps
1972      *          - From the center workspace
1973      *          - From another workspace
1974      */
closeAllApps(boolean animated)1975     void closeAllApps(boolean animated) {
1976         if (mAllAppsGrid.isVisible()) {
1977             mWorkspace.setVisibility(View.VISIBLE);
1978             mAllAppsGrid.zoom(0.0f, animated);
1979             ((View)mAllAppsGrid).setFocusable(false);
1980             mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
1981         }
1982     }
1983 
lockAllApps()1984     void lockAllApps() {
1985         // TODO
1986     }
1987 
unlockAllApps()1988     void unlockAllApps() {
1989         // TODO
1990     }
1991 
1992     /**
1993      * Displays the shortcut creation dialog and launches, if necessary, the
1994      * appropriate activity.
1995      */
1996     private class CreateShortcut implements DialogInterface.OnClickListener,
1997             DialogInterface.OnCancelListener, DialogInterface.OnDismissListener,
1998             DialogInterface.OnShowListener {
1999 
2000         private AddAdapter mAdapter;
2001 
createDialog()2002         Dialog createDialog() {
2003             mAdapter = new AddAdapter(Launcher.this);
2004 
2005             final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
2006             builder.setTitle(getString(R.string.menu_item_add_item));
2007             builder.setAdapter(mAdapter, this);
2008 
2009             builder.setInverseBackgroundForced(true);
2010 
2011             AlertDialog dialog = builder.create();
2012             dialog.setOnCancelListener(this);
2013             dialog.setOnDismissListener(this);
2014             dialog.setOnShowListener(this);
2015 
2016             return dialog;
2017         }
2018 
onCancel(DialogInterface dialog)2019         public void onCancel(DialogInterface dialog) {
2020             mWaitingForResult = false;
2021             cleanup();
2022         }
2023 
onDismiss(DialogInterface dialog)2024         public void onDismiss(DialogInterface dialog) {
2025         }
2026 
cleanup()2027         private void cleanup() {
2028             try {
2029                 dismissDialog(DIALOG_CREATE_SHORTCUT);
2030             } catch (Exception e) {
2031                 // An exception is thrown if the dialog is not visible, which is fine
2032             }
2033         }
2034 
2035         /**
2036          * Handle the action clicked in the "Add to home" dialog.
2037          */
onClick(DialogInterface dialog, int which)2038         public void onClick(DialogInterface dialog, int which) {
2039             Resources res = getResources();
2040             cleanup();
2041 
2042             switch (which) {
2043                 case AddAdapter.ITEM_SHORTCUT: {
2044                     // Insert extra item to handle picking application
2045                     pickShortcut();
2046                     break;
2047                 }
2048 
2049                 case AddAdapter.ITEM_APPWIDGET: {
2050                     int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
2051 
2052                     Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
2053                     pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
2054                     // start the pick activity
2055                     startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
2056                     break;
2057                 }
2058 
2059                 case AddAdapter.ITEM_LIVE_FOLDER: {
2060                     // Insert extra item to handle inserting folder
2061                     Bundle bundle = new Bundle();
2062 
2063                     ArrayList<String> shortcutNames = new ArrayList<String>();
2064                     shortcutNames.add(res.getString(R.string.group_folder));
2065                     bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
2066 
2067                     ArrayList<ShortcutIconResource> shortcutIcons =
2068                             new ArrayList<ShortcutIconResource>();
2069                     shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
2070                             R.drawable.ic_launcher_folder));
2071                     bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
2072 
2073                     Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
2074                     pickIntent.putExtra(Intent.EXTRA_INTENT,
2075                             new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER));
2076                     pickIntent.putExtra(Intent.EXTRA_TITLE,
2077                             getText(R.string.title_select_live_folder));
2078                     pickIntent.putExtras(bundle);
2079 
2080                     startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
2081                     break;
2082                 }
2083 
2084                 case AddAdapter.ITEM_WALLPAPER: {
2085                     startWallpaper();
2086                     break;
2087                 }
2088             }
2089         }
2090 
onShow(DialogInterface dialog)2091         public void onShow(DialogInterface dialog) {
2092             mWaitingForResult = true;
2093         }
2094     }
2095 
2096     /**
2097      * Receives notifications when applications are added/removed.
2098      */
2099     private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
2100         @Override
onReceive(Context context, Intent intent)2101         public void onReceive(Context context, Intent intent) {
2102             closeSystemDialogs();
2103             String reason = intent.getStringExtra("reason");
2104             if (!"homekey".equals(reason)) {
2105                 boolean animate = true;
2106                 if (mPaused || "lock".equals(reason)) {
2107                     animate = false;
2108                 }
2109                 closeAllApps(animate);
2110             }
2111         }
2112     }
2113 
2114     /**
2115      * Receives notifications whenever the appwidgets are reset.
2116      */
2117     private class AppWidgetResetObserver extends ContentObserver {
AppWidgetResetObserver()2118         public AppWidgetResetObserver() {
2119             super(new Handler());
2120         }
2121 
2122         @Override
onChange(boolean selfChange)2123         public void onChange(boolean selfChange) {
2124             onAppWidgetReset();
2125         }
2126     }
2127 
2128     /**
2129      * If the activity is currently paused, signal that we need to re-run the loader
2130      * in onResume.
2131      *
2132      * This needs to be called from incoming places where resources might have been loaded
2133      * while we are paused.  That is becaues the Configuration might be wrong
2134      * when we're not running, and if it comes back to what it was when we
2135      * were paused, we are not restarted.
2136      *
2137      * Implementation of the method from LauncherModel.Callbacks.
2138      *
2139      * @return true if we are currently paused.  The caller might be able to
2140      * skip some work in that case since we will come back again.
2141      */
setLoadOnResume()2142     public boolean setLoadOnResume() {
2143         if (mPaused) {
2144             Log.i(TAG, "setLoadOnResume");
2145             mOnResumeNeedsLoad = true;
2146             return true;
2147         } else {
2148             return false;
2149         }
2150     }
2151 
2152     /**
2153      * Implementation of the method from LauncherModel.Callbacks.
2154      */
getCurrentWorkspaceScreen()2155     public int getCurrentWorkspaceScreen() {
2156         if (mWorkspace != null) {
2157             return mWorkspace.getCurrentScreen();
2158         } else {
2159             return SCREEN_COUNT / 2;
2160         }
2161     }
2162 
2163     /**
2164      * Refreshes the shortcuts shown on the workspace.
2165      *
2166      * Implementation of the method from LauncherModel.Callbacks.
2167      */
startBinding()2168     public void startBinding() {
2169         final Workspace workspace = mWorkspace;
2170         int count = workspace.getChildCount();
2171         for (int i = 0; i < count; i++) {
2172             // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
2173             ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
2174         }
2175 
2176         if (DEBUG_USER_INTERFACE) {
2177             android.widget.Button finishButton = new android.widget.Button(this);
2178             finishButton.setText("Finish");
2179             workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
2180 
2181             finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
2182                 public void onClick(View v) {
2183                     finish();
2184                 }
2185             });
2186         }
2187     }
2188 
2189     /**
2190      * Bind the items start-end from the list.
2191      *
2192      * Implementation of the method from LauncherModel.Callbacks.
2193      */
bindItems(ArrayList<ItemInfo> shortcuts, int start, int end)2194     public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
2195 
2196         setLoadOnResume();
2197 
2198         final Workspace workspace = mWorkspace;
2199 
2200         for (int i=start; i<end; i++) {
2201             final ItemInfo item = shortcuts.get(i);
2202             mDesktopItems.add(item);
2203             switch (item.itemType) {
2204                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
2205                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
2206                     final View shortcut = createShortcut((ShortcutInfo)item);
2207                     workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
2208                             false);
2209                     break;
2210                 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
2211                     final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
2212                             (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
2213                             (UserFolderInfo) item);
2214                     workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
2215                             false);
2216                     break;
2217                 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
2218                     final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
2219                             R.layout.live_folder_icon, this,
2220                             (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
2221                             (LiveFolderInfo) item);
2222                     workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
2223                             false);
2224                     break;
2225             }
2226         }
2227 
2228         workspace.requestLayout();
2229     }
2230 
2231     /**
2232      * Implementation of the method from LauncherModel.Callbacks.
2233      */
bindFolders(HashMap<Long, FolderInfo> folders)2234     public void bindFolders(HashMap<Long, FolderInfo> folders) {
2235         setLoadOnResume();
2236         sFolders.clear();
2237         sFolders.putAll(folders);
2238     }
2239 
2240     /**
2241      * Add the views for a widget to the workspace.
2242      *
2243      * Implementation of the method from LauncherModel.Callbacks.
2244      */
bindAppWidget(LauncherAppWidgetInfo item)2245     public void bindAppWidget(LauncherAppWidgetInfo item) {
2246         setLoadOnResume();
2247 
2248         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
2249         if (DEBUG_WIDGETS) {
2250             Log.d(TAG, "bindAppWidget: " + item);
2251         }
2252         final Workspace workspace = mWorkspace;
2253 
2254         final int appWidgetId = item.appWidgetId;
2255         final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
2256         if (DEBUG_WIDGETS) {
2257             Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
2258         }
2259 
2260         item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
2261 
2262         item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
2263         item.hostView.setTag(item);
2264 
2265         workspace.addInScreen(item.hostView, item.screen, item.cellX,
2266                 item.cellY, item.spanX, item.spanY, false);
2267 
2268         workspace.requestLayout();
2269 
2270         mDesktopItems.add(item);
2271 
2272         if (DEBUG_WIDGETS) {
2273             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
2274                     + (SystemClock.uptimeMillis()-start) + "ms");
2275         }
2276     }
2277 
2278     /**
2279      * Callback saying that there aren't any more items to bind.
2280      *
2281      * Implementation of the method from LauncherModel.Callbacks.
2282      */
finishBindingItems()2283     public void finishBindingItems() {
2284         setLoadOnResume();
2285 
2286         if (mSavedState != null) {
2287             if (!mWorkspace.hasFocus()) {
2288                 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
2289             }
2290 
2291             final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
2292             if (userFolders != null) {
2293                 for (long folderId : userFolders) {
2294                     final FolderInfo info = sFolders.get(folderId);
2295                     if (info != null) {
2296                         openFolder(info);
2297                     }
2298                 }
2299                 final Folder openFolder = mWorkspace.getOpenFolder();
2300                 if (openFolder != null) {
2301                     openFolder.requestFocus();
2302                 }
2303             }
2304 
2305             mSavedState = null;
2306         }
2307 
2308         if (mSavedInstanceState != null) {
2309             super.onRestoreInstanceState(mSavedInstanceState);
2310             mSavedInstanceState = null;
2311         }
2312 
2313         mWorkspaceLoading = false;
2314     }
2315 
2316     /**
2317      * Add the icons for all apps.
2318      *
2319      * Implementation of the method from LauncherModel.Callbacks.
2320      */
bindAllApplications(ArrayList<ApplicationInfo> apps)2321     public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
2322         mAllAppsGrid.setApps(apps);
2323     }
2324 
2325     /**
2326      * A package was installed.
2327      *
2328      * Implementation of the method from LauncherModel.Callbacks.
2329      */
bindAppsAdded(ArrayList<ApplicationInfo> apps)2330     public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
2331         setLoadOnResume();
2332         removeDialog(DIALOG_CREATE_SHORTCUT);
2333         mAllAppsGrid.addApps(apps);
2334     }
2335 
2336     /**
2337      * A package was updated.
2338      *
2339      * Implementation of the method from LauncherModel.Callbacks.
2340      */
bindAppsUpdated(ArrayList<ApplicationInfo> apps)2341     public void bindAppsUpdated(ArrayList<ApplicationInfo> apps) {
2342         setLoadOnResume();
2343         removeDialog(DIALOG_CREATE_SHORTCUT);
2344         mWorkspace.updateShortcuts(apps);
2345         mAllAppsGrid.updateApps(apps);
2346     }
2347 
2348     /**
2349      * A package was uninstalled.
2350      *
2351      * Implementation of the method from LauncherModel.Callbacks.
2352      */
bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent)2353     public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent) {
2354         removeDialog(DIALOG_CREATE_SHORTCUT);
2355         if (permanent) {
2356             mWorkspace.removeItems(apps);
2357         }
2358         mAllAppsGrid.removeApps(apps);
2359     }
2360 
2361     /**
2362      * Prints out out state for debugging.
2363      */
dumpState()2364     public void dumpState() {
2365         Log.d(TAG, "BEGIN launcher2 dump state for launcher " + this);
2366         Log.d(TAG, "mSavedState=" + mSavedState);
2367         Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
2368         Log.d(TAG, "mRestoring=" + mRestoring);
2369         Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
2370         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
2371         Log.d(TAG, "mDesktopItems.size=" + mDesktopItems.size());
2372         Log.d(TAG, "sFolders.size=" + sFolders.size());
2373         mModel.dumpState();
2374         mAllAppsGrid.dumpState();
2375         Log.d(TAG, "END launcher2 dump state");
2376     }
2377 }
2378