Files
Lawnchair/src/com/android/launcher/Launcher.java
T
Romain Guy e3895ae921 Fixes #1921372. Pressing Home with the window shade down would show gestures pad.
This change checks for the window focus before taking action. If the Home's window
does not have focus, then the gestures pad is not shown.
2009-06-16 13:25:29 -07:00

2473 lines
90 KiB
Java

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Application;
import android.app.Dialog;
import android.app.IWallpaperService;
import android.app.SearchManager;
import android.app.StatusBarManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.Intent.ShortcutIconResource;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.PorterDuffXfermode;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.LiveFolders;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
import static android.util.Log.*;
import android.view.Display;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.MotionEvent;
import android.view.Gravity;
import android.view.View.OnLongClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.SlidingDrawer;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.ViewSwitcher;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.gesture.GestureOverlayView;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.Gesture;
import android.gesture.Prediction;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedList;
/**
* Default launcher application.
*/
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener {
static final String LOG_TAG = "Launcher";
static final boolean LOGD = false;
private static final boolean PROFILE_STARTUP = false;
private static final boolean PROFILE_DRAWER = false;
private static final boolean PROFILE_ROTATE = false;
private static final boolean DEBUG_USER_INTERFACE = false;
private static final boolean DEBUG_GESTURES = false;
private static final boolean CONFIG_GESTURES_IMMEDIATE_MODE = true;
private static final int WALLPAPER_SCREENS_SPAN = 2;
private static final int MENU_GROUP_ADD = 1;
private static final int MENU_ADD = Menu.FIRST + 1;
private static final int MENU_WALLPAPER_SETTINGS = MENU_ADD + 1;
private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1;
private static final int MENU_GESTURES = MENU_NOTIFICATIONS + 1;
private static final int MENU_SETTINGS = MENU_GESTURES + 1;
private static final int REQUEST_CREATE_SHORTCUT = 1;
private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
private static final int REQUEST_CREATE_APPWIDGET = 5;
private static final int REQUEST_PICK_APPLICATION = 6;
private static final int REQUEST_PICK_SHORTCUT = 7;
private static final int REQUEST_PICK_LIVE_FOLDER = 8;
private static final int REQUEST_PICK_APPWIDGET = 9;
private static final int REQUEST_PICK_GESTURE_ACTION = 10;
private static final int REQUEST_CREATE_GESTURE_ACTION = 11;
private static final int REQUEST_CREATE_GESTURE_APPLICATION_ACTION = 12;
static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
static final String EXTRA_CUSTOM_WIDGET = "custom_widget";
static final String SEARCH_WIDGET = "search_widget";
static final int SCREEN_COUNT = 3;
static final int DEFAULT_SCREN = 1;
static final int NUMBER_CELLS_X = 4;
static final int NUMBER_CELLS_Y = 4;
private static final int DIALOG_CREATE_SHORTCUT = 1;
static final int DIALOG_RENAME_FOLDER = 2;
private static final String PREFERENCES = "launcher";
private static final String KEY_LOCALE = "locale";
private static final String KEY_MCC = "mcc";
private static final String KEY_MNC = "mnc";
// Type: int
private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
// Type: boolean
private static final String RUNTIME_STATE_ALL_APPS_FOLDER = "launcher.all_apps_folder";
// Type: long
private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder";
// Type: int
private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
// Type: int
private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX";
// Type: int
private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY";
// Type: int
private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX";
// Type: int
private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY";
// Type: int
private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX";
// Type: int
private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY";
// Type: int[]
private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells";
// Type: boolean
private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
// Type: long
private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
// Type: Gesture (Parcelable)
private static final String RUNTIME_STATE_PENDING_GESTURE = "launcher.gesture";
private static final LauncherModel sModel = new LauncherModel();
private static Bitmap sWallpaper;
private static final Object sLock = new Object();
private static int sScreen = DEFAULT_SCREN;
private static WallpaperIntentReceiver sWallpaperReceiver;
private static GestureLibrary sLibrary;
private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver();
private final ContentObserver mObserver = new FavoritesChangeObserver();
private LayoutInflater mInflater;
private DragLayer mDragLayer;
private Workspace mWorkspace;
private AppWidgetManager mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
static final int APPWIDGET_HOST_ID = 1024;
private CellLayout.CellInfo mAddItemCellInfo;
private CellLayout.CellInfo mMenuAddInfo;
private final int[] mCellCoordinates = new int[2];
private FolderInfo mFolderInfo;
private SlidingDrawer mDrawer;
private TransitionDrawable mHandleIcon;
private HandleView mHandleView;
private AllAppsGridView mAllAppsGrid;
private boolean mDesktopLocked = true;
private Bundle mSavedState;
private SpannableStringBuilder mDefaultKeySsb = null;
private boolean mDestroyed;
private boolean mRestoring;
private boolean mWaitingForResult;
private boolean mLocaleChanged;
private Bundle mSavedInstanceState;
private DesktopBinder mBinder;
private View mGesturesPanel;
private GestureOverlayView mGesturesOverlay;
private ViewSwitcher mGesturesPrompt;
private ImageView mGesturesAdd;
private PopupWindow mGesturesWindow;
private Launcher.GesturesProcessor mGesturesProcessor;
private Gesture mCurrentGesture;
private GesturesAction mGesturesAction;
private boolean mHideGesturesPanel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInflater = getLayoutInflater();
if (sLibrary == null) {
// The context is not kept by the library so it's safe to do this
sLibrary = GestureLibraries.fromPrivateFile(Launcher.this,
GesturesConstants.STORE_NAME);
}
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
if (PROFILE_STARTUP) {
android.os.Debug.startMethodTracing("/sdcard/launcher");
}
checkForLocaleChange();
setWallpaperDimension();
setContentView(R.layout.launcher);
setupViews();
registerIntentReceivers();
registerContentObservers();
mSavedState = savedInstanceState;
restoreState(mSavedState);
if (PROFILE_STARTUP) {
android.os.Debug.stopMethodTracing();
}
if (!mRestoring) {
startLoaders();
}
// For handling default keys
mDefaultKeySsb = new SpannableStringBuilder();
Selection.setSelection(mDefaultKeySsb, 0);
}
private void checkForLocaleChange() {
final SharedPreferences preferences = getSharedPreferences(PREFERENCES, MODE_PRIVATE);
final Configuration configuration = getResources().getConfiguration();
final String previousLocale = preferences.getString(KEY_LOCALE, null);
final String locale = configuration.locale.toString();
final int previousMcc = preferences.getInt(KEY_MCC, -1);
final int mcc = configuration.mcc;
final int previousMnc = preferences.getInt(KEY_MNC, -1);
final int mnc = configuration.mnc;
mLocaleChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
if (mLocaleChanged) {
final SharedPreferences.Editor editor = preferences.edit();
editor.putString(KEY_LOCALE, locale);
editor.putInt(KEY_MCC, mcc);
editor.putInt(KEY_MNC, mnc);
editor.commit();
}
}
static int getScreen() {
synchronized (sLock) {
return sScreen;
}
}
static void setScreen(int screen) {
synchronized (sLock) {
sScreen = screen;
}
}
private void startLoaders() {
boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged);
sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications);
mRestoring = false;
}
private void setWallpaperDimension() {
IBinder binder = ServiceManager.getService(WALLPAPER_SERVICE);
IWallpaperService wallpaperService = IWallpaperService.Stub.asInterface(binder);
Display display = getWindowManager().getDefaultDisplay();
boolean isPortrait = display.getWidth() < display.getHeight();
final int width = isPortrait ? display.getWidth() : display.getHeight();
final int height = isPortrait ? display.getHeight() : display.getWidth();
try {
wallpaperService.setDimensionHints(width * WALLPAPER_SCREENS_SPAN, height);
} catch (RemoteException e) {
// System is dead!
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mWaitingForResult = false;
// The pattern used here is that a user PICKs a specific application,
// which, depending on the target, might need to CREATE the actual target.
// For example, the user would PICK_SHORTCUT for "Music playlist", and we
// launch over to the Music app to actually CREATE_SHORTCUT.
if (resultCode == RESULT_OK && (mAddItemCellInfo != null ||
((requestCode == REQUEST_PICK_GESTURE_ACTION ||
requestCode == REQUEST_CREATE_GESTURE_ACTION ||
requestCode == REQUEST_CREATE_GESTURE_APPLICATION_ACTION) && mCurrentGesture != null))) {
switch (requestCode) {
case REQUEST_PICK_APPLICATION:
completeAddApplication(this, data, mAddItemCellInfo, !mDesktopLocked);
break;
case REQUEST_PICK_SHORTCUT:
processShortcut(data, REQUEST_PICK_APPLICATION, REQUEST_CREATE_SHORTCUT);
break;
case REQUEST_CREATE_SHORTCUT:
completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked);
break;
case REQUEST_PICK_LIVE_FOLDER:
addLiveFolder(data);
break;
case REQUEST_CREATE_LIVE_FOLDER:
completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked);
break;
case REQUEST_PICK_APPWIDGET:
addAppWidget(data);
break;
case REQUEST_CREATE_APPWIDGET:
completeAddAppWidget(data, mAddItemCellInfo, !mDesktopLocked);
break;
case REQUEST_PICK_GESTURE_ACTION:
processShortcut(data, REQUEST_CREATE_GESTURE_APPLICATION_ACTION,
REQUEST_CREATE_GESTURE_ACTION);
break;
case REQUEST_CREATE_GESTURE_ACTION:
completeCreateGesture(data, true);
break;
case REQUEST_CREATE_GESTURE_APPLICATION_ACTION:
completeCreateGesture(data, false);
break;
}
} else if (requestCode == REQUEST_PICK_APPWIDGET &&
resultCode == RESULT_CANCELED && data != null) {
// Clean up the appWidgetId if we canceled
int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
if (appWidgetId != -1) {
mAppWidgetHost.deleteAppWidgetId(appWidgetId);
}
}
}
@Override
protected void onResume() {
super.onResume();
if (mRestoring) {
startLoaders();
}
// Make sure that the search gadget (if any) is in its normal place.
stopSearch();
}
@Override
protected void onPause() {
super.onPause();
if (mGesturesWindow != null) {
mGesturesWindow.setAnimationStyle(0);
mGesturesWindow.update();
}
closeDrawer(false);
}
@Override
protected void onStop() {
super.onStop();
if (mHideGesturesPanel) {
mHideGesturesPanel = false;
hideGesturesPanel();
}
}
@Override
public Object onRetainNonConfigurationInstance() {
// Flag any binder to stop early before switching
if (mBinder != null) {
mBinder.mTerminate = true;
}
if (PROFILE_ROTATE) {
android.os.Debug.startMethodTracing("/sdcard/launcher-rotate");
}
return null;
}
private boolean acceptFilter() {
final InputMethodManager inputManager = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
return !inputManager.isFullscreenMode();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean handled = super.onKeyDown(keyCode, event);
if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) {
boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
keyCode, event);
if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
// something usable has been typed - start a search
// the typed text will be retrieved and cleared by
// showSearchDialog()
// If there are multiple keystrokes before the search dialog takes focus,
// onSearchRequested() will be called for every keystroke,
// but it is idempotent, so it's fine.
return onSearchRequested();
}
}
return handled;
}
private String getTypedText() {
return mDefaultKeySsb.toString();
}
private void clearTypedText() {
mDefaultKeySsb.clear();
mDefaultKeySsb.clearSpans();
Selection.setSelection(mDefaultKeySsb, 0);
}
/**
* Restores the previous state, if it exists.
*
* @param savedState The previous state.
*/
private void restoreState(Bundle savedState) {
if (savedState == null) {
return;
}
final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
if (currentScreen > -1) {
mWorkspace.setCurrentScreen(currentScreen);
}
final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
if (addScreen > -1) {
mAddItemCellInfo = new CellLayout.CellInfo();
final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
addItemCellInfo.valid = true;
addItemCellInfo.screen = addScreen;
addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
addItemCellInfo.findVacantCellsFromOccupied(
savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS),
savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X),
savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y));
mRestoring = true;
}
boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
if (renameFolder) {
long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
mFolderInfo = sModel.getFolderById(this, id);
mRestoring = true;
}
mCurrentGesture = (Gesture) savedState.get(RUNTIME_STATE_PENDING_GESTURE);
}
/**
* Finds all the views we need and configure them properly.
*/
private void setupViews() {
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
final DragLayer dragLayer = mDragLayer;
mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
final Workspace workspace = mWorkspace;
mDrawer = (SlidingDrawer) dragLayer.findViewById(R.id.drawer);
final SlidingDrawer drawer = mDrawer;
mAllAppsGrid = (AllAppsGridView) drawer.getContent();
final AllAppsGridView grid = mAllAppsGrid;
final DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
mHandleView = (HandleView) drawer.findViewById(R.id.all_apps);
mHandleView.setLauncher(this);
mHandleIcon = (TransitionDrawable) mHandleView.getDrawable();
mHandleIcon.setCrossFadeEnabled(true);
drawer.lock();
final DrawerManager drawerManager = new DrawerManager();
drawer.setOnDrawerOpenListener(drawerManager);
drawer.setOnDrawerCloseListener(drawerManager);
drawer.setOnDrawerScrollListener(drawerManager);
grid.setTextFilterEnabled(false);
grid.setDragger(dragLayer);
grid.setLauncher(this);
workspace.setOnLongClickListener(this);
workspace.setDragger(dragLayer);
workspace.setLauncher(this);
loadWallpaper();
deleteZone.setLauncher(this);
deleteZone.setDragController(dragLayer);
deleteZone.setHandle(mHandleView);
dragLayer.setIgnoredDropTarget(grid);
dragLayer.setDragScoller(workspace);
dragLayer.setDragListener(deleteZone);
mGesturesPanel = mInflater.inflate(R.layout.gestures, mDragLayer, false);
final View gesturesPanel = mGesturesPanel;
mGesturesPrompt = (ViewSwitcher) gesturesPanel.findViewById(R.id.gestures_actions);
mGesturesAction = new GesturesAction();
mGesturesPrompt.getChildAt(0).setOnClickListener(mGesturesAction);
mGesturesPrompt.getChildAt(1).setOnClickListener(mGesturesAction);
mGesturesAdd = (ImageView) gesturesPanel.findViewById(R.id.gestures_add);
final ImageView gesturesAdd = mGesturesAdd;
gesturesAdd.setAlpha(128);
gesturesAdd.setEnabled(false);
gesturesAdd.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
createGesture();
}
});
mGesturesOverlay = (GestureOverlayView) gesturesPanel.findViewById(R.id.gestures_overlay);
mGesturesProcessor = new GesturesProcessor();
final GestureOverlayView overlay = mGesturesOverlay;
overlay.addOnGestureListener(mGesturesProcessor);
overlay.getGesturePaint().setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
}
private void createGesture() {
mCurrentGesture = mGesturesOverlay.getGesture();
mWaitingForResult = true;
pickShortcut(REQUEST_PICK_GESTURE_ACTION, R.string.title_select_shortcut);
}
private void completeCreateGesture(Intent data, boolean isShortcut) {
ApplicationInfo info;
if (isShortcut) {
info = infoFromShortcutIntent(this, data);
} else {
info = infoFromApplicationIntent(this, data);
}
boolean success = false;
if (info != null) {
info.isGesture = true;
if (LauncherModel.addGestureToDatabase(this, info, false)) {
mGesturesProcessor.addGesture(String.valueOf(info.id), mCurrentGesture);
mGesturesProcessor.update(info, mCurrentGesture);
Toast.makeText(this, getString(R.string.gestures_created, info.title),
Toast.LENGTH_SHORT).show();
success = true;
}
}
if (!success) {
Toast.makeText(this, getString(R.string.gestures_failed), Toast.LENGTH_SHORT).show();
}
mCurrentGesture = null;
}
/**
* Creates a view representing a shortcut.
*
* @param info The data structure describing the shortcut.
*
* @return A View inflated from R.layout.application.
*/
View createShortcut(ApplicationInfo info) {
return createShortcut(R.layout.application,
(ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
}
/**
* Creates a view representing a shortcut inflated from the specified resource.
*
* @param layoutResId The id of the XML layout used to create the shortcut.
* @param parent The group the shortcut belongs to.
* @param info The data structure describing the shortcut.
*
* @return A View inflated from layoutResId.
*/
View createShortcut(int layoutResId, ViewGroup parent, ApplicationInfo info) {
TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);
if (!info.filtered) {
info.icon = Utilities.createIconThumbnail(info.icon, this);
info.filtered = true;
}
favorite.setCompoundDrawablesWithIntrinsicBounds(null, info.icon, null, null);
favorite.setText(info.title);
favorite.setTag(info);
favorite.setOnClickListener(this);
return favorite;
}
/**
* Add an application shortcut to the workspace.
*
* @param data The intent describing the application.
* @param cellInfo The position on screen where to create the shortcut.
*/
void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo,
boolean insertAtFirst) {
cellInfo.screen = mWorkspace.getCurrentScreen();
if (!findSingleSlot(cellInfo)) return;
final ApplicationInfo info = infoFromApplicationIntent(context, data);
if (info != null) {
mWorkspace.addApplicationShortcut(info, cellInfo, insertAtFirst);
}
}
private static ApplicationInfo infoFromApplicationIntent(Context context, Intent data) {
ComponentName component = data.getComponent();
PackageManager packageManager = context.getPackageManager();
ActivityInfo activityInfo = null;
try {
activityInfo = packageManager.getActivityInfo(component, 0 /* no flags */);
} catch (NameNotFoundException e) {
e(LOG_TAG, "Couldn't find ActivityInfo for selected application", e);
}
if (activityInfo != null) {
ApplicationInfo itemInfo = new ApplicationInfo();
itemInfo.title = activityInfo.loadLabel(packageManager);
if (itemInfo.title == null) {
itemInfo.title = activityInfo.name;
}
itemInfo.setActivity(component, Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
itemInfo.icon = activityInfo.loadIcon(packageManager);
itemInfo.container = ItemInfo.NO_ID;
return itemInfo;
}
return null;
}
/**
* Add a shortcut to the workspace.
*
* @param data The intent describing the shortcut.
* @param cellInfo The position on screen where to create the shortcut.
* @param insertAtFirst
*/
private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo,
boolean insertAtFirst) {
cellInfo.screen = mWorkspace.getCurrentScreen();
if (!findSingleSlot(cellInfo)) return;
final ApplicationInfo info = addShortcut(this, data, cellInfo, false);
if (!mRestoring) {
sModel.addDesktopItem(info);
final View view = createShortcut(info);
mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
} else if (sModel.isDesktopLoaded()) {
sModel.addDesktopItem(info);
}
}
/**
* Add a widget to the workspace.
*
* @param data The intent describing the appWidgetId.
* @param cellInfo The position on screen where to create the widget.
*/
private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo,
boolean insertAtFirst) {
Bundle extras = data.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
d(LOG_TAG, "dumping extras content="+extras.toString());
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
// Calculate the grid spans needed to fit this widget
CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
// Try finding open space on Launcher screen
final int[] xy = mCellCoordinates;
if (!findSlot(cellInfo, xy, spans[0], spans[1])) return;
// Build Launcher-specific widget info and save to database
LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
launcherInfo.spanX = spans[0];
launcherInfo.spanY = spans[1];
LauncherModel.addItemToDatabase(this, launcherInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
if (!mRestoring) {
sModel.addDesktopAppWidget(launcherInfo);
// Perform actual inflation because we're live
launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
launcherInfo.hostView.setTag(launcherInfo);
mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
launcherInfo.spanX, launcherInfo.spanY, insertAtFirst);
} else if (sModel.isDesktopLoaded()) {
sModel.addDesktopAppWidget(launcherInfo);
}
}
public LauncherAppWidgetHost getAppWidgetHost() {
return mAppWidgetHost;
}
static ApplicationInfo addShortcut(Context context, Intent data,
CellLayout.CellInfo cellInfo, boolean notify) {
final ApplicationInfo info = infoFromShortcutIntent(context, data);
LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
return info;
}
private static ApplicationInfo infoFromShortcutIntent(Context context, Intent data) {
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
Bitmap bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
Drawable icon = null;
boolean filtered = false;
boolean customIcon = false;
ShortcutIconResource iconResource = null;
if (bitmap != null) {
icon = new FastBitmapDrawable(Utilities.createBitmapThumbnail(bitmap, context));
filtered = true;
customIcon = true;
} else {
Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
if (extra != null && extra instanceof ShortcutIconResource) {
try {
iconResource = (ShortcutIconResource) extra;
final PackageManager packageManager = context.getPackageManager();
Resources resources = packageManager.getResourcesForApplication(
iconResource.packageName);
final int id = resources.getIdentifier(iconResource.resourceName, null, null);
icon = resources.getDrawable(id);
} catch (Exception e) {
w(LOG_TAG, "Could not load shortcut icon: " + extra);
}
}
}
if (icon == null) {
icon = context.getPackageManager().getDefaultActivityIcon();
}
final ApplicationInfo info = new ApplicationInfo();
info.icon = icon;
info.filtered = filtered;
info.title = name;
info.intent = intent;
info.customIcon = customIcon;
info.iconResource = iconResource;
return info;
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// Close the menu
if (Intent.ACTION_MAIN.equals(intent.getAction())) {
getWindow().closeAllPanels();
try {
dismissDialog(DIALOG_CREATE_SHORTCUT);
// Unlock the workspace if the dialog was showing
mWorkspace.unlock();
} catch (Exception e) {
// An exception is thrown if the dialog is not visible, which is fine
}
try {
dismissDialog(DIALOG_RENAME_FOLDER);
// Unlock the workspace if the dialog was showing
mWorkspace.unlock();
} catch (Exception e) {
// An exception is thrown if the dialog is not visible, which is fine
}
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) !=
Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) {
if (mGesturesPanel != null && mDragLayer.getWindowVisibility() == View.VISIBLE &&
mDragLayer.hasWindowFocus()) {
SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
if (!searchManager.isVisible()) {
onHomeKeyPressed();
}
}
closeDrawer();
final View v = getWindow().peekDecorView();
if (v != null && v.getWindowToken() != null) {
InputMethodManager imm = (InputMethodManager)getSystemService(
INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
} else {
closeDrawer(false);
}
}
}
private void onHomeKeyPressed() {
if (mGesturesWindow == null || !mGesturesWindow.isShowing()) {
showGesturesPanel();
} else {
hideGesturesPanel();
}
}
private void showGesturesPanel() {
resetGesturesPrompt();
mGesturesAdd.setEnabled(false);
mGesturesAdd.setAlpha(128);
mGesturesOverlay.clear(false);
PopupWindow window;
if (mGesturesWindow == null) {
mGesturesWindow = new PopupWindow(this);
window = mGesturesWindow;
window.setFocusable(true);
window.setTouchable(true);
window.setBackgroundDrawable(null);
window.setContentView(mGesturesPanel);
} else {
window = mGesturesWindow;
}
window.setAnimationStyle(com.android.internal.R.style.Animation_SlidingCard);
final int[] xy = new int[2];
final DragLayer dragLayer = mDragLayer;
dragLayer.getLocationOnScreen(xy);
window.setWidth(dragLayer.getWidth());
window.setHeight(dragLayer.getHeight() - 1);
window.showAtLocation(dragLayer, Gravity.TOP | Gravity.LEFT, xy[0], xy[1] + 1);
}
private void resetGesturesPrompt() {
mGesturesAction.intent = null;
final TextView prompt = (TextView) mGesturesPrompt.getCurrentView();
prompt.setText(R.string.gestures_instructions);
prompt.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
prompt.setClickable(false);
}
private void resetGesturesNextPrompt() {
mGesturesAction.intent = null;
setGesturesNextPrompt(null, getString(R.string.gestures_instructions));
mGesturesPrompt.getNextView().setClickable(false);
}
private void setGesturesNextPrompt(Drawable icon, CharSequence title) {
final TextView prompt = (TextView) mGesturesPrompt.getNextView();
prompt.setText(title);
prompt.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
prompt.setClickable(true);
mGesturesPrompt.showNext();
}
void hideGesturesPanel() {
if (mGesturesWindow != null) {
mGesturesWindow.setAnimationStyle(com.android.internal.R.style.Animation_SlidingCard);
mGesturesWindow.update();
mGesturesWindow.dismiss();
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// Do not call super here
mSavedInstanceState = savedInstanceState;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
if (folders.size() > 0) {
final int count = folders.size();
long[] ids = new long[count];
for (int i = 0; i < count; i++) {
final FolderInfo info = folders.get(i).getInfo();
ids[i] = info.id;
}
outState.putLongArray(RUNTIME_STATE_USER_FOLDERS, ids);
} else {
super.onSaveInstanceState(outState);
}
// When the drawer is opened and we are saving the state because of a
// configuration change
if (mDrawer.isOpened() && getChangingConfigurations() != 0) {
outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true);
}
if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) {
final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen);
outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen);
outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX);
outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY);
outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX);
outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY);
outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX());
outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY());
outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS,
layout.getOccupiedCells());
}
if (mFolderInfo != null && mWaitingForResult) {
outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
}
if (mCurrentGesture != null && mWaitingForResult) {
outState.putParcelable(RUNTIME_STATE_PENDING_GESTURE, mCurrentGesture);
}
}
@Override
public void onDestroy() {
mDestroyed = true;
super.onDestroy();
try {
mAppWidgetHost.stopListening();
} catch (NullPointerException ex) {
w(LOG_TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
}
TextKeyListener.getInstance().release();
mAllAppsGrid.clearTextFilter();
mAllAppsGrid.setAdapter(null);
sModel.unbind();
sModel.abortLoaders();
getContentResolver().unregisterContentObserver(mObserver);
unregisterReceiver(mApplicationsReceiver);
}
@Override
public void startActivityForResult(Intent intent, int requestCode) {
mWaitingForResult = true;
super.startActivityForResult(intent, requestCode);
}
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData, boolean globalSearch) {
closeDrawer(false);
// Slide the search widget to the top, if it's on the current screen,
// otherwise show the search dialog immediately.
Search searchWidget = mWorkspace.findSearchWidgetOnCurrentScreen();
if (searchWidget == null) {
showSearchDialog(initialQuery, selectInitialQuery, appSearchData, globalSearch);
} else {
searchWidget.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
// show the currently typed text in the search widget while sliding
searchWidget.setQuery(getTypedText());
}
}
/**
* Show the search dialog immediately, without changing the search widget.
*
* @see Activity#startSearch(String, boolean, android.os.Bundle, boolean)
*/
void showSearchDialog(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData, boolean globalSearch) {
if (initialQuery == null) {
// Use any text typed in the launcher as the initial query
initialQuery = getTypedText();
clearTypedText();
}
if (appSearchData == null) {
appSearchData = new Bundle();
appSearchData.putString(SearchManager.SOURCE, "launcher-search");
}
final SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
final Search searchWidget = mWorkspace.findSearchWidgetOnCurrentScreen();
if (searchWidget != null) {
// This gets called when the user leaves the search dialog to go back to
// the Launcher.
searchManager.setOnCancelListener(new SearchManager.OnCancelListener() {
public void onCancel() {
searchManager.setOnCancelListener(null);
stopSearch();
}
});
}
searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
appSearchData, globalSearch);
}
/**
* Cancel search dialog if it is open.
*/
void stopSearch() {
// Close search dialog
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
if (searchManager.isVisible()) {
searchManager.stopSearch();
}
// Restore search widget to its normal position
Search searchWidget = mWorkspace.findSearchWidgetOnCurrentScreen();
if (searchWidget != null) {
searchWidget.stopSearch(false);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mDesktopLocked) return false;
super.onCreateOptionsMenu(menu);
menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
.setIcon(android.R.drawable.ic_menu_add)
.setAlphabeticShortcut('A');
menu.add(0, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
.setIcon(android.R.drawable.ic_menu_gallery)
.setAlphabeticShortcut('W');
menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
.setIcon(android.R.drawable.ic_search_category_default)
.setAlphabeticShortcut(SearchManager.MENU_KEY);
menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications)
.setIcon(com.android.internal.R.drawable.ic_menu_notifications)
.setAlphabeticShortcut('N');
final Intent gestures = new Intent(this, GesturesActivity.class);
menu.add(0, MENU_GESTURES, 0, R.string.menu_gestures)
.setIcon(com.android.internal.R.drawable.ic_menu_compose).setAlphabeticShortcut('G')
.setIntent(gestures);
final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings)
.setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P')
.setIntent(settings);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
mMenuAddInfo = mWorkspace.findAllVacantCells(null);
menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ADD:
addItems();
return true;
case MENU_WALLPAPER_SETTINGS:
startWallpaper();
return true;
case MENU_SEARCH:
onSearchRequested();
return true;
case MENU_NOTIFICATIONS:
showNotifications();
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* Indicates that we want global search for this activity by setting the globalSearch
* argument for {@link #startSearch} to true.
*/
@Override
public boolean onSearchRequested() {
startSearch(null, false, null, true);
return true;
}
private void addItems() {
showAddDialog(mMenuAddInfo);
}
private void removeShortcutsForPackage(String packageName) {
if (packageName != null && packageName.length() > 0) {
mWorkspace.removeShortcutsForPackage(packageName);
}
}
private void updateShortcutsForPackage(String packageName) {
if (packageName != null && packageName.length() > 0) {
mWorkspace.updateShortcutsForPackage(packageName);
}
}
void addAppWidget(Intent data) {
// TODO: catch bad widget exception when sent
int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET);
if (SEARCH_WIDGET.equals(customWidget)) {
// We don't need this any more, since this isn't a real app widget.
mAppWidgetHost.deleteAppWidgetId(appWidgetId);
// add the search widget
addSearch();
} else {
AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
if (appWidget.configure != null) {
// Launch over to configure widget, if needed
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
intent.setComponent(appWidget.configure);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
} else {
// Otherwise just add it
onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
}
}
}
void addSearch() {
final Widget info = Widget.makeSearch();
final CellLayout.CellInfo cellInfo = mAddItemCellInfo;
final int[] xy = mCellCoordinates;
final int spanX = info.spanX;
final int spanY = info.spanY;
if (!findSlot(cellInfo, xy, spanX, spanY)) return;
sModel.addDesktopItem(info);
LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
final View view = mInflater.inflate(info.layoutResource, null);
view.setTag(info);
Search search = (Search) view.findViewById(R.id.widget_search);
search.setLauncher(this);
mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
}
void processShortcut(Intent intent, int requestCodeApplication, int requestCodeShortcut) {
// Handle case where user selected "Applications"
String applicationName = getResources().getString(R.string.group_applications);
String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
if (applicationName != null && applicationName.equals(shortcutName)) {
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
startActivityForResult(pickIntent, requestCodeApplication);
} else {
startActivityForResult(intent, requestCodeShortcut);
}
}
void addLiveFolder(Intent intent) {
// Handle case where user selected "Folder"
String folderName = getResources().getString(R.string.group_folder);
String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
if (folderName != null && folderName.equals(shortcutName)) {
addFolder(!mDesktopLocked);
} else {
startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER);
}
}
void addFolder(boolean insertAtFirst) {
UserFolderInfo folderInfo = new UserFolderInfo();
folderInfo.title = getText(R.string.folder_name);
CellLayout.CellInfo cellInfo = mAddItemCellInfo;
cellInfo.screen = mWorkspace.getCurrentScreen();
if (!findSingleSlot(cellInfo)) return;
// Update the model
LauncherModel.addItemToDatabase(this, folderInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP,
mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
sModel.addDesktopItem(folderInfo);
sModel.addFolder(folderInfo);
// Create the view
FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
mWorkspace.addInCurrentScreen(newFolder,
cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
}
private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo,
boolean insertAtFirst) {
cellInfo.screen = mWorkspace.getCurrentScreen();
if (!findSingleSlot(cellInfo)) return;
final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
if (!mRestoring) {
sModel.addDesktopItem(info);
final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
(ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
} else if (sModel.isDesktopLoaded()) {
sModel.addDesktopItem(info);
}
}
static LiveFolderInfo addLiveFolder(Context context, Intent data,
CellLayout.CellInfo cellInfo, boolean notify) {
Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT);
String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME);
Drawable icon = null;
boolean filtered = false;
Intent.ShortcutIconResource iconResource = null;
Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON);
if (extra != null && extra instanceof Intent.ShortcutIconResource) {
try {
iconResource = (Intent.ShortcutIconResource) extra;
final PackageManager packageManager = context.getPackageManager();
Resources resources = packageManager.getResourcesForApplication(
iconResource.packageName);
final int id = resources.getIdentifier(iconResource.resourceName, null, null);
icon = resources.getDrawable(id);
} catch (Exception e) {
w(LOG_TAG, "Could not load live folder icon: " + extra);
}
}
if (icon == null) {
icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder);
}
final LiveFolderInfo info = new LiveFolderInfo();
info.icon = icon;
info.filtered = filtered;
info.title = name;
info.iconResource = iconResource;
info.uri = data.getData();
info.baseIntent = baseIntent;
info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
LiveFolders.DISPLAY_MODE_GRID);
LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
sModel.addFolder(info);
return info;
}
private boolean findSingleSlot(CellLayout.CellInfo cellInfo) {
final int[] xy = new int[2];
if (findSlot(cellInfo, xy, 1, 1)) {
cellInfo.cellX = xy[0];
cellInfo.cellY = xy[1];
return true;
}
return false;
}
private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
boolean[] occupied = mSavedState != null ?
mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null;
cellInfo = mWorkspace.findAllVacantCells(occupied);
if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
return false;
}
}
return true;
}
private void showNotifications() {
final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
if (statusBar != null) {
statusBar.expand();
}
}
private void startWallpaper() {
final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
startActivity(Intent.createChooser(pickWallpaper, getString(R.string.chooser_wallpaper)));
}
/**
* Registers various intent receivers. The current implementation registers
* only a wallpaper intent receiver to let other applications change the
* wallpaper.
*/
private void registerIntentReceivers() {
if (sWallpaperReceiver == null) {
final Application application = getApplication();
sWallpaperReceiver = new WallpaperIntentReceiver(application, this);
IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
application.registerReceiver(sWallpaperReceiver, filter);
} else {
sWallpaperReceiver.setLauncher(this);
}
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
registerReceiver(mApplicationsReceiver, filter);
}
/**
* Registers various content observers. The current implementation registers
* only a favorites observer to keep track of the favorites applications.
*/
private void registerContentObservers() {
ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mObserver);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BACK:
mWorkspace.dispatchKeyEvent(event);
if (mDrawer.isOpened()) {
closeDrawer();
} else {
closeFolder();
}
return true;
case KeyEvent.KEYCODE_HOME:
return true;
}
}
return super.dispatchKeyEvent(event);
}
private void closeDrawer() {
closeDrawer(true);
}
private void closeDrawer(boolean animated) {
if (mDrawer.isOpened()) {
if (animated) {
mDrawer.animateClose();
} else {
mDrawer.close();
}
if (mDrawer.hasFocus()) {
mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
}
}
}
private void closeFolder() {
Folder folder = mWorkspace.getOpenFolder();
if (folder != null) {
closeFolder(folder);
}
}
void closeFolder(Folder folder) {
folder.getInfo().opened = false;
ViewGroup parent = (ViewGroup) folder.getParent();
if (parent != null) {
parent.removeView(folder);
}
folder.onClose();
}
/**
* When the notification that favorites have changed is received, requests
* a favorites list refresh.
*/
private void onFavoritesChanged() {
mDesktopLocked = true;
mDrawer.lock();
sModel.loadUserItems(false, this, false, false);
}
void onDesktopItemsLoaded() {
if (mDestroyed) return;
bindDesktopItems();
}
/**
* Refreshes the shortcuts shown on the workspace.
*/
private void bindDesktopItems() {
final ArrayList<ItemInfo> shortcuts = sModel.getDesktopItems();
final ArrayList<LauncherAppWidgetInfo> appWidgets = sModel.getDesktopAppWidgets();
final ApplicationsAdapter drawerAdapter = sModel.getApplicationsAdapter();
if (shortcuts == null || appWidgets == null || drawerAdapter == null) {
return;
}
final Workspace workspace = mWorkspace;
int count = workspace.getChildCount();
for (int i = 0; i < count; i++) {
((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
}
if (DEBUG_USER_INTERFACE) {
android.widget.Button finishButton = new android.widget.Button(this);
finishButton.setText("Finish");
workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
// Flag any old binder to terminate early
if (mBinder != null) {
mBinder.mTerminate = true;
}
mBinder = new DesktopBinder(this, shortcuts, appWidgets, drawerAdapter);
mBinder.startBindingItems();
}
private void bindItems(Launcher.DesktopBinder binder,
ArrayList<ItemInfo> shortcuts, int start, int count) {
final Workspace workspace = mWorkspace;
final boolean desktopLocked = mDesktopLocked;
final int end = Math.min(start + DesktopBinder.ITEMS_COUNT, count);
int i = start;
for ( ; i < end; i++) {
final ItemInfo item = shortcuts.get(i);
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
final View shortcut = createShortcut((ApplicationInfo) item);
workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
!desktopLocked);
break;
case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
(UserFolderInfo) item);
workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
!desktopLocked);
break;
case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
R.layout.live_folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
(LiveFolderInfo) item);
workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
!desktopLocked);
break;
case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
final int screen = workspace.getCurrentScreen();
final View view = mInflater.inflate(R.layout.widget_search,
(ViewGroup) workspace.getChildAt(screen), false);
Search search = (Search) view.findViewById(R.id.widget_search);
search.setLauncher(this);
final Widget widget = (Widget) item;
view.setTag(widget);
workspace.addWidget(view, widget, !desktopLocked);
break;
}
}
workspace.requestLayout();
if (end >= count) {
finishBindDesktopItems();
binder.startBindingDrawer();
} else {
binder.obtainMessage(DesktopBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget();
}
}
private void finishBindDesktopItems() {
if (mSavedState != null) {
if (!mWorkspace.hasFocus()) {
mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
}
final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
if (userFolders != null) {
for (long folderId : userFolders) {
final FolderInfo info = sModel.findFolderById(folderId);
if (info != null) {
openFolder(info);
}
}
final Folder openFolder = mWorkspace.getOpenFolder();
if (openFolder != null) {
openFolder.requestFocus();
}
}
final boolean allApps = mSavedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
if (allApps) {
mDrawer.open();
}
mSavedState = null;
}
if (mSavedInstanceState != null) {
super.onRestoreInstanceState(mSavedInstanceState);
mSavedInstanceState = null;
}
if (mDrawer.isOpened() && !mDrawer.hasFocus()) {
mDrawer.requestFocus();
}
mDesktopLocked = false;
mDrawer.unlock();
}
private void bindDrawer(Launcher.DesktopBinder binder,
ApplicationsAdapter drawerAdapter) {
mAllAppsGrid.setAdapter(drawerAdapter);
binder.startBindingAppWidgetsWhenIdle();
}
private void bindAppWidgets(Launcher.DesktopBinder binder,
LinkedList<LauncherAppWidgetInfo> appWidgets) {
final Workspace workspace = mWorkspace;
final boolean desktopLocked = mDesktopLocked;
if (!appWidgets.isEmpty()) {
final LauncherAppWidgetInfo item = appWidgets.removeFirst();
final int appWidgetId = item.appWidgetId;
final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
if (LOGD) d(LOG_TAG, String.format("about to setAppWidget for id=%d, info=%s", appWidgetId, appWidgetInfo));
item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
item.hostView.setTag(item);
workspace.addInScreen(item.hostView, item.screen, item.cellX,
item.cellY, item.spanX, item.spanY, !desktopLocked);
workspace.requestLayout();
}
if (appWidgets.isEmpty()) {
if (PROFILE_ROTATE) {
android.os.Debug.stopMethodTracing();
}
} else {
binder.obtainMessage(DesktopBinder.MESSAGE_BIND_APPWIDGETS).sendToTarget();
}
}
DragController getDragController() {
return mDragLayer;
}
/**
* Launches the intent referred by the clicked shortcut.
*
* @param v The view representing the clicked shortcut.
*/
public void onClick(View v) {
Object tag = v.getTag();
if (tag instanceof ApplicationInfo) {
// Open shortcut
final Intent intent = ((ApplicationInfo) tag).intent;
startActivitySafely(intent);
} else if (tag instanceof FolderInfo) {
handleFolderClick((FolderInfo) tag);
}
}
void startActivitySafely(Intent intent) {
mHideGesturesPanel = true;
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
} catch (SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
e(LOG_TAG, "Launcher does not have the permission to launch " + intent +
". Make sure to create a MAIN intent-filter for the corresponding activity " +
"or use the exported attribute for this activity.", e);
}
}
private void handleFolderClick(FolderInfo folderInfo) {
if (!folderInfo.opened) {
// Close any open folder
closeFolder();
// Open the requested folder
openFolder(folderInfo);
} else {
// Find the open folder...
Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
int folderScreen;
if (openFolder != null) {
folderScreen = mWorkspace.getScreenForView(openFolder);
// .. and close it
closeFolder(openFolder);
if (folderScreen != mWorkspace.getCurrentScreen()) {
// Close any folder open on the current screen
closeFolder();
// Pull the folder onto this screen
openFolder(folderInfo);
}
}
}
}
private void loadWallpaper() {
// The first time the application is started, we load the wallpaper from
// the ApplicationContext
if (sWallpaper == null) {
final Drawable drawable = getWallpaper();
if (drawable instanceof BitmapDrawable) {
sWallpaper = ((BitmapDrawable) drawable).getBitmap();
} else {
throw new IllegalStateException("The wallpaper must be a BitmapDrawable.");
}
}
mWorkspace.loadWallpaper(sWallpaper);
}
/**
* Opens the user fodler described by the specified tag. The opening of the folder
* is animated relative to the specified View. If the View is null, no animation
* is played.
*
* @param folderInfo The FolderInfo describing the folder to open.
*/
private void openFolder(FolderInfo folderInfo) {
Folder openFolder;
if (folderInfo instanceof UserFolderInfo) {
openFolder = UserFolder.fromXml(this);
} else if (folderInfo instanceof LiveFolderInfo) {
openFolder = com.android.launcher.LiveFolder.fromXml(this, folderInfo);
} else {
return;
}
openFolder.setDragger(mDragLayer);
openFolder.setLauncher(this);
openFolder.bind(folderInfo);
folderInfo.opened = true;
mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4);
openFolder.onOpen();
}
/**
* Returns true if the workspace is being loaded. When the workspace is loading,
* no user interaction should be allowed to avoid any conflict.
*
* @return True if the workspace is locked, false otherwise.
*/
boolean isWorkspaceLocked() {
return mDesktopLocked;
}
public boolean onLongClick(View v) {
if (mDesktopLocked) {
return false;
}
if (!(v instanceof CellLayout)) {
v = (View) v.getParent();
}
CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
// This happens when long clicking an item with the dpad/trackball
if (cellInfo == null) {
return true;
}
if (mWorkspace.allowLongPress()) {
if (cellInfo.cell == null) {
if (cellInfo.valid) {
// User long pressed on empty space
mWorkspace.setAllowLongPress(false);
showAddDialog(cellInfo);
}
} else {
if (!(cellInfo.cell instanceof Folder)) {
// User long pressed on an item
mWorkspace.startDrag(cellInfo);
}
}
}
return true;
}
static LauncherModel getModel() {
return sModel;
}
static GestureLibrary getGestureLibrary() {
return sLibrary;
}
void closeAllApplications() {
mDrawer.close();
}
View getDrawerHandle() {
return mHandleView;
}
boolean isDrawerDown() {
return !mDrawer.isMoving() && !mDrawer.isOpened();
}
boolean isDrawerUp() {
return mDrawer.isOpened() && !mDrawer.isMoving();
}
boolean isDrawerMoving() {
return mDrawer.isMoving();
}
Workspace getWorkspace() {
return mWorkspace;
}
GridView getApplicationsGrid() {
return mAllAppsGrid;
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_CREATE_SHORTCUT:
return new CreateShortcut().createDialog();
case DIALOG_RENAME_FOLDER:
return new RenameFolder().createDialog();
}
return super.onCreateDialog(id);
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
case DIALOG_CREATE_SHORTCUT:
mWorkspace.lock();
break;
case DIALOG_RENAME_FOLDER:
mWorkspace.lock();
EditText input = (EditText) dialog.findViewById(R.id.folder_name);
final CharSequence text = mFolderInfo.title;
input.setText(text);
input.setSelection(0, text.length());
break;
}
}
void showRenameDialog(FolderInfo info) {
mFolderInfo = info;
mWaitingForResult = true;
showDialog(DIALOG_RENAME_FOLDER);
}
private void showAddDialog(CellLayout.CellInfo cellInfo) {
mAddItemCellInfo = cellInfo;
mWaitingForResult = true;
showDialog(DIALOG_CREATE_SHORTCUT);
}
private void pickShortcut(int requestCode, int title) {
Bundle bundle = new Bundle();
ArrayList<String> shortcutNames = new ArrayList<String>();
shortcutNames.add(getString(R.string.group_applications));
bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>();
shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
R.drawable.ic_launcher_application));
bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT));
pickIntent.putExtra(Intent.EXTRA_TITLE, getText(title));
pickIntent.putExtras(bundle);
startActivityForResult(pickIntent, requestCode);
}
private class RenameFolder {
private EditText mInput;
Dialog createDialog() {
mWaitingForResult = true;
final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null);
mInput = (EditText) layout.findViewById(R.id.folder_name);
AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
builder.setIcon(0);
builder.setTitle(getString(R.string.rename_folder_title));
builder.setCancelable(true);
builder.setOnCancelListener(new Dialog.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
cleanup();
}
});
builder.setNegativeButton(getString(R.string.cancel_action),
new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
cleanup();
}
}
);
builder.setPositiveButton(getString(R.string.rename_action),
new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
changeFolderName();
}
}
);
builder.setView(layout);
return builder.create();
}
private void changeFolderName() {
final String name = mInput.getText().toString();
if (!TextUtils.isEmpty(name)) {
// Make sure we have the right folder info
mFolderInfo = sModel.findFolderById(mFolderInfo.id);
mFolderInfo.title = name;
LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
if (mDesktopLocked) {
mDrawer.lock();
sModel.loadUserItems(false, Launcher.this, false, false);
} else {
final FolderIcon folderIcon = (FolderIcon)
mWorkspace.getViewForTag(mFolderInfo);
if (folderIcon != null) {
folderIcon.setText(name);
getWorkspace().requestLayout();
} else {
mDesktopLocked = true;
mDrawer.lock();
sModel.loadUserItems(false, Launcher.this, false, false);
}
}
}
cleanup();
}
private void cleanup() {
mWorkspace.unlock();
dismissDialog(DIALOG_RENAME_FOLDER);
mWaitingForResult = false;
mFolderInfo = null;
}
}
/**
* Displays the shortcut creation dialog and launches, if necessary, the
* appropriate activity.
*/
private class CreateShortcut implements DialogInterface.OnClickListener,
DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
private AddAdapter mAdapter;
Dialog createDialog() {
mWaitingForResult = true;
mAdapter = new AddAdapter(Launcher.this);
final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
builder.setTitle(getString(R.string.menu_item_add_item));
builder.setAdapter(mAdapter, this);
builder.setInverseBackgroundForced(true);
AlertDialog dialog = builder.create();
dialog.setOnCancelListener(this);
dialog.setOnDismissListener(this);
return dialog;
}
public void onCancel(DialogInterface dialog) {
mWaitingForResult = false;
cleanup();
}
public void onDismiss(DialogInterface dialog) {
mWorkspace.unlock();
}
private void cleanup() {
mWorkspace.unlock();
dismissDialog(DIALOG_CREATE_SHORTCUT);
}
/**
* Handle the action clicked in the "Add to home" dialog.
*/
public void onClick(DialogInterface dialog, int which) {
Resources res = getResources();
cleanup();
switch (which) {
case AddAdapter.ITEM_SHORTCUT: {
// Insert extra item to handle picking application
pickShortcut(REQUEST_PICK_SHORTCUT, R.string.title_select_shortcut);
break;
}
case AddAdapter.ITEM_APPWIDGET: {
int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
// add the search widget
ArrayList<AppWidgetProviderInfo> customInfo =
new ArrayList<AppWidgetProviderInfo>();
AppWidgetProviderInfo info = new AppWidgetProviderInfo();
info.provider = new ComponentName(getPackageName(), "XXX.YYY");
info.label = getString(R.string.group_search);
info.icon = R.drawable.ic_search_widget;
customInfo.add(info);
pickIntent.putParcelableArrayListExtra(
AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
ArrayList<Bundle> customExtras = new ArrayList<Bundle>();
Bundle b = new Bundle();
b.putString(EXTRA_CUSTOM_WIDGET, SEARCH_WIDGET);
customExtras.add(b);
pickIntent.putParcelableArrayListExtra(
AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
// start the pick activity
startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
break;
}
case AddAdapter.ITEM_LIVE_FOLDER: {
// Insert extra item to handle inserting folder
Bundle bundle = new Bundle();
ArrayList<String> shortcutNames = new ArrayList<String>();
shortcutNames.add(res.getString(R.string.group_folder));
bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
ArrayList<ShortcutIconResource> shortcutIcons =
new ArrayList<ShortcutIconResource>();
shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
R.drawable.ic_launcher_folder));
bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
pickIntent.putExtra(Intent.EXTRA_INTENT,
new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER));
pickIntent.putExtra(Intent.EXTRA_TITLE,
getText(R.string.title_select_live_folder));
pickIntent.putExtras(bundle);
startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
break;
}
case AddAdapter.ITEM_WALLPAPER: {
startWallpaper();
break;
}
}
}
}
/**
* Receives notifications when applications are added/removed.
*/
private class ApplicationsIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
final String packageName = intent.getData().getSchemeSpecificPart();
final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
if (LauncherModel.DEBUG_LOADERS) {
d(LauncherModel.LOG_TAG, "application intent received: " + action +
", replacing=" + replacing);
d(LauncherModel.LOG_TAG, " --> " + intent.getData());
}
if (!Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
if (!replacing) {
removeShortcutsForPackage(packageName);
if (LauncherModel.DEBUG_LOADERS) {
d(LauncherModel.LOG_TAG, " --> remove package");
}
sModel.removePackage(Launcher.this, packageName);
}
// else, we are replacing the package, so a PACKAGE_ADDED will be sent
// later, we will update the package at this time
} else {
if (!replacing) {
if (LauncherModel.DEBUG_LOADERS) {
d(LauncherModel.LOG_TAG, " --> add package");
}
sModel.addPackage(Launcher.this, packageName);
} else {
if (LauncherModel.DEBUG_LOADERS) {
d(LauncherModel.LOG_TAG, " --> update package " + packageName);
}
sModel.updatePackage(Launcher.this, packageName);
updateShortcutsForPackage(packageName);
}
}
removeDialog(DIALOG_CREATE_SHORTCUT);
} else {
if (LauncherModel.DEBUG_LOADERS) {
d(LauncherModel.LOG_TAG, " --> sync package " + packageName);
}
sModel.syncPackage(Launcher.this, packageName);
}
}
}
/**
* Receives notifications whenever the user favorites have changed.
*/
private class FavoritesChangeObserver extends ContentObserver {
public FavoritesChangeObserver() {
super(new Handler());
}
@Override
public void onChange(boolean selfChange) {
onFavoritesChanged();
}
}
/**
* Receives intents from other applications to change the wallpaper.
*/
private static class WallpaperIntentReceiver extends BroadcastReceiver {
private final Application mApplication;
private WeakReference<Launcher> mLauncher;
WallpaperIntentReceiver(Application application, Launcher launcher) {
mApplication = application;
setLauncher(launcher);
}
void setLauncher(Launcher launcher) {
mLauncher = new WeakReference<Launcher>(launcher);
}
@Override
public void onReceive(Context context, Intent intent) {
// Load the wallpaper from the ApplicationContext and store it locally
// until the Launcher Activity is ready to use it
final Drawable drawable = mApplication.getWallpaper();
if (drawable instanceof BitmapDrawable) {
sWallpaper = ((BitmapDrawable) drawable).getBitmap();
} else {
throw new IllegalStateException("The wallpaper must be a BitmapDrawable.");
}
// If Launcher is alive, notify we have a new wallpaper
if (mLauncher != null) {
final Launcher launcher = mLauncher.get();
if (launcher != null) {
launcher.loadWallpaper();
}
}
}
}
private class DrawerManager implements SlidingDrawer.OnDrawerOpenListener,
SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerScrollListener {
private boolean mOpen;
public void onDrawerOpened() {
if (!mOpen) {
mHandleIcon.reverseTransition(150);
final Rect bounds = mWorkspace.mDrawerBounds;
offsetBoundsToDragLayer(bounds, mAllAppsGrid);
mOpen = true;
}
}
private void offsetBoundsToDragLayer(Rect bounds, View view) {
view.getDrawingRect(bounds);
while (view != mDragLayer) {
bounds.offset(view.getLeft(), view.getTop());
view = (View) view.getParent();
}
}
public void onDrawerClosed() {
if (mOpen) {
mHandleIcon.reverseTransition(150);
mWorkspace.mDrawerBounds.setEmpty();
mOpen = false;
}
mAllAppsGrid.setSelection(0);
mAllAppsGrid.clearTextFilter();
}
public void onScrollStarted() {
if (PROFILE_DRAWER) {
android.os.Debug.startMethodTracing("/sdcard/launcher-drawer");
}
mWorkspace.mDrawerContentWidth = mAllAppsGrid.getWidth();
mWorkspace.mDrawerContentHeight = mAllAppsGrid.getHeight();
}
public void onScrollEnded() {
if (PROFILE_DRAWER) {
android.os.Debug.stopMethodTracing();
}
}
}
private static class DesktopBinder extends Handler implements MessageQueue.IdleHandler {
static final int MESSAGE_BIND_ITEMS = 0x1;
static final int MESSAGE_BIND_APPWIDGETS = 0x2;
static final int MESSAGE_BIND_DRAWER = 0x3;
// Number of items to bind in every pass
static final int ITEMS_COUNT = 6;
private final ArrayList<ItemInfo> mShortcuts;
private final LinkedList<LauncherAppWidgetInfo> mAppWidgets;
private final ApplicationsAdapter mDrawerAdapter;
private final WeakReference<Launcher> mLauncher;
public boolean mTerminate = false;
DesktopBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts,
ArrayList<LauncherAppWidgetInfo> appWidgets,
ApplicationsAdapter drawerAdapter) {
mLauncher = new WeakReference<Launcher>(launcher);
mShortcuts = shortcuts;
mDrawerAdapter = drawerAdapter;
// Sort widgets so active workspace is bound first
final int currentScreen = launcher.mWorkspace.getCurrentScreen();
final int size = appWidgets.size();
mAppWidgets = new LinkedList<LauncherAppWidgetInfo>();
for (int i = 0; i < size; i++) {
LauncherAppWidgetInfo appWidgetInfo = appWidgets.get(i);
if (appWidgetInfo.screen == currentScreen) {
mAppWidgets.addFirst(appWidgetInfo);
} else {
mAppWidgets.addLast(appWidgetInfo);
}
}
}
public void startBindingItems() {
obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget();
}
public void startBindingDrawer() {
obtainMessage(MESSAGE_BIND_DRAWER).sendToTarget();
}
public void startBindingAppWidgetsWhenIdle() {
// Ask for notification when message queue becomes idle
final MessageQueue messageQueue = Looper.myQueue();
messageQueue.addIdleHandler(this);
}
public boolean queueIdle() {
// Queue is idle, so start binding items
startBindingAppWidgets();
return false;
}
public void startBindingAppWidgets() {
obtainMessage(MESSAGE_BIND_APPWIDGETS).sendToTarget();
}
@Override
public void handleMessage(Message msg) {
Launcher launcher = mLauncher.get();
if (launcher == null || mTerminate) {
return;
}
switch (msg.what) {
case MESSAGE_BIND_ITEMS: {
launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2);
break;
}
case MESSAGE_BIND_DRAWER: {
launcher.bindDrawer(this, mDrawerAdapter);
break;
}
case MESSAGE_BIND_APPWIDGETS: {
launcher.bindAppWidgets(this, mAppWidgets);
break;
}
}
}
}
private class GesturesProcessor implements GestureOverlayView.OnGestureListener,
GestureOverlayView.OnGesturePerformedListener {
private final GestureMatcher mMatcher = new GestureMatcher();
GesturesProcessor() {
// TODO: Maybe the load should happen on a background thread?
sLibrary.load();
}
public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {
//noinspection PointlessBooleanExpression,ConstantConditions
if (!CONFIG_GESTURES_IMMEDIATE_MODE) {
overlay.removeCallbacks(mMatcher);
resetGesturesNextPrompt();
}
mGesturesAdd.setAlpha(128);
mGesturesAdd.setEnabled(false);
}
public void onGesture(GestureOverlayView overlay, MotionEvent event) {
}
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
}
public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
if (CONFIG_GESTURES_IMMEDIATE_MODE) {
mMatcher.gesture = overlay.getGesture();
if (mMatcher.gesture.getLength() < GesturesConstants.LENGTH_THRESHOLD) {
overlay.clear(false);
} else {
mMatcher.run();
}
} else {
overlay.removeCallbacks(mMatcher);
mMatcher.gesture = overlay.getGesture();
if (mMatcher.gesture.getLength() < GesturesConstants.LENGTH_THRESHOLD) {
overlay.clear(false);
} else {
overlay.postDelayed(mMatcher, GesturesConstants.MATCH_DELAY);
}
}
}
private void matchGesture(Gesture gesture) {
mGesturesAdd.setAlpha(255);
mGesturesAdd.setEnabled(true);
if (gesture != null) {
final ArrayList<Prediction> predictions = sLibrary.recognize(gesture);
if (DEBUG_GESTURES) {
for (Prediction p : predictions) {
d(LOG_TAG, String.format("name=%s, score=%f", p.name, p.score));
}
}
boolean match = false;
if (predictions.size() > 0) {
final Prediction prediction = predictions.get(0);
if (prediction.score > GesturesConstants.PREDICTION_THRESHOLD) {
match = true;
ApplicationInfo info = sModel.queryGesture(Launcher.this, prediction.name);
if (info != null) {
updatePrompt(info);
}
}
}
if (!match){
setGesturesNextPrompt(null, getString(R.string.gestures_unknown));
}
}
}
private void updatePrompt(ApplicationInfo info) {
if (mGesturesAction.intent != null &&
info.intent.toURI().equals(mGesturesAction.intent.toURI()) &&
info.title.equals(((TextView) mGesturesPrompt.getCurrentView()).getText())) {
return;
}
setGesturesNextPrompt(info.icon, info.title);
mGesturesAction.intent = info.intent;
}
public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {
//noinspection PointlessBooleanExpression,ConstantConditions
if (!CONFIG_GESTURES_IMMEDIATE_MODE) {
overlay.removeCallbacks(mMatcher);
}
}
void addGesture(String name, Gesture gesture) {
sLibrary.addGesture(name, gesture);
// TODO: On a background thread?
sLibrary.save();
}
void update(ApplicationInfo info, Gesture gesture) {
mGesturesOverlay.setGesture(gesture);
updatePrompt(info);
}
class GestureMatcher implements Runnable {
Gesture gesture;
public void run() {
if (gesture != null) {
matchGesture(gesture);
}
}
}
}
private class GesturesAction implements View.OnClickListener {
Intent intent;
public void onClick(View v) {
if (intent != null) {
startActivitySafely(intent);
}
}
}
}