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
This commit is contained in:
@@ -21,8 +21,18 @@
|
||||
android:elevation="@dimen/deep_shortcuts_elevation"
|
||||
android:background="@drawable/bg_white_pill">
|
||||
|
||||
<com.android.launcher3.shortcuts.DeepShortcutTextView
|
||||
<com.android.launcher3.shortcuts.DeepShortcutTextView
|
||||
android:id="@+id/deep_shortcut"
|
||||
style="@style/Icon.DeepShortcut"
|
||||
android:focusable="true"/>
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:focusable="true" />
|
||||
|
||||
<View
|
||||
android:id="@+id/deep_shortcut_icon"
|
||||
android:layout_width="@dimen/deep_shortcut_icon_size"
|
||||
android:layout_height="@dimen/deep_shortcut_icon_size"
|
||||
android:layout_margin="@dimen/deep_shortcut_padding_start"
|
||||
android:layout_gravity="start" />
|
||||
|
||||
</com.android.launcher3.shortcuts.DeepShortcutView>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
android:id="@+id/deep_shortcuts_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:elevation="@dimen/deep_shortcuts_elevation"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
@@ -88,9 +88,10 @@
|
||||
<item type="id" name="cell_layout_jail_id" />
|
||||
|
||||
<!-- Deep shortcuts -->
|
||||
<integer name="config_deepShortcutOpenDuration">300</integer>
|
||||
<integer name="config_deepShortcutOpenStagger">20</integer>
|
||||
<integer name="config_deepShortcutHoverDuration">120</integer>
|
||||
<integer name="config_deepShortcutOpenDuration">220</integer>
|
||||
<integer name="config_deepShortcutOpenStagger">40</integer>
|
||||
<integer name="config_deepShortcutCloseDuration">150</integer>
|
||||
<integer name="config_deepShortcutCloseStagger">20</integer>
|
||||
|
||||
<!-- Accessibility actions -->
|
||||
<item type="id" name="action_remove" />
|
||||
|
||||
@@ -80,8 +80,9 @@
|
||||
<style name="Icon.DeepShortcut">
|
||||
<item name="android:gravity">start|center_vertical</item>
|
||||
<item name="android:elevation">@dimen/deep_shortcuts_elevation</item>
|
||||
<item name="android:paddingStart">@dimen/deep_shortcut_padding_start</item>
|
||||
<item name="android:paddingStart">@dimen/bg_pill_height</item>
|
||||
<item name="android:paddingEnd">@dimen/deep_shortcut_padding_end</item>
|
||||
<item name="android:drawableEnd">@drawable/deep_shortcuts_drag_handle</item>
|
||||
<item name="android:drawablePadding">@dimen/deep_shortcut_drawable_padding</item>
|
||||
<item name="android:textColor">@color/quantum_panel_text_color</item>
|
||||
<item name="android:shadowRadius">0</item>
|
||||
|
||||
@@ -1864,7 +1864,7 @@ public class Launcher extends Activity
|
||||
mWorkspace.exitWidgetResizeMode();
|
||||
|
||||
closeFolder(alreadyOnHome);
|
||||
closeShortcutsContainer();
|
||||
closeShortcutsContainer(alreadyOnHome);
|
||||
exitSpringLoadedDragMode();
|
||||
|
||||
// If we are already on home, then just animate back to the workspace,
|
||||
@@ -1951,8 +1951,7 @@ public class Launcher extends Activity
|
||||
// this state is reflected.
|
||||
// TODO: Move folderInfo.isOpened out of the model and make it a UI state.
|
||||
closeFolder(false);
|
||||
|
||||
closeShortcutsContainer();
|
||||
closeShortcutsContainer(false);
|
||||
|
||||
if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
|
||||
mWaitingForResult) {
|
||||
@@ -3130,11 +3129,17 @@ public class Launcher extends Activity
|
||||
}
|
||||
|
||||
public void closeShortcutsContainer() {
|
||||
closeShortcutsContainer(true);
|
||||
}
|
||||
|
||||
public void closeShortcutsContainer(boolean animate) {
|
||||
DeepShortcutsContainer deepShortcutsContainer = getOpenShortcutsContainer();
|
||||
if (deepShortcutsContainer != null) {
|
||||
deepShortcutsContainer.cleanupDeferredDrag(true);
|
||||
mDragController.removeDragListener(deepShortcutsContainer);
|
||||
mDragLayer.removeView(deepShortcutsContainer);
|
||||
if (animate) {
|
||||
deepShortcutsContainer.animateClose();
|
||||
} else {
|
||||
deepShortcutsContainer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3146,7 +3151,8 @@ public class Launcher extends Activity
|
||||
// and will be one of the last views.
|
||||
for (int i = mDragLayer.getChildCount() - 1; i >= 0; i--) {
|
||||
View child = mDragLayer.getChildAt(i);
|
||||
if (child instanceof DeepShortcutsContainer) {
|
||||
if (child instanceof DeepShortcutsContainer
|
||||
&& ((DeepShortcutsContainer) child).isOpen()) {
|
||||
return (DeepShortcutsContainer) child;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2312,12 +2312,13 @@ public class Workspace extends PagedView
|
||||
+ "View: " + child + " tag: " + child.getTag();
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
beginDragShared(child, new Point(), source, accessible,
|
||||
(ItemInfo) dragObject, new DragPreviewProvider(child));
|
||||
beginDragShared(child, source, accessible, (ItemInfo) dragObject,
|
||||
new DragPreviewProvider(child));
|
||||
}
|
||||
|
||||
public void beginDragShared(View child, Point relativeTouchPos, DragSource source,
|
||||
boolean accessible, ItemInfo dragObject, DragPreviewProvider previewProvider) {
|
||||
|
||||
public DragView beginDragShared(View child, DragSource source, boolean accessible,
|
||||
ItemInfo dragObject, DragPreviewProvider previewProvider) {
|
||||
child.clearFocus();
|
||||
child.setPressed(false);
|
||||
|
||||
@@ -2329,34 +2330,19 @@ public class Workspace extends PagedView
|
||||
final Bitmap b = previewProvider.createDragBitmap(mCanvas);
|
||||
int halfPadding = previewProvider.previewPadding / 2;
|
||||
|
||||
final int bmpWidth = b.getWidth();
|
||||
final int bmpHeight = b.getHeight();
|
||||
|
||||
float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
|
||||
int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
|
||||
int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2 - halfPadding);
|
||||
float scale = previewProvider.getScaleAndPosition(b, mTempXY);
|
||||
int dragLayerX = mTempXY[0];
|
||||
int dragLayerY = mTempXY[1];
|
||||
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
Point dragVisualizeOffset = null;
|
||||
Rect dragRect = null;
|
||||
if (child instanceof BubbleTextView) {
|
||||
BubbleTextView icon = (BubbleTextView) child;
|
||||
int iconSize = grid.iconSizePx;
|
||||
int top = child.getPaddingTop();
|
||||
int left = (bmpWidth - iconSize) / 2;
|
||||
int left = (b.getWidth() - iconSize) / 2;
|
||||
int right = left + iconSize;
|
||||
int bottom = top + iconSize;
|
||||
if (icon.isLayoutHorizontal()) {
|
||||
// If the layout is horizontal, then if we are just picking up the icon, then just
|
||||
// use the child position since the icon is top-left aligned. Otherwise, offset
|
||||
// the drag layer position horizontally so that the icon is under the current
|
||||
// touch position.
|
||||
if (icon.getIcon().getBounds().contains(relativeTouchPos.x, relativeTouchPos.y)) {
|
||||
dragLayerX = Math.round(mTempXY[0]);
|
||||
} else {
|
||||
dragLayerX = Math.round(mTempXY[0] + relativeTouchPos.x - (bmpWidth / 2));
|
||||
}
|
||||
}
|
||||
dragLayerY += top;
|
||||
// Note: The drag region is used to calculate drag layer offsets, but the
|
||||
// dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
|
||||
@@ -2388,6 +2374,7 @@ public class Workspace extends PagedView
|
||||
if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
|
||||
mLauncher.enterSpringLoadedDragMode();
|
||||
}
|
||||
return dv;
|
||||
}
|
||||
|
||||
public boolean transitionStateShouldAllowDrop() {
|
||||
|
||||
@@ -47,7 +47,8 @@ import com.android.launcher3.R;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class DragView extends View {
|
||||
public static int COLOR_CHANGE_DURATION = 120;
|
||||
public static final int COLOR_CHANGE_DURATION = 120;
|
||||
public static final int VIEW_ZOOM_DURATION = 150;
|
||||
|
||||
@Thunk static float sDragAlpha = 1f;
|
||||
|
||||
@@ -73,6 +74,11 @@ public class DragView extends View {
|
||||
@Thunk float[] mCurrentFilter;
|
||||
private ValueAnimator mFilterAnimator;
|
||||
|
||||
private int mLastTouchX;
|
||||
private int mLastTouchY;
|
||||
private int mAnimatedShiftX;
|
||||
private int mAnimatedShiftY;
|
||||
|
||||
/**
|
||||
* Construct the drag view.
|
||||
* <p>
|
||||
@@ -98,7 +104,7 @@ public class DragView extends View {
|
||||
|
||||
// Animate the view into the correct position
|
||||
mAnim = LauncherAnimUtils.ofFloat(this, 0f, 1f);
|
||||
mAnim.setDuration(150);
|
||||
mAnim.setDuration(VIEW_ZOOM_DURATION);
|
||||
mAnim.addUpdateListener(new AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
@@ -310,7 +316,6 @@ public class DragView extends View {
|
||||
/**
|
||||
* Create a window containing this view and show it.
|
||||
*
|
||||
* @param windowToken obtained from v.getWindowToken() from one of your views
|
||||
* @param touchX the x coordinate the user touched in DragLayer coordinates
|
||||
* @param touchY the y coordinate the user touched in DragLayer coordinates
|
||||
*/
|
||||
@@ -323,8 +328,7 @@ public class DragView extends View {
|
||||
lp.height = mBitmap.getHeight();
|
||||
lp.customPosition = true;
|
||||
setLayoutParams(lp);
|
||||
setTranslationX(touchX - mRegistrationX);
|
||||
setTranslationY(touchY - mRegistrationY);
|
||||
move(touchX, touchY);
|
||||
// Post the animation to skip other expensive work happening on the first frame
|
||||
post(new Runnable() {
|
||||
public void run() {
|
||||
@@ -347,8 +351,32 @@ public class DragView extends View {
|
||||
* @param touchY the y coordinate the user touched in DragLayer coordinates
|
||||
*/
|
||||
public void move(int touchX, int touchY) {
|
||||
setTranslationX(touchX - mRegistrationX);
|
||||
setTranslationY(touchY - mRegistrationY);
|
||||
mLastTouchX = touchX;
|
||||
mLastTouchY = touchY;
|
||||
applyTranslation();
|
||||
}
|
||||
|
||||
public void animateShift(final int shiftX, final int shiftY) {
|
||||
if (mAnim.isStarted()) {
|
||||
return;
|
||||
}
|
||||
mAnimatedShiftX = shiftX;
|
||||
mAnimatedShiftY = shiftY;
|
||||
applyTranslation();
|
||||
mAnim.addUpdateListener(new AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
float fraction = 1 - animation.getAnimatedFraction();
|
||||
mAnimatedShiftX = (int) (fraction * shiftX);
|
||||
mAnimatedShiftY = (int) (fraction * shiftY);
|
||||
applyTranslation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void applyTranslation() {
|
||||
setTranslationX(mLastTouchX - mRegistrationX + mAnimatedShiftX);
|
||||
setTranslationY(mLastTouchY - mRegistrationY + mAnimatedShiftY);
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
|
||||
@@ -25,13 +25,12 @@ import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.launcher3.HolographicOutlineHelper;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.PreloadIconDrawable;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A utility class to generate preview bitmap for dragging.
|
||||
*/
|
||||
@@ -59,9 +58,7 @@ public class DragPreviewProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the View v into the given Canvas.
|
||||
*
|
||||
* @param destCanvas the canvas to draw on
|
||||
* Draws the {@link #mView} into the given {@param destCanvas}.
|
||||
*/
|
||||
private void drawDragView(Canvas destCanvas) {
|
||||
destCanvas.save();
|
||||
@@ -98,7 +95,7 @@ public class DragPreviewProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new bitmap to show when the given View is being dragged around.
|
||||
* Returns a new bitmap to show when the {@link #mView} is being dragged around.
|
||||
* Responsibility for the bitmap is transferred to the caller.
|
||||
*/
|
||||
public Bitmap createDragBitmap(Canvas canvas) {
|
||||
@@ -152,4 +149,12 @@ public class DragPreviewProvider {
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public float getScaleAndPosition(Bitmap preview, int[] outPos) {
|
||||
float scale = Launcher.getLauncher(mView.getContext())
|
||||
.getDragLayer().getLocationInDragLayer(mView, outPos);
|
||||
outPos[0] = Math.round(outPos[0] - (preview.getWidth() - scale * mView.getWidth()) / 2);
|
||||
outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getHeight() / 2 - previewPadding / 2);
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* Creates an animator to play when the shortcut container is being opened or closed.
|
||||
*/
|
||||
public Animator createOpenAnimation(long animationDelay, boolean isContainerAboveIcon) {
|
||||
final Resources res = getResources();
|
||||
setVisibility(INVISIBLE);
|
||||
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);
|
||||
}
|
||||
|
||||
AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet();
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
Animator reveal = new PillRevealOutlineProvider((int) getPivotX(), (int) getPivotY(),
|
||||
mPillRect, mRadius).createRevealAnimator(this);
|
||||
reveal.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
setVisibility(VISIBLE);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Returns the position of the center of the icon relative to the container.
|
||||
*/
|
||||
public Point getIconCenter() {
|
||||
sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2;
|
||||
if (Utilities.isRtl(getResources())) {
|
||||
sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
|
||||
}
|
||||
return sTempPoint;
|
||||
}
|
||||
|
||||
float transY = res.getDimensionPixelSize(R.dimen.deep_shortcut_anim_translation_y);
|
||||
Animator translationY = ObjectAnimator.ofFloat(this, TRANSLATION_Y,
|
||||
isContainerAboveIcon ? transY : -transY, 0);
|
||||
/**
|
||||
* Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height.
|
||||
*/
|
||||
private static class ZoomRevealOutlineProvider extends PillRevealOutlineProvider {
|
||||
|
||||
// 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);
|
||||
private final View mTranslateView;
|
||||
private final View mZoomView;
|
||||
|
||||
openAnimation.playTogether(reveal, translationY, scale);
|
||||
openAnimation.setStartDelay(animationDelay);
|
||||
openAnimation.setDuration(res.getInteger(R.integer.config_deepShortcutOpenDuration));
|
||||
openAnimation.setInterpolator(new DecelerateInterpolator());
|
||||
return openAnimation;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
+41
-27
@@ -14,60 +14,55 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.graphics;
|
||||
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.TextView;
|
||||
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.Workspace;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.graphics.DragPreviewProvider;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size
|
||||
* Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
|
||||
*/
|
||||
public class ScaledPreviewProvider extends DragPreviewProvider {
|
||||
public class ShortcutDragPreviewProvider extends DragPreviewProvider {
|
||||
|
||||
public ScaledPreviewProvider(View v) {
|
||||
super(v);
|
||||
private final Point mPositionShift;
|
||||
|
||||
public ShortcutDragPreviewProvider(View icon, Point shift) {
|
||||
super(icon);
|
||||
mPositionShift = shift;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap createDragOutline(Canvas canvas) {
|
||||
if (mView instanceof TextView) {
|
||||
Bitmap b = drawScaledPreview(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;
|
||||
}
|
||||
return super.createDragOutline(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) {
|
||||
if (mView instanceof TextView) {
|
||||
Bitmap b = drawScaledPreview(canvas);
|
||||
canvas.setBitmap(null);
|
||||
return b;
|
||||
|
||||
} else {
|
||||
return super.createDragBitmap(canvas);
|
||||
}
|
||||
Bitmap b = drawScaledPreview(canvas);
|
||||
canvas.setBitmap(null);
|
||||
return b;
|
||||
}
|
||||
|
||||
private Bitmap drawScaledPreview(Canvas canvas) {
|
||||
Drawable d = Workspace.getTextViewIcon((TextView) mView);
|
||||
Drawable d = mView.getBackground();
|
||||
Rect bounds = getDrawableBounds(d);
|
||||
|
||||
int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
|
||||
@@ -86,4 +81,23 @@ public class ScaledPreviewProvider extends DragPreviewProvider {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -31,19 +31,18 @@ public class PillRevealOutlineProvider extends RevealOutlineAnimation {
|
||||
|
||||
private int mCenterX;
|
||||
private int mCenterY;
|
||||
private Rect mPillRect;
|
||||
protected Rect mPillRect;
|
||||
|
||||
/**
|
||||
* @param x reveal center x
|
||||
* @param y reveal center y
|
||||
* @param pillRect round rect that represents the final pill shape
|
||||
* @param pillRectRadius radius of the round rect
|
||||
*/
|
||||
public PillRevealOutlineProvider(int x, int y, Rect pillRect, float pillRectRadius) {
|
||||
public PillRevealOutlineProvider(int x, int y, Rect pillRect) {
|
||||
mCenterX = x;
|
||||
mCenterY = y;
|
||||
mPillRect = pillRect;
|
||||
mOutlineRadius = pillRectRadius;
|
||||
mOutlineRadius = pillRect.height() / 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,5 +61,6 @@ public class PillRevealOutlineProvider extends RevealOutlineAnimation {
|
||||
mOutline.top = Math.max(mPillRect.top, mCenterY - currentSize);
|
||||
mOutline.right = Math.min(mPillRect.right, mCenterX + currentSize);
|
||||
mOutline.bottom = Math.min(mPillRect.bottom, mCenterY + currentSize);
|
||||
mOutlineRadius = mOutline.height() / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* Extension of {@link PillRevealOutlineProvider} which only changes the width of the pill.
|
||||
*/
|
||||
public class PillWidthRevealOutlineProvider extends PillRevealOutlineProvider {
|
||||
|
||||
private final int mStartLeft;
|
||||
private final int mStartRight;
|
||||
|
||||
public PillWidthRevealOutlineProvider(Rect pillRect, int left, int right) {
|
||||
super(0, 0, pillRect);
|
||||
mOutline.set(pillRect);
|
||||
mStartLeft = left;
|
||||
mStartRight = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(float progress) {
|
||||
mOutline.left = (int) (progress * mPillRect.left + (1 - progress) * mStartLeft);
|
||||
mOutline.right = (int) (progress * mPillRect.right + (1 - progress) * mStartRight);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,12 @@ public abstract class RevealOutlineAnimation extends ViewOutlineProvider {
|
||||
abstract void setProgress(float progress);
|
||||
|
||||
public ValueAnimator createRevealAnimator(final View revealView) {
|
||||
ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
|
||||
return createRevealAnimator(revealView, false);
|
||||
}
|
||||
|
||||
public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {
|
||||
ValueAnimator va =
|
||||
isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
|
||||
final float elevation = revealView.getElevation();
|
||||
|
||||
va.addListener(new AnimatorListenerAdapter() {
|
||||
@@ -54,7 +59,7 @@ public abstract class RevealOutlineAnimation extends ViewOutlineProvider {
|
||||
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator arg0) {
|
||||
float progress = arg0.getAnimatedFraction();
|
||||
float progress = (Float) arg0.getAnimatedValue();
|
||||
setProgress(progress);
|
||||
revealView.invalidateOutline();
|
||||
if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
|
||||
|
||||
Reference in New Issue
Block a user