blob: cc882f32c2dae06d3b7484be34a5d9487fd3ee8e [file] [log] [blame]
/*
* 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.
* <p>
* There are two colors associated with this drawable:
* <p>
* A background color, which is loaded from a resource
* R.color.mode_icon_hover_highlight.
* <p>
* And, a main color, which is attached to the main circle that is expanded last
* and is drawn on top of the other colors.
* <p>
* 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;
}
}