diff options
author | Daniel Sandler <dsandler@android.com> | 2013-06-05 22:57:57 -0400 |
---|---|---|
committer | Daniel Sandler <dsandler@android.com> | 2013-06-05 23:30:20 -0400 |
commit | 325dc23624160689e59fbac708cf6f222b20d025 (patch) | |
tree | 3c6a13a52a6e5688c7e4404890e5e8f88d544856 /src/com/android/launcher3/DeleteDropTarget.java | |
parent | b582cd201fccece65d36b8915cf84fef3546cffa (diff) | |
download | android_packages_apps_Trebuchet-325dc23624160689e59fbac708cf6f222b20d025.tar.gz android_packages_apps_Trebuchet-325dc23624160689e59fbac708cf6f222b20d025.tar.bz2 android_packages_apps_Trebuchet-325dc23624160689e59fbac708cf6f222b20d025.zip |
Launcher2 is now Launcher3.
Changes include
- moving from com.android.launcher{,2} to
com.android.launcher3
- removing wallpapers
- new temporary icon
Change-Id: I1eabd06059e94a8f3bdf6b620777bd1d2b7c212b
Diffstat (limited to 'src/com/android/launcher3/DeleteDropTarget.java')
-rw-r--r-- | src/com/android/launcher3/DeleteDropTarget.java | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java new file mode 100644 index 000000000..eba154732 --- /dev/null +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.drawable.TransitionDrawable; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; + +import com.android.launcher3.R; + +public class DeleteDropTarget extends ButtonDropTarget { + private static int DELETE_ANIMATION_DURATION = 285; + private static int FLING_DELETE_ANIMATION_DURATION = 350; + private static float FLING_TO_DELETE_FRICTION = 0.035f; + private static int MODE_FLING_DELETE_TO_TRASH = 0; + private static int MODE_FLING_DELETE_ALONG_VECTOR = 1; + + private final int mFlingDeleteMode = MODE_FLING_DELETE_ALONG_VECTOR; + + private ColorStateList mOriginalTextColor; + private TransitionDrawable mUninstallDrawable; + private TransitionDrawable mRemoveDrawable; + private TransitionDrawable mCurrentDrawable; + + public DeleteDropTarget(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public DeleteDropTarget(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + // Get the drawable + mOriginalTextColor = getTextColors(); + + // Get the hover color + Resources r = getResources(); + mHoverColor = r.getColor(R.color.delete_target_hover_tint); + mUninstallDrawable = (TransitionDrawable) + r.getDrawable(R.drawable.uninstall_target_selector); + mRemoveDrawable = (TransitionDrawable) r.getDrawable(R.drawable.remove_target_selector); + + mRemoveDrawable.setCrossFadeEnabled(true); + mUninstallDrawable.setCrossFadeEnabled(true); + + // The current drawable is set to either the remove drawable or the uninstall drawable + // and is initially set to the remove drawable, as set in the layout xml. + mCurrentDrawable = (TransitionDrawable) getCurrentDrawable(); + + // Remove the text in the Phone UI in landscape + int orientation = getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + if (!LauncherApplication.isScreenLarge()) { + setText(""); + } + } + } + + private boolean isAllAppsApplication(DragSource source, Object info) { + return (source instanceof AppsCustomizePagedView) && (info instanceof ApplicationInfo); + } + private boolean isAllAppsWidget(DragSource source, Object info) { + if (source instanceof AppsCustomizePagedView) { + if (info instanceof PendingAddItemInfo) { + PendingAddItemInfo addInfo = (PendingAddItemInfo) info; + switch (addInfo.itemType) { + case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + return true; + } + } + } + return false; + } + private boolean isDragSourceWorkspaceOrFolder(DragObject d) { + return (d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder); + } + private boolean isWorkspaceOrFolderApplication(DragObject d) { + return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof ShortcutInfo); + } + private boolean isWorkspaceOrFolderWidget(DragObject d) { + return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof LauncherAppWidgetInfo); + } + private boolean isWorkspaceFolder(DragObject d) { + return (d.dragSource instanceof Workspace) && (d.dragInfo instanceof FolderInfo); + } + + private void setHoverColor() { + mCurrentDrawable.startTransition(mTransitionDuration); + setTextColor(mHoverColor); + } + private void resetHoverColor() { + mCurrentDrawable.resetTransition(); + setTextColor(mOriginalTextColor); + } + + @Override + public boolean acceptDrop(DragObject d) { + // We can remove everything including App shortcuts, folders, widgets, etc. + return true; + } + + @Override + public void onDragStart(DragSource source, Object info, int dragAction) { + boolean isVisible = true; + boolean isUninstall = false; + + // If we are dragging a widget from AppsCustomize, hide the delete target + if (isAllAppsWidget(source, info)) { + isVisible = false; + } + + // If we are dragging an application from AppsCustomize, only show the control if we can + // delete the app (it was downloaded), and rename the string to "uninstall" in such a case + if (isAllAppsApplication(source, info)) { + ApplicationInfo appInfo = (ApplicationInfo) info; + if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) != 0) { + isUninstall = true; + } else { + isVisible = false; + } + } + + if (isUninstall) { + setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null); + } else { + setCompoundDrawablesRelativeWithIntrinsicBounds(mRemoveDrawable, null, null, null); + } + mCurrentDrawable = (TransitionDrawable) getCurrentDrawable(); + + mActive = isVisible; + resetHoverColor(); + ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE); + if (getText().length() > 0) { + setText(isUninstall ? R.string.delete_target_uninstall_label + : R.string.delete_target_label); + } + } + + @Override + public void onDragEnd() { + super.onDragEnd(); + mActive = false; + } + + public void onDragEnter(DragObject d) { + super.onDragEnter(d); + + setHoverColor(); + } + + public void onDragExit(DragObject d) { + super.onDragExit(d); + + if (!d.dragComplete) { + resetHoverColor(); + } else { + // Restore the hover color if we are deleting + d.dragView.setColor(mHoverColor); + } + } + + private void animateToTrashAndCompleteDrop(final DragObject d) { + DragLayer dragLayer = mLauncher.getDragLayer(); + Rect from = new Rect(); + dragLayer.getViewRectRelativeToSelf(d.dragView, from); + Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), + mCurrentDrawable.getIntrinsicWidth(), mCurrentDrawable.getIntrinsicHeight()); + float scale = (float) to.width() / from.width(); + + mSearchDropTargetBar.deferOnDragEnd(); + Runnable onAnimationEndRunnable = new Runnable() { + @Override + public void run() { + mSearchDropTargetBar.onDragEnd(); + mLauncher.exitSpringLoadedDragMode(); + completeDrop(d); + } + }; + dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f, + DELETE_ANIMATION_DURATION, new DecelerateInterpolator(2), + new LinearInterpolator(), onAnimationEndRunnable, + DragLayer.ANIMATION_END_DISAPPEAR, null); + } + + private void completeDrop(DragObject d) { + ItemInfo item = (ItemInfo) d.dragInfo; + + if (isAllAppsApplication(d.dragSource, item)) { + // Uninstall the application if it is being dragged from AppsCustomize + mLauncher.startApplicationUninstallActivity((ApplicationInfo) item); + } else if (isWorkspaceOrFolderApplication(d)) { + LauncherModel.deleteItemFromDatabase(mLauncher, item); + } else if (isWorkspaceFolder(d)) { + // Remove the folder from the workspace and delete the contents from launcher model + FolderInfo folderInfo = (FolderInfo) item; + mLauncher.removeFolder(folderInfo); + LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo); + } else if (isWorkspaceOrFolderWidget(d)) { + // Remove the widget from the workspace + mLauncher.removeAppWidget((LauncherAppWidgetInfo) item); + LauncherModel.deleteItemFromDatabase(mLauncher, item); + + final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item; + final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost(); + if (appWidgetHost != null) { + // Deleting an app widget ID is a void call but writes to disk before returning + // to the caller... + new Thread("deleteAppWidgetId") { + public void run() { + appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId); + } + }.start(); + } + } + } + + public void onDrop(DragObject d) { + animateToTrashAndCompleteDrop(d); + } + + /** + * Creates an animation from the current drag view to the delete trash icon. + */ + private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer, + DragObject d, PointF vel, ViewConfiguration config) { + final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), + mCurrentDrawable.getIntrinsicWidth(), mCurrentDrawable.getIntrinsicHeight()); + final Rect from = new Rect(); + dragLayer.getViewRectRelativeToSelf(d.dragView, from); + + // Calculate how far along the velocity vector we should put the intermediate point on + // the bezier curve + float velocity = Math.abs(vel.length()); + float vp = Math.min(1f, velocity / (config.getScaledMaximumFlingVelocity() / 2f)); + int offsetY = (int) (-from.top * vp); + int offsetX = (int) (offsetY / (vel.y / vel.x)); + final float y2 = from.top + offsetY; // intermediate t/l + final float x2 = from.left + offsetX; + final float x1 = from.left; // drag view t/l + final float y1 = from.top; + final float x3 = to.left; // delete target t/l + final float y3 = to.top; + + final TimeInterpolator scaleAlphaInterpolator = new TimeInterpolator() { + @Override + public float getInterpolation(float t) { + return t * t * t * t * t * t * t * t; + } + }; + return new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final DragView dragView = (DragView) dragLayer.getAnimatedView(); + float t = ((Float) animation.getAnimatedValue()).floatValue(); + float tp = scaleAlphaInterpolator.getInterpolation(t); + float initialScale = dragView.getInitialScale(); + float finalAlpha = 0.5f; + float scale = dragView.getScaleX(); + float x1o = ((1f - scale) * dragView.getMeasuredWidth()) / 2f; + float y1o = ((1f - scale) * dragView.getMeasuredHeight()) / 2f; + float x = (1f - t) * (1f - t) * (x1 - x1o) + 2 * (1f - t) * t * (x2 - x1o) + + (t * t) * x3; + float y = (1f - t) * (1f - t) * (y1 - y1o) + 2 * (1f - t) * t * (y2 - x1o) + + (t * t) * y3; + + dragView.setTranslationX(x); + dragView.setTranslationY(y); + dragView.setScaleX(initialScale * (1f - tp)); + dragView.setScaleY(initialScale * (1f - tp)); + dragView.setAlpha(finalAlpha + (1f - finalAlpha) * (1f - tp)); + } + }; + } + + /** + * Creates an animation from the current drag view along its current velocity vector. + * For this animation, the alpha runs for a fixed duration and we update the position + * progressively. + */ + private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener { + private DragLayer mDragLayer; + private PointF mVelocity; + private Rect mFrom; + private long mPrevTime; + private boolean mHasOffsetForScale; + private float mFriction; + + private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f); + + public FlingAlongVectorAnimatorUpdateListener(DragLayer dragLayer, PointF vel, Rect from, + long startTime, float friction) { + mDragLayer = dragLayer; + mVelocity = vel; + mFrom = from; + mPrevTime = startTime; + mFriction = 1f - (dragLayer.getResources().getDisplayMetrics().density * friction); + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final DragView dragView = (DragView) mDragLayer.getAnimatedView(); + float t = ((Float) animation.getAnimatedValue()).floatValue(); + long curTime = AnimationUtils.currentAnimationTimeMillis(); + + if (!mHasOffsetForScale) { + mHasOffsetForScale = true; + float scale = dragView.getScaleX(); + float xOffset = ((scale - 1f) * dragView.getMeasuredWidth()) / 2f; + float yOffset = ((scale - 1f) * dragView.getMeasuredHeight()) / 2f; + + mFrom.left += xOffset; + mFrom.top += yOffset; + } + + mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f); + mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f); + + dragView.setTranslationX(mFrom.left); + dragView.setTranslationY(mFrom.top); + dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t)); + + mVelocity.x *= mFriction; + mVelocity.y *= mFriction; + mPrevTime = curTime; + } + }; + private AnimatorUpdateListener createFlingAlongVectorAnimatorListener(final DragLayer dragLayer, + DragObject d, PointF vel, final long startTime, final int duration, + ViewConfiguration config) { + final Rect from = new Rect(); + dragLayer.getViewRectRelativeToSelf(d.dragView, from); + + return new FlingAlongVectorAnimatorUpdateListener(dragLayer, vel, from, startTime, + FLING_TO_DELETE_FRICTION); + } + + public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) { + final boolean isAllApps = d.dragSource instanceof AppsCustomizePagedView; + + // Don't highlight the icon as it's animating + d.dragView.setColor(0); + d.dragView.updateInitialScaleToCurrentScale(); + // Don't highlight the target if we are flinging from AllApps + if (isAllApps) { + resetHoverColor(); + } + + if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) { + // Defer animating out the drop target if we are animating to it + mSearchDropTargetBar.deferOnDragEnd(); + mSearchDropTargetBar.finishAnimations(); + } + + final ViewConfiguration config = ViewConfiguration.get(mLauncher); + final DragLayer dragLayer = mLauncher.getDragLayer(); + final int duration = FLING_DELETE_ANIMATION_DURATION; + final long startTime = AnimationUtils.currentAnimationTimeMillis(); + + // NOTE: Because it takes time for the first frame of animation to actually be + // called and we expect the animation to be a continuation of the fling, we have + // to account for the time that has elapsed since the fling finished. And since + // we don't have a startDelay, we will always get call to update when we call + // start() (which we want to ignore). + final TimeInterpolator tInterpolator = new TimeInterpolator() { + private int mCount = -1; + private float mOffset = 0f; + + @Override + public float getInterpolation(float t) { + if (mCount < 0) { + mCount++; + } else if (mCount == 0) { + mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() - + startTime) / duration); + mCount++; + } + return Math.min(1f, mOffset + t); + } + }; + AnimatorUpdateListener updateCb = null; + if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) { + updateCb = createFlingToTrashAnimatorListener(dragLayer, d, vel, config); + } else if (mFlingDeleteMode == MODE_FLING_DELETE_ALONG_VECTOR) { + updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime, + duration, config); + } + Runnable onAnimationEndRunnable = new Runnable() { + @Override + public void run() { + mSearchDropTargetBar.onDragEnd(); + + // If we are dragging from AllApps, then we allow AppsCustomizePagedView to clean up + // itself, otherwise, complete the drop to initiate the deletion process + if (!isAllApps) { + mLauncher.exitSpringLoadedDragMode(); + completeDrop(d); + } + mLauncher.getDragController().onDeferredEndFling(d); + } + }; + dragLayer.animateView(d.dragView, updateCb, duration, tInterpolator, onAnimationEndRunnable, + DragLayer.ANIMATION_END_DISAPPEAR, null); + } +} |