summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:44:05 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:44:05 -0800
commit15a8880cb1b9010ce4503c10c1666568d49415b1 (patch)
tree29fa05cbaa19101da95b010de82290cebfb6d092 /src
parentbb96f41aa99687260370cf8cdee7683653e538a7 (diff)
downloadandroid_packages_apps_Trebuchet-15a8880cb1b9010ce4503c10c1666568d49415b1.tar.gz
android_packages_apps_Trebuchet-15a8880cb1b9010ce4503c10c1666568d49415b1.tar.bz2
android_packages_apps_Trebuchet-15a8880cb1b9010ce4503c10c1666568d49415b1.zip
auto import from //branches/cupcake/...@130745
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher/AddAdapter.java493
-rw-r--r--src/com/android/launcher/BubbleTextView.java19
-rw-r--r--src/com/android/launcher/CellLayout.java155
-rw-r--r--src/com/android/launcher/DeleteZone.java6
-rw-r--r--src/com/android/launcher/DragLayer.java45
-rw-r--r--src/com/android/launcher/ItemInfo.java6
-rw-r--r--src/com/android/launcher/Launcher.java558
-rw-r--r--src/com/android/launcher/LauncherGadgetHost.java131
-rw-r--r--src/com/android/launcher/LauncherGadgetInfo.java53
-rw-r--r--src/com/android/launcher/LauncherModel.java76
-rw-r--r--src/com/android/launcher/LauncherProvider.java61
-rw-r--r--src/com/android/launcher/LauncherSettings.java12
-rw-r--r--src/com/android/launcher/PhotoFrame.java42
-rw-r--r--src/com/android/launcher/Search.java74
-rw-r--r--src/com/android/launcher/WallpaperChooser.java9
-rw-r--r--src/com/android/launcher/Workspace.java12
16 files changed, 952 insertions, 800 deletions
diff --git a/src/com/android/launcher/AddAdapter.java b/src/com/android/launcher/AddAdapter.java
index 8eebe39d9..14107084e 100644
--- a/src/com/android/launcher/AddAdapter.java
+++ b/src/com/android/launcher/AddAdapter.java
@@ -16,456 +16,111 @@
package com.android.launcher;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.BaseAdapter;
import android.widget.TextView;
-import android.widget.BaseExpandableListAdapter;
-import android.graphics.drawable.Drawable;
-import android.provider.LiveFolders;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
/**
- * Shows a list of all the items that can be added to the workspace.
+ * Adapter showing the types of items that can be added to a {@link Workspace}.
*/
-public final class AddAdapter extends BaseExpandableListAdapter {
- private static final int GROUP_APPLICATIONS = 0;
- private static final int GROUP_SHORTCUTS = 1;
- private static final int GROUP_WIDGETS = 2;
- private static final int GROUP_LIVE_FOLDERS = 3;
- private static final int GROUP_WALLPAPERS = 4;
-
- private final Intent mCreateShortcutIntent;
- private final Intent mCreateLiveFolderIntent;
- private Intent mSetWallpaperIntent;
+public class AddAdapter extends BaseAdapter {
+
+ private final Launcher mLauncher;
private final LayoutInflater mInflater;
- private Launcher mLauncher;
- private Group[] mGroups;
-
- /**
- * Abstract class representing one thing that can be added
- */
- public abstract class AddAction implements Runnable {
- protected final Context mContext;
-
- AddAction(Context context) {
- mContext = context;
- }
-
- Drawable getIcon(int resource) {
- return mContext.getResources().getDrawable(resource);
- }
-
- public abstract void bindView(View v);
- }
-
- /**
- * Class representing an action that will create set the wallpaper.
- */
- public class SetWallpaperAction extends CreateShortcutAction {
- SetWallpaperAction(Context context, ResolveInfo info) {
- super(context, info);
- }
-
- public void run() {
- Intent intent = new Intent(mSetWallpaperIntent);
- ActivityInfo activityInfo = mInfo.activityInfo;
- intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
- activityInfo.name));
- mLauncher.startActivity(intent);
- }
- }
+
+ private final ArrayList<ListItem> mItems = new ArrayList<ListItem>();
+
+ public static final int ITEM_APPLICATION = 0;
+ public static final int ITEM_SHORTCUT = 1;
+ public static final int ITEM_SEARCH = 2;
+ public static final int ITEM_GADGET = 3;
+ public static final int ITEM_LIVE_FOLDER = 4;
+ public static final int ITEM_FOLDER = 5;
+ public static final int ITEM_WALLPAPER = 6;
/**
- * Class representing an action that will create a specific type
- * of shortcut
+ * Specific item in our list.
*/
- public class CreateShortcutAction extends AddAction {
+ public class ListItem {
+ public final CharSequence text;
+ public final Drawable image;
+ public final int actionTag;
- ResolveInfo mInfo;
- private CharSequence mLabel;
- private Drawable mIcon;
-
- CreateShortcutAction(Context context, ResolveInfo info) {
- super(context);
- mInfo = info;
- }
-
- @Override
- public void bindView(View view) {
- ResolveInfo info = mInfo;
- TextView text = (TextView) view;
-
- PackageManager pm = mLauncher.getPackageManager();
-
- if (mLabel == null) {
- mLabel = info.loadLabel(pm);
- if (mLabel == null) {
- mLabel = info.activityInfo.name;
- }
+ public ListItem(Resources res, int textResourceId, int imageResourceId, int actionTag) {
+ text = res.getString(textResourceId);
+ if (imageResourceId != -1) {
+ image = res.getDrawable(imageResourceId);
+ } else {
+ image = null;
}
-
- if (mIcon == null) {
- mIcon = Utilities.createIconThumbnail(info.loadIcon(pm), mContext);
- }
-
- text.setText(mLabel);
- text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
- }
-
- public void run() {
- Intent intent = new Intent(mCreateShortcutIntent);
- ActivityInfo activityInfo = mInfo.activityInfo;
- intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
- activityInfo.name));
- mLauncher.addShortcut(intent);
- }
- }
-
- /**
- * Class representing an action that will create a specific type
- * of live folder
- */
- public class CreateLiveFolderAction extends CreateShortcutAction {
- CreateLiveFolderAction(Context context, ResolveInfo info) {
- super(context, info);
- }
-
- @Override
- public void run() {
- Intent intent = new Intent(mCreateLiveFolderIntent);
- ActivityInfo activityInfo = mInfo.activityInfo;
- intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
- activityInfo.name));
- mLauncher.addLiveFolder(intent);
- }
- }
-
- /**
- * Class representing an action that will add a folder
- */
- public class CreateFolderAction extends AddAction {
- private Drawable mIcon;
-
- CreateFolderAction(Context context) {
- super(context);
- }
-
- @Override
- public void bindView(View view) {
- TextView text = (TextView) view;
- text.setText(R.string.add_folder);
- if (mIcon == null) mIcon = getIcon(R.drawable.ic_launcher_folder);
- text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
- }
-
- public void run() {
- mLauncher.addFolder();
- }
- }
-
- /**
- * Class representing an action that will add a folder
- */
- public class CreateClockAction extends AddAction {
-
- CreateClockAction(Context context) {
- super(context);
- }
-
- @Override
- public void bindView(View view) {
- TextView text = (TextView) view;
- text.setText(R.string.add_clock);
- Drawable icon = getIcon(R.drawable.ic_launcher_alarmclock);
- text.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
- }
-
- public void run() {
- mLauncher.addClock();
- }
- }
-
- /**
- * Class representing an action that will add a PhotoFrame
- */
- public class CreatePhotoFrameAction extends AddAction {
- private Drawable mIcon;
-
- CreatePhotoFrameAction(Context context) {
- super(context);
- }
-
- @Override
- public void bindView(View view) {
- TextView text = (TextView) view;
- text.setText(R.string.add_photo_frame);
- if (mIcon == null) mIcon = getIcon(R.drawable.ic_launcher_gallery);
- text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
- }
-
- public void run() {
- mLauncher.getPhotoForPhotoFrame();
- }
- }
-
-
- /**
- * Class representing an action that will add a Search widget
- */
- public class CreateSearchAction extends AddAction {
- private Drawable mIcon;
-
- CreateSearchAction(Context context) {
- super(context);
- }
-
- @Override
- public void bindView(View view) {
- TextView text = (TextView) view;
- text.setText(R.string.add_search);
- if (mIcon == null) mIcon = getIcon(R.drawable.ic_search_gadget);
- text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
- }
-
- public void run() {
- mLauncher.addSearch();
+ this.actionTag = actionTag;
}
}
- private class Group {
- private String mName;
- private ArrayList<AddAction> mList;
-
- Group(String name) {
- mName = name;
- mList = new ArrayList<AddAction>();
- }
-
- void add(AddAction action) {
- mList.add(action);
- }
-
- int size() {
- return mList.size();
- }
-
- String getName() {
- return mName;
- }
-
- void run(int position) {
- mList.get(position).run();
- }
-
- void bindView(int childPosition, View view) {
- mList.get(childPosition).bindView(view);
- }
-
- public Object get(int childPosition) {
- return mList.get(childPosition);
- }
- }
-
- private class ApplicationsGroup extends Group {
- private final Launcher mLauncher;
- private final ArrayList<ApplicationInfo> mApplications;
-
- ApplicationsGroup(Launcher launcher, String name) {
- super(name);
- mLauncher = launcher;
- mApplications = Launcher.getModel().getApplications();
- }
-
- @Override
- int size() {
- return mApplications == null ? 0 : mApplications.size();
- }
-
- @Override
- void add(AddAction action) {
- }
-
- @Override
- void run(int position) {
- final ApplicationInfo info = mApplications.get(position);
- mLauncher.addApplicationShortcut(info);
- }
-
- @Override
- void bindView(int childPosition, View view) {
- TextView text = (TextView) view.findViewById(R.id.title);
-
- final ApplicationInfo info = mApplications.get(childPosition);
- text.setText(info.title);
- if (!info.filtered) {
- info.icon = Utilities.createIconThumbnail(info.icon, mLauncher);
- info.filtered = true;
- }
- text.setCompoundDrawablesWithIntrinsicBounds(info.icon, null, null, null);
- }
-
- @Override
- public Object get(int childPosition) {
- return mApplications.get(childPosition);
- }
- }
-
- public AddAdapter(Launcher launcher, boolean forFolder) {
- mCreateShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
- mCreateShortcutIntent.setComponent(null);
-
- mCreateLiveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
- mCreateLiveFolderIntent.setComponent(null);
-
- mSetWallpaperIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
- mSetWallpaperIntent.setComponent(null);
-
+ public AddAdapter(Launcher launcher) {
+ super();
+
mLauncher = launcher;
- mInflater = (LayoutInflater) launcher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- mGroups = new Group[forFolder ? 2 : 5];
- final Group[] groups = mGroups;
- groups[GROUP_APPLICATIONS] = new ApplicationsGroup(mLauncher,
- mLauncher.getString(R.string.group_applications));
- groups[GROUP_SHORTCUTS] = new Group(mLauncher.getString(R.string.group_shortcuts));
- groups[GROUP_LIVE_FOLDERS] = new Group(mLauncher.getString(R.string.group_live_folders));
-
- if (!forFolder) {
- groups[GROUP_WALLPAPERS] = new Group(mLauncher.getString(R.string.group_wallpapers));
- groups[GROUP_SHORTCUTS].add(new CreateFolderAction(launcher));
- groups[GROUP_WIDGETS] = new Group(mLauncher.getString(R.string.group_widgets));
-
- final Group widgets = groups[GROUP_WIDGETS];
- widgets.add(new CreateClockAction(launcher));
- widgets.add(new CreatePhotoFrameAction(launcher));
- widgets.add(new CreateSearchAction(launcher));
- }
+ mInflater = (LayoutInflater) mLauncher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- PackageManager packageManager = launcher.getPackageManager();
-
- List<ResolveInfo> list = findTargetsForIntent(mCreateShortcutIntent, packageManager);
- if (list != null && list.size() > 0) {
- int count = list.size();
- final Group shortcuts = groups[GROUP_SHORTCUTS];
- for (int i = 0; i < count; i++) {
- ResolveInfo resolveInfo = list.get(i);
- shortcuts.add(new CreateShortcutAction(launcher, resolveInfo));
- }
- }
-
- list = findTargetsForIntent(mCreateLiveFolderIntent, packageManager);
- if (list != null && list.size() > 0) {
- int count = list.size();
- final Group shortcuts = groups[GROUP_LIVE_FOLDERS];
- for (int i = 0; i < count; i++) {
- ResolveInfo resolveInfo = list.get(i);
- shortcuts.add(new CreateLiveFolderAction(launcher, resolveInfo));
- }
- }
-
- list = findTargetsForIntent(mSetWallpaperIntent, packageManager);
- if (list != null && list.size() > 0) {
- int count = list.size();
- final Group shortcuts = groups[GROUP_WALLPAPERS];
- for (int i = 0; i < count; i++) {
- ResolveInfo resolveInfo = list.get(i);
- shortcuts.add(new SetWallpaperAction(launcher, resolveInfo));
- }
- }
- }
-
- private List<ResolveInfo> findTargetsForIntent(Intent intent, PackageManager packageManager) {
- List<ResolveInfo> list = packageManager.queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
- if (list != null) {
- int count = list.size();
- if (count > 1) {
- // Only display the first matches that are either of equal
- // priority or have asked to be default options.
- ResolveInfo firstInfo = list.get(0);
- for (int i=1; i<count; i++) {
- ResolveInfo resolveInfo = list.get(i);
- if (firstInfo.priority != resolveInfo.priority ||
- firstInfo.isDefault != resolveInfo.isDefault) {
- while (i < count) {
- list.remove(i);
- count--;
- }
- }
- }
- Collections.sort(list, new ResolveInfo.DisplayNameComparator(packageManager));
- }
- }
- return list;
- }
-
- public int getGroupCount() {
- return mGroups.length;
- }
-
- public int getChildrenCount(int groupPosition) {
- return mGroups[groupPosition].size();
- }
-
- public Object getGroup(int groupPosition) {
- return mGroups[groupPosition].getName();
- }
-
- public Object getChild(int groupPosition, int childPosition) {
- return mGroups[groupPosition].get(childPosition);
- }
-
- public long getGroupId(int groupPosition) {
- return groupPosition;
- }
-
- public long getChildId(int groupPosition, int childPosition) {
- return (groupPosition << 16) | childPosition;
- }
+ // Create default actions
+ Resources res = launcher.getResources();
+
+ mItems.add(new ListItem(res, R.string.group_applications,
+ R.drawable.ic_launcher_application, ITEM_APPLICATION));
+
+ mItems.add(new ListItem(res, R.string.group_shortcuts,
+ R.drawable.ic_launcher_empty, ITEM_SHORTCUT));
+
+ mItems.add(new ListItem(res, R.string.group_search,
+ R.drawable.ic_search_gadget, ITEM_SEARCH));
+
+ mItems.add(new ListItem(res, R.string.group_gadgets,
+ R.drawable.ic_launcher_gadget, ITEM_GADGET));
+
+ mItems.add(new ListItem(res, R.string.group_live_folders,
+ R.drawable.ic_launcher_empty, ITEM_LIVE_FOLDER));
+
+ mItems.add(new ListItem(res, R.string.group_folder,
+ R.drawable.ic_launcher_folder, ITEM_FOLDER));
+
+ mItems.add(new ListItem(res, R.string.group_wallpapers,
+ R.drawable.ic_launcher_gallery, ITEM_WALLPAPER));
- public boolean hasStableIds() {
- return true;
}
- public View getGroupView(int groupPosition, boolean isExpanded,
- View convertView, ViewGroup parent) {
- View view;
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ListItem item = (ListItem) getItem(position);
+
if (convertView == null) {
- view = mInflater.inflate(R.layout.create_shortcut_group_item, parent, false);
- } else {
- view = convertView;
+ convertView = mInflater.inflate(R.layout.add_list_item, parent, false);
}
- ((TextView) view).setText(mGroups[groupPosition].getName());
- return view;
+
+ TextView textView = (TextView) convertView;
+ textView.setTag(item);
+ textView.setText(item.text);
+ textView.setCompoundDrawablesWithIntrinsicBounds(item.image, null, null, null);
+
+ return convertView;
}
- public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
- View convertView, ViewGroup parent) {
- View view;
- if (convertView == null) {
- view = mInflater.inflate(R.layout.create_shortcut_list_item, parent, false);
- } else {
- view = convertView;
- }
- mGroups[groupPosition].bindView(childPosition, view);
- return view;
+ public int getCount() {
+ return mItems.size();
}
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
+ public Object getItem(int position) {
+ return mItems.get(position);
}
- void performAction(int groupPosition, int childPosition) {
- mGroups[groupPosition].run(childPosition);
+ public long getItemId(int position) {
+ return position;
}
+
}
diff --git a/src/com/android/launcher/BubbleTextView.java b/src/com/android/launcher/BubbleTextView.java
index f2c31e93f..37824545e 100644
--- a/src/com/android/launcher/BubbleTextView.java
+++ b/src/com/android/launcher/BubbleTextView.java
@@ -40,6 +40,9 @@ public class BubbleTextView extends TextView {
private boolean mBackgroundSizeChanged;
private Drawable mBackground;
+ private float mCornerRadius;
+ private float mPaddingH;
+ private float mPaddingV;
public BubbleTextView(Context context) {
super(context);
@@ -64,6 +67,12 @@ public class BubbleTextView extends TextView {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(getContext().getResources().getColor(R.color.bubble_dark_background));
+
+ final float scale = getContext().getResources().getDisplayMetrics().density;
+ mCornerRadius = CORNER_RADIUS * scale;
+ mPaddingH = PADDING_H * scale;
+ //noinspection PointlessArithmeticExpression
+ mPaddingV = PADDING_V * scale;
}
@Override
@@ -114,11 +123,11 @@ public class BubbleTextView extends TextView {
final int left = getCompoundPaddingLeft();
final int top = getExtendedPaddingTop();
- rect.set(left + layout.getLineLeft(0) - PADDING_H,
- top + layout.getLineTop(0) - PADDING_V,
- Math.min(left + layout.getLineRight(0) + PADDING_H, mScrollX + mRight - mLeft),
- top + layout.getLineBottom(0) + PADDING_V);
- canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, mPaint);
+ rect.set(left + layout.getLineLeft(0) - mPaddingH,
+ top + layout.getLineTop(0) - mPaddingV,
+ Math.min(left + layout.getLineRight(0) + mPaddingH, mScrollX + mRight - mLeft),
+ top + layout.getLineBottom(0) + mPaddingV);
+ canvas.drawRoundRect(rect, mCornerRadius, mCornerRadius, mPaint);
super.draw(canvas);
}
diff --git a/src/com/android/launcher/CellLayout.java b/src/com/android/launcher/CellLayout.java
index 02646bff9..3e5a74ac6 100644
--- a/src/com/android/launcher/CellLayout.java
+++ b/src/com/android/launcher/CellLayout.java
@@ -56,6 +56,8 @@ public class CellLayout extends ViewGroup {
private RectF mDragRect = new RectF();
+ private boolean mDirtyTag;
+
public CellLayout(Context context) {
this(context, null);
}
@@ -157,6 +159,7 @@ public class CellLayout extends ViewGroup {
cellInfo.spanY = lp.cellVSpan;
cellInfo.valid = true;
found = true;
+ mDirtyTag = false;
break;
}
}
@@ -181,10 +184,12 @@ public class CellLayout extends ViewGroup {
cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
- if (cellInfo.valid) {
- findIntersectingVacantCells(cellInfo, cellXY[0], cellXY[1],
- xCount, yCount, occupied);
- }
+ // Instead of finding the interesting vacant cells here, wait until a
+ // caller invokes getTag() to retrieve the result. Finding the vacant
+ // cells is a bit expensive and can generate many new objects, it's
+ // therefore better to defer it until we know we actually need it.
+
+ mDirtyTag = true;
}
setTag(cellInfo);
} else if (action == MotionEvent.ACTION_UP) {
@@ -194,12 +199,31 @@ public class CellLayout extends ViewGroup {
cellInfo.spanX = 0;
cellInfo.spanY = 0;
cellInfo.valid = false;
+ mDirtyTag = false;
setTag(cellInfo);
}
return false;
}
+ @Override
+ public CellInfo getTag() {
+ final CellInfo info = (CellInfo) super.getTag();
+ if (mDirtyTag && info.valid) {
+ final boolean portrait = mPortrait;
+ final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
+ final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
+
+ final boolean[][] occupied = mOccupied;
+ findOccupiedCells(xCount, yCount, occupied);
+
+ findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
+
+ mDirtyTag = false;
+ }
+ return info;
+ }
+
private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y,
int xCount, int yCount, boolean[][] occupied) {
@@ -207,14 +231,15 @@ public class CellLayout extends ViewGroup {
cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
- cellInfo.vacantCells = new ArrayList<CellInfo.VacantCell>();
+ cellInfo.clearVacantCells();
if (occupied[x][y]) {
return;
}
- Rect current = new Rect(x, y, x, y);
- findVacantCell(current, xCount, yCount, occupied, cellInfo);
+ cellInfo.current.set(x, y, x, y);
+
+ findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
}
private static void findVacantCell(Rect current, int xCount, int yCount, boolean[][] occupied,
@@ -256,7 +281,7 @@ public class CellLayout extends ViewGroup {
}
private static void addVacantCell(Rect current, CellInfo cellInfo) {
- CellInfo.VacantCell cell = new CellInfo.VacantCell();
+ CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
cell.cellX = current.left;
cell.cellY = current.top;
cell.spanX = current.right - current.left + 1;
@@ -317,10 +342,9 @@ public class CellLayout extends ViewGroup {
cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
- cellInfo.vacantCells = new ArrayList<CellInfo.VacantCell>();
cellInfo.screen = mCellInfo.screen;
- Rect current = new Rect();
+ Rect current = cellInfo.current;
for (int x = 0; x < xCount; x++) {
for (int y = 0; y < yCount; y++) {
@@ -634,6 +658,26 @@ public class CellLayout extends ViewGroup {
dragRect.set(x, y, x + width, y + height);
}
+
+ /**
+ * Computes the required horizontal and vertical cell spans to always
+ * fit the given rectangle.
+ *
+ * @param width Width in pixels
+ * @param height Height in pixels
+ * @param cellInfo {@link CellInfo} to fill with calculated span parameters
+ */
+ public void rectToCell(int width, int height, CellInfo cellInfo) {
+ // Always assume we're working with the smallest span to make sure we
+ // reserve enough space in both orientations.
+ int actualWidth = mCellWidth + mWidthGap;
+ int actualHeight = mCellHeight + mHeightGap;
+ int smallerSize = Math.min(actualWidth, actualHeight);
+
+ // Always round up to next largest cell
+ cellInfo.spanX = (width + smallerSize) / smallerSize;
+ cellInfo.spanY = (height + smallerSize) / smallerSize;
+ }
/**
* Find the first vacant cell, if there is one.
@@ -811,12 +855,54 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
}
static final class CellInfo implements ContextMenu.ContextMenuInfo {
+ /**
+ * See View.AttachInfo.InvalidateInfo for futher explanations about
+ * the recycling mechanism. In this case, we recycle the vacant cells
+ * instances because up to several hundreds can be instanciated when
+ * the user long presses an empty cell.
+ */
static final class VacantCell {
int cellX;
int cellY;
int spanX;
int spanY;
+ // We can create up to 523 vacant cells on a 4x4 grid, 100 seems
+ // like a reasonable compromise given the size of a VacantCell and
+ // the fact that the user is not likely to touch an empty 4x4 grid
+ // very often
+ private static final int POOL_LIMIT = 100;
+ private static final Object sLock = new Object();
+
+ private static int sAcquiredCount = 0;
+ private static VacantCell sRoot;
+
+ private VacantCell next;
+
+ static VacantCell acquire() {
+ synchronized (sLock) {
+ if (sRoot == null) {
+ return new VacantCell();
+ }
+
+ VacantCell info = sRoot;
+ sRoot = info.next;
+ sAcquiredCount--;
+
+ return info;
+ }
+ }
+
+ void release() {
+ synchronized (sLock) {
+ if (sAcquiredCount < POOL_LIMIT) {
+ sAcquiredCount++;
+ next = sRoot;
+ sRoot = this;
+ }
+ }
+ }
+
@Override
public String toString() {
return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + spanX +
@@ -832,17 +918,27 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
int screen;
boolean valid;
- ArrayList<VacantCell> vacantCells;
+ final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
int maxVacantSpanX;
int maxVacantSpanXSpanY;
int maxVacantSpanY;
int maxVacantSpanYSpanX;
+ final Rect current = new Rect();
+
+ private void clearVacantCells() {
+ final ArrayList<VacantCell> list = vacantCells;
+ final int count = list.size();
+
+ for (int i = 0; i < count; i++) list.get(i).release();
+
+ list.clear();
+ }
void findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
if (cellX < 0 || cellY < 0) {
maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
- vacantCells = new ArrayList<VacantCell>();
+ clearVacantCells();
return;
}
@@ -855,26 +951,40 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
}
+ /**
+ * This method can be called only once! Calling #findVacantCellsFromOccupied will
+ * restore the ability to call this method.
+ *
+ * Finds the upper-left coordinate of the first rectangle in the grid that can
+ * hold a cell of the specified dimensions.
+ *
+ * @param cellXY The array that will contain the position of a vacant cell if such a cell
+ * can be found.
+ * @param spanX The horizontal span of the cell we want to find.
+ * @param spanY The vertical span of the cell we want to find.
+ *
+ * @return True if a vacant cell of the specified dimension was found, false otherwise.
+ */
boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
- if (vacantCells == null) {
- return false;
- }
+ final ArrayList<VacantCell> list = vacantCells;
+ final int count = list.size();
+
+ boolean found = false;
if (this.spanX >= spanX && this.spanY >= spanY) {
cellXY[0] = cellX;
cellXY[1] = cellY;
- return true;
+ found = true;
}
- final ArrayList<VacantCell> list = vacantCells;
- final int count = list.size();
// Look for an exact match first
for (int i = 0; i < count; i++) {
VacantCell cell = list.get(i);
if (cell.spanX == spanX && cell.spanY == spanY) {
cellXY[0] = cell.cellX;
cellXY[1] = cell.cellY;
- return true;
+ found = true;
+ break;
}
}
@@ -884,11 +994,14 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
if (cell.spanX >= spanX && cell.spanY >= spanY) {
cellXY[0] = cell.cellX;
cellXY[1] = cell.cellY;
- return true;
+ found = true;
+ break;
}
}
- return false;
+ clearVacantCells();
+
+ return found;
}
@Override
diff --git a/src/com/android/launcher/DeleteZone.java b/src/com/android/launcher/DeleteZone.java
index 798cf0de8..6f67884db 100644
--- a/src/com/android/launcher/DeleteZone.java
+++ b/src/com/android/launcher/DeleteZone.java
@@ -97,6 +97,12 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
final UserFolderInfo userFolderInfo = (UserFolderInfo)item;
LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo);
model.removeUserFolder(userFolderInfo);
+ } else if (item instanceof LauncherGadgetInfo) {
+ final LauncherGadgetInfo launcherGadgetInfo = (LauncherGadgetInfo)item;
+ final LauncherGadgetHost gadgetHost = mLauncher.getGadgetHost();
+ if (gadgetHost != null) {
+ gadgetHost.deleteGadgetId(launcherGadgetInfo.gadgetId);
+ }
}
LauncherModel.deleteItemFromDatabase(mLauncher, item);
}
diff --git a/src/com/android/launcher/DragLayer.java b/src/com/android/launcher/DragLayer.java
index 56140dd07..aa6615a22 100644
--- a/src/com/android/launcher/DragLayer.java
+++ b/src/com/android/launcher/DragLayer.java
@@ -320,40 +320,33 @@ public class DragLayer extends FrameLayout implements DragController {
break;
case MotionEvent.ACTION_MOVE:
- if (Launcher.sOpenGlEnabled) {
- mLastMotionX = x;
- mLastMotionY = y;
+ final int scrollX = mScrollX;
+ final int scrollY = mScrollY;
- invalidate();
- } else {
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
+ final float touchX = mTouchOffsetX;
+ final float touchY = mTouchOffsetY;
- final float touchX = mTouchOffsetX;
- final float touchY = mTouchOffsetY;
+ final int offsetX = mBitmapOffsetX;
+ final int offsetY = mBitmapOffsetY;
- final int offsetX = mBitmapOffsetX;
- final int offsetY = mBitmapOffsetY;
+ int left = (int) (scrollX + mLastMotionX - touchX - offsetX);
+ int top = (int) (scrollY + mLastMotionY - touchY - offsetY);
- int left = (int) (scrollX + mLastMotionX - touchX - offsetX);
- int top = (int) (scrollY + mLastMotionY - touchY - offsetY);
+ final Bitmap dragBitmap = mDragBitmap;
+ final int width = dragBitmap.getWidth();
+ final int height = dragBitmap.getHeight();
- final Bitmap dragBitmap = mDragBitmap;
- final int width = dragBitmap.getWidth();
- final int height = dragBitmap.getHeight();
+ final Rect rect = mRect;
+ rect.set(left - 1, top - 1, left + width + 1, top + height + 1);
- final Rect rect = mRect;
- rect.set(left - 1, top - 1, left + width + 1, top + height + 1);
-
- mLastMotionX = x;
- mLastMotionY = y;
+ mLastMotionX = x;
+ mLastMotionY = y;
- left = (int) (scrollX + x - touchX - offsetX);
- top = (int) (scrollY + y - touchY - offsetY);
+ left = (int) (scrollX + x - touchX - offsetX);
+ top = (int) (scrollY + y - touchY - offsetY);
- rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
- invalidate(rect);
- }
+ rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
+ invalidate(rect);
final int[] coordinates = mDropCoordinates;
DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
diff --git a/src/com/android/launcher/ItemInfo.java b/src/com/android/launcher/ItemInfo.java
index 61745dda0..8899f44aa 100644
--- a/src/com/android/launcher/ItemInfo.java
+++ b/src/com/android/launcher/ItemInfo.java
@@ -38,10 +38,8 @@ class ItemInfo {
/**
* One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
* {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_USER_FOLDER},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_CLOCK},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_SEARCH} or
- * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_PHOTO_FRAME},
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_USER_FOLDER}, or
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_GADGET}.
*/
int itemType;
diff --git a/src/com/android/launcher/Launcher.java b/src/com/android/launcher/Launcher.java
index 465400b8d..293abe808 100644
--- a/src/com/android/launcher/Launcher.java
+++ b/src/com/android/launcher/Launcher.java
@@ -24,16 +24,21 @@ 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.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.gadget.GadgetInfo;
+import android.gadget.GadgetManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -64,16 +69,15 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.View.OnLongClickListener;
import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
import android.widget.EditText;
-import android.widget.ExpandableListView;
-import android.widget.ImageView;
+import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.GridView;
+import android.widget.SlidingDrawer;
import android.app.IWallpaperService;
-import com.android.internal.widget.SlidingDrawer;
-
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -86,8 +90,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On
private static final boolean PROFILE_STARTUP = false;
private static final boolean DEBUG_USER_INTERFACE = false;
- private static final boolean REMOVE_SHORTCUT_ON_PACKAGE_REMOVE = false;
-
private static final int WALLPAPER_SCREENS_SPAN = 2;
private static final int MENU_GROUP_ADD = 1;
@@ -98,9 +100,12 @@ public final class Launcher extends Activity implements View.OnClickListener, On
private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
private static final int REQUEST_CREATE_SHORTCUT = 1;
- private static final int REQUEST_CHOOSE_PHOTO = 2;
- private static final int REQUEST_UPDATE_PHOTO = 3;
private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
+ private static final int REQUEST_CREATE_GADGET = 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_GADGET = 9;
static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
@@ -148,10 +153,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On
private static Bitmap sWallpaper;
- // Indicates whether the OpenGL pipeline was enabled, either through
- // USE_OPENGL_BY_DEFAULT or the system property launcher.opengl
- static boolean sOpenGlEnabled;
-
private static final Object sLock = new Object();
private static int sScreen = DEFAULT_SCREN;
@@ -164,7 +165,12 @@ public final class Launcher extends Activity implements View.OnClickListener, On
private DragLayer mDragLayer;
private Workspace mWorkspace;
-
+
+ private GadgetManager mGadgetManager;
+ private LauncherGadgetHost mGadgetHost;
+
+ private static final int GADGET_HOST_ID = 1024;
+
private CellLayout.CellInfo mAddItemCellInfo;
private CellLayout.CellInfo mMenuAddInfo;
private final int[] mCellCoordinates = new int[2];
@@ -189,6 +195,11 @@ public final class Launcher extends Activity implements View.OnClickListener, On
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInflater = getLayoutInflater();
+
+ mGadgetManager = GadgetManager.getInstance(this);
+ mGadgetHost = new LauncherGadgetHost(this, GADGET_HOST_ID);
+
+ // TODO: figure out if this is first launch and correctly clear GadgetHost database
if (PROFILE_STARTUP) {
android.os.Debug.startMethodTracing("/sdcard/launcher");
@@ -223,6 +234,18 @@ public final class Launcher extends Activity implements View.OnClickListener, On
Selection.setSelection(mDefaultKeySsb, 0);
}
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mGadgetHost.startListening();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mGadgetHost.stopListening();
+ }
+
private void checkForLocaleChange() {
final SharedPreferences preferences = getSharedPreferences(PREFERENCES, MODE_PRIVATE);
final Configuration configuration = getResources().getConfiguration();
@@ -283,20 +306,42 @@ public final class Launcher extends Activity implements View.OnClickListener, On
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // 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) {
switch (requestCode) {
+ case REQUEST_PICK_APPLICATION:
+ completeAddApplication(this, data, mAddItemCellInfo);
+ break;
+ case REQUEST_PICK_SHORTCUT:
+ addShortcut(data);
+ break;
case REQUEST_CREATE_SHORTCUT:
completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked);
break;
- case REQUEST_CHOOSE_PHOTO:
- completeAddPhotoFrame(data, mAddItemCellInfo);
- break;
- case REQUEST_UPDATE_PHOTO:
- completeUpdatePhotoFrame(data, mAddItemCellInfo);
+ case REQUEST_PICK_LIVE_FOLDER:
+ addLiveFolder(data);
break;
case REQUEST_CREATE_LIVE_FOLDER:
completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked);
break;
+ case REQUEST_PICK_GADGET:
+ addGadget(data);
+ break;
+ case REQUEST_CREATE_GADGET:
+ completeAddGadget(data, mAddItemCellInfo, !mDesktopLocked);
+ break;
+ }
+ } else if (requestCode == REQUEST_PICK_GADGET &&
+ resultCode == RESULT_CANCELED && data != null) {
+ // Clean up the gadgetId if we canceled
+ int gadgetId = data.getIntExtra(GadgetManager.EXTRA_GADGET_ID, -1);
+ if (gadgetId != -1) {
+ mGadgetHost.deleteGadgetId(gadgetId);
}
}
mWaitingForResult = false;
@@ -398,7 +443,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On
mRestoring = true;
}
-
boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
if (renameFolder) {
long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
@@ -439,10 +483,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On
grid.setTextFilterEnabled(true);
grid.setDragger(dragLayer);
grid.setLauncher(this);
- if (sOpenGlEnabled) {
- grid.setScrollingCacheEnabled(false);
- grid.setFadingEdgeLength(0);
- }
workspace.setOnLongClickListener(this);
workspace.setDragger(dragLayer);
@@ -456,18 +496,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On
dragLayer.setIgnoredDropTarget(grid);
dragLayer.setDragScoller(workspace);
dragLayer.setDragListener(deleteZone);
-
- 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();
- }
- });
- }
}
/**
@@ -507,11 +535,42 @@ public final class Launcher extends Activity implements View.OnClickListener, On
return favorite;
}
- void addApplicationShortcut(ApplicationInfo info) {
- mAddItemCellInfo.screen = mWorkspace.getCurrentScreen();
- mWorkspace.addApplicationShortcut(info, mAddItemCellInfo);
- }
+ /**
+ * 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) {
+ cellInfo.screen = mWorkspace.getCurrentScreen();
+ // Find details for this application
+ ComponentName component = data.getComponent();
+ PackageManager packageManager = context.getPackageManager();
+ ActivityInfo activityInfo = null;
+ try {
+ activityInfo = packageManager.getActivityInfo(component, 0 /* no flags */);
+ } catch (NameNotFoundException e) {
+ Log.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;
+
+ mWorkspace.addApplicationShortcut(itemInfo, mAddItemCellInfo);
+ }
+ }
+
/**
* Add a shortcut to the workspace.
*
@@ -535,6 +594,74 @@ public final class Launcher extends Activity implements View.OnClickListener, On
}
}
+
+ /**
+ * Add a gadget to the workspace.
+ *
+ * @param data The intent describing the gadgetId.
+ * @param cellInfo The position on screen where to create the shortcut.
+ * @param insertAtFirst
+ */
+ private void completeAddGadget(Intent data, CellLayout.CellInfo cellInfo,
+ boolean insertAtFirst) {
+
+ Bundle extras = data.getExtras();
+ int gadgetId = extras.getInt(GadgetManager.EXTRA_GADGET_ID, -1);
+
+ Log.d(LOG_TAG, "dumping extras content="+extras.toString());
+
+ GadgetInfo gadgetInfo = mGadgetManager.getGadgetInfo(gadgetId);
+
+ // Calculate the grid spans needed to fit this gadget
+ CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
+ layout.rectToCell(gadgetInfo.minWidth, gadgetInfo.minHeight, cellInfo);
+
+ // Try finding open space on Launcher screen
+ final int[] xy = mCellCoordinates;
+ if (!findSlot(cellInfo, xy, cellInfo.spanX, cellInfo.spanY)) return;
+
+ // Build Launcher-specific Gadget info and save to database
+ LauncherGadgetInfo launcherInfo = new LauncherGadgetInfo(gadgetId);
+ launcherInfo.spanX = cellInfo.spanX;
+ launcherInfo.spanY = cellInfo.spanY;
+
+ LauncherModel.addItemToDatabase(this, launcherInfo,
+ LauncherSettings.Favorites.CONTAINER_DESKTOP,
+ mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
+
+ if (!mRestoring) {
+ sModel.addDesktopItem(launcherInfo);
+
+ // Perform actual inflation because we're live
+ launcherInfo.hostView = mGadgetHost.createView(this, gadgetId, gadgetInfo);
+
+ launcherInfo.hostView.setGadget(gadgetId, gadgetInfo);
+ launcherInfo.hostView.setTag(launcherInfo);
+
+ mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
+ launcherInfo.spanX, launcherInfo.spanY, insertAtFirst);
+ } else if (sModel.isDesktopLoaded()) {
+ sModel.addDesktopItem(launcherInfo);
+ }
+
+ // Request fresh update if we needed to config this gadget to
+ // remove the stale UPDATE from the initial bind
+ if (!extras.containsKey(SKIP_CONFIG) || true) {
+ // TODO: move this down into GadgetManager to prevent abuse? (anyone
+ // could force a specific gadget into the ground through flooding)
+ Log.d(LOG_TAG, "requesting new gadget update because we had a config step");
+ Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
+ intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, new int[] { gadgetId });
+ intent.setComponent(gadgetInfo.provider);
+ sendBroadcast(intent);
+ }
+
+ }
+
+ public LauncherGadgetHost getGadgetHost() {
+ return mGadgetHost;
+ }
+
static ApplicationInfo addShortcut(Context context, Intent data,
CellLayout.CellInfo cellInfo, boolean notify) {
@@ -584,107 +711,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On
return info;
}
- /**
- * Add a PhotFrame to the workspace.
- *
- * @param data The intent describing the photo.
- * @param cellInfo The position on screen where to create the shortcut.
- */
- private void completeAddPhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
- final Bundle extras = data.getExtras();
- if (extras != null) {
- Bitmap photo = extras.getParcelable("data");
-
- Widget info = Widget.makePhotoFrame();
- info.photo = photo;
-
- final int[] xy = mCellCoordinates;
- if (!findSlot(cellInfo, xy, info.spanX, info.spanY)) return;
-
- LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
- mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
-
- if (!mRestoring) {
- sModel.addDesktopItem(info);
-
- final PhotoFrame view = (PhotoFrame) mInflater.inflate(info.layoutResource, null);
- view.setImageBitmap(photo);
- view.setTag(info);
-
- mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, info.spanY);
- } else if (sModel.isDesktopLoaded()) {
- sModel.addDesktopItem(info);
- }
- }
- }
-
- /**
- * Updates a workspace PhotoFrame.
- *
- * @param data The intent describing the photo.
- * @param cellInfo The position on screen of the PhotoFrame to update.
- */
- private void completeUpdatePhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
- final Bundle extras = data.getExtras();
- if (extras != null) {
- Widget info;
- Bitmap photo = extras.getParcelable("data");
-
- if (!mRestoring) {
- final CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
- final PhotoFrame view = (PhotoFrame) layout.findCell(cellInfo.cellX, cellInfo.cellY,
- cellInfo.spanX, cellInfo.spanY, null);
- view.setImageBitmap(photo);
- info = (Widget) view.getTag();
- } else {
- info = LauncherModel.getPhotoFrameInfo(this, cellInfo.screen,
- cellInfo.cellX, cellInfo.cellY);
- }
-
- info.photo = photo;
- LauncherModel.updateItemInDatabase(this, info);
- }
- }
-
- /**
- * Starts a new Intent to let the user update the PhotoFrame defined by the
- * specified Widget.
- *
- * @param widget The Widget info defining which PhotoFrame to update.
- */
- void updatePhotoFrame(Widget widget) {
- CellLayout.CellInfo info = new CellLayout.CellInfo();
- info.screen = widget.screen;
- info.cellX = widget.cellX;
- info.cellY = widget.cellY;
- info.spanX = widget.spanX;
- info.spanY = widget.spanY;
- mAddItemCellInfo = info;
-
- startActivityForResult(createPhotoPickIntent(), Launcher.REQUEST_UPDATE_PHOTO);
- }
-
- /**
- * Creates an Intent used to let the user pick a photo for a PhotoFrame.
- *
- * @return The Intent to pick a photo suited for a PhotoFrame.
- */
- private static Intent createPhotoPickIntent() {
- // TODO: Move this method to PhotoFrame?
- // TODO: get these values from constants somewhere
- // TODO: Adjust the PhotoFrame's image size to avoid on the fly scaling
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
- intent.setType("image/*");
- intent.putExtra("crop", "true");
- intent.putExtra("aspectX", 1);
- intent.putExtra("aspectY", 1);
- intent.putExtra("outputX", 192);
- intent.putExtra("outputY", 192);
- intent.putExtra("noFaceDetection", true);
- intent.putExtra("return-data", true);
- return intent;
- }
-
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -871,10 +897,51 @@ public final class Launcher extends Activity implements View.OnClickListener, On
private void removeShortcutsForPackage(String packageName) {
if (packageName != null && packageName.length() > 0) {
- android.util.Log.d(LOG_TAG, packageName);
mWorkspace.removeShortcutsForPackage(packageName);
}
}
+
+ static final String SKIP_CONFIG = "skip_config";
+
+ void addGadget(Intent data) {
+ int gadgetId = data.getIntExtra(GadgetManager.EXTRA_GADGET_ID, -1);
+ GadgetInfo gadget = mGadgetManager.getGadgetInfo(gadgetId);
+
+ if (gadget.configure != null) {
+ // Launch over to configure gadget, if needed
+ Intent intent = new Intent(GadgetManager.GADGET_CONFIGURE_ACTION);
+ intent.setComponent(gadget.configure);
+ intent.putExtra(GadgetManager.EXTRA_GADGET_ID, gadgetId);
+
+ startActivityForResult(intent, REQUEST_CREATE_GADGET);
+ } else {
+ // Otherwise just add it
+ Log.d(LOG_TAG, "dumping extras content="+data.getExtras().toString());
+ data.putExtra(SKIP_CONFIG, true);
+ Log.d(LOG_TAG, "dumping extras content="+data.getExtras().toString());
+ onActivityResult(REQUEST_CREATE_GADGET, 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);
+
+ mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
+ }
void addShortcut(Intent intent) {
startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
@@ -964,39 +1031,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On
return info;
}
- void getPhotoForPhotoFrame() {
- startActivityForResult(createPhotoPickIntent(), REQUEST_CHOOSE_PHOTO);
- }
-
- void addClock() {
- final Widget info = Widget.makeClock();
- addWidget(info);
- }
-
- void addSearch() {
- final Widget info = Widget.makeSearch();
- addWidget(info);
- }
-
- private void addWidget(final Widget info) {
- 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);
-
- mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
- }
-
private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
boolean[] occupied = mSavedState != null ?
@@ -1136,6 +1170,18 @@ public final class Launcher extends Activity implements View.OnClickListener, On
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();
+ }
+ });
+ }
count = shortcuts.size();
@@ -1176,11 +1222,41 @@ public final class Launcher extends Activity implements View.OnClickListener, On
workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
!desktopLocked);
break;
- default:
+ case LauncherSettings.Favorites.ITEM_TYPE_GADGET:
+ final LauncherGadgetInfo launcherInfo = (LauncherGadgetInfo) item;
+
+ final int gadgetId = launcherInfo.gadgetId;
+ GadgetInfo gadgetInfo = mGadgetManager.getGadgetInfo(gadgetId);
+ launcherInfo.hostView = mGadgetHost.createView(this, gadgetId, gadgetInfo);
+
+ Log.d(LOG_TAG, "about to setGadget during desktop bind");
+ launcherInfo.hostView.setGadget(gadgetId, gadgetInfo);
+ launcherInfo.hostView.setTag(launcherInfo);
+
+ workspace.addInScreen(launcherInfo.hostView, item.screen, item.cellX,
+ item.cellY, item.spanX, item.spanY, !desktopLocked);
+
+ // Now that we've bound the item, request an update for it
+ if (gadgetInfo != null) {
+ if (gadgetInfo.provider != null) {
+ Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
+ intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, new int[] { gadgetId });
+ intent.setComponent(gadgetInfo.provider);
+ sendBroadcast(intent);
+ }
+ }
+
+ 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);
+
final Widget widget = (Widget) item;
- final View view = createWidget(mInflater, widget);
view.setTag(widget);
+
workspace.addWidget(view, widget, !desktopLocked);
+ break;
}
}
@@ -1226,17 +1302,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On
mDrawer.unlock();
}
- private View createWidget(LayoutInflater inflater, Widget widget) {
- final Workspace workspace = mWorkspace;
- final int screen = workspace.getCurrentScreen();
- View v = inflater.inflate(widget.layoutResource,
- (ViewGroup) workspace.getChildAt(screen), false);
- if (widget.itemType == LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
- ((ImageView)v).setImageBitmap(widget.photo);
- }
- return v;
- }
-
DragController getDragController() {
return mDragLayer;
}
@@ -1263,6 +1328,11 @@ public final class Launcher extends Activity implements View.OnClickListener, On
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();
+ Log.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);
}
}
@@ -1508,24 +1578,24 @@ public final class Launcher extends Activity implements View.OnClickListener, On
* Displays the shortcut creation dialog and launches, if necessary, the
* appropriate activity.
*/
- private class CreateShortcut implements ExpandableListView.OnChildClickListener,
- DialogInterface.OnCancelListener, ExpandableListView.OnGroupExpandListener {
+ private class CreateShortcut implements AdapterView.OnItemClickListener,
+ DialogInterface.OnCancelListener {
private AddAdapter mAdapter;
- private ExpandableListView mList;
-
+ private ListView mList;
+
Dialog createDialog() {
mWaitingForResult = true;
- mAdapter = new AddAdapter(Launcher.this, false);
+
+ mAdapter = new AddAdapter(Launcher.this);
final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
builder.setTitle(getString(R.string.menu_item_add_item));
builder.setIcon(0);
- mList = (ExpandableListView)
+ mList = (ListView)
View.inflate(Launcher.this, R.layout.create_shortcut_list, null);
mList.setAdapter(mAdapter);
- mList.setOnChildClickListener(this);
- mList.setOnGroupExpandListener(this);
+ mList.setOnItemClickListener(this);
builder.setView(mList);
builder.setInverseBackgroundForced(true);
@@ -1539,13 +1609,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On
return dialog;
}
- public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
- int childPosition, long id) {
- mAdapter.performAction(groupPosition, childPosition);
- cleanup();
- return true;
- }
-
public void onCancel(DialogInterface dialog) {
mWaitingForResult = false;
cleanup();
@@ -1556,10 +1619,76 @@ public final class Launcher extends Activity implements View.OnClickListener, On
dismissDialog(DIALOG_CREATE_SHORTCUT);
}
- public void onGroupExpand(int groupPosition) {
- long packged = ExpandableListView.getPackedPositionForGroup(groupPosition);
- int position = mList.getFlatListPosition(packged);
- mList.setSelectionFromTop(position, 0);
+ public void onItemClick(AdapterView parent, View view, int position, long id) {
+ // handle which item was clicked based on position
+ // this will launch off pick intent
+
+ Object tag = view.getTag();
+ if (tag instanceof AddAdapter.ListItem) {
+ AddAdapter.ListItem item = (AddAdapter.ListItem) tag;
+ cleanup();
+ switch (item.actionTag) {
+ case AddAdapter.ITEM_APPLICATION: {
+ 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, REQUEST_PICK_APPLICATION);
+ break;
+ }
+
+ case AddAdapter.ITEM_SHORTCUT: {
+ Intent shortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+
+ Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+ pickIntent.putExtra(Intent.EXTRA_INTENT, shortcutIntent);
+ pickIntent.putExtra(Intent.EXTRA_TITLE,
+ getText(R.string.title_select_shortcut));
+ startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
+ break;
+ }
+
+ case AddAdapter.ITEM_SEARCH: {
+ addSearch();
+ break;
+ }
+
+ case AddAdapter.ITEM_GADGET: {
+ int gadgetId = Launcher.this.mGadgetHost.allocateGadgetId();
+
+ Intent pickIntent = new Intent(GadgetManager.GADGET_PICK_ACTION);
+ pickIntent.putExtra(GadgetManager.EXTRA_HOST_ID, GADGET_HOST_ID);
+ pickIntent.putExtra(GadgetManager.EXTRA_GADGET_ID, gadgetId);
+ startActivityForResult(pickIntent, REQUEST_PICK_GADGET);
+ break;
+ }
+
+ case AddAdapter.ITEM_LIVE_FOLDER: {
+ Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
+
+ Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+ pickIntent.putExtra(Intent.EXTRA_INTENT, liveFolderIntent);
+ pickIntent.putExtra(Intent.EXTRA_TITLE,
+ getText(R.string.title_select_live_folder));
+ startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
+ break;
+ }
+
+ case AddAdapter.ITEM_FOLDER: {
+ addFolder();
+ dismissDialog(DIALOG_CREATE_SHORTCUT);
+ break;
+ }
+
+ case AddAdapter.ITEM_WALLPAPER: {
+ startWallpaper();
+ break;
+ }
+
+ }
+
+ }
}
}
@@ -1569,13 +1698,20 @@ public final class Launcher extends Activity implements View.OnClickListener, On
private class ApplicationsIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- //noinspection ConstantConditions
- if (REMOVE_SHORTCUT_ON_PACKAGE_REMOVE &&
- Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
- removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
+ boolean reloadWorkspace = false;
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
+ } else {
+ reloadWorkspace = true;
+ }
}
removeDialog(DIALOG_CREATE_SHORTCUT);
- sModel.loadApplications(false, Launcher.this, false);
+ if (!reloadWorkspace) {
+ sModel.loadApplications(false, Launcher.this, false);
+ } else {
+ sModel.loadUserItems(false, Launcher.this, false, true);
+ }
}
}
diff --git a/src/com/android/launcher/LauncherGadgetHost.java b/src/com/android/launcher/LauncherGadgetHost.java
new file mode 100644
index 000000000..4f7e8f2f2
--- /dev/null
+++ b/src/com/android/launcher/LauncherGadgetHost.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2009 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.content.Context;
+import android.gadget.GadgetHost;
+import android.gadget.GadgetHostView;
+import android.gadget.GadgetInfo;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.TextView;
+
+/**
+ * Specific {@link GadgetHost} that creates our {@link LauncherGadgetHostView} which correctly
+ * captures all long-press events. This ensures that users can always pick up and move gadgets.
+ */
+public class LauncherGadgetHost extends GadgetHost {
+ public LauncherGadgetHost(Context context, int hostId) {
+ super(context, hostId);
+ }
+
+ protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) {
+ return new LauncherGadgetHostView(context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public class LauncherGadgetHostView extends GadgetHostView {
+ static final String TAG = "LauncherGadgetHostView";
+
+ private boolean mHasPerformedLongPress;
+
+ private CheckForLongPress mPendingCheckForLongPress;
+
+ private LayoutInflater mInflater;
+
+ public LauncherGadgetHostView(Context context) {
+ super(context);
+
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ // Prepare our default transition animations
+ setAnimateFirstView(true);
+ setInAnimation(context, android.R.anim.fade_in);
+ setOutAnimation(context, android.R.anim.fade_out);
+ }
+
+ @Override
+ protected View getErrorView() {
+ return mInflater.inflate(R.layout.gadget_error, this, false);
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+
+ // Consume any touch events for ourselves after longpress is triggered
+ if (mHasPerformedLongPress) {
+ mHasPerformedLongPress = false;
+ return true;
+ }
+
+ // Watch for longpress events at this level to make sure
+ // users can always pick up this Gadget
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ postCheckForLongClick();
+ break;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ mHasPerformedLongPress = false;
+ if (mPendingCheckForLongPress != null) {
+ removeCallbacks(mPendingCheckForLongPress);
+ }
+ break;
+ }
+ }
+
+ // Otherwise continue letting touch events fall through to children
+ return false;
+ }
+
+ class CheckForLongPress implements Runnable {
+ private int mOriginalWindowAttachCount;
+
+ public void run() {
+ if ((mParent != null) && hasWindowFocus()
+ && mOriginalWindowAttachCount == getWindowAttachCount()
+ && !mHasPerformedLongPress) {
+ if (performLongClick()) {
+ mHasPerformedLongPress = true;
+ }
+ }
+ }
+
+ public void rememberWindowAttachCount() {
+ mOriginalWindowAttachCount = getWindowAttachCount();
+ }
+ }
+
+ private void postCheckForLongClick() {
+ mHasPerformedLongPress = false;
+
+ if (mPendingCheckForLongPress == null) {
+ mPendingCheckForLongPress = new CheckForLongPress();
+ }
+ mPendingCheckForLongPress.rememberWindowAttachCount();
+ postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());
+ }
+
+ }
+
+}
+
diff --git a/src/com/android/launcher/LauncherGadgetInfo.java b/src/com/android/launcher/LauncherGadgetInfo.java
new file mode 100644
index 000000000..65fa7742f
--- /dev/null
+++ b/src/com/android/launcher/LauncherGadgetInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 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.content.ContentValues;
+import android.gadget.GadgetHostView;
+
+/**
+ * Represents a gadget, which just contains an identifier.
+ */
+class LauncherGadgetInfo extends ItemInfo {
+
+ /**
+ * Identifier for this gadget when talking with {@link GadgetManager} for updates.
+ */
+ int gadgetId;
+
+ /**
+ * View that holds this gadget after it's been created. This view isn't created
+ * until Launcher knows it's needed.
+ */
+ GadgetHostView hostView = null;
+
+ LauncherGadgetInfo(int gadgetId) {
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_GADGET;
+ this.gadgetId = gadgetId;
+ }
+
+ @Override
+ void onAddToDatabase(ContentValues values) {
+ super.onAddToDatabase(values);
+ values.put(LauncherSettings.Favorites.GADGET_ID, gadgetId);
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(gadgetId);
+ }
+}
diff --git a/src/com/android/launcher/LauncherModel.java b/src/com/android/launcher/LauncherModel.java
index 314a502d4..5d0179663 100644
--- a/src/com/android/launcher/LauncherModel.java
+++ b/src/com/android/launcher/LauncherModel.java
@@ -272,7 +272,8 @@ public class LauncherModel {
try {
while (c.moveToNext()) {
try {
- if (c.getInt(itemTypeIndex) != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ if (c.getInt(itemTypeIndex) !=
+ LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
continue;
}
@@ -374,15 +375,19 @@ public class LauncherModel {
final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+ final int gadgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.GADGET_ID);
final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+ final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
+ final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
ApplicationInfo info;
String intentDescription;
Widget widgetInfo = null;
+ LauncherGadgetInfo gadgetInfo = null;
int container;
long id;
Intent intent;
@@ -494,41 +499,44 @@ public class LauncherModel {
break;
}
break;
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
- switch (itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
- widgetInfo = Widget.makeClock();
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
- widgetInfo = Widget.makeSearch();
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
- widgetInfo = Widget.makePhotoFrame();
- byte[] data = c.getBlob(iconIndex);
- if (data != null) {
- widgetInfo.photo =
- BitmapFactory.decodeByteArray(data, 0, data.length);
- }
- break;
- }
+ widgetInfo = Widget.makeSearch();
- if (widgetInfo != null) {
- container = c.getInt(containerIndex);
- if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- Log.e(Launcher.LOG_TAG, "Widget found where container "
- + "!= CONTAINER_DESKTOP -- ignoring!");
- continue;
- }
- widgetInfo.id = c.getLong(idIndex);
- widgetInfo.screen = c.getInt(screenIndex);
- widgetInfo.container = container;
- widgetInfo.cellX = c.getInt(cellXIndex);
- widgetInfo.cellY = c.getInt(cellYIndex);
+ container = c.getInt(containerIndex);
+ if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ Log.e(Launcher.LOG_TAG, "Widget found where container "
+ + "!= CONTAINER_DESKTOP ignoring!");
+ continue;
+ }
+
+ widgetInfo.id = c.getLong(idIndex);
+ widgetInfo.screen = c.getInt(screenIndex);
+ widgetInfo.container = container;
+ widgetInfo.cellX = c.getInt(cellXIndex);
+ widgetInfo.cellY = c.getInt(cellYIndex);
+
+ desktopItems.add(widgetInfo);
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_GADGET:
+ // Read all Launcher-specific gadget details
+ int gadgetId = c.getInt(gadgetIdIndex);
+ gadgetInfo = new LauncherGadgetInfo(gadgetId);
+ gadgetInfo.id = c.getLong(idIndex);
+ gadgetInfo.screen = c.getInt(screenIndex);
+ gadgetInfo.cellX = c.getInt(cellXIndex);
+ gadgetInfo.cellY = c.getInt(cellYIndex);
+ gadgetInfo.spanX = c.getInt(spanXIndex);
+ gadgetInfo.spanY = c.getInt(spanYIndex);
- desktopItems.add(widgetInfo);
+ container = c.getInt(containerIndex);
+ if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ Log.e(Launcher.LOG_TAG, "Gadget found where container "
+ + "!= CONTAINER_DESKTOP -- ignoring!");
+ continue;
}
+ gadgetInfo.container = c.getInt(containerIndex);
+
+ desktopItems.add(gadgetInfo);
break;
}
} catch (Exception e) {
@@ -972,7 +980,7 @@ public class LauncherModel {
final ContentResolver cr = context.getContentResolver();
cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
- cr.delete(LauncherSettings.Favorites.CONTENT_URI, LauncherSettings.Favorites.CONTAINER + "=" + info.id,
- null);
+ cr.delete(LauncherSettings.Favorites.CONTENT_URI,
+ LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
}
}
diff --git a/src/com/android/launcher/LauncherProvider.java b/src/com/android/launcher/LauncherProvider.java
index a3e529d32..47db647e4 100644
--- a/src/com/android/launcher/LauncherProvider.java
+++ b/src/com/android/launcher/LauncherProvider.java
@@ -29,6 +29,7 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.Cursor;
+import android.database.SQLException;
import android.util.Log;
import android.util.Xml;
import android.net.Uri;
@@ -49,7 +50,8 @@ public class LauncherProvider extends ContentProvider {
private static final String LOG_TAG = "LauncherSettingsProvider";
private static final String DATABASE_NAME = "launcher.db";
- private static final int DATABASE_VERSION = 1;
+
+ private static final int DATABASE_VERSION = 2;
static final String AUTHORITY = "com.android.launcher.settings";
@@ -187,6 +189,7 @@ public class LauncherProvider extends ContentProvider {
"spanX INTEGER," +
"spanY INTEGER," +
"itemType INTEGER," +
+ "gadgetId INTEGER NOT NULL DEFAULT -1," +
"isShortcut INTEGER," +
"iconType INTEGER," +
"iconPackage TEXT," +
@@ -196,6 +199,11 @@ public class LauncherProvider extends ContentProvider {
"displayMode INTEGER" +
");");
+ // TODO: During first database creation, trigger wipe of any gadgets that
+ // might have been left around during a wipe-data.
+// GadgetManager gadgetManager = GadgetManager.getInstance(mContext);
+
+
if (!convertDatabase(db)) {
// Populate favorites table with initial favorites
loadFavorites(db, DEFAULT_FAVORITES_PATH);
@@ -206,7 +214,7 @@ public class LauncherProvider extends ContentProvider {
boolean converted = false;
final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
- "/favorites?notify=true");
+ "/old_favorites?notify=true");
final ContentResolver resolver = mContext.getContentResolver();
Cursor cursor = null;
@@ -261,6 +269,7 @@ public class LauncherProvider extends ContentProvider {
values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
+ values.put(LauncherSettings.Favorites.GADGET_ID, -1);
values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
@@ -290,11 +299,31 @@ public class LauncherProvider extends ContentProvider {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + " to " +
- newVersion + ", which will destroy all old data");
-
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
- onCreate(db);
+ int version = oldVersion;
+ if (version == 1) {
+ // upgrade 1 -> 2 added gadgetId column
+ db.beginTransaction();
+ try {
+ // TODO: convert any existing widgets for search and clock
+ // this might involve a FORCE_ADD_GADGET permission in GadgetManager that
+ // Launcher could then use to add these gadgets without user interaction
+ db.execSQL("ALTER TABLE favorites " +
+ "ADD COLUMN gadgetId INTEGER NOT NULL DEFAULT -1;");
+ db.setTransactionSuccessful();
+ version = 2;
+ } catch (SQLException ex) {
+ // Old version remains, which means we wipe old data
+ Log.e(LOG_TAG, ex.getMessage(), ex);
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ if (version != DATABASE_VERSION) {
+ Log.w(LOG_TAG, "Destroying all old data.");
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
+ onCreate(db);
+ }
}
@@ -370,20 +399,7 @@ public class LauncherProvider extends ContentProvider {
} catch (IOException e) {
Log.w(LOG_TAG, "Got exception parsing favorites.", e);
}
-
- // Add a clock
- values.clear();
- values.put(LauncherSettings.Favorites.CONTAINER,
- LauncherSettings.Favorites.CONTAINER_DESKTOP);
- values.put(LauncherSettings.Favorites.ITEM_TYPE,
- LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK);
- values.put(LauncherSettings.Favorites.SCREEN, 1);
- values.put(LauncherSettings.Favorites.CELLX, 1);
- values.put(LauncherSettings.Favorites.CELLY, 0);
- values.put(LauncherSettings.Favorites.SPANX, 2);
- values.put(LauncherSettings.Favorites.SPANY, 2);
- db.insert(TABLE_FAVORITES, null, values);
-
+
// Add a search box
values.clear();
values.put(LauncherSettings.Favorites.CONTAINER,
@@ -396,6 +412,9 @@ public class LauncherProvider extends ContentProvider {
values.put(LauncherSettings.Favorites.SPANX, 4);
values.put(LauncherSettings.Favorites.SPANY, 1);
db.insert(TABLE_FAVORITES, null, values);
+
+ // TODO: automatically add clock and search gadget to
+ // default locations. this might need the FORCE permission mentioned above
return i;
}
diff --git a/src/com/android/launcher/LauncherSettings.java b/src/com/android/launcher/LauncherSettings.java
index c5dfd1ff0..77bdc73fe 100644
--- a/src/com/android/launcher/LauncherSettings.java
+++ b/src/com/android/launcher/LauncherSettings.java
@@ -147,6 +147,11 @@ class LauncherSettings {
static final int ITEM_TYPE_LIVE_FOLDER = 3;
/**
+ * The favorite is a gadget
+ */
+ static final int ITEM_TYPE_GADGET = 4;
+
+ /**
* The favorite is a clock
*/
static final int ITEM_TYPE_WIDGET_CLOCK = 1000;
@@ -162,6 +167,13 @@ class LauncherSettings {
static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002;
/**
+ * The gadgetId of the gadget
+ *
+ * <P>Type: INTEGER</P>
+ */
+ static final String GADGET_ID = "gadgetId";
+
+ /**
* Indicates whether this favorite is an application-created shortcut or not.
* If the value is 0, the favorite is not an application-created shortcut, if the
* value is 1, it is an application-created shortcut.
diff --git a/src/com/android/launcher/PhotoFrame.java b/src/com/android/launcher/PhotoFrame.java
deleted file mode 100644
index 115132227..000000000
--- a/src/com/android/launcher/PhotoFrame.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ImageView;
-
-
-/**
- * Desktop widget that holds a user folder
- *
- */
-public class PhotoFrame extends ImageView implements OnClickListener {
-
- public PhotoFrame(Context context, AttributeSet attrs) {
- super(context, attrs);
- setClickable(true);
- setOnClickListener(this);
- setWillNotCacheDrawing(true);
- }
-
- public void onClick(View v) {
- ((Launcher) mContext).updatePhotoFrame((Widget) getTag());
- }
-}
diff --git a/src/com/android/launcher/Search.java b/src/com/android/launcher/Search.java
index 41d6562c7..449caf997 100644
--- a/src/com/android/launcher/Search.java
+++ b/src/com/android/launcher/Search.java
@@ -16,8 +16,6 @@
package com.android.launcher;
-import java.util.List;
-
import android.app.ISearchManager;
import android.app.SearchManager;
import android.content.ActivityNotFoundException;
@@ -59,6 +57,8 @@ import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
+import java.util.List;
+
public class Search extends LinearLayout implements OnClickListener, OnKeyListener,
OnLongClickListener, TextWatcher, OnItemClickListener, OnItemSelectedListener {
@@ -66,6 +66,7 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen
private AutoCompleteTextView mSearchText;
private ImageButton mGoButton;
+ private ImageButton mVoiceButton;
private OnLongClickListener mLongClickListener;
// Support for suggestions
@@ -76,6 +77,9 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen
private String mSuggestionQuery = null;
private int mItemSelected = -1;
+ // For voice searching
+ private Intent mVoiceSearchIntent;
+
private Rect mTempRect = new Rect();
/**
@@ -86,6 +90,10 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen
*/
public Search(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ mVoiceSearchIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+ mVoiceSearchIntent.putExtra(android.speech.RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ android.speech.RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
}
/**
@@ -94,6 +102,14 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen
public void onClick(View v) {
if (v == mGoButton) {
query();
+ } else if (v == mVoiceButton) {
+ try {
+ getContext().startActivity(mVoiceSearchIntent);
+ } catch (ActivityNotFoundException ex) {
+ // Should not happen, since we check the availability of
+ // voice search before showing the button. But just in case...
+ Log.w(TAG, "Could not find voice search activity");
+ }
}
}
@@ -206,7 +222,7 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen
return true;
}
}
- } else if (v == mGoButton) {
+ } else if (v == mGoButton || v == mVoiceButton) {
boolean handled = false;
if (!event.isSystem() &&
(keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
@@ -243,7 +259,14 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- requestFocusFromTouch();
+ // Request focus unless the user tapped on the voice search button
+ final int x = (int) ev.getX();
+ final int y = (int) ev.getY();
+ final Rect frame = mTempRect;
+ mVoiceButton.getHitRect(frame);
+ if (!frame.contains(x, y)) {
+ requestFocusFromTouch();
+ }
return super.onInterceptTouchEvent(ev);
}
@@ -268,26 +291,29 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen
mSearchText.addTextChangedListener(this);
mGoButton = (ImageButton) findViewById(R.id.search_go_btn);
+ mVoiceButton = (ImageButton) findViewById(R.id.search_voice_btn);
mGoButton.setOnClickListener(this);
+ mVoiceButton.setOnClickListener(this);
mGoButton.setOnKeyListener(this);
+ mVoiceButton.setOnKeyListener(this);
mSearchText.setOnLongClickListener(this);
mGoButton.setOnLongClickListener(this);
+ mVoiceButton.setOnLongClickListener(this);
// disable the button since we start out w/empty input
mGoButton.setEnabled(false);
mGoButton.setFocusable(false);
+ configureSearchableInfo();
configureSuggestions();
+ configureVoiceSearchButton();
}
- /** The rest of the class deals with providing search suggestions */
-
/**
- * Set up the suggestions provider mechanism
+ * Read the searchable info from the search manager
*/
- private void configureSuggestions() {
- // get SearchableInfo
+ private void configureSearchableInfo() {
ISearchManager sms;
SearchableInfo searchable;
sms = ISearchManager.Stub.asInterface(ServiceManager.getService(Context.SEARCH_SERVICE));
@@ -303,6 +329,36 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen
return;
}
mSearchable = searchable;
+ }
+
+ /**
+ * If appropriate & available, configure voice search
+ *
+ * Note: Because the home screen search widget is always web search, we only check for
+ * getVoiceSearchLaunchWebSearch() modes. We don't support the alternate form of app-specific
+ * voice search.
+ */
+ private void configureVoiceSearchButton() {
+ boolean voiceSearchVisible = false;
+ if (mSearchable.getVoiceSearchEnabled() && mSearchable.getVoiceSearchLaunchWebSearch()) {
+ // Enable the voice search button if there is an activity that can handle it
+ PackageManager pm = getContext().getPackageManager();
+ List<ResolveInfo> list = pm.queryIntentActivities(mVoiceSearchIntent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ voiceSearchVisible = list.size() > 0;
+ }
+
+ // finally, set visible state of voice search button, as appropriate
+ mVoiceButton.setVisibility(voiceSearchVisible ? View.VISIBLE : View.GONE);
+ }
+
+ /** The rest of the class deals with providing search suggestions */
+
+ /**
+ * Set up the suggestions provider mechanism
+ */
+ private void configureSuggestions() {
+ // get SearchableInfo
mSearchText.setOnItemClickListener(this);
mSearchText.setOnItemSelectedListener(this);
diff --git a/src/com/android/launcher/WallpaperChooser.java b/src/com/android/launcher/WallpaperChooser.java
index afbe6f352..1eb8d0cb3 100644
--- a/src/com/android/launcher/WallpaperChooser.java
+++ b/src/com/android/launcher/WallpaperChooser.java
@@ -123,9 +123,6 @@ public class WallpaperChooser extends Activity implements AdapterView.OnItemSele
final String[] extras = resources.getStringArray(R.array.extra_wallpapers);
final String packageName = getApplication().getPackageName();
- final ArrayList<Integer> images = mImages;
- final ArrayList<Integer> thumbs = mThumbs;
-
for (String extra : extras) {
int res = resources.getIdentifier(extra, "drawable", packageName);
if (res != 0) {
@@ -133,8 +130,8 @@ public class WallpaperChooser extends Activity implements AdapterView.OnItemSele
"drawable", packageName);
if (thumbRes != 0) {
- images.add(res);
- thumbs.add(res);
+ mThumbs.add(res);
+ mImages.add(res);
}
}
}
@@ -148,7 +145,7 @@ public class WallpaperChooser extends Activity implements AdapterView.OnItemSele
public void onItemSelected(AdapterView parent, View v, int position, long id) {
final ImageView view = mImageView;
- Bitmap b = BitmapFactory.decodeResource(getResources(), IMAGE_IDS[position], mOptions);
+ Bitmap b = BitmapFactory.decodeResource(getResources(), mImages.get(position), mOptions);
view.setImageBitmap(b);
// Help the GC
diff --git a/src/com/android/launcher/Workspace.java b/src/com/android/launcher/Workspace.java
index 0ca1b5af6..510dad490 100644
--- a/src/com/android/launcher/Workspace.java
+++ b/src/com/android/launcher/Workspace.java
@@ -89,6 +89,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
private boolean mAllowLongPress;
private boolean mLocked;
+ private int mTouchSlop;
+
/**
* Used to inflate the Workspace from XML.
*
@@ -126,6 +128,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
mPaint = new Paint();
mPaint.setDither(false);
+
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
/**
@@ -626,8 +630,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
*/
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int yDiff = (int) Math.abs(y - mLastMotionY);
- final int touchSlop = ViewConfiguration.getTouchSlop();
-
+
+ final int touchSlop = mTouchSlop;
boolean xMoved = xDiff > touchSlop;
boolean yMoved = yDiff > touchSlop;
@@ -1155,6 +1159,10 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
}
}
}
+
+ // TODO: remove gadgets when gadgetmanager tells us they're gone
+// void removeGadgetsForProvider() {
+// }
void moveToDefaultScreen() {
snapToScreen(mDefaultScreen);