/* * Copyright (C) 2014 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.camera.ui; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.ValueAnimator; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.drawable.Drawable; import com.android.camera.util.Gusterpolator; import com.android.camera2.R; /** * This class implements a circular drawable that starts with a zero radius * and can be triggered to animate expand to a given radius. *

* There are two colors associated with this drawable: *

* A background color, which is loaded from a resource * R.color.mode_icon_hover_highlight. *

* And, a main color, which is attached to the main circle that is expanded last * and is drawn on top of the other colors. *

* The driving purpose for this class is to implement a Material-like look and * feel for mode switcher touch events. */ public class TouchCircleDrawable extends Drawable { private static final int CIRCLE_ANIM_DURATION_MS = 250; private Paint mColorPaint = new Paint(); private Paint mBackgroundPaint = new Paint(); private int mColor; private int mColorAlpha = 0xff; private int mColorRadius; private int mBackgroundRadius; private Drawable mIconDrawable; private int mIconDrawableSize; private boolean mDrawBackground; private Animator.AnimatorListener mAnimatorListener; private ValueAnimator.AnimatorUpdateListener mUpdateListener; private static final int INVALID = -1; private int mW = INVALID; private int mH = INVALID; private Point mCenter; /** * Constructor * * @param resources Resources, needed to poke around for the background * color value. * @param color The main this circle drawable expands to. * @param baseColor The color of the initial expanded circle * (draws behind the main color). */ public TouchCircleDrawable(Resources resources, int color, int baseColor) { super(); mColorPaint.setAntiAlias(true); mBackgroundPaint.setAntiAlias(true); mBackgroundPaint.setColor(resources.getColor(R.color.mode_icon_hover_highlight)); setColor(color); } /** * Constructor * * @param resources Resources, needed to poke around for the background color value. */ public TouchCircleDrawable(Resources resources) { this(resources, 0xffffff, 0xffffff); } /** * Set the size of this drawable. * * @param w Width to set. * @param h Height to set. */ public void setSize(int w, int h) { mW = w; mH = h; } /** * Set the center of the circle for this drawable. * * @param p The center point. */ public void setCenter(Point p) { mCenter = p; updateIconBounds(); } /** * @return The center of this drawable. */ public Point getCenter() { return mCenter; } @Override public void draw(Canvas canvas) { int w = mW; int h = mH; if (w == INVALID || h == INVALID || mCenter == null) { return; } if (mDrawBackground) { canvas.drawCircle(mCenter.x, mCenter.y, mBackgroundRadius, mBackgroundPaint); } canvas.drawCircle(mCenter.x, mCenter.y, mColorRadius, mColorPaint); if (mIconDrawable != null) { mIconDrawable.draw(canvas); } } @Override public void setAlpha(int alpha) { mColorAlpha = alpha; } @Override public void setColorFilter(ColorFilter cf) { mColorPaint.setColorFilter(cf); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } /** * Set the main color. * * @param color The main color. */ public void setColor(int color) { mColor = color; mColorPaint.setColor(mColor); mColorPaint.setAlpha(mColorAlpha); } public void setIconDrawable(Drawable d, int size) { mIconDrawable = d; mIconDrawableSize = size; updateIconBounds(); } private void updateIconBounds() { if (mCenter != null) { mIconDrawable.setBounds( mCenter.x - mIconDrawableSize/2, mCenter.y - mIconDrawableSize/2, mCenter.x + mIconDrawableSize/2, mCenter.y + mIconDrawableSize/2); } } /** * Start the expand animation. */ public void animate() { mBackgroundRadius = Math.min(mW/2, mH/2); final ValueAnimator colorAnimator = ValueAnimator.ofInt(0, Math.min(mW/2, mH/2)); colorAnimator.setDuration(CIRCLE_ANIM_DURATION_MS); colorAnimator.setInterpolator(Gusterpolator.INSTANCE); colorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mColorRadius = (Integer) animation.getAnimatedValue(); invalidateSelf(); if (mUpdateListener != null) { mUpdateListener.onAnimationUpdate(animation); } } }); colorAnimator.addListener(new AnimatorListener() { @Override public void onAnimationStart(Animator animation) { mDrawBackground = true; if (mAnimatorListener != null) { mAnimatorListener.onAnimationStart(animation); } } @Override public void onAnimationEnd(Animator animation) { mDrawBackground = false; if (mAnimatorListener != null) { mAnimatorListener.onAnimationEnd(animation); } } @Override public void onAnimationCancel(Animator animation) { mDrawBackground = false; if (mAnimatorListener != null) { mAnimatorListener.onAnimationCancel(animation); } } @Override public void onAnimationRepeat(Animator animation) { if (mAnimatorListener != null) { mAnimatorListener.onAnimationRepeat(animation); } } }); colorAnimator.start(); } /** * Reset this drawable to its initial, preanimated state. */ public void reset() { mColorRadius = 0; } /** * Set an {@link android.animation.Animator.AnimatorListener} to be * attached to the animation. * * @param listener The listener. */ public void setAnimatorListener(Animator.AnimatorListener listener) { mAnimatorListener = listener; } /** * Set an {@link android.animation.ValueAnimator} to be * attached to the animation. * * @param listener The listener. */ public void setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) { mUpdateListener = listener; } }