diff options
Diffstat (limited to 'src/com/android/launcher3/popup/PopupItemView.java')
-rw-r--r-- | src/com/android/launcher3/popup/PopupItemView.java | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java new file mode 100644 index 000000000..25d496a4b --- /dev/null +++ b/src/com/android/launcher3/popup/PopupItemView.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2017 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.popup; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import com.android.launcher3.LogAccelerateInterpolator; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.PillRevealOutlineProvider; +import com.android.launcher3.util.PillWidthRevealOutlineProvider; + +/** + * An abstract {@link FrameLayout} that supports animating an item's content + * (e.g. icon and text) separate from the item's background. + */ +public abstract class PopupItemView extends FrameLayout + implements ValueAnimator.AnimatorUpdateListener { + + protected static final Point sTempPoint = new Point(); + + private final Rect mPillRect; + private float mOpenAnimationProgress; + + protected View mIconView; + + public PopupItemView(Context context) { + this(context, null, 0); + } + + public PopupItemView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public PopupItemView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + mPillRect = new Rect(); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mIconView = findViewById(R.id.popup_item_icon); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + } + + public boolean willDrawIcon() { + return true; + } + + /** + * Creates an animator to play when the shortcut container is being opened. + */ + public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) { + Point center = getIconCenter(); + ValueAnimator openAnimator = new ZoomRevealOutlineProvider(center.x, center.y, + mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft) + .createRevealAnimator(this, false); + mOpenAnimationProgress = 0f; + openAnimator.addUpdateListener(this); + return openAnimator; + } + + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + mOpenAnimationProgress = valueAnimator.getAnimatedFraction(); + } + + public boolean isOpenOrOpening() { + return mOpenAnimationProgress > 0; + } + + /** + * Creates an animator to play when the shortcut container is being closed. + */ + public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft, + long duration) { + Point center = getIconCenter(); + ValueAnimator closeAnimator = new ZoomRevealOutlineProvider(center.x, center.y, + mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft) + .createRevealAnimator(this, true); + // Scale down the duration and interpolator according to the progress + // that the open animation was at when the close started. + closeAnimator.setDuration((long) (duration * mOpenAnimationProgress)); + closeAnimator.setInterpolator(new CloseInterpolator(mOpenAnimationProgress)); + closeAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mOpenAnimationProgress = 0; + } + }); + return closeAnimator; + } + + /** + * 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); + } + + /** + * 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; + } + + /** + * 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); + } + } + + /** + * An interpolator that reverses the current open animation progress. + */ + private static class CloseInterpolator extends LogAccelerateInterpolator { + private float mStartProgress; + private float mRemainingProgress; + + /** + * @param openAnimationProgress The progress that the open interpolator ended at. + */ + public CloseInterpolator(float openAnimationProgress) { + super(100, 0); + mStartProgress = 1f - openAnimationProgress; + mRemainingProgress = openAnimationProgress; + } + + @Override + public float getInterpolation(float v) { + return mStartProgress + super.getInterpolation(v) * mRemainingProgress; + } + } +} |