Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.camera.ui;
     18 
     19 import android.animation.Animator;
     20 import android.animation.Animator.AnimatorListener;
     21 import android.animation.ValueAnimator;
     22 import android.content.res.Resources;
     23 import android.graphics.Canvas;
     24 import android.graphics.ColorFilter;
     25 import android.graphics.Paint;
     26 import android.graphics.PixelFormat;
     27 import android.graphics.Point;
     28 import android.graphics.drawable.Drawable;
     29 
     30 import com.android.camera.util.Gusterpolator;
     31 import com.android.camera2.R;
     32 
     33 /**
     34  * This class implements a circular drawable that starts with a zero radius
     35  * and can be triggered to animate expand to a given radius.
     36  * <p>
     37  * There are two colors associated with this drawable:
     38  * <p>
     39  * A background color, which is loaded from a resource
     40  * R.color.mode_icon_hover_highlight.
     41  * <p>
     42  * And, a main color, which is attached to the main circle that is expanded last
     43  * and is drawn on top of the other colors.
     44  * <p>
     45  * The driving purpose for this class is to implement a Material-like look and
     46  * feel for mode switcher touch events.
     47  */
     48 public class TouchCircleDrawable extends Drawable {
     49     private static final int CIRCLE_ANIM_DURATION_MS = 250;
     50 
     51     private Paint mColorPaint = new Paint();
     52     private Paint mBackgroundPaint = new Paint();
     53     private int mColor;
     54     private int mColorAlpha = 0xff;
     55     private int mColorRadius;
     56     private int mBackgroundRadius;
     57     private Drawable mIconDrawable;
     58     private int mIconDrawableSize;
     59     private boolean mDrawBackground;
     60 
     61     private Animator.AnimatorListener mAnimatorListener;
     62     private ValueAnimator.AnimatorUpdateListener mUpdateListener;
     63 
     64     private static final int INVALID = -1;
     65     private int mW = INVALID;
     66     private int mH = INVALID;
     67     private Point mCenter;
     68 
     69     /**
     70      * Constructor
     71      *
     72      * @param resources Resources, needed to poke around for the background
     73      * color value.
     74      * @param color The main this circle drawable expands to.
     75      * @param baseColor The color of the initial expanded circle
     76      * (draws behind the main color).
     77      */
     78     public TouchCircleDrawable(Resources resources, int color, int baseColor) {
     79         super();
     80 
     81         mColorPaint.setAntiAlias(true);
     82         mBackgroundPaint.setAntiAlias(true);
     83         mBackgroundPaint.setColor(resources.getColor(R.color.mode_icon_hover_highlight));
     84 
     85         setColor(color);
     86     }
     87 
     88     /**
     89      * Constructor
     90      *
     91      * @param resources Resources, needed to poke around for the background color value.
     92      */
     93     public TouchCircleDrawable(Resources resources) {
     94         this(resources, 0xffffff, 0xffffff);
     95     }
     96 
     97     /**
     98      * Set the size of this drawable.
     99      *
    100      * @param w Width to set.
    101      * @param h Height to set.
    102      */
    103     public void setSize(int w, int h) {
    104         mW = w;
    105         mH = h;
    106     }
    107 
    108     /**
    109      * Set the center of the circle for this drawable.
    110      *
    111      * @param p The center point.
    112      */
    113     public void setCenter(Point p) {
    114         mCenter = p;
    115         updateIconBounds();
    116     }
    117 
    118     /**
    119      * @return The center of this drawable.
    120      */
    121     public Point getCenter() {
    122         return mCenter;
    123     }
    124 
    125     @Override
    126     public void draw(Canvas canvas) {
    127         int w = mW;
    128         int h = mH;
    129 
    130         if (w == INVALID || h == INVALID || mCenter == null) {
    131             return;
    132         }
    133 
    134         if (mDrawBackground) {
    135             canvas.drawCircle(mCenter.x, mCenter.y, mBackgroundRadius, mBackgroundPaint);
    136         }
    137         canvas.drawCircle(mCenter.x, mCenter.y, mColorRadius, mColorPaint);
    138         if (mIconDrawable != null) {
    139             mIconDrawable.draw(canvas);
    140         }
    141     }
    142 
    143     @Override
    144     public void setAlpha(int alpha) {
    145         mColorAlpha = alpha;
    146     }
    147 
    148     @Override
    149     public void setColorFilter(ColorFilter cf) {
    150         mColorPaint.setColorFilter(cf);
    151     }
    152 
    153     @Override
    154     public int getOpacity() {
    155         return PixelFormat.TRANSLUCENT;
    156     }
    157 
    158     /**
    159      * Set the main color.
    160      *
    161      * @param color The main color.
    162      */
    163     public void setColor(int color) {
    164         mColor = color;
    165         mColorPaint.setColor(mColor);
    166         mColorPaint.setAlpha(mColorAlpha);
    167     }
    168 
    169     public void setIconDrawable(Drawable d, int size) {
    170         mIconDrawable = d;
    171         mIconDrawableSize = size;
    172         updateIconBounds();
    173     }
    174 
    175     private void updateIconBounds() {
    176         if (mCenter != null) {
    177             mIconDrawable.setBounds(
    178                 mCenter.x - mIconDrawableSize/2, mCenter.y - mIconDrawableSize/2,
    179                 mCenter.x + mIconDrawableSize/2, mCenter.y + mIconDrawableSize/2);
    180         }
    181     }
    182 
    183     /**
    184      * Start the expand animation.
    185      */
    186     public void animate() {
    187         mBackgroundRadius = Math.min(mW/2, mH/2);
    188 
    189         final ValueAnimator colorAnimator =
    190                 ValueAnimator.ofInt(0, Math.min(mW/2, mH/2));
    191         colorAnimator.setDuration(CIRCLE_ANIM_DURATION_MS);
    192         colorAnimator.setInterpolator(Gusterpolator.INSTANCE);
    193         colorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    194             @Override
    195             public void onAnimationUpdate(ValueAnimator animation) {
    196                 mColorRadius = (Integer) animation.getAnimatedValue();
    197                 invalidateSelf();
    198                 if (mUpdateListener != null) {
    199                     mUpdateListener.onAnimationUpdate(animation);
    200                 }
    201             }
    202         });
    203 
    204         colorAnimator.addListener(new AnimatorListener() {
    205             @Override
    206             public void onAnimationStart(Animator animation) {
    207                 mDrawBackground = true;
    208 
    209                 if (mAnimatorListener != null) {
    210                     mAnimatorListener.onAnimationStart(animation);
    211                 }
    212             }
    213 
    214             @Override
    215             public void onAnimationEnd(Animator animation) {
    216                 mDrawBackground = false;
    217 
    218                 if (mAnimatorListener != null) {
    219                     mAnimatorListener.onAnimationEnd(animation);
    220                 }
    221             }
    222 
    223             @Override
    224             public void onAnimationCancel(Animator animation) {
    225                 mDrawBackground = false;
    226 
    227                 if (mAnimatorListener != null) {
    228                     mAnimatorListener.onAnimationCancel(animation);
    229                 }
    230             }
    231 
    232             @Override
    233             public void onAnimationRepeat(Animator animation) {
    234                 if (mAnimatorListener != null) {
    235                     mAnimatorListener.onAnimationRepeat(animation);
    236                 }
    237             }
    238         });
    239 
    240         colorAnimator.start();
    241     }
    242 
    243     /**
    244      *  Reset this drawable to its initial, preanimated state.
    245      */
    246     public void reset() {
    247         mColorRadius = 0;
    248     }
    249 
    250     /**
    251      * Set an {@link android.animation.Animator.AnimatorListener} to be
    252      * attached to the animation.
    253      *
    254      * @param listener The listener.
    255      */
    256     public void setAnimatorListener(Animator.AnimatorListener listener) {
    257         mAnimatorListener = listener;
    258     }
    259 
    260     /**
    261      * Set an {@link android.animation.ValueAnimator} to be
    262      * attached to the animation.
    263      *
    264      * @param listener The listener.
    265      */
    266     public void setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) {
    267         mUpdateListener = listener;
    268     }
    269 }
    270