From a9abd0e0bdedb5cbbd12b84cb83037a735e79a20 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 27 Oct 2010 17:18:37 -0700 Subject: Initial changes to allow dragging external shortcuts to launcher using the same InstallShortcut intent. Change-Id: I21b57115429ed37d604084ae01308d1d3f33ee7e --- src/com/android/launcher2/CellLayout.java | 32 ++++-- .../android/launcher2/InstallShortcutReceiver.java | 2 +- src/com/android/launcher2/Launcher.java | 4 + src/com/android/launcher2/LauncherModel.java | 23 +++- src/com/android/launcher2/Workspace.java | 126 ++++++++++++++++++++- 5 files changed, 165 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java index 63da108d0..3c82290f8 100644 --- a/src/com/android/launcher2/CellLayout.java +++ b/src/com/android/launcher2/CellLayout.java @@ -16,7 +16,7 @@ package com.android.launcher2; -import com.android.launcher.R; +import java.util.Arrays; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -26,6 +26,7 @@ import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.WallpaperManager; +import android.content.ClipDescription; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -41,6 +42,7 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.ContextMenu; +import android.view.DragEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; @@ -49,7 +51,7 @@ import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; import android.view.animation.LayoutAnimationController; -import java.util.Arrays; +import com.android.launcher.R; public class CellLayout extends ViewGroup implements Dimmable { static final String TAG = "CellLayout"; @@ -972,7 +974,11 @@ public class CellLayout extends ViewGroup implements Dimmable { final int oldDragCellX = mDragCell[0]; final int oldDragCellY = mDragCell[1]; final int[] nearest = findNearestVacantArea(originX, originY, spanX, spanY, v, mDragCell); - mDragCenter.set(originX + (v.getWidth() / 2), originY + (v.getHeight() / 2)); + if (v != null) { + mDragCenter.set(originX + (v.getWidth() / 2), originY + (v.getHeight() / 2)); + } else { + mDragCenter.set(originX, originY); + } if (nearest != null && (nearest[0] != oldDragCellX || nearest[1] != oldDragCellY)) { // Find the top left corner of the rect the object will occupy @@ -982,15 +988,17 @@ public class CellLayout extends ViewGroup implements Dimmable { int left = topLeft[0]; int top = topLeft[1]; - if (v.getParent() instanceof CellLayout) { - LayoutParams lp = (LayoutParams) v.getLayoutParams(); - left += lp.leftMargin; - top += lp.topMargin; - } + if (v != null) { + if (v.getParent() instanceof CellLayout) { + LayoutParams lp = (LayoutParams) v.getLayoutParams(); + left += lp.leftMargin; + top += lp.topMargin; + } - // Offsets due to the size difference between the View and the dragOutline - left += (v.getWidth() - dragOutline.getWidth()) / 2; - top += (v.getHeight() - dragOutline.getHeight()) / 2; + // Offsets due to the size difference between the View and the dragOutline + left += (v.getWidth() - dragOutline.getWidth()) / 2; + top += (v.getHeight() - dragOutline.getHeight()) / 2; + } final int oldIndex = mDragOutlineCurrent; mDragOutlineAnims[oldIndex].animateOut(); @@ -1271,7 +1279,7 @@ public class CellLayout extends ViewGroup implements Dimmable { * It may have begun over this layout (in which case onDragChild is called first), * or it may have begun on another layout. */ - void onDragEnter(View dragView) { + void onDragEnter() { if (!mDragging) { // Fade in the drag indicators if (mCrosshairsAnimator != null) { diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java index caeb12b4a..8d72531d5 100644 --- a/src/com/android/launcher2/InstallShortcutReceiver.java +++ b/src/com/android/launcher2/InstallShortcutReceiver.java @@ -26,7 +26,7 @@ import android.widget.Toast; import com.android.launcher.R; public class InstallShortcutReceiver extends BroadcastReceiver { - private static final String ACTION_INSTALL_SHORTCUT = + public static final String ACTION_INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT"; private final int[] mCoordinates = new int[2]; diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index 444357ebc..346e472c5 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -1144,6 +1144,10 @@ public final class Launcher extends Activity return mAppWidgetHost; } + public LauncherModel getModel() { + return mModel; + } + void closeSystemDialogs() { getWindow().closeAllPanels(); diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java index 1e58ca0e4..179a5d5c0 100644 --- a/src/com/android/launcher2/LauncherModel.java +++ b/src/com/android/launcher2/LauncherModel.java @@ -1513,14 +1513,25 @@ public class LauncherModel extends BroadcastReceiver { ShortcutInfo addShortcut(Context context, Intent data, int screen, int cellX, int cellY, boolean notify) { - final ShortcutInfo info = infoFromShortcutIntent(context, data); + final ShortcutInfo info = infoFromShortcutIntent(context, data, null); addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, cellX, cellY, notify); return info; } - private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) { + /** + * Ensures that a given shortcut intent actually has all the fields that we need to create a + * proper ShortcutInfo. + */ + boolean validateShortcutIntent(Intent data) { + // We don't require Intent.EXTRA_SHORTCUT_ICON, since we can pull a default fallback icon + return InstallShortcutReceiver.ACTION_INSTALL_SHORTCUT.equals(data.getAction()) && + (data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT) != null) && + (data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME) != null); + } + + ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) { Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); @@ -1553,8 +1564,12 @@ public class LauncherModel extends BroadcastReceiver { final ShortcutInfo info = new ShortcutInfo(); if (icon == null) { - icon = getFallbackIcon(); - info.usingFallbackIcon = true; + if (fallbackIcon != null) { + icon = fallbackIcon; + } else { + icon = getFallbackIcon(); + info.usingFallbackIcon = true; + } } info.setIcon(icon); diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index ce613f135..07faed101 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -16,7 +16,8 @@ package com.android.launcher2; -import com.android.launcher.R; +import java.util.ArrayList; +import java.util.HashSet; import android.animation.Animator; import android.animation.Animator.AnimatorListener; @@ -27,6 +28,8 @@ import android.animation.PropertyValuesHolder; import android.app.WallpaperManager; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; +import android.content.ClipData; +import android.content.ClipDescription; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -36,8 +39,11 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Matrix; +import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -45,12 +51,13 @@ import android.os.IBinder; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; +import android.view.DragEvent; import android.view.MotionEvent; import android.view.View; import android.widget.TextView; +import android.widget.Toast; -import java.util.ArrayList; -import java.util.HashSet; +import com.android.launcher.R; /** * The workspace is a wide area with a wallpaper and a finite number of pages. @@ -144,6 +151,9 @@ public class Workspace extends SmoothPagedView private final Rect mTempRect = new Rect(); private final int[] mTempXY = new int[2]; + // Paint used to draw external drop outline + private final Paint mExternalDragOutlinePaint = new Paint(); + /** * Used to inflate the Workspace from XML. * @@ -193,6 +203,7 @@ public class Workspace extends SmoothPagedView Launcher.setScreen(mCurrentPage); LauncherApplication app = (LauncherApplication)context.getApplicationContext(); mIconCache = app.getIconCache(); + mExternalDragOutlinePaint.setAntiAlias(true); mUnshrinkAnimationListener = new AnimatorListenerAdapter() { public void onAnimationStart(Animator animation) { @@ -983,6 +994,29 @@ public class Workspace extends SmoothPagedView return b; } + /** + * Creates a drag outline to represent a drop (that we don't have the actual information for + * yet). May be changed in the future to alter the drop outline slightly depending on the + * clip description mime data. + */ + private Bitmap createExternalDragOutline(Canvas canvas, int padding) { + Resources r = getResources(); + final int outlineColor = r.getColor(R.color.drag_outline_color); + final int iconWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width); + final int iconHeight = r.getDimensionPixelSize(R.dimen.workspace_cell_height); + final int rectRadius = r.getDimensionPixelSize(R.dimen.external_drop_icon_rect_radius); + final int inset = (int) (Math.min(iconWidth, iconHeight) * 0.2f); + final Bitmap b = Bitmap.createBitmap( + iconWidth + padding, iconHeight + padding, Bitmap.Config.ARGB_8888); + + canvas.setBitmap(b); + canvas.drawRoundRect(new RectF(inset, inset, iconWidth - inset, iconHeight - inset), + rectRadius, rectRadius, mExternalDragOutlinePaint); + mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true); + + return b; + } + /** * Returns a new bitmap to show when the given View is being dragged around. * Responsibility for the bitmap is transferred to the caller. @@ -1136,7 +1170,7 @@ public class Workspace extends SmoothPagedView if (!mIsSmall) { mDragTargetLayout = getCurrentDropLayout(); - mDragTargetLayout.onDragEnter(dragView); + mDragTargetLayout.onDragEnter(); showOutlines(); } } @@ -1182,6 +1216,88 @@ public class Workspace extends SmoothPagedView return null; } + /** + * Global drag and drop handler + */ + @Override + public boolean onDragEvent(DragEvent event) { + final CellLayout layout = (CellLayout) getChildAt(mCurrentPage); + final int[] pos = new int[2]; + layout.getLocationOnScreen(pos); + // We need to offset the drag coordinates to layout coordinate space + final int x = (int) event.getX() - pos[0]; + final int y = (int) event.getY() - pos[1]; + + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_STARTED: + // Check if we have enough space on this screen to add a new shortcut + if (!layout.findCellForSpan(pos, 1, 1)) { + Toast.makeText(mContext, mContext.getString(R.string.out_of_space), + Toast.LENGTH_SHORT).show(); + return false; + } + + ClipDescription desc = event.getClipDescription(); + if (desc.filterMimeTypes(ClipDescription.MIMETYPE_TEXT_INTENT) != null) { + // Create the drag outline + // We need to add extra padding to the bitmap to make room for the glow effect + final Canvas canvas = new Canvas(); + final int bitmapPadding = HolographicOutlineHelper.OUTER_BLUR_RADIUS; + mDragOutline = createExternalDragOutline(canvas, bitmapPadding); + + // Show the current page outlines to indicate that we can accept this drop + showOutlines(); + layout.setHover(true); + layout.onDragEnter(); + layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1); + + return true; + } + break; + case DragEvent.ACTION_DRAG_LOCATION: + // Visualize the drop location + layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1); + return true; + case DragEvent.ACTION_DROP: + // Check if we have enough space on this screen to add a new shortcut + if (!layout.findCellForSpan(pos, 1, 1)) { + Toast.makeText(mContext, mContext.getString(R.string.out_of_space), + Toast.LENGTH_SHORT).show(); + return false; + } + + // Try and add any shortcuts + int newDropCount = 0; + final LauncherModel model = mLauncher.getModel(); + final ClipData data = event.getClipData(); + final int itemCount = data.getItemCount(); + for (int i = 0; i < itemCount; ++i) { + final Intent intent = data.getItem(i).getIntent(); + if (intent != null && model.validateShortcutIntent(intent)) { + ShortcutInfo info = model.infoFromShortcutIntent(mContext, intent, data. + getIcon()); + onDropExternal(x, y, info, layout); + newDropCount++; + } + } + + // Show error message if we couldn't accept any of the items + if (newDropCount <= 0) { + Toast.makeText(mContext, "Only Shortcut Intents accepted.", + Toast.LENGTH_SHORT).show(); + } + + return true; + case DragEvent.ACTION_DRAG_ENDED: + // Hide the page outlines after the drop + layout.setHover(false); + layout.onDragExit(); + hideOutlines(); + return true; + } + return super.onDragEvent(event); + } + /* * * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's @@ -1382,7 +1498,7 @@ public class Workspace extends SmoothPagedView if (mDragTargetLayout != null) { mDragTargetLayout.onDragExit(); } - layout.onDragEnter(dragView); + layout.onDragEnter(); mDragTargetLayout = layout; } -- cgit v1.2.3