diff options
-rw-r--r-- | src/com/android/launcher2/AppsCustomizePagedView.java | 106 | ||||
-rw-r--r-- | src/com/android/launcher2/DeleteDropTarget.java | 24 | ||||
-rw-r--r-- | src/com/android/launcher2/DragLayer.java | 73 | ||||
-rw-r--r-- | src/com/android/launcher2/DragView.java | 36 | ||||
-rw-r--r-- | src/com/android/launcher2/FolderIcon.java | 5 | ||||
-rw-r--r-- | src/com/android/launcher2/Launcher.java | 139 | ||||
-rw-r--r-- | src/com/android/launcher2/PagedViewWidget.java | 65 | ||||
-rw-r--r-- | src/com/android/launcher2/PendingAddItemInfo.java | 4 | ||||
-rw-r--r-- | src/com/android/launcher2/Workspace.java | 132 |
9 files changed, 464 insertions, 120 deletions
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java index 7cfe3be2f..de96edade 100644 --- a/src/com/android/launcher2/AppsCustomizePagedView.java +++ b/src/com/android/launcher2/AppsCustomizePagedView.java @@ -19,6 +19,7 @@ package com.android.launcher2; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; @@ -39,6 +40,8 @@ import android.graphics.Rect; import android.graphics.TableMaskFilter; import android.graphics.drawable.Drawable; import android.os.AsyncTask; +import android.os.Handler; +import android.os.HandlerThread; import android.os.Process; import android.util.AttributeSet; import android.util.Log; @@ -167,7 +170,7 @@ class AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTas */ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements AllAppsView, View.OnClickListener, View.OnKeyListener, DragSource, - PagedViewIcon.PressedCallback { + PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener { static final String LOG_TAG = "AppsCustomizePagedView"; /** @@ -229,6 +232,14 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen ArrayList<AppsCustomizeAsyncTask> mRunningTasks; private static final int sPageSleepDelay = 200; + private Runnable mInflateWidgetRunnable = null; + private Runnable mBindWidgetRunnable = null; + static final int WIDGET_NO_CLEANUP_REQUIRED = -1; + static final int WIDGET_BOUND = 0; + static final int WIDGET_INFLATED = 1; + int mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED; + int mWidgetLoadingId = -1; + public AppsCustomizePagedView(Context context, AttributeSet attrs) { super(context, attrs); mLayoutInflater = LayoutInflater.from(context); @@ -536,7 +547,64 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mLauncher.getWorkspace().beginDragShared(v, this); } + private void loadWidgetInBackground(final PendingAddWidgetInfo info) { + final AppWidgetProviderInfo pInfo = info.info; + if (pInfo.configure != null) { + return; + } + + mBindWidgetRunnable = new Runnable() { + @Override + public void run() { + mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); + AppWidgetManager.getInstance(mLauncher).bindAppWidgetId(mWidgetLoadingId, + info.componentName); + mWidgetCleanupState = WIDGET_BOUND; + } + }; + post(mBindWidgetRunnable); + + mInflateWidgetRunnable = new Runnable() { + @Override + public void run() { + AppWidgetHostView hostView = + mLauncher.getAppWidgetHost().createView(mContext, mWidgetLoadingId, pInfo); + info.boundWidget = hostView; + mWidgetCleanupState = WIDGET_INFLATED; + } + }; + post(mInflateWidgetRunnable); + } + + @Override + public void onShortPress(View v) { + // We are anticipating a long press, and we use this time to load bind and instantiate + // the widget. This will need to be cleaned up if it turns out no long press occurs. + PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) v.getTag(); + loadWidgetInBackground(createWidgetInfo); + } + + @Override + public void cleanUpShortPress(View v) { + PendingAddWidgetInfo info = (PendingAddWidgetInfo) v.getTag(); + if (mWidgetCleanupState >= 0 && mWidgetLoadingId != -1) { + mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); + } + if (mWidgetCleanupState == WIDGET_BOUND) { + removeCallbacks(mInflateWidgetRunnable); + } else if (mWidgetCleanupState == WIDGET_INFLATED) { + AppWidgetHostView widget = info.boundWidget; + int widgetId = widget.getAppWidgetId(); + mLauncher.getAppWidgetHost().deleteAppWidgetId(widgetId); + } + mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED; + mWidgetLoadingId = -1; + } + private void beginDraggingWidget(View v) { + mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED; + mWidgetLoadingId = -1; + // Get the widget preview as the drag representation ImageView image = (ImageView) v.findViewById(R.id.widget_preview); PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag(); @@ -547,13 +615,13 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen if (createItemInfo instanceof PendingAddWidgetInfo) { PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo; int[] spanXY = mLauncher.getSpanForWidget(createWidgetInfo, null); + int[] size = mLauncher.getWorkspace().estimateItemSize(spanXY[0], + spanXY[1], createWidgetInfo, true); createItemInfo.spanX = spanXY[0]; createItemInfo.spanY = spanXY[1]; - int[] maxSize = mLauncher.getWorkspace().estimateItemSize(spanXY[0], spanXY[1], - createWidgetInfo, true); preview = getWidgetPreview(createWidgetInfo.componentName, createWidgetInfo.previewImage, - createWidgetInfo.icon, spanXY[0], spanXY[1], maxSize[0], maxSize[1]); + createWidgetInfo.icon, spanXY[0], spanXY[1], size[0], size[1]); } else { // Workaround for the fact that we don't keep the original ResolveInfo associated with // the shortcut around. To get the icon, we just render the preview image (which has @@ -593,24 +661,33 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen outline.recycle(); preview.recycle(); } - @Override - protected boolean beginDragging(View v) { - // Dismiss the cling - mLauncher.dismissAllAppsCling(null); + @Override + protected boolean beginDragging(final View v) { if (!super.beginDragging(v)) return false; - // Reset the alpha on the dragged icon before we drag - resetDrawableState(); - - // Go into spring loaded mode (must happen before we startDrag()) - mLauncher.enterSpringLoadedDragMode(); - if (v instanceof PagedViewIcon) { beginDraggingApplication(v); } else if (v instanceof PagedViewWidget) { beginDraggingWidget(v); } + + // We delay entering spring-loaded mode slightly to make sure the UI + // thready is free of any work. + postDelayed(new Runnable() { + @Override + public void run() { + // Dismiss the cling + mLauncher.dismissAllAppsCling(null); + + // Reset the alpha on the dragged icon before we drag + resetDrawableState(); + + // Go into spring loaded mode (must happen before we startDrag()) + mLauncher.enterSpringLoadedDragMode(); + } + },150); + return true; } private void endDragging(View target, boolean success) { @@ -1045,6 +1122,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int[] cellSpans = mLauncher.getSpanForWidget(info, null); widget.applyFromAppWidgetProviderInfo(info, -1, cellSpans); widget.setTag(createItemInfo); + widget.setShortPressListener(this); } else if (rawInfo instanceof ResolveInfo) { // Fill in the shortcuts information ResolveInfo info = (ResolveInfo) rawInfo; diff --git a/src/com/android/launcher2/DeleteDropTarget.java b/src/com/android/launcher2/DeleteDropTarget.java index 3b82f9e94..a6b2b5c8b 100644 --- a/src/com/android/launcher2/DeleteDropTarget.java +++ b/src/com/android/launcher2/DeleteDropTarget.java @@ -165,23 +165,30 @@ public class DeleteDropTarget extends ButtonDropTarget { } } - private void animateToTrashAndCompleteDrop(final DragObject d) { + Rect getDeleteRect(int deleteItemWidth, int deleteItemHeight) { DragLayer dragLayer = mLauncher.getDragLayer(); - Rect from = new Rect(); + Rect to = new Rect(); - dragLayer.getViewRectRelativeToSelf(d.dragView, from); dragLayer.getViewRectRelativeToSelf(this, to); - int width = mCurrentDrawable.getIntrinsicWidth(); int height = mCurrentDrawable.getIntrinsicHeight(); to.set(to.left + getPaddingLeft(), to.top + getPaddingTop(), to.left + getPaddingLeft() + width, to.bottom); // Center the destination rect about the trash icon - int xOffset = (int) -(d.dragView.getMeasuredWidth() - width) / 2; - int yOffset = (int) -(d.dragView.getMeasuredHeight() - height) / 2; + int xOffset = (int) -(deleteItemWidth - width) / 2; + int yOffset = (int) -(deleteItemHeight - height) / 2; to.offset(xOffset, yOffset); + return to; + } + + private void animateToTrashAndCompleteDrop(final DragObject d) { + DragLayer dragLayer = mLauncher.getDragLayer(); + Rect from = new Rect(); + dragLayer.getViewRectRelativeToSelf(d.dragView, from); + Rect to = getDeleteRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight()); + mSearchDropTargetBar.deferOnDragEnd(); Runnable onAnimationEndRunnable = new Runnable() { @Override @@ -191,9 +198,10 @@ public class DeleteDropTarget extends ButtonDropTarget { completeDrop(d); } }; - dragLayer.animateView(d.dragView, from, to, 0.1f, 0.1f, + dragLayer.animateView(d.dragView, from, to, 0.1f, 1, 1, 0.1f, 0.1f, DELETE_ANIMATION_DURATION, new DecelerateInterpolator(2), - new DecelerateInterpolator(1.5f), onAnimationEndRunnable, false, null); + new DecelerateInterpolator(1.5f), onAnimationEndRunnable, + DragLayer.ANIMATION_END_DISAPPEAR, null); } private void completeDrop(DragObject d) { diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java index c315b6018..6f3bcd1ac 100644 --- a/src/com/android/launcher2/DragLayer.java +++ b/src/com/android/launcher2/DragLayer.java @@ -67,12 +67,16 @@ public class DragLayer extends FrameLayout { private View mAnchorView = null; private int[] mDropViewPos = new int[2]; - private float mDropViewScale; + private float mDropViewScaleX; + private float mDropViewScaleY; private float mDropViewAlpha; private boolean mHoverPointClosesFolder = false; private Rect mHitRect = new Rect(); private int mWorkspaceIndex = -1; private int mQsbIndex = -1; + public static final int ANIMATION_END_DISAPPEAR = 0; + public static final int ANIMATION_END_FADE_OUT = 1; + public static final int ANIMATION_END_REMAIN_VISIBLE = 2; /** * Used to create a new DragLayer from XML. @@ -414,15 +418,23 @@ public class DragLayer extends FrameLayout { animateViewIntoPosition(dragView, child, null); } - public void animateViewIntoPosition(DragView dragView, final int[] pos, float scale, - Runnable onFinishRunnable) { + public void animateViewIntoPosition(DragView dragView, final int[] pos, float scaleX, float + scaleY, int animationEndStyle, Runnable onFinishRunnable, int duration) { Rect r = new Rect(); getViewRectRelativeToSelf(dragView, r); final int fromX = r.left; final int fromY = r.top; - animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], scale, - onFinishRunnable, true, -1, null); + animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], 1, 1, 1, scaleX, scaleY, + onFinishRunnable, animationEndStyle, duration, null); + } + + public void scaleViewIntoPosition(DragView dragView, final int[] pos, float finalAlpha, + float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable, + int duration) { + animateViewIntoPosition(dragView, pos[0], pos[1], pos[0], pos[1], finalAlpha, + mDropViewScaleX, mDropViewScaleY, scaleX, scaleY, onFinishRunnable, + animationEndStyle, duration, null); } public void animateViewIntoPosition(DragView dragView, final View child, @@ -486,18 +498,19 @@ public class DragLayer extends FrameLayout { oa.start(); } }; - animateViewIntoPosition(dragView, fromX, fromY, toX, toY, scale, - onCompleteRunnable, true, duration, anchorView); + animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, scale, scale, + onCompleteRunnable, ANIMATION_END_FADE_OUT, duration, anchorView); } private void animateViewIntoPosition(final View view, final int fromX, final int fromY, - final int toX, final int toY, float finalScale, Runnable onCompleteRunnable, - boolean fadeOut, int duration, View anchorView) { + final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY, + float finalScaleX, float finalScaleY, Runnable onCompleteRunnable, + int animationEndStyle, int duration, View anchorView) { Rect from = new Rect(fromX, fromY, fromX + view.getMeasuredWidth(), fromY + view.getMeasuredHeight()); Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight()); - animateView(view, from, to, 1f, finalScale, duration, null, null, - onCompleteRunnable, true, anchorView); + animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration, + null, null, onCompleteRunnable, animationEndStyle, anchorView); } /** @@ -522,9 +535,10 @@ public class DragLayer extends FrameLayout { * only used for the X dimension for the case of the workspace. */ public void animateView(final View view, final Rect from, final Rect to, final float finalAlpha, - final float finalScale, int duration, final Interpolator motionInterpolator, + final float initScaleX, final float initScaleY, final float finalScaleX, + final float finalScaleY, int duration, final Interpolator motionInterpolator, final Interpolator alphaInterpolator, final Runnable onCompleteRunnable, - final boolean fadeOut, View anchorView) { + final int animationEndStyle, View anchorView) { // Calculate the duration of the animation based on the object's distance final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) + Math.pow(to.top - from.top, 2)); @@ -578,10 +592,10 @@ public class DragLayer extends FrameLayout { mDropViewPos[0] = from.left + (int) Math.round(((to.left - from.left) * motionPercent)); mDropViewPos[1] = from.top + (int) Math.round(((to.top - from.top) * motionPercent)); - mDropViewScale = percent * finalScale + (1 - percent); + mDropViewScaleX = percent * finalScaleX + (1 - percent) * initScaleX; + mDropViewScaleY = percent * finalScaleY + (1 - percent) * initScaleY; mDropViewAlpha = alphaPercent * finalAlpha + (1 - alphaPercent) * initialAlpha; - invalidate(mDropViewPos[0], mDropViewPos[1], - mDropViewPos[0] + width, mDropViewPos[1] + height); + invalidate(); } }); mDropAnim.addListener(new AnimatorListenerAdapter() { @@ -589,16 +603,32 @@ public class DragLayer extends FrameLayout { if (onCompleteRunnable != null) { onCompleteRunnable.run(); } - if (fadeOut) { + switch (animationEndStyle) { + case ANIMATION_END_DISAPPEAR: + clearAnimatedView(); + break; + case ANIMATION_END_FADE_OUT: fadeOutDragView(); - } else { - mDropView = null; + break; + case ANIMATION_END_REMAIN_VISIBLE: + break; } } }); mDropAnim.start(); } + public void clearAnimatedView() { + mDropView = null; + mDropViewScaleX = 1; + mDropViewScaleY = 1; + invalidate(); + } + + public View getAnimatedView() { + return mDropView; + } + private void fadeOutDragView() { mFadeOutAnim = new ValueAnimator(); mFadeOutAnim.setDuration(150); @@ -617,6 +647,7 @@ public class DragLayer extends FrameLayout { mFadeOutAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { mDropView = null; + invalidate(); } }); mFadeOutAnim.start(); @@ -679,8 +710,8 @@ public class DragLayer extends FrameLayout { int width = mDropView.getMeasuredWidth(); int height = mDropView.getMeasuredHeight(); canvas.translate(xPos, yPos); - canvas.translate((1 - mDropViewScale) * width / 2, (1 - mDropViewScale) * height / 2); - canvas.scale(mDropViewScale, mDropViewScale); + canvas.translate((1 - mDropViewScaleX) * width / 2, (1 - mDropViewScaleY) * height / 2); + canvas.scale(mDropViewScaleX, mDropViewScaleY); mDropView.setAlpha(mDropViewAlpha); mDropView.draw(canvas); canvas.restore(); diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java index a3063b6e9..15d9c5449 100644 --- a/src/com/android/launcher2/DragView.java +++ b/src/com/android/launcher2/DragView.java @@ -33,6 +33,7 @@ import com.android.launcher.R; public class DragView extends View { private Bitmap mBitmap; + private Bitmap mCrossFadeBitmap; private Paint mPaint; private int mRegistrationX; private int mRegistrationY; @@ -41,6 +42,7 @@ public class DragView extends View { private Rect mDragRegion = null; private DragLayer mDragLayer = null; private boolean mHasDrawn = false; + private float mCrossFadeProgress = 0f; ValueAnimator mAnim; private float mOffsetX = 0.0f; @@ -164,9 +166,43 @@ public class DragView extends View { p.setColor(0xaaffffff); canvas.drawRect(0, 0, getWidth(), getHeight(), p); } + if (mPaint == null) { + mPaint = new Paint(); + } mHasDrawn = true; + boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null; + if (crossFade) { + int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255; + mPaint.setAlpha(alpha); + } canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint); + if (crossFade) { + mPaint.setAlpha((int) (255 * mCrossFadeProgress)); + canvas.save(); + float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth(); + float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight(); + canvas.scale(sX, sY); + canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint); + canvas.restore(); + } + } + + public void setCrossFadeBitmap(Bitmap crossFadeBitmap) { + mCrossFadeBitmap = crossFadeBitmap; + } + + public void crossFade(int duration) { + ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); + va.setDuration(duration); + va.setInterpolator(new DecelerateInterpolator(1.5f)); + va.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mCrossFadeProgress = animation.getAnimatedFraction(); + } + }); + va.start(); } public void setPaint(Paint paint) { diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java index 2a711f88b..ca537d8c4 100644 --- a/src/com/android/launcher2/FolderIcon.java +++ b/src/com/android/launcher2/FolderIcon.java @@ -359,10 +359,11 @@ public class FolderIcon extends LinearLayout implements FolderListener { float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f; + float finalScale = scale * scaleRelativeToDragLayer; dragLayer.animateView(animateView, from, to, finalAlpha, - scale * scaleRelativeToDragLayer, DROP_IN_ANIMATION_DURATION, + 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION, new DecelerateInterpolator(2), new AccelerateInterpolator(2), - postAnimationRunnable, false, null); + postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); postDelayed(new Runnable() { public void run() { addItem(item); diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index 7d974a513..708d5d631 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -49,6 +49,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; +import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -246,6 +247,7 @@ public final class Launcher extends Activity private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2]; static final ArrayList<String> sDumpLogs = new ArrayList<String>(); + PendingAddWidgetInfo mWidgetBeingConfigured = null; private BubbleTextView mWaitingForResume; @@ -498,7 +500,7 @@ public final class Launcher extends Activity break; case REQUEST_CREATE_APPWIDGET: int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); - completeAddAppWidget(appWidgetId, args.container, args.screen); + completeAddAppWidget(appWidgetId, args.container, args.screen, null, null); result = true; break; case REQUEST_PICK_WALLPAPER: @@ -509,10 +511,20 @@ public final class Launcher extends Activity } @Override - protected void onActivityResult(final int requestCode, int resultCode, final Intent data) { + protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { boolean delayExitSpringLoadedMode = false; + boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET || + requestCode == REQUEST_CREATE_APPWIDGET); mWaitingForResult = false; + // We have special handling for widgets + if (isWidgetDrop) { + int appWidgetId = data != null ? + data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; + completeTwoStageWidgetDrop(resultCode, appWidgetId); + return; + } + // The pattern used here is that a user PICKs a specific application, // which, depending on the target, might need to CREATE the actual target. @@ -526,26 +538,50 @@ public final class Launcher extends Activity args.screen = mPendingAddInfo.screen; args.cellX = mPendingAddInfo.cellX; args.cellY = mPendingAddInfo.cellY; - - // If the loader is still running, defer the add until it is done. if (isWorkspaceLocked()) { sPendingAddList.add(args); } else { delayExitSpringLoadedMode = completeAdd(args); } - } else if ((requestCode == REQUEST_PICK_APPWIDGET || - requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED) { - if (data != null) { - // Clean up the appWidgetId if we canceled - int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); - if (appWidgetId != -1) { - mAppWidgetHost.deleteAppWidgetId(appWidgetId); - } - } } - + mDragLayer.clearAnimatedView(); // Exit spring loaded mode if necessary after cancelling the configuration of a widget - exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode); + exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode, + null); + } + + private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) { + CellLayout cellLayout = (CellLayout) mWorkspace.getChildAt(mWidgetBeingConfigured.screen); + Runnable onCompleteRunnable = null; + int animationType = 0; + + if (resultCode == RESULT_OK) { + animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION; + final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId, + mWidgetBeingConfigured.info); + mWidgetBeingConfigured.boundWidget = layout; + onCompleteRunnable = new Runnable() { + @Override + public void run() { + completeAddAppWidget(appWidgetId, mPendingAddInfo.container, + mPendingAddInfo.screen, layout, null); + exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false, + null); + } + }; + } else if (resultCode == RESULT_CANCELED) { + animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION; + onCompleteRunnable = new Runnable() { + @Override + public void run() { + exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false, + null); + } + }; + } + mWorkspace.animateExternalDrop(mWidgetBeingConfigured, cellLayout, + (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable, + animationType); } @Override @@ -934,8 +970,11 @@ public final class Launcher extends Activity * @param appWidgetId The app widget id * @param cellInfo The position on screen where to create the widget. */ - private void completeAddAppWidget(final int appWidgetId, long container, int screen) { - AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); + private void completeAddAppWidget(final int appWidgetId, long container, int screen, + AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) { + if (appWidgetInfo == null) { + appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); + } // Calculate the grid spans needed to fit this widget CellLayout layout = getCellLayout(container, screen); @@ -984,12 +1023,16 @@ public final class Launcher extends Activity container, screen, cellXY[0], cellXY[1], false); if (!mRestoring) { - // Perform actual inflation because we're live - launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); + if (hostView == null) { + // Perform actual inflation because we're live + launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); + launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo); + } else { + // The AppWidgetHostView has already been inflated and instantiated + launcherInfo.hostView = hostView; + } - launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo); launcherInfo.hostView.setTag(launcherInfo); - mWorkspace.addInScreen(launcherInfo.hostView, container, screen, cellXY[0], cellXY[1], launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked()); @@ -1427,9 +1470,9 @@ public final class Launcher extends Activity addAppWidgetImpl(appWidgetId, null); } - void addAppWidgetImpl(int appWidgetId, PendingAddWidgetInfo info) { - AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId); - + void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info) { + final AppWidgetProviderInfo appWidget = info.info; + Runnable configurationActivity = null; if (appWidget.configure != null) { // Launch over to configure widget, if needed Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); @@ -1437,9 +1480,8 @@ public final class Launcher extends Activity intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); if (info != null) { if (info.mimeType != null && !info.mimeType.isEmpty()) { - intent.putExtra( - InstallWidgetReceiver.EXTRA_APPWIDGET_CONFIGURATION_DATA_MIME_TYPE, - info.mimeType); + intent.putExtra(InstallWidgetReceiver. + EXTRA_APPWIDGET_CONFIGURATION_DATA_MIME_TYPE, info.mimeType); final String mimeType = info.mimeType; final ClipData clipData = (ClipData) info.configurationData; @@ -1450,8 +1492,8 @@ public final class Launcher extends Activity final CharSequence stringData = item.getText(); final Uri uriData = item.getUri(); final Intent intentData = item.getIntent(); - final String key = - InstallWidgetReceiver.EXTRA_APPWIDGET_CONFIGURATION_DATA; + final String key = InstallWidgetReceiver. + EXTRA_APPWIDGET_CONFIGURATION_DATA; if (uriData != null) { intent.putExtra(key, uriData); } else if (intentData != null) { @@ -1464,14 +1506,13 @@ public final class Launcher extends Activity } } } - startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET); + mWidgetBeingConfigured = info; } else { // Otherwise just add it - completeAddAppWidget(appWidgetId, info.container, info.screen); - + completeAddAppWidget(appWidgetId, info.container, info.screen, info.boundWidget, appWidget); // Exit spring loaded mode if necessary after adding the widget - exitSpringLoadedDragModeDelayed(true, false); + exitSpringLoadedDragModeDelayed(true, false, null); } } @@ -1519,9 +1560,16 @@ public final class Launcher extends Activity mPendingAddInfo.cellY = cell[1]; } - int appWidgetId = getAppWidgetHost().allocateAppWidgetId(); - AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, info.componentName); + AppWidgetHostView hostView = info.boundWidget; + int appWidgetId; + if (hostView != null) { + appWidgetId = hostView.getAppWidgetId(); + } else { + appWidgetId = getAppWidgetHost().allocateAppWidgetId(); + AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, info.componentName); + } addAppWidgetImpl(appWidgetId, info); + } void processShortcut(Intent intent) { @@ -2368,8 +2416,9 @@ public final class Launcher extends Activity * This is the opposite of showAppsCustomizeHelper. * @param animated If true, the transition will be animated. */ - private void hideAppsCustomizeHelper( - State toState, final boolean animated, final boolean springLoaded) { + private void hideAppsCustomizeHelper(State toState, final boolean animated, + final boolean springLoaded, final Runnable onCompleteRunnable) { + if (mStateAnimation != null) { mStateAnimation.cancel(); mStateAnimation = null; @@ -2426,6 +2475,9 @@ public final class Launcher extends Activity dispatchOnLauncherTransitionEnd(fromView, animated, true); dispatchOnLauncherTransitionEnd(toView, animated, true); mWorkspace.hideScrollingIndicator(false); + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } } }); @@ -2453,9 +2505,13 @@ public final class Launcher extends Activity } void showWorkspace(boolean animated) { + showWorkspace(animated, null); + } + + void showWorkspace(boolean animated, Runnable onCompleteRunnable) { if (mState != State.WORKSPACE) { mWorkspace.setVisibility(View.VISIBLE); - hideAppsCustomizeHelper(State.WORKSPACE, animated, false); + hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable); // Show the search bar and hotseat mSearchDropTargetBar.showSearchBar(animated); @@ -2504,13 +2560,14 @@ public final class Launcher extends Activity void enterSpringLoadedDragMode() { if (mState == State.APPS_CUSTOMIZE) { - hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true); + hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true, null); hideDockDivider(); mState = State.APPS_CUSTOMIZE_SPRING_LOADED; } } - void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay) { + void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay, + final Runnable onCompleteRunnable) { if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return; mHandler.postDelayed(new Runnable() { @@ -2522,7 +2579,7 @@ public final class Launcher extends Activity // clean up our state transition functions mAppsCustomizeTabHost.setVisibility(View.GONE); mSearchDropTargetBar.showSearchBar(true); - showWorkspace(true); + showWorkspace(true, onCompleteRunnable); } else { exitSpringLoadedDragMode(); } diff --git a/src/com/android/launcher2/PagedViewWidget.java b/src/com/android/launcher2/PagedViewWidget.java index 12e9c4673..5ba869118 100644 --- a/src/com/android/launcher2/PagedViewWidget.java +++ b/src/com/android/launcher2/PagedViewWidget.java @@ -23,6 +23,7 @@ import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -40,6 +41,9 @@ public class PagedViewWidget extends LinearLayout { private ImageView mPreviewImageView; private String mDimensionsFormatString; + CheckForShortPress mPendingCheckForShortPress = null; + ShortPressListener mShortPressListener = null; + boolean mShortPressTriggered = false; public PagedViewWidget(Context context) { this(context, null); @@ -127,8 +131,67 @@ public class PagedViewWidget extends LinearLayout { } } + void setShortPressListener(ShortPressListener listener) { + mShortPressListener = listener; + } + + interface ShortPressListener { + void onShortPress(View v); + void cleanUpShortPress(View v); + } + + class CheckForShortPress implements Runnable { + public void run() { + if (mShortPressListener != null) { + mShortPressListener.onShortPress(PagedViewWidget.this); + } + mShortPressTriggered = true; + } + } + + private void checkForShortPress() { + if (mPendingCheckForShortPress == null) { + mPendingCheckForShortPress = new CheckForShortPress(); + } + postDelayed(mPendingCheckForShortPress, 120); + } + + /** + * Remove the longpress detection timer. + */ + private void removeShortPressCallback() { + if (mPendingCheckForShortPress != null) { + removeCallbacks(mPendingCheckForShortPress); + } + } + + private void cleanUpShortPress() { + removeShortPressCallback(); + if (mShortPressTriggered) { + if (mShortPressListener != null) { + mShortPressListener.cleanUpShortPress(PagedViewWidget.this); + } + mShortPressTriggered = false; + } + } + @Override public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + + switch (event.getAction()) { + case MotionEvent.ACTION_UP: + cleanUpShortPress(); + break; + case MotionEvent.ACTION_DOWN: + checkForShortPress(); + break; + case MotionEvent.ACTION_CANCEL: + cleanUpShortPress(); + break; + case MotionEvent.ACTION_MOVE: + break; + } // We eat up the touch events here, since the PagedView (which uses the same swiping // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when // the user is scrolling between pages. This means that if the pages themselves don't @@ -136,6 +199,6 @@ public class PagedViewWidget extends LinearLayout { // onTouchEvent() handling will prevent further intercept touch events from being called // (it's the same view in that case). This is not ideal, but to prevent more changes, // we just always mark the touch event as handled. - return super.onTouchEvent(event) || true; + return true; } } diff --git a/src/com/android/launcher2/PendingAddItemInfo.java b/src/com/android/launcher2/PendingAddItemInfo.java index 9c52ecfa0..851dddb15 100644 --- a/src/com/android/launcher2/PendingAddItemInfo.java +++ b/src/com/android/launcher2/PendingAddItemInfo.java @@ -16,6 +16,7 @@ package com.android.launcher2; +import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.os.Parcelable; @@ -35,6 +36,8 @@ class PendingAddWidgetInfo extends PendingAddItemInfo { int minHeight; int previewImage; int icon; + AppWidgetProviderInfo info; + AppWidgetHostView boundWidget; // Any configuration data that we want to pass to a configuration activity when // starting up a widget @@ -43,6 +46,7 @@ class PendingAddWidgetInfo extends PendingAddItemInfo { public PendingAddWidgetInfo(AppWidgetProviderInfo i, String dataMimeType, Parcelable data) { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; + this.info = i; componentName = i.provider; minWidth = i.minWidth; minHeight = i.minHeight; diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 746c68229..c8cab163c 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -56,6 +56,7 @@ import android.view.DragEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.View.MeasureSpec; import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; import android.widget.TextView; @@ -204,6 +205,11 @@ public class Workspace extends SmoothPagedView final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3; final static float TOUCH_SLOP_DAMPING_FACTOR = 4; + // Relating to the animation of items being dropped externally + public static final int ANIMATE_INTO_POSITION = 0; + public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 1; + public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 2; + // These variables are used for storing the initial and final values during workspace animations private int mSavedScrollX; private float mSavedRotationY; @@ -334,15 +340,6 @@ public class Workspace extends SmoothPagedView int hCell, int vCell, int hSpan, int vSpan) { RectF r = new RectF(); cl.cellToRect(hCell, vCell, hSpan, vSpan, r); - if (pendingInfo instanceof PendingAddWidgetInfo) { - PendingAddWidgetInfo widgetInfo = (PendingAddWidgetInfo) pendingInfo; - Rect p = AppWidgetHostView.getDefaultPaddingForWidget(mContext, - widgetInfo.componentName, null); - r.top += p.top; - r.left += p.left; - r.right -= p.right; - r.bottom -= p.bottom; - } return r; } @@ -502,10 +499,12 @@ public class Workspace extends SmoothPagedView child.setOnKeyListener(new IconKeyEventListener()); } - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); - if (lp == null) { + LayoutParams genericLp = child.getLayoutParams(); + CellLayout.LayoutParams lp; + if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) { lp = new CellLayout.LayoutParams(x, y, spanX, spanY); } else { + lp = (CellLayout.LayoutParams) genericLp; lp.cellX = x; lp.cellY = y; lp.cellHSpan = spanX; @@ -2869,7 +2868,7 @@ public class Workspace extends SmoothPagedView final Runnable exitSpringLoadedRunnable = new Runnable() { @Override public void run() { - mLauncher.exitSpringLoadedDragModeDelayed(true, false); + mLauncher.exitSpringLoadedDragModeDelayed(true, false, null); } }; @@ -2930,27 +2929,8 @@ public class Workspace extends SmoothPagedView } }; - // Now we animate the dragView, (ie. the widget or shortcut preview) into its final - // location and size on the home screen. - RectF r = estimateItemPosition(cellLayout, pendingInfo, - mTargetCell[0], mTargetCell[1], spanX, spanY); - int loc[] = new int[2]; - loc[0] = (int) r.left; - loc[1] = (int) r.top; - setFinalTransitionTransform(cellLayout); - float cellLayoutScale = - mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(cellLayout, loc); - resetTransitionTransform(cellLayout); - - float dragViewScale = Math.min(r.width() / d.dragView.getMeasuredWidth(), - r.height() / d.dragView.getMeasuredHeight()); - // The animation will scale the dragView about its center, so we need to center about - // the final location. - loc[0] -= (d.dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2; - loc[1] -= (d.dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2; - - mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, loc, - dragViewScale * cellLayoutScale, onAnimationCompleteRunnable); + animateExternalDrop((PendingAddItemInfo) info, cellLayout, d.dragView, + onAnimationCompleteRunnable, ANIMATE_INTO_POSITION); } else { // This is for other drag/drop cases, like dragging from All Apps View view = null; @@ -3016,6 +2996,92 @@ public class Workspace extends SmoothPagedView } } + // The following methods deal with animating an item from external drop + void onPreDraw(View v) { + if (v instanceof ViewGroup) { + ViewGroup vg = (ViewGroup) v; + for (int i = 0; i < vg.getChildCount(); i++) { + View child = vg.getChildAt(i); + onPreDraw(child); + } + } else if (v instanceof TextView) { + ((TextView) v).onPreDraw(); + } + } + + public Bitmap createWidgetBitmap(PendingAddWidgetInfo widgetInfo) { + int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX, + widgetInfo.spanY, widgetInfo, false); + View layout = widgetInfo.boundWidget; + layout.setVisibility(VISIBLE); + + int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY); + int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY); + Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1], + Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + + layout.measure(width, height); + layout.layout(0, 0, unScaledSize[0], unScaledSize[1]); + onPreDraw(layout); + layout.draw(c); + c.setBitmap(null); + return b; + } + + public void animateExternalDrop(PendingAddItemInfo pendingInfo, CellLayout cellLayout, + DragView dragView, Runnable onCompleteRunnable, int animationType) { + // Now we animate the dragView, (ie. the widget or shortcut preview) into its final + // location and size on the home screen. + int spanX = pendingInfo.spanX; + int spanY = pendingInfo.spanY; + RectF r = estimateItemPosition(cellLayout, pendingInfo, + mTargetCell[0], mTargetCell[1], spanX, spanY); + int loc[] = new int[2]; + loc[0] = (int) r.left; + loc[1] = (int) r.top; + setFinalTransitionTransform(cellLayout); + float cellLayoutScale = + mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(cellLayout, loc); + resetTransitionTransform(cellLayout); + + float dragViewScaleX = r.width() / dragView.getMeasuredWidth(); + float dragViewScaleY = r.height() / dragView.getMeasuredHeight(); + // The animation will scale the dragView about its center, so we need to center about + // the final location. + loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2; + loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2; + + float scaleX = dragViewScaleX * cellLayoutScale; + float scaleY = dragViewScaleY * cellLayoutScale; + + Resources res = mLauncher.getResources(); + int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200; + + int animationEnd = DragLayer.ANIMATION_END_REMAIN_VISIBLE; + if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && + (((PendingAddWidgetInfo) pendingInfo).info.configure == null || + animationType == COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION)) { + Bitmap crossFadeBitmap = createWidgetBitmap((PendingAddWidgetInfo) pendingInfo); + dragView.setCrossFadeBitmap(crossFadeBitmap); + dragView.crossFade((int) (duration * 0.8f)); + animationEnd = DragLayer.ANIMATION_END_DISAPPEAR; + } else { + scaleX = scaleY = Math.min(scaleX, scaleY); + } + + if (animationType == COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION) { + mLauncher.getDragLayer().scaleViewIntoPosition(dragView, loc, 1, scaleX, scaleY, + animationEnd, onCompleteRunnable, duration); + } else if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) { + mLauncher.getDragLayer().scaleViewIntoPosition(dragView, loc, 0, 0.1f, 0.1f, + DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration); + } else { + mLauncher.getDragLayer().animateViewIntoPosition(dragView, loc, scaleX, scaleY, + animationEnd, onCompleteRunnable, duration); + } + } + public void setFinalTransitionTransform(CellLayout layout) { if (isSwitchingState()) { int index = indexOfChild(layout); |