summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/shortcuts
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2016-07-22 10:50:11 -0700
committerSunny Goyal <sunnygoyal@google.com>2016-07-27 17:37:23 -0700
commita2454ad2d8dcffa94f670853eb464726c73597f1 (patch)
tree63899483e75a7faadff477cb117bf5c5cfc54962 /src/com/android/launcher3/shortcuts
parent71538da6e2e70af15684cc270a6e67c9b5a010dc (diff)
downloadandroid_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')
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutTextView.java6
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutView.java153
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java207
-rw-r--r--src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java103
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;
+ }
+}