diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2016-07-22 10:50:11 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2016-07-27 17:37:23 -0700 |
commit | a2454ad2d8dcffa94f670853eb464726c73597f1 (patch) | |
tree | 63899483e75a7faadff477cb117bf5c5cfc54962 /src/com/android/launcher3/shortcuts | |
parent | 71538da6e2e70af15684cc270a6e67c9b5a010dc (diff) | |
download | android_packages_apps_Trebuchet-a2454ad2d8dcffa94f670853eb464726c73597f1.tar.gz android_packages_apps_Trebuchet-a2454ad2d8dcffa94f670853eb464726c73597f1.tar.bz2 android_packages_apps_Trebuchet-a2454ad2d8dcffa94f670853eb464726c73597f1.zip |
Launcher shortcuts animations update.
> The shortcut container closes with an animation
> When opening/closing the animation only the icon scales
and not the title and drag handle
> When dragging the icon, it starts from the original icon position and
moves under the user finger. The container grows to follow the drag view.
Bug: 28980830
Change-Id: Ic0353c30b682d1f018cbf4d62e8a6e8e7d7d4664
Diffstat (limited to 'src/com/android/launcher3/shortcuts')
4 files changed, 383 insertions, 86 deletions
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java index c48d16008..450d36da5 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java @@ -21,7 +21,6 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import com.android.launcher3.BubbleTextView; -import com.android.launcher3.R; /** * A {@link BubbleTextView} that has the shortcut icon on the left and drag handle on the right. @@ -41,10 +40,7 @@ public class DeepShortcutTextView extends BubbleTextView { } @Override - /** Use the BubbleTextView icon for the start and the drag handle for the end. */ protected void applyCompoundDrawables(Drawable icon) { - Drawable dragHandle = getResources().getDrawable(R.drawable.deep_shortcuts_drag_handle); - dragHandle.setBounds(0, 0, dragHandle.getIntrinsicWidth(), dragHandle.getIntrinsicHeight()); - setCompoundDrawablesRelative(icon, null, dragHandle, null); + // The icon is drawn in a separate view. } } diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java index 7cb2d43d7..b651f255b 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java @@ -17,21 +17,20 @@ package com.android.launcher3.shortcuts; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.content.Context; -import android.content.res.Resources; +import android.graphics.Point; import android.graphics.Rect; import android.util.AttributeSet; -import android.view.animation.DecelerateInterpolator; +import android.view.View; import android.widget.FrameLayout; -import com.android.launcher3.BubbleTextView; -import com.android.launcher3.LauncherAnimUtils; -import com.android.launcher3.LauncherViewPropertyAnimator; +import com.android.launcher3.IconCache; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.Utilities; import com.android.launcher3.util.PillRevealOutlineProvider; +import com.android.launcher3.util.PillWidthRevealOutlineProvider; /** * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}. @@ -39,10 +38,12 @@ import com.android.launcher3.util.PillRevealOutlineProvider; */ public class DeepShortcutView extends FrameLayout { - private int mRadius; - private Rect mPillRect; + private static final Point sTempPoint = new Point(); - private BubbleTextView mBubbleText; + private final Rect mPillRect; + + private DeepShortcutTextView mBubbleText; + private View mIconView; public DeepShortcutView(Context context) { this(context, null, 0); @@ -55,70 +56,116 @@ public class DeepShortcutView extends FrameLayout { public DeepShortcutView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mRadius = getResources().getDimensionPixelSize(R.dimen.bg_pill_radius); mPillRect = new Rect(); } @Override protected void onFinishInflate() { super.onFinishInflate(); - mBubbleText = (BubbleTextView) findViewById(R.id.deep_shortcut); + mIconView = findViewById(R.id.deep_shortcut_icon); + mBubbleText = (DeepShortcutTextView) findViewById(R.id.deep_shortcut); } - public BubbleTextView getBubbleText() { + public DeepShortcutTextView getBubbleText() { return mBubbleText; } + public void setWillDrawIcon(boolean willDraw) { + mIconView.setVisibility(willDraw ? View.VISIBLE : View.INVISIBLE); + } + + public boolean willDrawIcon() { + return mIconView.getVisibility() == View.VISIBLE; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); } - @Override - public void setPivotX(float pivotX) { - super.setPivotX(pivotX); - mBubbleText.setPivotX(pivotX); + public void applyShortcutInfo(ShortcutInfo info) { + IconCache cache = LauncherAppState.getInstance().getIconCache(); + mBubbleText.applyFromShortcutInfo(info, cache); + mIconView.setBackground(mBubbleText.getIcon()); } - @Override - public void setPivotY(float pivotY) { - super.setPivotY(pivotY); - mBubbleText.setPivotY(pivotY); + public View getIconView() { + return mIconView; + } + + /** + * Creates an animator to play when the shortcut container is being opened or closed. + */ + public Animator createOpenCloseAnimation( + boolean isContainerAboveIcon, boolean pivotLeft, boolean isReverse) { + Point center = getIconCenter(); + return new ZoomRevealOutlineProvider(center.x, center.y, mPillRect, + this, mIconView, isContainerAboveIcon, pivotLeft) + .createRevealAnimator(this, isReverse); + } + + /** + * Creates an animator which clips the container to form a circle around the icon. + */ + public Animator collapseToIcon() { + int halfHeight = getMeasuredHeight() / 2; + int iconCenterX = getIconCenter().x; + return new PillWidthRevealOutlineProvider(mPillRect, + iconCenterX - halfHeight, iconCenterX + halfHeight) + .createRevealAnimator(this, true); } /** - * Creates an animator to play when the shortcut container is being opened. + * Returns the position of the center of the icon relative to the container. */ - public Animator createOpenAnimation(long animationDelay, boolean isContainerAboveIcon) { - final Resources res = getResources(); - setVisibility(INVISIBLE); - - AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet(); - - Animator reveal = new PillRevealOutlineProvider((int) getPivotX(), (int) getPivotY(), - mPillRect, mRadius).createRevealAnimator(this); - reveal.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - setVisibility(VISIBLE); - } - }); - - float transY = res.getDimensionPixelSize(R.dimen.deep_shortcut_anim_translation_y); - Animator translationY = ObjectAnimator.ofFloat(this, TRANSLATION_Y, - isContainerAboveIcon ? transY : -transY, 0); - - // Only scale mBubbleText (the icon and text, not the background). - mBubbleText.setScaleX(0); - mBubbleText.setScaleY(0); - LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(mBubbleText) - .scaleX(1).scaleY(1); - - openAnimation.playTogether(reveal, translationY, scale); - openAnimation.setStartDelay(animationDelay); - openAnimation.setDuration(res.getInteger(R.integer.config_deepShortcutOpenDuration)); - openAnimation.setInterpolator(new DecelerateInterpolator()); - return openAnimation; + public Point getIconCenter() { + sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2; + if (Utilities.isRtl(getResources())) { + sTempPoint.x = getMeasuredWidth() - sTempPoint.x; + } + return sTempPoint; + } + + /** + * Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height. + */ + private static class ZoomRevealOutlineProvider extends PillRevealOutlineProvider { + + private final View mTranslateView; + private final View mZoomView; + + private final float mFullHeight; + private final float mTranslateYMultiplier; + + private final boolean mPivotLeft; + private final float mTranslateX; + + public ZoomRevealOutlineProvider(int x, int y, Rect pillRect, + View translateView, View zoomView, boolean isContainerAboveIcon, boolean pivotLeft) { + super(x, y, pillRect); + mTranslateView = translateView; + mZoomView = zoomView; + mFullHeight = pillRect.height(); + + mTranslateYMultiplier = isContainerAboveIcon ? 0.5f : -0.5f; + + mPivotLeft = pivotLeft; + mTranslateX = pivotLeft ? pillRect.height() / 2 : pillRect.right - pillRect.height() / 2; + } + + @Override + public void setProgress(float progress) { + super.setProgress(progress); + + mZoomView.setScaleX(progress); + mZoomView.setScaleY(progress); + + float height = mOutline.height(); + mTranslateView.setTranslationY(mTranslateYMultiplier * (mFullHeight - height)); + + float pivotX = mPivotLeft ? (mOutline.left + height / 2) : (mOutline.right - height / 2); + mTranslateView.setTranslationX(mTranslateX - pivotX); + } } } diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java index a693f150a..3a513f128 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java @@ -17,6 +17,7 @@ package com.android.launcher3.shortcuts; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.annotation.TargetApi; import android.content.ComponentName; @@ -39,6 +40,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import android.view.animation.DecelerateInterpolator; import android.widget.LinearLayout; import com.android.launcher3.BubbleTextView; @@ -50,6 +52,7 @@ import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherViewPropertyAnimator; +import com.android.launcher3.LogAccelerateInterpolator; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; @@ -59,7 +62,6 @@ import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; -import com.android.launcher3.graphics.ScaledPreviewProvider; import com.android.launcher3.graphics.TriangleShape; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.userevent.nano.LauncherLogProto; @@ -78,6 +80,8 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC UserEventDispatcher.LaunchSourceProvider { private static final String TAG = "ShortcutsContainer"; + private final Point mIconShift = new Point(); + private final Launcher mLauncher; private final DeepShortcutManager mDeepShortcutsManager; private final int mDragDeadzone; @@ -96,8 +100,12 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC private boolean mIsLeftAligned; private boolean mIsAboveIcon; private View mArrow; + + private Animator mOpenCloseAnimator; + private boolean mDeferContainerRemoval; + private boolean mIsOpen; + private boolean mSrcIconDragStarted; - private LauncherViewPropertyAnimator mArrowHoverAnimator; private boolean mIsRtl; private int mArrowHorizontalOffset; @@ -167,7 +175,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC final int arrowVerticalOffset = resources.getDimensionPixelSize( R.dimen.deep_shortcuts_arrow_vertical_offset); mArrow = addArrowView(mArrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight); - mArrowHoverAnimator = new LauncherViewPropertyAnimator(mArrow); animateOpen(); @@ -218,9 +225,9 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC @Override public void run() { + DeepShortcutView shortcutViewContainer = getShortcutAt(mShortcutChildIndex); + shortcutViewContainer.applyShortcutInfo(mShortcutChildInfo); BubbleTextView shortcutView = getShortcutAt(mShortcutChildIndex).getBubbleText(); - shortcutView.applyFromShortcutInfo(mShortcutChildInfo, - LauncherAppState.getInstance().getIconCache()); // Use the long label as long as it exists and fits. int availableWidth = shortcutView.getWidth() - shortcutView.getTotalPaddingLeft() - shortcutView.getTotalPaddingRight(); @@ -248,30 +255,54 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC private void animateOpen() { setVisibility(View.VISIBLE); + mIsOpen = true; final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet(); final int shortcutCount = getShortcutCount(); - final int pivotX = mIsLeftAligned ? mArrowHorizontalOffset - : getMeasuredWidth() - mArrowHorizontalOffset; - final int pivotY = getShortcutAt(0).getMeasuredHeight() / 2; + + final long duration = getResources().getInteger( + R.integer.config_deepShortcutOpenDuration); + final long stagger = getResources().getInteger( + R.integer.config_deepShortcutOpenStagger); + + // Animate shortcuts + DecelerateInterpolator interpolator = new DecelerateInterpolator(); for (int i = 0; i < shortcutCount; i++) { - DeepShortcutView deepShortcutView = getShortcutAt(i); - deepShortcutView.setPivotX(pivotX); - deepShortcutView.setPivotY(pivotY); + final DeepShortcutView deepShortcutView = getShortcutAt(i); + deepShortcutView.setVisibility(INVISIBLE); + + Animator anim = deepShortcutView.createOpenCloseAnimation( + mIsAboveIcon, mIsLeftAligned, false); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + deepShortcutView.setVisibility(VISIBLE); + } + }); + anim.setDuration(duration); int animationIndex = mIsAboveIcon ? shortcutCount - i - 1 : i; - long animationDelay = animationIndex * getResources().getInteger( - R.integer.config_deepShortcutOpenStagger); - shortcutAnims.play(deepShortcutView.createOpenAnimation(animationDelay, mIsAboveIcon)); + anim.setStartDelay(stagger * animationIndex); + anim.setInterpolator(interpolator); + shortcutAnims.play(anim); } + shortcutAnims.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mOpenCloseAnimator = null; + } + }); + + // Animate the arrow mArrow.setScaleX(0); mArrow.setScaleY(0); - final long shortcutAnimDuration = shortcutAnims.getChildAnimations().get(0).getDuration(); - final long arrowScaleDelay = shortcutAnimDuration / 6; - final long arrowScaleDuration = shortcutAnimDuration - arrowScaleDelay; + final long arrowScaleDelay = duration / 6; + final long arrowScaleDuration = duration - arrowScaleDelay; Animator arrowScale = new LauncherViewPropertyAnimator(mArrow).scaleX(1).scaleY(1); arrowScale.setStartDelay(arrowScaleDelay); arrowScale.setDuration(arrowScaleDuration); shortcutAnims.play(arrowScale); + + mOpenCloseAnimator = shortcutAnims; shortcutAnims.start(); } @@ -432,7 +463,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC Utilities.translateEventCoordinates(this, mLauncher.getDragLayer(), ev); final int dragLayerX = (int) ev.getX(); final int dragLayerY = (int) ev.getY(); - int shortcutCount = getShortcutCount(); if (action == MotionEvent.ACTION_MOVE) { if (mLastX != 0 || mLastY != 0) { mDistanceDragged += Math.hypot(mLastX - x, mLastY - y); @@ -441,8 +471,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC mLastY = y; if (shouldStartDeferredDrag((int) x, (int) y)) { - DeepShortcutView topShortcut = getShortcutAt(0); - DeepShortcutView bottomShortcut = getShortcutAt(shortcutCount - 1); mSrcIconDragStarted = true; cleanupDeferredDrag(true); mDeferredDragIcon.getParent().requestDisallowInterceptTouchEvent(false); @@ -480,7 +508,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC return distFromTouchDown > mStartDragThreshold; } - public void cleanupDeferredDrag(boolean updateSrcVisibility) { + private void cleanupDeferredDrag(boolean updateSrcVisibility) { if (mDragView != null) { mDragView.remove(); } @@ -502,8 +530,8 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC } public boolean onLongClick(View v) { - // Return early if this is not initiated from a touch - if (!v.isInTouchMode()) return false; + // Return early if this is not initiated from a touch or not the correct view + if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false; // Return if global dragging is not enabled if (!mLauncher.isDraggingEnabled()) return false; @@ -514,8 +542,20 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC mLauncher.getModel().updateShortcutInfo(unbadgedInfo.mDetail, badged); // Long clicked on a shortcut. - mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false, badged, - new ScaledPreviewProvider(v)); + + mDeferContainerRemoval = true; + DeepShortcutView sv = (DeepShortcutView) v.getParent(); + sv.setWillDrawIcon(false); + + // Move the icon to align with the center-top of the touch point + mIconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x; + mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx; + + DragView dv = mLauncher.getWorkspace().beginDragShared( + sv.getBubbleText(), this, false, badged, + new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift)); + dv.animateShift(-mIconShift.x, -mIconShift.y); + // TODO: support dragging from within folder without having to close it mLauncher.closeFolder(); return false; @@ -560,13 +600,25 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC public void onDragStart(DragSource source, ItemInfo info, int dragAction) { // Either the original icon or one of the shortcuts was dragged. // Hide the container, but don't remove it yet because that interferes with touch events. - setVisibility(INVISIBLE); + animateClose(); } @Override public void onDragEnd() { - // Now remove the container. - mLauncher.closeShortcutsContainer(); + if (mIsOpen) { + animateClose(); + } else { + if (mOpenCloseAnimator != null) { + // Close animation is running. + mDeferContainerRemoval = false; + } else { + // Close animation is not running. + if (mDeferContainerRemoval) { + mDeferContainerRemoval = false; + mLauncher.getDragLayer().removeView(this); + } + } + } } @Override @@ -576,6 +628,105 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC targetParent.containerType = LauncherLogProto.DEEPSHORTCUTS; } + public void animateClose() { + if (!mIsOpen) { + return; + } + if (mOpenCloseAnimator != null) { + mOpenCloseAnimator.cancel(); + } + mIsOpen = false; + mLauncher.getDragController().removeDragListener(this); + + final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet(); + final int numShortcuts = getShortcutCount(); + final long duration = getResources().getInteger( + R.integer.config_deepShortcutCloseDuration); + final long stagger = getResources().getInteger( + R.integer.config_deepShortcutCloseStagger); + + long arrowDelay = (numShortcuts - 1) * stagger + (duration * 4 / 6); + int firstShortcutIndex = mIsAboveIcon ? (numShortcuts - 1) : 0; + LogAccelerateInterpolator interpolator = new LogAccelerateInterpolator(100, 0); + for (int i = 0; i < numShortcuts; i++) { + final DeepShortcutView view = getShortcutAt(i); + Animator anim; + if (view.willDrawIcon()) { + anim = view.createOpenCloseAnimation(mIsAboveIcon, mIsLeftAligned, true); + int animationIndex = mIsAboveIcon ? i : numShortcuts - i - 1; + anim.setStartDelay(stagger * animationIndex); + anim.setDuration(duration); + anim.setInterpolator(interpolator); + } else { + // The view is being dragged. Animate it such that it collapses with the drag view + anim = view.collapseToIcon(); + anim.setDuration(DragView.VIEW_ZOOM_DURATION); + + // Scale and translate the view to follow the drag view. + Point iconCenter = view.getIconCenter(); + view.setPivotX(iconCenter.x); + view.setPivotY(iconCenter.y); + + float scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / view.getHeight(); + LauncherViewPropertyAnimator anim2 = new LauncherViewPropertyAnimator(view) + .scaleX(scale) + .scaleY(scale) + .translationX(mIconShift.x) + .translationY(mIconShift.y); + anim2.setDuration(DragView.VIEW_ZOOM_DURATION); + shortcutAnims.play(anim2); + + if (i == firstShortcutIndex) { + arrowDelay = 0; + } + } + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setVisibility(INVISIBLE); + } + }); + shortcutAnims.play(anim); + } + Animator arrowAnim = new LauncherViewPropertyAnimator(mArrow) + .scaleX(0).scaleY(0).setDuration(duration / 6); + arrowAnim.setStartDelay(arrowDelay); + shortcutAnims.play(arrowAnim); + + shortcutAnims.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mOpenCloseAnimator = null; + if (mDeferContainerRemoval) { + setVisibility(INVISIBLE); + } else { + close(); + } + } + }); + mOpenCloseAnimator = shortcutAnims; + shortcutAnims.start(); + } + + /** + * Closes the folder without animation. + */ + public void close() { + if (mOpenCloseAnimator != null) { + mOpenCloseAnimator.cancel(); + mOpenCloseAnimator = null; + } + mIsOpen = false; + mDeferContainerRemoval = false; + cleanupDeferredDrag(true); + mLauncher.getDragController().removeDragListener(this); + mLauncher.getDragLayer().removeView(this); + } + + public boolean isOpen() { + return mIsOpen; + } + /** * Shows the shortcuts container for {@param icon} * @return the container if shown or null. diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java new file mode 100644 index 000000000..a25e475d4 --- /dev/null +++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 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.shortcuts; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.ImageView; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.HolographicOutlineHelper; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.graphics.DragPreviewProvider; + +/** + * Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size. + */ +public class ShortcutDragPreviewProvider extends DragPreviewProvider { + + private final Point mPositionShift; + + public ShortcutDragPreviewProvider(View icon, Point shift) { + super(icon); + mPositionShift = shift; + } + + @Override + public Bitmap createDragOutline(Canvas canvas) { + Bitmap b = drawScaledPreview(canvas); + + final int outlineColor = mView.getResources().getColor(R.color.outline_color); + HolographicOutlineHelper.obtain(mView.getContext()) + .applyExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor); + canvas.setBitmap(null); + return b; + } + + @Override + public Bitmap createDragBitmap(Canvas canvas) { + Bitmap b = drawScaledPreview(canvas); + canvas.setBitmap(null); + return b; + } + + private Bitmap drawScaledPreview(Canvas canvas) { + Drawable d = mView.getBackground(); + Rect bounds = getDrawableBounds(d); + + int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx; + + final Bitmap b = Bitmap.createBitmap( + size + DRAG_BITMAP_PADDING, + size + DRAG_BITMAP_PADDING, + Bitmap.Config.ARGB_8888); + + canvas.setBitmap(b); + canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2); + canvas.scale(((float) size) / bounds.width(), ((float) size) / bounds.height(), 0, 0); + canvas.translate(bounds.left, bounds.top); + d.draw(canvas); + canvas.restore(); + return b; + } + + @Override + public float getScaleAndPosition(Bitmap preview, int[] outPos) { + Launcher launcher = Launcher.getLauncher(mView.getContext()); + int iconSize = getDrawableBounds(mView.getBackground()).width(); + float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos); + + int iconLeft = mView.getPaddingStart(); + if (Utilities.isRtl(mView.getResources())) { + iconLeft = mView.getWidth() - iconSize - iconLeft; + } + + outPos[0] += Math.round(scale * iconLeft + (scale * iconSize - preview.getWidth()) / 2 + + mPositionShift.x); + outPos[1] += Math.round((scale * mView.getHeight() - preview.getHeight()) / 2 + + mPositionShift.y); + float size = launcher.getDeviceProfile().iconSizePx; + return scale * iconSize / size; + } +} |