Merge "Import revised translations."
diff --git a/res/layout-land/user_folder.xml b/res/layout-land/user_folder.xml
index 5da4aa5..ca437bf 100644
--- a/res/layout-land/user_folder.xml
+++ b/res/layout-land/user_folder.xml
@@ -14,7 +14,9 @@
limitations under the License.
-->
-<com.android.launcher2.UserFolder xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher2.UserFolder
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
android:orientation="vertical">
<Button
@@ -29,17 +31,24 @@
<FrameLayout
android:layout_width="match_parent"
- android:layout_height="0dip"
+ android:layout_height="0dip"
android:layout_weight="1"
android:background="@drawable/box_launcher_bottom">
- <GridView
+ <com.android.launcher2.CellLayout
android:id="@id/folder_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#ff333333"
+ launcher:cellWidth="@dimen/folder_cell_width"
+ launcher:cellHeight="@dimen/folder_cell_height"
+ launcher:xAxisStartPadding="0dip"
+ launcher:xAxisEndPadding="0dip"
+ launcher:yAxisStartPadding="0dip"
+ launcher:yAxisEndPadding="0dip"
+
android:scrollbarAlwaysDrawVerticalTrack="true"
android:scrollbarStyle="insideInset"
android:drawSelectorOnTop="false"
diff --git a/res/layout-port/user_folder.xml b/res/layout-port/user_folder.xml
index 0e6df66..c00b548 100644
--- a/res/layout-port/user_folder.xml
+++ b/res/layout-port/user_folder.xml
@@ -14,7 +14,9 @@
limitations under the License.
-->
-<com.android.launcher2.UserFolder xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher2.UserFolder
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
android:orientation="vertical">
<Button
@@ -29,16 +31,23 @@
<FrameLayout
android:layout_width="match_parent"
- android:layout_height="0dip"
+ android:layout_height="0dip"
android:layout_weight="1"
android:background="@drawable/box_launcher_bottom">
- <GridView
+ <com.android.launcher2.CellLayout
android:id="@id/folder_content"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="match_parent"
android:cacheColorHint="#ff333333"
+ launcher:cellWidth="@dimen/folder_cell_width"
+ launcher:cellHeight="@dimen/folder_cell_height"
+ launcher:xAxisStartPadding="0dip"
+ launcher:xAxisEndPadding="0dip"
+ launcher:yAxisStartPadding="0dip"
+ launcher:yAxisEndPadding="0dip"
+
android:scrollbarAlwaysDrawVerticalTrack="true"
android:scrollbarStyle="insideInset"
android:drawSelectorOnTop="false"
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 159cbb4..9cb549b 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -17,5 +17,7 @@
<resources>
<dimen name="workspace_cell_width">106dip</dimen>
<dimen name="workspace_cell_height">74dip</dimen>
+ <dimen name="folder_cell_width">100dip</dimen>
+ <dimen name="folder_cell_height">74dip</dimen>
<dimen name="button_bar_height">62dip</dimen>
</resources>
diff --git a/res/values-port/dimens.xml b/res/values-port/dimens.xml
index 65a3fd3..ad5922d 100644
--- a/res/values-port/dimens.xml
+++ b/res/values-port/dimens.xml
@@ -17,4 +17,6 @@
<resources>
<dimen name="workspace_cell_width">80dip</dimen>
<dimen name="workspace_cell_height">100dip</dimen>
+ <dimen name="folder_cell_width">74dip</dimen>
+ <dimen name="folder_cell_height">86dip</dimen>
</resources>
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index c05fe75..3d3c1ff 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -24,6 +24,7 @@
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -290,6 +291,16 @@
c.translate((v.getWidth() - icon.getIntrinsicWidth()) / 2, v.getPaddingTop());
icon.draw(c);
+ Rect dragRect = null;
+ if (v instanceof TextView) {
+ int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
+ int top = v.getPaddingTop();
+ int left = (b.getWidth() - iconSize) / 2;
+ int right = left + iconSize;
+ int bottom = top + iconSize;
+ dragRect = new Rect(left, top, right, bottom);
+ }
+
// We toggle the checked state _after_ we create the view for the drag in case toggling the
// checked state changes the view's look
if (v instanceof Checkable) {
@@ -309,7 +320,7 @@
// Start the drag
mLauncher.lockScreenOrientation();
mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1, b);
- mDragController.startDrag(v, b, this, app, DragController.DRAG_ACTION_COPY, null);
+ mDragController.startDrag(v, b, this, app, DragController.DRAG_ACTION_COPY, dragRect);
b.recycle();
return true;
}
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index ef83f27..636b041 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -95,7 +95,7 @@
private float mGlowBackgroundScale;
private float mGlowBackgroundAlpha;
- private boolean mAcceptsDrops = false;
+ private boolean mAcceptsDrops = true;
// If we're actively dragging something over this screen, mIsDragOverlapping is true
private boolean mIsDragOverlapping = false;
private boolean mIsDragOccuring = false;
@@ -736,6 +736,22 @@
result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
}
+ /**
+ * Given a cell coordinate, return the point that represents the upper left corner of that cell
+ *
+ * @param cellX X coordinate of the cell
+ * @param cellY Y coordinate of the cell
+ *
+ * @param result Array of 2 ints to hold the x and y coordinate of the point
+ */
+ void cellToCenterPoint(int cellX, int cellY, int[] result) {
+ final int hStartPadding = getLeftPadding();
+ final int vStartPadding = getTopPadding();
+
+ result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) + mCellWidth / 2;
+ result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) + mCellHeight / 2;
+ }
+
int getCellWidth() {
return mCellWidth;
}
@@ -1010,16 +1026,23 @@
* @param pixelY The Y location at which you want to search for a vacant area.
* @param spanX Horizontal span of the object.
* @param spanY Vertical span of the object.
- * @param ignoreView Considers space occupied by this view as unoccupied
- * @param result Previously returned value to possibly recycle.
+ * @param ignoreOccupied If true, the result can be an occupied cell
+ * @param result Array in which to place the result, or null (in which case a new array will
+ * be allocated)
* @return The X, Y cell of a vacant area that can contain this object,
* nearest the requested location.
*/
- int[] findNearestVacantArea(
- int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
+ int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, View ignoreView,
+ boolean ignoreOccupied, int[] result) {
// mark space take by ignoreView as available (method checks if ignoreView is null)
markCellsAsUnoccupiedForView(ignoreView);
+ // For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds
+ // to the center of the item, but we are searching based on the top-left cell, so
+ // we translate the point over to correspond to the top-left.
+ pixelX -= (mCellWidth + mWidthGap) * (spanX - 1) / 2f;
+ pixelY -= (mCellHeight + mHeightGap) * (spanY - 1) / 2f;
+
// Keep track of best-scoring drop area
final int[] bestXY = result != null ? result : new int[2];
double bestDistance = Double.MAX_VALUE;
@@ -1031,18 +1054,20 @@
for (int y = 0; y < countY - (spanY - 1); y++) {
inner:
for (int x = 0; x < countX - (spanX - 1); x++) {
- for (int i = 0; i < spanX; i++) {
- for (int j = 0; j < spanY; j++) {
- if (occupied[x + i][y + j]) {
- // small optimization: we can skip to after the column we just found
- // an occupied cell
- x += i;
- continue inner;
+ if (ignoreOccupied) {
+ for (int i = 0; i < spanX; i++) {
+ for (int j = 0; j < spanY; j++) {
+ if (occupied[x + i][y + j]) {
+ // small optimization: we can skip to after the column we
+ // just found an occupied cell
+ x += i;
+ continue inner;
+ }
}
}
}
final int[] cellXY = mTmpCellXY;
- cellToPoint(x, y, cellXY);
+ cellToCenterPoint(x, y, cellXY);
double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
+ Math.pow(cellXY[1] - pixelY, 2));
@@ -1064,6 +1089,42 @@
}
}
+ /**
+ * Find a vacant area that will fit the given bounds nearest the requested
+ * cell location. Uses Euclidean distance to score multiple vacant areas.
+ *
+ * @param pixelX The X location at which you want to search for a vacant area.
+ * @param pixelY The Y location at which you want to search for a vacant area.
+ * @param spanX Horizontal span of the object.
+ * @param spanY Vertical span of the object.
+ * @param ignoreView Considers space occupied by this view as unoccupied
+ * @param result Previously returned value to possibly recycle.
+ * @return The X, Y cell of a vacant area that can contain this object,
+ * nearest the requested location.
+ */
+ int[] findNearestVacantArea(
+ int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
+ return findNearestArea(pixelX, pixelY, spanX, spanY, ignoreView, true, result);
+ }
+
+ /**
+ * Find a starting cell position that will fit the given bounds nearest the requested
+ * cell location. Uses Euclidean distance to score multiple vacant areas.
+ *
+ * @param pixelX The X location at which you want to search for a vacant area.
+ * @param pixelY The Y location at which you want to search for a vacant area.
+ * @param spanX Horizontal span of the object.
+ * @param spanY Vertical span of the object.
+ * @param ignoreView Considers space occupied by this view as unoccupied
+ * @param result Previously returned value to possibly recycle.
+ * @return The X, Y cell of a vacant area that can contain this object,
+ * nearest the requested location.
+ */
+ int[] findNearestArea(
+ int pixelX, int pixelY, int spanX, int spanY, int[] result) {
+ return findNearestArea(pixelX, pixelY, spanX, spanY, null, false, result);
+ }
+
boolean existsEmptyCell() {
return findCellForSpan(null, 1, 1);
}
@@ -1459,6 +1520,14 @@
}
}
+ public boolean isOccupied(int x, int y) {
+ if (x < mCountX && y < mCountY) {
+ return mOccupied[x][y];
+ } else {
+ throw new RuntimeException("Position exceeds the bound of this CellLayout");
+ }
+ }
+
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new CellLayout.LayoutParams(getContext(), attrs);
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index cb4509b..5ca1e1c 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -73,10 +73,10 @@
private boolean mDragging;
/** X coordinate of the down event. */
- private float mMotionDownX;
+ private int mMotionDownX;
/** Y coordinate of the down event. */
- private float mMotionDownY;
+ private int mMotionDownY;
/** Info about the screen for clamping. */
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -85,10 +85,10 @@
private View mOriginator;
/** X offset from the upper-left corner of the cell to where we touched. */
- private float mTouchOffsetX;
+ private int mTouchOffsetX;
/** Y offset from the upper-left corner of the cell to where we touched. */
- private float mTouchOffsetY;
+ private int mTouchOffsetY;
/** the area at the edge of the screen that makes the workspace go left
* or right while you're dragging.
@@ -289,13 +289,14 @@
listener.onDragStart(source, dragInfo, dragAction);
}
- int registrationX = ((int)mMotionDownX) - screenX;
- int registrationY = ((int)mMotionDownY) - screenY;
+ final int registrationX = ((int)mMotionDownX) - screenX;
+ final int registrationY = ((int)mMotionDownY) - screenY;
final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
- mTouchOffsetX = mMotionDownX - screenX - dragRegionLeft;
- mTouchOffsetY = mMotionDownY - screenY - dragRegionTop;
+
+ mTouchOffsetX = mMotionDownX - (screenX + dragRegionLeft);
+ mTouchOffsetY = mMotionDownY - (screenY + dragRegionTop);
mDragging = true;
mDragSource = source;
@@ -314,8 +315,7 @@
});
if (dragRegion != null) {
- dragView.setDragRegion(dragRegionLeft, dragRegion.top,
- dragRegion.right - dragRegionLeft, dragRegion.bottom - dragRegionTop);
+ dragView.setDragRegion(new Rect(dragRegion));
}
dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index ab99744..b02e22b 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -26,6 +26,7 @@
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.os.IBinder;
import android.view.Gravity;
import android.view.View;
@@ -42,10 +43,7 @@
private int mRegistrationX;
private int mRegistrationY;
- private int mDragRegionLeft = 0;
- private int mDragRegionTop = 0;
- private int mDragRegionWidth;
- private int mDragRegionHeight;
+ private Rect mDragRegion = null;
ValueAnimator mAnim;
private float mOffsetX = 0.0f;
@@ -117,7 +115,7 @@
});
mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
- setDragRegion(0, 0, width, height);
+ setDragRegion(new Rect(0, 0, width, height));
// The point in our scaled bitmap that the touch events are located
mRegistrationX = registrationX;
@@ -132,31 +130,32 @@
return mOffsetY;
}
- public void setDragRegion(int left, int top, int width, int height) {
- mDragRegionLeft = left;
- mDragRegionTop = top;
- mDragRegionWidth = width;
- mDragRegionHeight = height;
- }
-
public void setOnDrawRunnable(Runnable r) {
mOnDrawRunnable = r;
}
public int getDragRegionLeft() {
- return mDragRegionLeft;
+ return mDragRegion.left;
}
public int getDragRegionTop() {
- return mDragRegionTop;
+ return mDragRegion.top;
}
public int getDragRegionWidth() {
- return mDragRegionWidth;
+ return mDragRegion.width();
}
public int getDragRegionHeight() {
- return mDragRegionHeight;
+ return mDragRegion.height();
+ }
+
+ public void setDragRegion(Rect r) {
+ mDragRegion = r;
+ }
+
+ public Rect getDragRegion() {
+ return mDragRegion;
}
@Override
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 059e73d..33a1cd3 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -37,7 +37,6 @@
public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener,
OnItemClickListener, OnClickListener, View.OnLongClickListener {
- protected AbsListView mContent;
protected DragController mDragController;
protected Launcher mLauncher;
@@ -66,10 +65,6 @@
protected void onFinishInflate() {
super.onFinishInflate();
- mContent = (AbsListView) findViewById(R.id.folder_content);
- mContent.setOnItemClickListener(this);
- mContent.setOnItemLongClickListener(this);
-
mCloseButton = (Button) findViewById(R.id.folder_close);
mCloseButton.setOnClickListener(this);
mCloseButton.setOnLongClickListener(this);
@@ -121,18 +116,7 @@
public void onDragViewVisible() {
}
- /**
- * Sets the adapter used to populate the content area. The adapter must only
- * contains ShortcutInfo items.
- *
- * @param adapter The list of applications to display in the folder.
- */
- void setContentAdapter(BaseAdapter adapter) {
- mContent.setAdapter(adapter);
- }
-
void notifyDataSetChanged() {
- ((BaseAdapter) mContent.getAdapter()).notifyDataSetChanged();
}
void setLauncher(Launcher launcher) {
@@ -146,10 +130,7 @@
return mInfo;
}
- // When the folder opens, we need to refresh the GridView's selection by
- // forcing a layout
void onOpen() {
- mContent.requestLayout();
}
void onClose() {
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index bbc3409..1122d77 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -77,6 +77,11 @@
&& item.container != mInfo.id;
}
+ public void addItem(ShortcutInfo item) {
+ mInfo.add(item);
+ LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, 0, 0);
+ }
+
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
ShortcutInfo item;
@@ -86,8 +91,9 @@
} else {
item = (ShortcutInfo)dragInfo;
}
- mInfo.add(item);
- LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, 0, 0);
+ item.cellX = -1;
+ item.cellY = -1;
+ addItem(item);
}
public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 19a943b..60cab8e 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -107,7 +107,6 @@
import java.util.HashMap;
import java.util.List;
-
/**
* Default launcher application.
*/
@@ -315,7 +314,6 @@
// share the same customization workspace across all the tabs
mCustomizePagedView = (CustomizePagedView) findViewById(
R.id.customization_drawer_tab_contents);
-
}
setupViews();
@@ -1810,7 +1808,7 @@
}
}
- void addFolder(int screen, int intersectCellX, int intersectCellY) {
+ FolderIcon addFolder(int screen, int intersectCellX, int intersectCellY) {
UserFolderInfo folderInfo = new UserFolderInfo();
folderInfo.title = getText(R.string.folder_name);
@@ -1818,7 +1816,7 @@
final int[] cellXY = mTmpAddItemCellCoordinates;
if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
showOutOfSpaceMessage();
- return;
+ return null;
}
// Update the model
@@ -1832,6 +1830,7 @@
(ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()),
folderInfo, mIconCache);
mWorkspace.addInScreen(newFolder, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
+ return newFolder;
}
void removeFolder(FolderInfo folder) {
@@ -3398,6 +3397,8 @@
*/
public void startBinding() {
final Workspace workspace = mWorkspace;
+
+ mWorkspace.clearDropTargets();
int count = workspace.getChildCount();
for (int i = 0; i < count; i++) {
// Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
diff --git a/src/com/android/launcher2/LiveFolder.java b/src/com/android/launcher2/LiveFolder.java
index 07a295f..b692953 100644
--- a/src/com/android/launcher2/LiveFolder.java
+++ b/src/com/android/launcher2/LiveFolder.java
@@ -22,10 +22,12 @@
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
+import android.widget.BaseAdapter;
import android.net.Uri;
import android.provider.LiveFolders;
import android.os.AsyncTask;
import android.database.Cursor;
+import android.widget.GridView;
import java.lang.ref.WeakReference;
@@ -33,6 +35,7 @@
public class LiveFolder extends Folder {
private AsyncTask<LiveFolderInfo,Void,Cursor> mLoadingTask;
+ protected GridView mContent;
public LiveFolder(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -44,6 +47,15 @@
return (LiveFolder) LayoutInflater.from(context).inflate(layout, null);
}
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mContent = (GridView) findViewById(R.id.folder_content);
+ mContent.setOnItemClickListener(this);
+ mContent.setOnItemLongClickListener(this);
+ }
+
private static boolean isDisplayModeList(FolderInfo folderInfo) {
return ((LiveFolderInfo) folderInfo).displayMode ==
LiveFolders.DISPLAY_MODE_LIST;
@@ -68,6 +80,21 @@
}
}
+ /**
+ * Sets the adapter used to populate the content area. The adapter must only
+ * contains ShortcutInfo items.
+ *
+ * @param adapter The list of applications to display in the folder.
+ */
+ void setContentAdapter(BaseAdapter adapter) {
+ mContent.setAdapter(adapter);
+ }
+
+ @Override
+ void notifyDataSetChanged() {
+ ((BaseAdapter) mContent.getAdapter()).notifyDataSetChanged();
+ }
+
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
return false;
@@ -84,6 +111,9 @@
@Override
void onOpen() {
super.onOpen();
+ // When the folder opens, we need to refresh the GridView's selection by
+ // forcing a layout
+ mContent.requestLayout();
requestFocus();
}
diff --git a/src/com/android/launcher2/UserFolder.java b/src/com/android/launcher2/UserFolder.java
index 251b3f9..b800b07 100644
--- a/src/com/android/launcher2/UserFolder.java
+++ b/src/com/android/launcher2/UserFolder.java
@@ -1,11 +1,18 @@
package com.android.launcher2;
import android.content.Context;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.GridView;
+import android.widget.TextView;
import com.android.launcher.R;
+import java.util.ArrayList;
/**
* Folder which contains applications or shortcuts chosen by the user.
@@ -14,8 +21,21 @@
public class UserFolder extends Folder implements DropTarget {
private static final String TAG = "Launcher.UserFolder";
+ protected CellLayout mContent;
+ private final LayoutInflater mInflater;
+ private final IconCache mIconCache;
+
public UserFolder(Context context, AttributeSet attrs) {
super(context, attrs);
+ mInflater = LayoutInflater.from(context);
+ mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mContent = (CellLayout) findViewById(R.id.folder_content);
}
/**
@@ -29,6 +49,48 @@
return (UserFolder) LayoutInflater.from(context).inflate(R.layout.user_folder, null);
}
+ @Override
+ void notifyDataSetChanged() {
+ // recreate all the children if the data set changes under us. We may want to do this more
+ // intelligently (ie just removing the views that should no longer exist)
+ mContent.removeAllViewsInLayout();
+ bind(mInfo);
+ }
+
+ public void onClick(View v) {
+ Object tag = v.getTag();
+ if (tag instanceof ShortcutInfo) {
+ // refactor this code from Folder
+ ShortcutInfo item = (ShortcutInfo) tag;
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ item.intent.setSourceBounds(new Rect(pos[0], pos[1],
+ pos[0] + v.getWidth(), pos[1] + v.getHeight()));
+ mLauncher.startActivitySafely(item.intent, item);
+ } else {
+ super.onClick(v);
+ }
+ }
+
+ public boolean onLongClick(View v) {
+ Object tag = v.getTag();
+ if (tag instanceof ShortcutInfo) {
+ // refactor this code from Folder
+ ShortcutInfo item = (ShortcutInfo) tag;
+ if (!v.isInTouchMode()) {
+ return false;
+ }
+
+ mDragController.startDrag(v, this, item, DragController.DRAG_ACTION_COPY);
+ mLauncher.closeFolder(this);
+ mDragItem = item;
+
+ return true;
+ } else {
+ return super.onLongClick(v);
+ }
+ }
+
public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
final ItemInfo item = (ItemInfo) dragInfo;
@@ -44,11 +106,45 @@
if (dragInfo instanceof ApplicationInfo) {
// Came from all apps -- make a copy
item = ((ApplicationInfo)dragInfo).makeShortcut();
+ item.spanX = 1;
+ item.spanY = 1;
} else {
item = (ShortcutInfo)dragInfo;
}
- ((ShortcutsAdapter)mContent.getAdapter()).add(item);
- LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, 0, 0);
+ findAndSetEmptyCells(item);
+ ((UserFolderInfo)mInfo).add(item);
+ createAndAddShortcut(item);
+ LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+ }
+
+ protected boolean findAndSetEmptyCells(ShortcutInfo item) {
+ int[] emptyCell = new int[2];
+ if (mContent.findCellForSpan(emptyCell, item.spanX, item.spanY)) {
+ item.cellX = emptyCell[0];
+ item.cellY = emptyCell[1];
+ LauncherModel.addOrMoveItemInDatabase(
+ mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ protected void createAndAddShortcut(ShortcutInfo item) {
+ final TextView textView =
+ (TextView) mInflater.inflate(R.layout.application_boxed, this, false);
+ textView.setCompoundDrawablesWithIntrinsicBounds(null,
+ new FastBitmapDrawable(item.getIcon(mIconCache)), null, null);
+ textView.setText(item.title);
+ textView.setTag(item);
+
+ textView.setOnClickListener(this);
+ textView.setOnLongClickListener(this);
+
+ CellLayout.LayoutParams lp =
+ new CellLayout.LayoutParams(item.cellX, item.cellY, item.spanX, item.spanY);
+ boolean insert = false;
+ mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true);
}
public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
@@ -66,8 +162,7 @@
@Override
public void onDropCompleted(View target, Object dragInfo, boolean success) {
if (success) {
- ShortcutsAdapter adapter = (ShortcutsAdapter)mContent.getAdapter();
- adapter.remove(mDragItem);
+ ((UserFolderInfo)mInfo).remove(mDragItem);
}
}
@@ -77,14 +172,24 @@
void bind(FolderInfo info) {
super.bind(info);
- setContentAdapter(new ShortcutsAdapter(mContext, ((UserFolderInfo) info).contents));
+ ArrayList<ShortcutInfo> children = ((UserFolderInfo)info).contents;
+ for (int i = 0; i < children.size(); i++) {
+ ShortcutInfo child = (ShortcutInfo) children.get(i);
+ if ((child.cellX == -1 && child.cellY == -1) ||
+ mContent.isOccupied(child.cellX, child.cellY)) {
+ findAndSetEmptyCells(child);
+ }
+ createAndAddShortcut((ShortcutInfo) children.get(i));
+ }
}
- // When the folder opens, we need to refresh the GridView's selection by
- // forcing a layout
@Override
void onOpen() {
super.onOpen();
+ // When the folder opens, we need to refresh the GridView's selection by
+ // forcing a layout
+ // TODO: find out if this is still necessary
+ mContent.requestLayout();
requestFocus();
}
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 585b210..66f1ceb 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -155,6 +155,7 @@
// return an (x, y) value from helper functions. Do NOT use them to maintain other state.
private int[] mTempCell = new int[2];
private int[] mTempEstimate = new int[2];
+ private float[] mDragViewVisualCenter = new float[2];
private float[] mTempOriginXY = new float[2];
private float[] mTempDragCoordinates = new float[2];
private float[] mTempTouchCoordinates = new float[2];
@@ -2132,12 +2133,23 @@
final int bmpWidth = b.getWidth();
final int bmpHeight = b.getHeight();
+
child.getLocationOnScreen(mTempXY);
final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2;
- mLauncher.lockScreenOrientation();
- mDragController.startDrag(
- b, screenX, screenY, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
+
+ Rect dragRect = null;
+ if (child instanceof BubbleTextView) {
+ int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
+ int top = child.getPaddingTop();
+ int left = (bmpWidth - iconSize) / 2;
+ int right = left + iconSize;
+ int bottom = top + iconSize;
+ dragRect = new Rect(left, top, right, bottom);
+ }
+
+ mDragController.startDrag(b, screenX, screenY, this, child.getTag(),
+ DragController.DRAG_ACTION_MOVE, dragRect);
b.recycle();
}
@@ -2161,8 +2173,6 @@
// Based on the position of the drag view, find the top left of the original view
int viewX = dragViewX + (dragView.getWidth() - child.getMeasuredWidth()) / 2;
int viewY = dragViewY + (dragView.getHeight() - child.getMeasuredHeight()) / 2;
- viewX += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
- viewY += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
// Set its old pos (in the new parent's coordinates); it will be animated
// in animateViewIntoPosition after the next layout pass
@@ -2265,22 +2275,12 @@
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
- boolean largeOrSpringLoaded = !mIsSmall || mWasSpringLoadedOnDragExit;
- int originX = largeOrSpringLoaded ? x - xOffset : x - xOffset + dragView.getWidth() / 2;
- int originY = largeOrSpringLoaded ? y - yOffset : y - yOffset + dragView.getHeight() / 2;
- if (mIsSmall || mIsInUnshrinkAnimation) {
- // get originX and originY in the local coordinate system of the screen
- mTempOriginXY[0] = originX;
- mTempOriginXY[1] = originY;
- mapPointFromSelfToChild(mDragTargetLayout, mTempOriginXY);
- originX = (int)mTempOriginXY[0];
- originY = (int)mTempOriginXY[1];
- if (!largeOrSpringLoaded) {
- originX -= mDragTargetLayout.getCellWidth() / 2;
- originY -= mDragTargetLayout.getCellHeight() / 2;
- }
- }
+ mDragViewVisualCenter = getDragViewVisualCenter(x, y, xOffset, yOffset, dragView,
+ mDragViewVisualCenter);
+
+ // We want the point to be mapped to the dragTarget.
+ mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
// When you are in customization mode and drag to a particular screen, make that the
// new current/default screen, so any subsequent taps add items to that screen
@@ -2292,14 +2292,15 @@
}
if (source != this) {
- final int[] touchXY = new int[] { originX, originY };
+ final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
+ (int) mDragViewVisualCenter[1] };
if ((mIsSmall || mIsInUnshrinkAnimation) && !mLauncher.isAllAppsVisible()) {
// When the workspace is shrunk and the drop comes from customize, don't actually
// add the item to the screen -- customize will do this itself
((ItemInfo) dragInfo).dropPos = touchXY;
return;
}
- onDropExternal(touchXY, dragInfo, mDragTargetLayout, false, dragView, originX, originY);
+ onDropExternal(touchXY, dragInfo, mDragTargetLayout, false, dragView);
} else if (mDragInfo != null) {
final View cell = mDragInfo.cell;
CellLayout dropTargetLayout = mDragTargetLayout;
@@ -2316,13 +2317,39 @@
if (dropTargetLayout != null) {
// Move internally
- mTargetCell = findNearestVacantArea(originX, originY,
- mDragInfo.spanX, mDragInfo.spanY, cell, dropTargetLayout,
- mTargetCell);
+
+ // First we find the cell nearest to point at which the item is dropped, without
+ // any consideration to whether there is an item there.
+ mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
+ (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY,
+ dropTargetLayout, mTargetCell);
final int screen = (mTargetCell == null) ?
mDragInfo.screen : indexOfChild(dropTargetLayout);
+ View v = dropTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
+ boolean hasMoved = !(mDragInfo.cellX == mTargetCell[0] &&
+ mDragInfo.cellY == mTargetCell[1]);
+
+ // If the item being dropped is a shortcut and the nearest drop cell also contains
+ // a shortcut, then create a folder with the two shortcuts.
+ if (v != null && (v.getTag() instanceof ShortcutInfo) &&
+ dragInfo instanceof ShortcutInfo && hasMoved) {
+ ShortcutInfo info1 = (ShortcutInfo) v.getTag();
+ ShortcutInfo info2 = (ShortcutInfo) dragInfo;
+ dropTargetLayout.removeView(v);
+ FolderIcon fi = mLauncher.addFolder(screen, mTargetCell[0], mTargetCell[1]);
+ fi.addItem(info1);
+ fi.addItem(info2);
+ return;
+ }
+
+ // Aside from the special case where we're dropping a shortcut onto a shortcut,
+ // we need to find the nearest cell location that is vacant
+ mTargetCell = findNearestVacantArea((int) mDragViewVisualCenter[0],
+ (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY, cell,
+ dropTargetLayout, mTargetCell);
+
if (screen != mCurrentPage) {
snapToPage(screen);
}
@@ -2380,14 +2407,30 @@
final CellLayout parent = (CellLayout) cell.getParent().getParent();
+ int loc[] = new int[2];
+ getViewLocationRelativeToSelf(dragView, loc);
+
// Prepare it to be animated into its new position
// This must be called after the view has been re-parented
- setPositionForDropAnimation(dragView, originX, originY, parent, cell);
+ setPositionForDropAnimation(dragView, loc[0], loc[1], parent, cell);
boolean animateDrop = !mWasSpringLoadedOnDragExit;
parent.onDropChild(cell, animateDrop);
}
}
+ private void getViewLocationRelativeToSelf(View v, int[] location) {
+ getLocationOnScreen(location);
+ int x = location[0];
+ int y = location[1];
+
+ v.getLocationOnScreen(location);
+ int vX = location[0];
+ int vY = location[1];
+
+ location[0] = vX - x;
+ location[1] = vY - y;
+ }
+
public void onDragEnter(DragSource source, int x, int y, int xOffset,
int yOffset, DragView dragView, Object dragInfo) {
mDragTargetLayout = null; // Reset the drag state
@@ -2616,11 +2659,11 @@
xy[1] -= (mScrollY - v.getTop());
}
- static private float squaredDistance(float[] point1, float[] point2) {
+ static private float squaredDistance(float[] point1, float[] point2) {
float distanceX = point1[0] - point2[0];
float distanceY = point2[1] - point2[1];
return distanceX * distanceX + distanceY * distanceY;
- }
+ }
/*
*
@@ -2718,21 +2761,57 @@
return bestMatchingScreen;
}
+ // This is used to compute the visual center of the dragView. This point is then
+ // used to visualize drop locations and determine where to drop an item. The idea is that
+ // the visual center represents the user's interpretation of where the item is, and hence
+ // is the appropriate point to use when determining drop location.
+ private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
+ DragView dragView, float[] recycle) {
+ float res[];
+ if (recycle == null) {
+ res = new float[2];
+ } else {
+ res = recycle;
+ }
+
+ // First off, the drag view has been shifted in a way that is not represented in the
+ // x and y values or the x/yOffsets. Here we account for that shift.
+ x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
+ y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
+
+ // These represent the visual top and left of drag view if a dragRect was provided.
+ // If a dragRect was not provided, then they correspond to the actual view left and
+ // top, as the dragRect is in that case taken to be the entire dragView.
+ // R.dimen.dragViewOffsetY.
+ int left = x - xOffset;
+ int top = y - yOffset;
+
+ // In order to find the visual center, we shift by half the dragRect
+ res[0] = left + dragView.getDragRegion().width() / 2;
+ res[1] = top + dragView.getDragRegion().height() / 2;
+
+ return res;
+ }
+
public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
// When touch is inside the scroll area, skip dragOver actions for the current screen
if (!mInScrollArea) {
CellLayout layout;
- int originX = x - xOffset;
- int originY = y - yOffset;
+ int left = x - xOffset;
+ int top = y - yOffset;
+
+ mDragViewVisualCenter = getDragViewVisualCenter(x, y, xOffset, yOffset, dragView,
+ mDragViewVisualCenter);
+
boolean shrunken = mIsSmall || mIsInUnshrinkAnimation;
if (shrunken) {
mLastDragView = dragView;
- mLastDragOriginX = originX;
- mLastDragOriginY = originY;
+ mLastDragOriginX = left;
+ mLastDragOriginY = top;
mLastDragXOffset = xOffset;
mLastDragYOffset = yOffset;
- layout = findMatchingPageForDragOver(dragView, originX, originY, xOffset, yOffset);
+ layout = findMatchingPageForDragOver(dragView, left, top, xOffset, yOffset);
if (layout != mDragTargetLayout) {
if (mDragTargetLayout != null) {
@@ -2778,35 +2857,14 @@
}
}
- if (source instanceof AllAppsPagedView) {
- // This is a hack to fix the point used to determine which cell an icon from
- // the all apps screen is over
- if (item != null && item.spanX == 1 && layout != null) {
- int dragRegionLeft = (dragView.getWidth() - layout.getCellWidth()) / 2;
-
- originX += dragRegionLeft - dragView.getDragRegionLeft();
- if (dragView.getDragRegionWidth() != layout.getCellWidth()) {
- dragView.setDragRegion(dragView.getDragRegionLeft(),
- dragView.getDragRegionTop(),
- layout.getCellWidth(),
- dragView.getDragRegionHeight());
- }
- }
- } else if (source == this) {
- // When dragging from the workspace, the drag view is slightly bigger than
- // the original view, and offset vertically. Adjust to account for this.
- final View origView = mDragInfo.cell;
- originX += (dragView.getMeasuredWidth() - origView.getWidth()) / 2;
- originY += (dragView.getMeasuredHeight() - origView.getHeight()) / 2
- + dragView.getOffsetY();
- }
-
if (mDragTargetLayout != null) {
final View child = (mDragInfo == null) ? null : mDragInfo.cell;
- float[] localOrigin = { originX, originY };
- mapPointFromSelfToChild(mDragTargetLayout, localOrigin, null);
+ // We want the point to be mapped to the dragTarget.
+ mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
- (int) localOrigin[0], (int) localOrigin[1], item.spanX, item.spanY);
+ (int) mDragViewVisualCenter[0],
+ (int) mDragViewVisualCenter[1],
+ item.spanX, item.spanY);
}
}
}
@@ -2854,7 +2912,7 @@
private void onDropExternal(int[] touchXY, Object dragInfo,
CellLayout cellLayout, boolean insertAtFirst) {
- onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null, 0, 0);
+ onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
}
/**
@@ -2866,8 +2924,7 @@
* to add an item to one of the workspace screens.
*/
private void onDropExternal(int[] touchXY, Object dragInfo,
- CellLayout cellLayout, boolean insertAtFirst, DragView dragView,
- int dragViewX, int dragViewY) {
+ CellLayout cellLayout, boolean insertAtFirst, DragView dragView) {
int screen = indexOfChild(cellLayout);
if (dragInfo instanceof PendingAddItemInfo) {
PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
@@ -2913,7 +2970,8 @@
mTargetCell = new int[2];
if (touchXY != null) {
// when dragging and dropping, just find the closest free spot
- cellLayout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, mTargetCell);
+ mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, null, cellLayout,
+ mTargetCell);
} else {
cellLayout.findCellForSpan(mTargetCell, 1, 1);
}
@@ -2926,7 +2984,11 @@
cellLayout.getChildrenLayout().measureChild(view);
if (dragView != null) {
- setPositionForDropAnimation(dragView, dragViewX, dragViewY, cellLayout, view);
+ // we have the visual center of the drag view, we need to find the actual
+ // left and top of the dragView.
+ int loc[] = new int[2];
+ getViewLocationRelativeToSelf(dragView, loc);
+ setPositionForDropAnimation(dragView, loc[0], loc[1], cellLayout, view);
}
LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
@@ -2955,16 +3017,24 @@
/**
* Calculate the nearest cell where the given object would be dropped.
+ *
+ * pixelX and pixelY should be in the coordinate system of layout
*/
private int[] findNearestVacantArea(int pixelX, int pixelY,
int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
-
- int localPixelX = pixelX - (layout.getLeft() - mScrollX);
- int localPixelY = pixelY - (layout.getTop() - mScrollY);
-
- // Find the best target drop location
return layout.findNearestVacantArea(
- localPixelX, localPixelY, spanX, spanY, ignoreView, recycle);
+ pixelX, pixelY, spanX, spanY, ignoreView, recycle);
+ }
+
+ /**
+ * Calculate the nearest cell where the given object would be dropped.
+ *
+ * pixelX and pixelY should be in the coordinate system of layout
+ */
+ private int[] findNearestArea(int pixelX, int pixelY,
+ int spanX, int spanY, CellLayout layout, int[] recycle) {
+ return layout.findNearestArea(
+ pixelX, pixelY, spanX, spanY, recycle);
}
void setLauncher(Launcher launcher) {
@@ -3117,6 +3187,21 @@
return null;
}
+ void clearDropTargets() {
+ final int screenCount = getChildCount();
+
+ for (int i = 0; i < screenCount; i++) {
+ final CellLayout layoutParent = (CellLayout) getChildAt(i);
+ final ViewGroup layout = layoutParent.getChildrenLayout();
+ int childCount = layout.getChildCount();
+ for (int j = 0; j < childCount; j++) {
+ View v = layout.getChildAt(j);
+ if (v instanceof DropTarget) {
+ mDragController.removeDropTarget((DropTarget) v);
+ }
+ }
+ }
+ }
void removeItems(final ArrayList<ApplicationInfo> apps) {
final int screenCount = getChildCount();