blob: af1b0f3027807393faa54b3e5f08704df2cbf6be [file] [log] [blame]
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001/*
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
17package com.android.launcher;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.Application;
22import android.app.Dialog;
23import android.app.SearchManager;
24import android.app.StatusBarManager;
25import android.content.ActivityNotFoundException;
26import android.content.BroadcastReceiver;
27import android.content.ContentResolver;
28import android.content.Context;
29import android.content.DialogInterface;
30import android.content.Intent;
31import android.content.IntentFilter;
The Android Open Source Projectd097a182008-12-17 18:05:58 -080032import android.content.SharedPreferences;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070033import android.content.pm.PackageManager;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070034import android.content.res.Resources;
The Android Open Source Projectd097a182008-12-17 18:05:58 -080035import android.content.res.Configuration;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070036import android.database.ContentObserver;
37import android.graphics.Bitmap;
38import android.graphics.drawable.BitmapDrawable;
39import android.graphics.drawable.Drawable;
40import android.graphics.drawable.TransitionDrawable;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070041import android.net.Uri;
42import android.os.Bundle;
43import android.os.Handler;
44import android.os.IBinder;
45import android.os.Parcelable;
46import android.os.RemoteException;
47import android.os.ServiceManager;
The Android Open Source Project0e320b22009-01-09 17:51:25 -080048import android.os.Message;
The Android Open Source Projectd097a182008-12-17 18:05:58 -080049import android.provider.*;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070050import android.telephony.PhoneNumberUtils;
51import android.text.Selection;
52import android.text.SpannableStringBuilder;
53import android.text.TextUtils;
54import android.text.method.TextKeyListener;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070055import android.util.Log;
56import android.view.Display;
57import android.view.Gravity;
58import android.view.KeyEvent;
59import android.view.LayoutInflater;
60import android.view.Menu;
61import android.view.MenuItem;
62import android.view.View;
63import android.view.ViewGroup;
64import android.view.WindowManager;
65import android.view.View.OnLongClickListener;
The Android Open Source Projectd097a182008-12-17 18:05:58 -080066import android.view.inputmethod.InputMethodManager;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070067import android.widget.EditText;
68import android.widget.ExpandableListView;
69import android.widget.ImageView;
70import android.widget.TextView;
71import android.widget.Toast;
The Android Open Source Projectd097a182008-12-17 18:05:58 -080072import android.widget.GridView;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070073import android.app.IWallpaperService;
74
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070075import com.android.internal.widget.SlidingDrawer;
76
77import java.lang.ref.WeakReference;
78import java.util.ArrayList;
79
80/**
81 * Default launcher application.
82 */
83public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener {
84 static final String LOG_TAG = "Launcher";
85
86 private static final boolean PROFILE_STARTUP = false;
87 private static final boolean DEBUG_USER_INTERFACE = false;
88
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070089 private static final boolean REMOVE_SHORTCUT_ON_PACKAGE_REMOVE = false;
90
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -070091 private static final int WALLPAPER_SCREENS_SPAN = 2;
92
93 private static final int MENU_GROUP_ADD = 1;
94 private static final int MENU_ADD = Menu.FIRST + 1;
95 private static final int MENU_WALLPAPER_SETTINGS = MENU_ADD + 1;
96 private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
97 private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1;
98 private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
99
100 private static final int REQUEST_CREATE_SHORTCUT = 1;
101 private static final int REQUEST_CHOOSE_PHOTO = 2;
102 private static final int REQUEST_UPDATE_PHOTO = 3;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800103 private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700104
105 static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
106
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800107 static final int SCREEN_COUNT = 3;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700108 static final int DEFAULT_SCREN = 1;
109 static final int NUMBER_CELLS_X = 4;
110 static final int NUMBER_CELLS_Y = 4;
111
112 private static final int DIALOG_CREATE_SHORTCUT = 1;
113 static final int DIALOG_RENAME_FOLDER = 2;
114
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800115 private static final String PREFERENCES = "launcher";
116 private static final String KEY_LOCALE = "locale";
117 private static final String KEY_MCC = "mcc";
118 private static final String KEY_MNC = "mnc";
119
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700120 // Type: int
121 private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
122 // Type: boolean
123 private static final String RUNTIME_STATE_ALL_APPS_FOLDER = "launcher.all_apps_folder";
124 // Type: long
125 private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder";
126 // Type: int
127 private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
128 // Type: int
129 private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX";
130 // Type: int
131 private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY";
132 // Type: int
133 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX";
134 // Type: int
135 private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY";
136 // Type: int
137 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX";
138 // Type: int
139 private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY";
140 // Type: int[]
141 private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells";
142 // Type: boolean
143 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
144 // Type: long
145 private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
146
147 private static LauncherModel sModel;
148
149 private static Bitmap sWallpaper;
150
151 // Indicates whether the OpenGL pipeline was enabled, either through
152 // USE_OPENGL_BY_DEFAULT or the system property launcher.opengl
153 static boolean sOpenGlEnabled;
154
155 private static final Object sLock = new Object();
156 private static int sScreen = DEFAULT_SCREN;
157
158 private static WallpaperIntentReceiver sWallpaperReceiver;
159
160 private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver();
161 private final ContentObserver mObserver = new FavoritesChangeObserver();
162
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700163 private LayoutInflater mInflater;
164
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700165 private DragLayer mDragLayer;
166 private Workspace mWorkspace;
167
168 private CellLayout.CellInfo mAddItemCellInfo;
169 private CellLayout.CellInfo mMenuAddInfo;
170 private final int[] mCellCoordinates = new int[2];
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800171 private FolderInfo mFolderInfo;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700172
173 private SlidingDrawer mDrawer;
174 private TransitionDrawable mHandleIcon;
175 private AllAppsGridView mAllAppsGrid;
176
177 private boolean mDesktopLocked = true;
178 private Bundle mSavedState;
179
180 private SpannableStringBuilder mDefaultKeySsb = null;
181
182 private boolean mDestroyed;
183
184 private boolean mRestoring;
185 private boolean mWaitingForResult;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800186 private boolean mLocaleChanged;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700187
188 @Override
189 protected void onCreate(Bundle savedInstanceState) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700190 super.onCreate(savedInstanceState);
191 mInflater = getLayoutInflater();
192
193 if (PROFILE_STARTUP) {
194 android.os.Debug.startMethodTracing("/sdcard/launcher");
195 }
196
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800197 checkForLocaleChange();
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700198 setWallpaperDimension();
199
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700200 if (sModel == null) {
201 sModel = new LauncherModel();
202 }
203
204 setContentView(R.layout.launcher);
205 setupViews();
206
207 registerIntentReceivers();
208 registerContentObservers();
209
210 mSavedState = savedInstanceState;
211 restoreState(mSavedState);
212
213 if (PROFILE_STARTUP) {
214 android.os.Debug.stopMethodTracing();
215 }
216
217 if (!mRestoring) {
218 startLoaders();
219 }
220
221 // For handling default keys
222 mDefaultKeySsb = new SpannableStringBuilder();
223 Selection.setSelection(mDefaultKeySsb, 0);
224 }
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800225
226 private void checkForLocaleChange() {
227 final SharedPreferences preferences = getSharedPreferences(PREFERENCES, MODE_PRIVATE);
228 final Configuration configuration = getResources().getConfiguration();
229
230 final String previousLocale = preferences.getString(KEY_LOCALE, null);
231 final String locale = configuration.locale.toString();
232
233 final int previousMcc = preferences.getInt(KEY_MCC, -1);
234 final int mcc = configuration.mcc;
235
236 final int previousMnc = preferences.getInt(KEY_MNC, -1);
237 final int mnc = configuration.mnc;
238
239 mLocaleChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
240
241 if (mLocaleChanged) {
242 final SharedPreferences.Editor editor = preferences.edit();
243 editor.putString(KEY_LOCALE, locale);
244 editor.putInt(KEY_MCC, mcc);
245 editor.putInt(KEY_MNC, mnc);
246 editor.commit();
247 }
248 }
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700249
250 static int getScreen() {
251 synchronized (sLock) {
252 return sScreen;
253 }
254 }
255
256 static void setScreen(int screen) {
257 synchronized (sLock) {
258 sScreen = screen;
259 }
260 }
261
262 private void startLoaders() {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800263 sModel.loadApplications(true, this, mLocaleChanged);
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800264 sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, true);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700265 mRestoring = false;
266 }
267
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700268 private void setWallpaperDimension() {
269 IBinder binder = ServiceManager.getService(WALLPAPER_SERVICE);
270 IWallpaperService wallpaperService = IWallpaperService.Stub.asInterface(binder);
271
272 Display display = getWindowManager().getDefaultDisplay();
273 boolean isPortrait = display.getWidth() < display.getHeight();
274
275 final int width = isPortrait ? display.getWidth() : display.getHeight();
276 final int height = isPortrait ? display.getHeight() : display.getWidth();
277 try {
278 wallpaperService.setDimensionHints(width * WALLPAPER_SCREENS_SPAN, height);
279 } catch (RemoteException e) {
280 // System is dead!
281 }
282 }
283
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700284 @Override
285 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
286 if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
287 switch (requestCode) {
288 case REQUEST_CREATE_SHORTCUT:
289 completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked);
290 break;
291 case REQUEST_CHOOSE_PHOTO:
292 completeAddPhotoFrame(data, mAddItemCellInfo);
293 break;
294 case REQUEST_UPDATE_PHOTO:
295 completeUpdatePhotoFrame(data, mAddItemCellInfo);
296 break;
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800297 case REQUEST_CREATE_LIVE_FOLDER:
298 completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked);
299 break;
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700300 }
301 }
302 mWaitingForResult = false;
303 }
304
305 @Override
306 protected void onResume() {
307 super.onResume();
308
309 if (mRestoring) {
310 startLoaders();
311 }
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700312 }
313
314 @Override
315 public boolean onKeyUp(int keyCode, KeyEvent event) {
316 boolean handled = super.onKeyUp(keyCode, event);
317 if (keyCode == KeyEvent.KEYCODE_SEARCH) {
318 handled = mWorkspace.snapToSearch();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800319 if (handled) closeDrawer(true);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700320 }
321 return handled;
322 }
323
324 @Override
325 public boolean onKeyDown(int keyCode, KeyEvent event) {
326 boolean handled = super.onKeyDown(keyCode, event);
327 if (!handled && keyCode != KeyEvent.KEYCODE_ENTER) {
328 boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
329 keyCode, event);
330 if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
331 // something usable has been typed - dispatch it now.
332 final String str = mDefaultKeySsb.toString();
333
334 boolean isDialable = true;
335 final int count = str.length();
336 for (int i = 0; i < count; i++) {
337 if (!PhoneNumberUtils.isReallyDialable(str.charAt(i))) {
338 isDialable = false;
339 break;
340 }
341 }
342 Intent intent;
343 if (isDialable) {
344 intent = new Intent(Intent.ACTION_DIAL, Uri.fromParts("tel", str, null));
345 } else {
346 intent = new Intent(Contacts.Intents.UI.FILTER_CONTACTS_ACTION);
347 intent.putExtra(Contacts.Intents.UI.FILTER_TEXT_EXTRA_KEY, str);
348 }
349
350 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
351
352 try {
353 startActivity(intent);
354 } catch (android.content.ActivityNotFoundException ex) {
355 // Oh well... no one knows how to filter/dial. Life goes on.
356 }
357
358 mDefaultKeySsb.clear();
359 mDefaultKeySsb.clearSpans();
360 Selection.setSelection(mDefaultKeySsb, 0);
361
362 return true;
363 }
364 }
365
366 return handled;
367 }
368
369 /**
370 * Restores the previous state, if it exists.
371 *
372 * @param savedState The previous state.
373 */
374 private void restoreState(Bundle savedState) {
375 if (savedState == null) {
376 return;
377 }
378
379 final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
380 if (currentScreen > -1) {
381 mWorkspace.setCurrentScreen(currentScreen);
382 }
383
384 final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
385 if (addScreen > -1) {
386 mAddItemCellInfo = new CellLayout.CellInfo();
387 final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
388 addItemCellInfo.valid = true;
389 addItemCellInfo.screen = addScreen;
390 addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
391 addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
392 addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
393 addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
394 addItemCellInfo.findVacantCellsFromOccupied(
395 savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS),
396 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X),
397 savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y));
398 mRestoring = true;
399 }
400
401
402 boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
403 if (renameFolder) {
404 long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
405 mFolderInfo = sModel.getFolderById(this, id);
406 mRestoring = true;
407 }
408 }
409
410 /**
411 * Finds all the views we need and configure them properly.
412 */
413 private void setupViews() {
414 mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
415 final DragLayer dragLayer = mDragLayer;
416
417 mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
418 final Workspace workspace = mWorkspace;
419
420 mDrawer = (SlidingDrawer) dragLayer.findViewById(R.id.drawer);
421 final SlidingDrawer drawer = mDrawer;
422
423 mAllAppsGrid = (AllAppsGridView) drawer.getContent();
424 final AllAppsGridView grid = mAllAppsGrid;
425
426 final DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
427
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800428 final HandleView handleIcon = (HandleView) drawer.findViewById(R.id.all_apps);
429 handleIcon.setLauncher(this);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700430 mHandleIcon = (TransitionDrawable) handleIcon.getDrawable();
431 mHandleIcon.setCrossFadeEnabled(true);
432
433 drawer.lock();
434 final DrawerManager drawerManager = new DrawerManager();
435 drawer.setOnDrawerOpenListener(drawerManager);
436 drawer.setOnDrawerCloseListener(drawerManager);
437 drawer.setOnDrawerScrollListener(drawerManager);
438
439 grid.setTextFilterEnabled(true);
440 grid.setDragger(dragLayer);
441 grid.setLauncher(this);
442 if (sOpenGlEnabled) {
443 grid.setScrollingCacheEnabled(false);
444 grid.setFadingEdgeLength(0);
445 }
446
447 workspace.setOnLongClickListener(this);
448 workspace.setDragger(dragLayer);
449 workspace.setLauncher(this);
450 loadWallpaper();
451
452 deleteZone.setLauncher(this);
453 deleteZone.setDragController(dragLayer);
454 deleteZone.setHandle(handleIcon);
455
456 dragLayer.setIgnoredDropTarget(grid);
457 dragLayer.setDragScoller(workspace);
458 dragLayer.setDragListener(deleteZone);
459
460 if (DEBUG_USER_INTERFACE) {
461 android.widget.Button finishButton = new android.widget.Button(this);
462 finishButton.setText("Finish");
463 workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
464
465 finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
466 public void onClick(View v) {
467 finish();
468 }
469 });
470 }
471 }
472
473 /**
474 * Creates a view representing a shortcut.
475 *
476 * @param info The data structure describing the shortcut.
477 *
478 * @return A View inflated from R.layout.application.
479 */
480 View createShortcut(ApplicationInfo info) {
481 return createShortcut(R.layout.application,
482 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
483 }
484
485 /**
486 * Creates a view representing a shortcut inflated from the specified resource.
487 *
488 * @param layoutResId The id of the XML layout used to create the shortcut.
489 * @param parent The group the shortcut belongs to.
490 * @param info The data structure describing the shortcut.
491 *
492 * @return A View inflated from layoutResId.
493 */
494 View createShortcut(int layoutResId, ViewGroup parent, ApplicationInfo info) {
495 TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);
496
497 if (!info.filtered) {
498 info.icon = Utilities.createIconThumbnail(info.icon, this);
499 info.filtered = true;
500 }
501
502 favorite.setCompoundDrawablesWithIntrinsicBounds(null, info.icon, null, null);
503 favorite.setText(info.title);
504 favorite.setTag(info);
505 favorite.setOnClickListener(this);
506
507 return favorite;
508 }
509
510 void addApplicationShortcut(ApplicationInfo info) {
511 mAddItemCellInfo.screen = mWorkspace.getCurrentScreen();
512 mWorkspace.addApplicationShortcut(info, mAddItemCellInfo);
513 }
514
515 /**
516 * Add a shortcut to the workspace.
517 *
518 * @param data The intent describing the shortcut.
519 * @param cellInfo The position on screen where to create the shortcut.
520 * @param insertAtFirst
521 */
522 private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo,
523 boolean insertAtFirst) {
524
525 cellInfo.screen = mWorkspace.getCurrentScreen();
526 final ApplicationInfo info = addShortcut(this, data, cellInfo, false);
527
528 if (!mRestoring) {
529 sModel.addDesktopItem(info);
530
531 final View view = createShortcut(info);
532 mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
533 } else if (sModel.isDesktopLoaded()) {
534 sModel.addDesktopItem(info);
535 }
536 }
537
538 static ApplicationInfo addShortcut(Context context, Intent data,
539 CellLayout.CellInfo cellInfo, boolean notify) {
540
541 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
542 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
543 Bitmap bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
544
545 Drawable icon = null;
546 boolean filtered = false;
547 boolean customIcon = false;
548 Intent.ShortcutIconResource iconResource = null;
549
550 if (bitmap != null) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800551 icon = new FastBitmapDrawable(Utilities.createBitmapThumbnail(bitmap, context));
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700552 filtered = true;
553 customIcon = true;
554 } else {
555 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
556 if (extra != null && extra instanceof Intent.ShortcutIconResource) {
557 try {
558 iconResource = (Intent.ShortcutIconResource) extra;
559 final PackageManager packageManager = context.getPackageManager();
560 Resources resources = packageManager.getResourcesForApplication(
561 iconResource.packageName);
562 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
563 icon = resources.getDrawable(id);
564 } catch (Exception e) {
565 Log.w(LOG_TAG, "Could not load shortcut icon: " + extra);
566 }
567 }
568 }
569
570 if (icon == null) {
571 icon = context.getPackageManager().getDefaultActivityIcon();
572 }
573
574 final ApplicationInfo info = new ApplicationInfo();
575 info.icon = icon;
576 info.filtered = filtered;
577 info.title = name;
578 info.intent = intent;
579 info.customIcon = customIcon;
580 info.iconResource = iconResource;
581
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800582 LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700583 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
584 return info;
585 }
586
587 /**
588 * Add a PhotFrame to the workspace.
589 *
590 * @param data The intent describing the photo.
591 * @param cellInfo The position on screen where to create the shortcut.
592 */
593 private void completeAddPhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
594 final Bundle extras = data.getExtras();
595 if (extras != null) {
596 Bitmap photo = extras.getParcelable("data");
597
598 Widget info = Widget.makePhotoFrame();
599 info.photo = photo;
600
601 final int[] xy = mCellCoordinates;
602 if (!findSlot(cellInfo, xy, info.spanX, info.spanY)) return;
603
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800604 LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700605 mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
606
607 if (!mRestoring) {
608 sModel.addDesktopItem(info);
609
610 final PhotoFrame view = (PhotoFrame) mInflater.inflate(info.layoutResource, null);
611 view.setImageBitmap(photo);
612 view.setTag(info);
613
614 mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, info.spanY);
615 } else if (sModel.isDesktopLoaded()) {
616 sModel.addDesktopItem(info);
617 }
618 }
619 }
620
621 /**
622 * Updates a workspace PhotoFrame.
623 *
624 * @param data The intent describing the photo.
625 * @param cellInfo The position on screen of the PhotoFrame to update.
626 */
627 private void completeUpdatePhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
628 final Bundle extras = data.getExtras();
629 if (extras != null) {
630 Widget info;
631 Bitmap photo = extras.getParcelable("data");
632
633 if (!mRestoring) {
634 final CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
635 final PhotoFrame view = (PhotoFrame) layout.findCell(cellInfo.cellX, cellInfo.cellY,
636 cellInfo.spanX, cellInfo.spanY, null);
637 view.setImageBitmap(photo);
638 info = (Widget) view.getTag();
639 } else {
640 info = LauncherModel.getPhotoFrameInfo(this, cellInfo.screen,
641 cellInfo.cellX, cellInfo.cellY);
642 }
643
644 info.photo = photo;
645 LauncherModel.updateItemInDatabase(this, info);
646 }
647 }
648
649 /**
650 * Starts a new Intent to let the user update the PhotoFrame defined by the
651 * specified Widget.
652 *
653 * @param widget The Widget info defining which PhotoFrame to update.
654 */
655 void updatePhotoFrame(Widget widget) {
656 CellLayout.CellInfo info = new CellLayout.CellInfo();
657 info.screen = widget.screen;
658 info.cellX = widget.cellX;
659 info.cellY = widget.cellY;
660 info.spanX = widget.spanX;
661 info.spanY = widget.spanY;
662 mAddItemCellInfo = info;
663
664 startActivityForResult(createPhotoPickIntent(), Launcher.REQUEST_UPDATE_PHOTO);
665 }
666
667 /**
668 * Creates an Intent used to let the user pick a photo for a PhotoFrame.
669 *
670 * @return The Intent to pick a photo suited for a PhotoFrame.
671 */
672 private static Intent createPhotoPickIntent() {
673 // TODO: Move this method to PhotoFrame?
674 // TODO: get these values from constants somewhere
675 // TODO: Adjust the PhotoFrame's image size to avoid on the fly scaling
676 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
677 intent.setType("image/*");
678 intent.putExtra("crop", "true");
679 intent.putExtra("aspectX", 1);
680 intent.putExtra("aspectY", 1);
681 intent.putExtra("outputX", 192);
682 intent.putExtra("outputY", 192);
683 intent.putExtra("noFaceDetection", true);
684 intent.putExtra("return-data", true);
685 return intent;
686 }
687
688 @Override
689 protected void onNewIntent(Intent intent) {
690 super.onNewIntent(intent);
691
692 // Close the menu
693 if (Intent.ACTION_MAIN.equals(intent.getAction())) {
694 getWindow().closeAllPanels();
695
696 try {
697 dismissDialog(DIALOG_CREATE_SHORTCUT);
698 // Unlock the workspace if the dialog was showing
699 mWorkspace.unlock();
700 } catch (Exception e) {
701 // An exception is thrown if the dialog is not visible, which is fine
702 }
703
704 try {
705 dismissDialog(DIALOG_RENAME_FOLDER);
706 // Unlock the workspace if the dialog was showing
707 mWorkspace.unlock();
708 } catch (Exception e) {
709 // An exception is thrown if the dialog is not visible, which is fine
710 }
711
712 // If we are already in front we go back to the default screen,
713 // otherwise we don't
714 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) !=
715 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) {
716 if (!mWorkspace.isDefaultScreenShowing()) {
717 mWorkspace.moveToDefaultScreen();
718 }
719 closeDrawer();
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800720 View v = getWindow().peekDecorView();
721 if (v != null && v.getWindowToken() != null) {
722 InputMethodManager imm = (InputMethodManager)getSystemService(
723 INPUT_METHOD_SERVICE);
The Android Open Source Project946cd912009-01-15 16:12:13 -0800724 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800725 }
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700726 } else {
727 closeDrawer(false);
728 }
729 }
730 }
731
732 @Override
733 protected void onSaveInstanceState(Bundle outState) {
734 outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
735
736 final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
737 if (folders.size() > 0) {
738 final int count = folders.size();
739 long[] ids = new long[count];
740 for (int i = 0; i < count; i++) {
741 final FolderInfo info = folders.get(i).getInfo();
742 ids[i] = info.id;
743 }
744 outState.putLongArray(RUNTIME_STATE_USER_FOLDERS, ids);
745 } else {
746 super.onSaveInstanceState(outState);
747 }
748
749 if (mDrawer.isOpened()) {
750 outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true);
751 }
752
753 if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) {
754 final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
755 final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen);
756
757 outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen);
758 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX);
759 outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY);
760 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX);
761 outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY);
762 outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX());
763 outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY());
764 outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS,
765 layout.getOccupiedCells());
766 }
767
768 if (mFolderInfo != null && mWaitingForResult) {
769 outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
770 outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
771 }
772 }
773
774 @Override
775 public void onDestroy() {
776 mDestroyed = true;
777
778 super.onDestroy();
779
780 TextKeyListener.getInstance().release();
781
782 mAllAppsGrid.clearTextFilter();
783 mAllAppsGrid.setAdapter(null);
784 sModel.unbind();
785 sModel.abortLoaders();
786
787 getContentResolver().unregisterContentObserver(mObserver);
788 unregisterReceiver(mApplicationsReceiver);
789 }
790
791 @Override
792 public void startActivityForResult(Intent intent, int requestCode) {
793 mWaitingForResult = true;
794 super.startActivityForResult(intent, requestCode);
795 }
796
797 @Override
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800798 public void startSearch(String initialQuery, boolean selectInitialQuery,
799 Bundle appSearchData, boolean globalSearch) {
800 if (appSearchData == null) {
801 appSearchData = new Bundle();
802 appSearchData.putString(SearchManager.SOURCE, "launcher-search");
803 }
804 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
805 }
806
807 @Override
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700808 public boolean onCreateOptionsMenu(Menu menu) {
809 if (mDesktopLocked) return false;
810
811 super.onCreateOptionsMenu(menu);
812 menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
813 .setIcon(android.R.drawable.ic_menu_add)
814 .setAlphabeticShortcut('A');
815 menu.add(0, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800816 .setIcon(android.R.drawable.ic_menu_gallery)
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700817 .setAlphabeticShortcut('W');
818 menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
819 .setIcon(android.R.drawable.ic_search_category_default)
820 .setAlphabeticShortcut(SearchManager.MENU_KEY);
821 menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications)
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800822 .setIcon(android.R.drawable.ic_menu_notifications)
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700823 .setAlphabeticShortcut('N');
824
825 final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
826 settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
827 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
828
829 menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings)
The Android Open Source Project0e320b22009-01-09 17:51:25 -0800830 .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P')
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700831 .setIntent(settings);
832
833 return true;
834 }
835
836 @Override
837 public boolean onPrepareOptionsMenu(Menu menu) {
838 super.onPrepareOptionsMenu(menu);
839
840 mMenuAddInfo = mWorkspace.findAllVacantCells(null);
841 menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid);
842
843 return true;
844 }
845
846 @Override
847 public boolean onOptionsItemSelected(MenuItem item) {
848 switch (item.getItemId()) {
849 case MENU_ADD:
850 addItems();
851 return true;
852 case MENU_WALLPAPER_SETTINGS:
853 startWallpaper();
854 return true;
855 case MENU_SEARCH:
856 if (!mWorkspace.snapToSearch()) {
857 onSearchRequested();
858 }
859 return true;
860 case MENU_NOTIFICATIONS:
861 showNotifications();
862 return true;
863 }
864
865 return super.onOptionsItemSelected(item);
866 }
867
868 private void addItems() {
869 showAddDialog(mMenuAddInfo);
870 }
871
872 private void removeShortcutsForPackage(String packageName) {
873 if (packageName != null && packageName.length() > 0) {
874 android.util.Log.d(LOG_TAG, packageName);
875 mWorkspace.removeShortcutsForPackage(packageName);
876 }
877 }
878
879 void addShortcut(Intent intent) {
880 startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
881 }
882
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800883 void addLiveFolder(Intent intent) {
884 startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER);
885 }
886
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700887 void addFolder() {
888 UserFolderInfo folderInfo = new UserFolderInfo();
889 folderInfo.title = getText(R.string.folder_name);
890 int cellX = mAddItemCellInfo.cellX;
891 int cellY = mAddItemCellInfo.cellY;
892
893 // Update the model
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800894 LauncherModel.addItemToDatabase(this, folderInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP,
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700895 mWorkspace.getCurrentScreen(), cellX, cellY, false);
896 sModel.addDesktopItem(folderInfo);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800897 sModel.addFolder(folderInfo);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700898
899 // Create the view
900 FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
901 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
902 mWorkspace.addInCurrentScreen(newFolder, cellX, cellY, 1, 1);
903 }
904
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800905 private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo,
906 boolean insertAtFirst) {
907
908 cellInfo.screen = mWorkspace.getCurrentScreen();
909 final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
910
911 if (!mRestoring) {
912 sModel.addDesktopItem(info);
913
914 final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
915 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
916 mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
917 } else if (sModel.isDesktopLoaded()) {
918 sModel.addDesktopItem(info);
919 }
920 }
921
922 static LiveFolderInfo addLiveFolder(Context context, Intent data,
923 CellLayout.CellInfo cellInfo, boolean notify) {
924
925 Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT);
926 String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME);
927
928 Drawable icon = null;
929 boolean filtered = false;
930 Intent.ShortcutIconResource iconResource = null;
931
932 Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON);
933 if (extra != null && extra instanceof Intent.ShortcutIconResource) {
934 try {
935 iconResource = (Intent.ShortcutIconResource) extra;
936 final PackageManager packageManager = context.getPackageManager();
937 Resources resources = packageManager.getResourcesForApplication(
938 iconResource.packageName);
939 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
940 icon = resources.getDrawable(id);
941 } catch (Exception e) {
942 Log.w(LOG_TAG, "Could not load live folder icon: " + extra);
943 }
944 }
945
946 if (icon == null) {
947 icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder);
948 }
949
950 final LiveFolderInfo info = new LiveFolderInfo();
951 info.icon = icon;
952 info.filtered = filtered;
953 info.title = name;
954 info.iconResource = iconResource;
955 info.uri = data.getData();
956 info.baseIntent = baseIntent;
957 info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
958 LiveFolders.DISPLAY_MODE_GRID);
959
960 LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
961 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
962 sModel.addFolder(info);
963
964 return info;
965 }
966
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700967 void getPhotoForPhotoFrame() {
968 startActivityForResult(createPhotoPickIntent(), REQUEST_CHOOSE_PHOTO);
969 }
970
971 void addClock() {
972 final Widget info = Widget.makeClock();
973 addWidget(info);
974 }
975
976 void addSearch() {
977 final Widget info = Widget.makeSearch();
978 addWidget(info);
979 }
980
981 private void addWidget(final Widget info) {
982 final CellLayout.CellInfo cellInfo = mAddItemCellInfo;
983
984 final int[] xy = mCellCoordinates;
985 final int spanX = info.spanX;
986 final int spanY = info.spanY;
987
988 if (!findSlot(cellInfo, xy, spanX, spanY)) return;
989
990 sModel.addDesktopItem(info);
The Android Open Source Projectd097a182008-12-17 18:05:58 -0800991 LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -0700992 mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
993
994 final View view = mInflater.inflate(info.layoutResource, null);
995 view.setTag(info);
996
997 mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
998 }
999
1000 private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
1001 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
1002 boolean[] occupied = mSavedState != null ?
1003 mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null;
1004 cellInfo = mWorkspace.findAllVacantCells(occupied);
1005 if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
1006 Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
1007 return false;
1008 }
1009 }
1010 return true;
1011 }
1012
1013 private void showNotifications() {
1014 final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
1015 if (statusBar != null) {
1016 statusBar.expand();
1017 }
1018 }
1019
1020 private void startWallpaper() {
1021 final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
1022 startActivity(Intent.createChooser(pickWallpaper, getString(R.string.chooser_wallpaper)));
1023 }
1024
1025 /**
1026 * Registers various intent receivers. The current implementation registers
1027 * only a wallpaper intent receiver to let other applications change the
1028 * wallpaper.
1029 */
1030 private void registerIntentReceivers() {
1031 if (sWallpaperReceiver == null) {
1032 final Application application = getApplication();
1033
1034 sWallpaperReceiver = new WallpaperIntentReceiver(application, this);
1035
1036 IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
1037 application.registerReceiver(sWallpaperReceiver, filter);
1038 } else {
1039 sWallpaperReceiver.setLauncher(this);
1040 }
1041
1042 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1043 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1044 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1045 filter.addDataScheme("package");
1046 registerReceiver(mApplicationsReceiver, filter);
1047 }
1048
1049 /**
1050 * Registers various content observers. The current implementation registers
1051 * only a favorites observer to keep track of the favorites applications.
1052 */
1053 private void registerContentObservers() {
1054 ContentResolver resolver = getContentResolver();
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001055 resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mObserver);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001056 }
1057
1058 @Override
1059 public boolean dispatchKeyEvent(KeyEvent event) {
1060 if (event.getAction() == KeyEvent.ACTION_DOWN) {
1061 switch (event.getKeyCode()) {
1062 case KeyEvent.KEYCODE_BACK:
1063 mWorkspace.dispatchKeyEvent(event);
1064 closeFolder();
1065 closeDrawer();
1066 return true;
1067 case KeyEvent.KEYCODE_HOME:
1068 return true;
1069 }
1070 }
1071
1072 return super.dispatchKeyEvent(event);
1073 }
1074
1075 private void closeDrawer() {
1076 closeDrawer(true);
1077 }
1078
1079 private void closeDrawer(boolean animated) {
1080 if (mDrawer.isOpened()) {
1081 if (animated) {
1082 mDrawer.animateClose();
1083 } else {
1084 mDrawer.close();
1085 }
1086 if (mDrawer.hasFocus()) {
1087 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
1088 }
1089 }
1090 }
1091
1092 private void closeFolder() {
1093 Folder folder = mWorkspace.getOpenFolder();
1094 if (folder != null) {
1095 closeFolder(folder);
1096 }
1097 }
1098
1099 void closeFolder(Folder folder) {
1100 folder.getInfo().opened = false;
1101 ViewGroup parent = (ViewGroup) folder.getParent();
1102 if (parent != null) {
1103 parent.removeView(folder);
1104 }
1105 folder.onClose();
1106 }
1107
1108 /**
1109 * When the notification that favorites have changed is received, requests
1110 * a favorites list refresh.
1111 */
1112 private void onFavoritesChanged() {
1113 mDesktopLocked = true;
1114 mDrawer.lock();
The Android Open Source Project0e320b22009-01-09 17:51:25 -08001115 sModel.loadUserItems(false, this, false, false);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001116 }
1117
1118 void onDesktopItemsLoaded() {
1119 if (mDestroyed) return;
1120
1121 bindDesktopItems();
1122 mAllAppsGrid.setAdapter(Launcher.getModel().getApplicationsAdapter());
The Android Open Source Project0e320b22009-01-09 17:51:25 -08001123 }
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001124
The Android Open Source Project0e320b22009-01-09 17:51:25 -08001125 /**
1126 * Refreshes the shortcuts shown on the workspace.
1127 */
1128 private void bindDesktopItems() {
1129 final ArrayList<ItemInfo> shortcuts = sModel.getDesktopItems();
1130 if (shortcuts == null) {
1131 return;
1132 }
1133
1134 final Workspace workspace = mWorkspace;
1135 int count = workspace.getChildCount();
1136 for (int i = 0; i < count; i++) {
1137 ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
1138 }
1139
1140 count = shortcuts.size();
1141
1142 final DesktopItemsBinder binder = new DesktopItemsBinder(this, shortcuts);
1143 binder.obtainMessage(DesktopItemsBinder.MESSAGE_BIND_ITEMS, 0, count).sendToTarget();
1144 }
1145
1146 private void bindItems(Launcher.DesktopItemsBinder binder,
1147 ArrayList<ItemInfo> shortcuts, int start, int count) {
1148
1149 final Workspace workspace = mWorkspace;
1150 final boolean desktopLocked = mDesktopLocked;
1151
1152 final int end = Math.min(start + DesktopItemsBinder.ITEMS_COUNT, count);
1153 int i = start;
1154
1155 for ( ; i < end; i++) {
1156 final ItemInfo item = shortcuts.get(i);
1157 switch (item.itemType) {
1158 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1159 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1160 final View shortcut = createShortcut((ApplicationInfo) item);
1161 workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
1162 !desktopLocked);
1163 break;
1164 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1165 final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
1166 (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
1167 (UserFolderInfo) item);
1168 workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
1169 !desktopLocked);
1170 break;
1171 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
1172 final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
1173 R.layout.live_folder_icon, this,
1174 (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
1175 (LiveFolderInfo) item);
1176 workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
1177 !desktopLocked);
1178 break;
1179 default:
1180 final Widget widget = (Widget) item;
1181 final View view = createWidget(mInflater, widget);
1182 view.setTag(widget);
1183 workspace.addWidget(view, widget, !desktopLocked);
1184 }
1185 }
1186
1187 workspace.requestLayout();
1188
1189 if (end >= count) {
1190 finishBindDesktopItems();
1191 } else {
1192 binder.obtainMessage(DesktopItemsBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget();
1193 }
1194 }
1195
1196 private void finishBindDesktopItems() {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001197 if (mSavedState != null) {
1198 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
1199
1200 final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
1201 if (userFolders != null) {
1202 for (long folderId : userFolders) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001203 final FolderInfo info = sModel.findFolderById(folderId);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001204 if (info != null) {
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001205 openFolder(info);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001206 }
1207 }
1208 final Folder openFolder = mWorkspace.getOpenFolder();
1209 if (openFolder != null) {
1210 openFolder.requestFocus();
1211 } else {
1212 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
1213 }
1214 }
1215
1216 final boolean allApps = mSavedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
1217 if (allApps) {
1218 mDrawer.open();
1219 mDrawer.requestFocus();
1220 }
1221
1222 mSavedState = null;
1223 }
1224
1225 mDesktopLocked = false;
1226 mDrawer.unlock();
1227 }
1228
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001229 private View createWidget(LayoutInflater inflater, Widget widget) {
1230 final Workspace workspace = mWorkspace;
1231 final int screen = workspace.getCurrentScreen();
1232 View v = inflater.inflate(widget.layoutResource,
1233 (ViewGroup) workspace.getChildAt(screen), false);
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001234 if (widget.itemType == LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001235 ((ImageView)v).setImageBitmap(widget.photo);
1236 }
1237 return v;
1238 }
1239
1240 DragController getDragController() {
1241 return mDragLayer;
1242 }
1243
1244 /**
1245 * Launches the intent referred by the clicked shortcut.
1246 *
1247 * @param v The view representing the clicked shortcut.
1248 */
1249 public void onClick(View v) {
1250 Object tag = v.getTag();
1251 if (tag instanceof ApplicationInfo) {
1252 // Open shortcut
1253 final Intent intent = ((ApplicationInfo) tag).intent;
1254 startActivitySafely(intent);
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001255 } else if (tag instanceof FolderInfo) {
1256 handleFolderClick((FolderInfo) tag);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001257 }
1258 }
1259
1260 void startActivitySafely(Intent intent) {
1261 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1262 try {
1263 startActivity(intent);
1264 } catch (ActivityNotFoundException e) {
1265 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
1266 }
1267 }
1268
1269 private void handleFolderClick(FolderInfo folderInfo) {
1270 if (!folderInfo.opened) {
1271 // Close any open folder
1272 closeFolder();
1273 // Open the requested folder
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001274 openFolder(folderInfo);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001275 } else {
1276 // Find the open folder...
1277 Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
1278 int folderScreen;
1279 if (openFolder != null) {
1280 folderScreen = mWorkspace.getScreenForView(openFolder);
1281 // .. and close it
1282 closeFolder(openFolder);
1283 if (folderScreen != mWorkspace.getCurrentScreen()) {
1284 // Close any folder open on the current screen
1285 closeFolder();
1286 // Pull the folder onto this screen
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001287 openFolder(folderInfo);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001288 }
1289 }
1290 }
1291 }
1292
1293 private void loadWallpaper() {
1294 // The first time the application is started, we load the wallpaper from
1295 // the ApplicationContext
1296 if (sWallpaper == null) {
1297 final Drawable drawable = getWallpaper();
1298 if (drawable instanceof BitmapDrawable) {
1299 sWallpaper = ((BitmapDrawable) drawable).getBitmap();
1300 } else {
1301 throw new IllegalStateException("The wallpaper must be a BitmapDrawable.");
1302 }
1303 }
1304 mWorkspace.loadWallpaper(sWallpaper);
1305 }
1306
1307 /**
1308 * Opens the user fodler described by the specified tag. The opening of the folder
1309 * is animated relative to the specified View. If the View is null, no animation
1310 * is played.
1311 *
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001312 * @param folderInfo The FolderInfo describing the folder to open.
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001313 */
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001314 private void openFolder(FolderInfo folderInfo) {
1315 Folder openFolder;
1316
1317 if (folderInfo instanceof UserFolderInfo) {
1318 openFolder = UserFolder.fromXml(this);
1319 } else if (folderInfo instanceof LiveFolderInfo) {
1320 openFolder = com.android.launcher.LiveFolder.fromXml(this, folderInfo);
1321 } else {
1322 return;
1323 }
1324
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001325 openFolder.setDragger(mDragLayer);
1326 openFolder.setLauncher(this);
1327
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001328 openFolder.bind(folderInfo);
1329 folderInfo.opened = true;
1330
1331 mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4);
1332 openFolder.onOpen();
1333 }
1334
1335 /**
1336 * Returns true if the workspace is being loaded. When the workspace is loading,
1337 * no user interaction should be allowed to avoid any conflict.
1338 *
1339 * @return True if the workspace is locked, false otherwise.
1340 */
1341 boolean isWorkspaceLocked() {
1342 return mDesktopLocked;
1343 }
1344
1345 public boolean onLongClick(View v) {
1346 if (mDesktopLocked) {
1347 return false;
1348 }
1349
1350 if (!(v instanceof CellLayout)) {
1351 v = (View) v.getParent();
1352 }
1353
1354 CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
1355
1356 // This happens when long clicking an item with the dpad/trackball
1357 if (cellInfo == null) {
1358 return false;
1359 }
1360
1361 if (mWorkspace.allowLongPress()) {
1362 if (cellInfo.cell == null) {
1363 if (cellInfo.valid) {
1364 // User long pressed on empty space
1365 showAddDialog(cellInfo);
1366 }
1367 } else {
1368 if (!(cellInfo.cell instanceof Folder)) {
1369 // User long pressed on an item
1370 mWorkspace.startDrag(cellInfo);
1371 }
1372 }
1373 }
1374 return true;
1375 }
1376
1377 static LauncherModel getModel() {
1378 return sModel;
1379 }
1380
1381 void closeAllApplications() {
1382 mDrawer.close();
1383 }
1384
1385 boolean isDrawerDown() {
1386 return !mDrawer.isMoving() && !mDrawer.isOpened();
1387 }
1388
1389 Workspace getWorkspace() {
1390 return mWorkspace;
1391 }
1392
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001393 GridView getApplicationsGrid() {
1394 return mAllAppsGrid;
1395 }
1396
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001397 @Override
1398 protected Dialog onCreateDialog(int id) {
1399 switch (id) {
1400 case DIALOG_CREATE_SHORTCUT:
1401 return new CreateShortcut().createDialog();
1402 case DIALOG_RENAME_FOLDER:
1403 return new RenameFolder().createDialog();
1404 }
1405
1406 return super.onCreateDialog(id);
1407 }
1408
1409 @Override
1410 protected void onPrepareDialog(int id, Dialog dialog) {
1411 switch (id) {
1412 case DIALOG_CREATE_SHORTCUT:
1413 mWorkspace.lock();
1414 break;
1415 case DIALOG_RENAME_FOLDER:
1416 mWorkspace.lock();
1417 EditText input = (EditText) dialog.findViewById(R.id.folder_name);
1418 final CharSequence text = mFolderInfo.title;
1419 input.setText(text);
1420 input.setSelection(0, text.length());
1421 break;
1422 }
1423 }
1424
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001425 void showRenameDialog(FolderInfo info) {
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001426 mFolderInfo = info;
1427 mWaitingForResult = true;
1428 showDialog(DIALOG_RENAME_FOLDER);
1429 }
1430
1431 private void showAddDialog(CellLayout.CellInfo cellInfo) {
1432 mAddItemCellInfo = cellInfo;
1433 mWaitingForResult = true;
1434 showDialog(DIALOG_CREATE_SHORTCUT);
1435 }
1436
1437 private class RenameFolder {
1438 private EditText mInput;
1439
1440 Dialog createDialog() {
1441 mWaitingForResult = true;
1442 final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null);
1443 mInput = (EditText) layout.findViewById(R.id.folder_name);
1444
1445 AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
1446 builder.setIcon(0);
1447 builder.setTitle(getString(R.string.rename_folder_title));
1448 builder.setCancelable(true);
1449 builder.setOnCancelListener(new Dialog.OnCancelListener() {
1450 public void onCancel(DialogInterface dialog) {
1451 cleanup();
1452 }
1453 });
1454 builder.setNegativeButton(getString(R.string.cancel_action),
1455 new Dialog.OnClickListener() {
1456 public void onClick(DialogInterface dialog, int which) {
1457 cleanup();
1458 }
1459 }
1460 );
1461 builder.setPositiveButton(getString(R.string.rename_action),
1462 new Dialog.OnClickListener() {
1463 public void onClick(DialogInterface dialog, int which) {
1464 changeFolderName();
1465 }
1466 }
1467 );
1468 builder.setView(layout);
1469 return builder.create();
1470 }
1471
1472 private void changeFolderName() {
1473 final String name = mInput.getText().toString();
1474 if (!TextUtils.isEmpty(name)) {
1475 // Make sure we have the right folder info
1476 mFolderInfo = sModel.findFolderById(mFolderInfo.id);
1477 mFolderInfo.title = name;
1478 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
1479
1480 if (mDesktopLocked) {
1481 mDrawer.lock();
The Android Open Source Project0e320b22009-01-09 17:51:25 -08001482 sModel.loadUserItems(false, Launcher.this, false, false);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001483 } else {
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001484 final FolderIcon folderIcon = (FolderIcon)
1485 mWorkspace.getViewForTag(mFolderInfo);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001486 if (folderIcon != null) {
1487 folderIcon.setText(name);
1488 getWorkspace().requestLayout();
1489 } else {
1490 mDesktopLocked = true;
1491 mDrawer.lock();
The Android Open Source Project0e320b22009-01-09 17:51:25 -08001492 sModel.loadUserItems(false, Launcher.this, false, false);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001493 }
1494 }
1495 }
1496 cleanup();
1497 }
1498
1499 private void cleanup() {
1500 mWorkspace.unlock();
1501 dismissDialog(DIALOG_RENAME_FOLDER);
1502 mWaitingForResult = false;
1503 mFolderInfo = null;
1504 }
1505 }
1506
1507 /**
1508 * Displays the shortcut creation dialog and launches, if necessary, the
1509 * appropriate activity.
1510 */
1511 private class CreateShortcut implements ExpandableListView.OnChildClickListener,
1512 DialogInterface.OnCancelListener, ExpandableListView.OnGroupExpandListener {
1513 private AddAdapter mAdapter;
1514 private ExpandableListView mList;
1515
1516 Dialog createDialog() {
1517 mWaitingForResult = true;
1518 mAdapter = new AddAdapter(Launcher.this, false);
1519
1520 final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
1521 builder.setTitle(getString(R.string.menu_item_add_item));
1522 builder.setIcon(0);
1523
1524 mList = (ExpandableListView)
1525 View.inflate(Launcher.this, R.layout.create_shortcut_list, null);
1526 mList.setAdapter(mAdapter);
1527 mList.setOnChildClickListener(this);
1528 mList.setOnGroupExpandListener(this);
1529 builder.setView(mList);
1530 builder.setInverseBackgroundForced(true);
1531
1532 AlertDialog dialog = builder.create();
1533 dialog.setOnCancelListener(this);
1534
1535 WindowManager.LayoutParams attributes = dialog.getWindow().getAttributes();
1536 attributes.gravity = Gravity.TOP;
1537 dialog.onWindowAttributesChanged(attributes);
1538
1539 return dialog;
1540 }
1541
1542 public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
1543 int childPosition, long id) {
1544 mAdapter.performAction(groupPosition, childPosition);
1545 cleanup();
1546 return true;
1547 }
1548
1549 public void onCancel(DialogInterface dialog) {
1550 mWaitingForResult = false;
1551 cleanup();
1552 }
1553
1554 private void cleanup() {
1555 mWorkspace.unlock();
1556 dismissDialog(DIALOG_CREATE_SHORTCUT);
1557 }
1558
1559 public void onGroupExpand(int groupPosition) {
1560 long packged = ExpandableListView.getPackedPositionForGroup(groupPosition);
1561 int position = mList.getFlatListPosition(packged);
1562 mList.setSelectionFromTop(position, 0);
1563 }
1564 }
1565
1566 /**
1567 * Receives notifications when applications are added/removed.
1568 */
1569 private class ApplicationsIntentReceiver extends BroadcastReceiver {
1570 @Override
1571 public void onReceive(Context context, Intent intent) {
1572 //noinspection ConstantConditions
1573 if (REMOVE_SHORTCUT_ON_PACKAGE_REMOVE &&
1574 Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
1575 removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
1576 }
1577 removeDialog(DIALOG_CREATE_SHORTCUT);
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001578 sModel.loadApplications(false, Launcher.this, false);
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001579 }
1580 }
1581
1582 /**
1583 * Receives notifications whenever the user favorites have changed.
1584 */
1585 private class FavoritesChangeObserver extends ContentObserver {
1586 public FavoritesChangeObserver() {
The Android Open Source Projectd097a182008-12-17 18:05:58 -08001587 super(new Handler());
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001588 }
1589
1590 @Override
1591 public void onChange(boolean selfChange) {
1592 onFavoritesChanged();
1593 }
1594 }
1595
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001596 /**
1597 * Receives intents from other applications to change the wallpaper.
1598 */
1599 private static class WallpaperIntentReceiver extends BroadcastReceiver {
1600 private final Application mApplication;
1601 private WeakReference<Launcher> mLauncher;
1602
1603 WallpaperIntentReceiver(Application application, Launcher launcher) {
1604 mApplication = application;
1605 setLauncher(launcher);
1606 }
1607
1608 void setLauncher(Launcher launcher) {
1609 mLauncher = new WeakReference<Launcher>(launcher);
1610 }
1611
1612 @Override
1613 public void onReceive(Context context, Intent intent) {
1614 // Load the wallpaper from the ApplicationContext and store it locally
1615 // until the Launcher Activity is ready to use it
1616 final Drawable drawable = mApplication.getWallpaper();
1617 if (drawable instanceof BitmapDrawable) {
1618 sWallpaper = ((BitmapDrawable) drawable).getBitmap();
1619 } else {
1620 throw new IllegalStateException("The wallpaper must be a BitmapDrawable.");
1621 }
1622
1623 // If Launcher is alive, notify we have a new wallpaper
1624 if (mLauncher != null) {
1625 final Launcher launcher = mLauncher.get();
1626 if (launcher != null) {
1627 launcher.loadWallpaper();
1628 }
1629 }
1630 }
1631 }
1632
1633 private class DrawerManager implements SlidingDrawer.OnDrawerOpenListener,
1634 SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerScrollListener {
1635 private boolean mOpen;
1636
1637 public void onDrawerOpened() {
1638 if (!mOpen) {
1639 mHandleIcon.reverseTransition(150);
1640 mOpen = true;
1641 }
1642 }
1643
1644 public void onDrawerClosed() {
1645 if (mOpen) {
1646 mHandleIcon.reverseTransition(150);
1647 mOpen = false;
1648 }
1649 mAllAppsGrid.setSelection(0);
1650 mAllAppsGrid.clearTextFilter();
1651 mWorkspace.clearChildrenCache();
1652 }
1653
1654 public void onScrollStarted() {
1655 mWorkspace.enableChildrenCache();
1656 }
1657
1658 public void onScrollEnded() {
1659 }
1660 }
The Android Open Source Project0e320b22009-01-09 17:51:25 -08001661
1662 private static class DesktopItemsBinder extends Handler {
1663 static final int MESSAGE_BIND_ITEMS = 0x1;
1664 // Number of items to bind in every pass
1665 static final int ITEMS_COUNT = 6;
1666
1667 private final ArrayList<ItemInfo> mShortcuts;
1668 private final WeakReference<Launcher> mLauncher;
1669
1670 DesktopItemsBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts) {
1671 mLauncher = new WeakReference<Launcher>(launcher);
1672 mShortcuts = shortcuts;
1673 }
1674
1675 @Override
1676 public void handleMessage(Message msg) {
1677 switch (msg.what) {
1678 case MESSAGE_BIND_ITEMS:
1679 Launcher launcher = mLauncher.get();
1680 if (launcher != null) launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2);
1681 break;
1682 }
1683 }
1684 }
The Android Open Source Projectc8f00b62008-10-21 07:00:00 -07001685}