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.systemui.statusbar.phone;
18 
19 import com.android.systemui.R;
20 
21 import android.content.Context;
22 import android.content.res.TypedArray;
23 import android.graphics.Bitmap;
24 import android.graphics.BitmapShader;
25 import android.graphics.Canvas;
26 import android.graphics.Matrix;
27 import android.graphics.Paint;
28 import android.graphics.Shader;
29 import android.graphics.drawable.Drawable;
30 import android.util.AttributeSet;
31 import android.view.View;
32 
33 /**
34  * A view that displays a user image cropped to a circle with a frame.
35  */
36 public class UserAvatarView extends View {
37 
38     private int mActiveFrameColor;
39     private int mFrameColor;
40     private float mFrameWidth;
41     private float mFramePadding;
42     private Bitmap mBitmap;
43     private Drawable mDrawable;
44 
45     private final Paint mFramePaint = new Paint();
46     private final Paint mBitmapPaint = new Paint();
47     private final Matrix mDrawMatrix = new Matrix();
48 
49     private float mScale = 1;
50 
UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)51     public UserAvatarView(Context context, AttributeSet attrs,
52             int defStyleAttr,
53             int defStyleRes) {
54         super(context, attrs, defStyleAttr, defStyleRes);
55         final TypedArray a = context.obtainStyledAttributes(
56                 attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
57         final int N = a.getIndexCount();
58         for (int i = 0; i < N; i++) {
59             int attr = a.getIndex(i);
60             switch (attr) {
61                 case R.styleable.UserAvatarView_frameWidth:
62                     setFrameWidth(a.getDimension(attr, 0));
63                     break;
64                 case R.styleable.UserAvatarView_framePadding:
65                     setFramePadding(a.getDimension(attr, 0));
66                     break;
67                 case R.styleable.UserAvatarView_activeFrameColor:
68                     setActiveFrameColor(a.getColor(attr, 0));
69                     break;
70                 case R.styleable.UserAvatarView_frameColor:
71                     setFrameColor(a.getColor(attr, 0));
72                     break;
73             }
74         }
75         a.recycle();
76 
77         mFramePaint.setAntiAlias(true);
78         mFramePaint.setStyle(Paint.Style.STROKE);
79         mBitmapPaint.setAntiAlias(true);
80     }
81 
UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr)82     public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
83         this(context, attrs, defStyleAttr, 0);
84     }
85 
UserAvatarView(Context context, AttributeSet attrs)86     public UserAvatarView(Context context, AttributeSet attrs) {
87         this(context, attrs, 0);
88     }
89 
UserAvatarView(Context context)90     public UserAvatarView(Context context) {
91         this(context, null);
92     }
93 
setBitmap(Bitmap bitmap)94     public void setBitmap(Bitmap bitmap) {
95         setDrawable(null);
96         mBitmap = bitmap;
97         if (mBitmap != null) {
98             mBitmapPaint.setShader(new BitmapShader(
99                     bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
100         } else {
101             mBitmapPaint.setShader(null);
102         }
103         configureBounds();
104         invalidate();
105     }
106 
setFrameColor(int frameColor)107     public void setFrameColor(int frameColor) {
108         mFrameColor = frameColor;
109         invalidate();
110     }
111 
setActiveFrameColor(int activeFrameColor)112     public void setActiveFrameColor(int activeFrameColor) {
113         mActiveFrameColor = activeFrameColor;
114         invalidate();
115     }
116 
setFrameWidth(float frameWidth)117     public void setFrameWidth(float frameWidth) {
118         mFrameWidth = frameWidth;
119         invalidate();
120     }
121 
setFramePadding(float framePadding)122     public void setFramePadding(float framePadding) {
123         mFramePadding = framePadding;
124         invalidate();
125     }
126 
127     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)128     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
129         super.onLayout(changed, left, top, right, bottom);
130         configureBounds();
131     }
132 
configureBounds()133     public void configureBounds() {
134         int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
135         int vheight = getHeight() - mPaddingTop - mPaddingBottom;
136 
137         int dwidth;
138         int dheight;
139         if (mBitmap != null) {
140             dwidth = mBitmap.getWidth();
141             dheight = mBitmap.getHeight();
142         } else if (mDrawable != null) {
143             vwidth -= 2 * (mFrameWidth - 1);
144             vheight -= 2 * (mFrameWidth - 1);
145             dwidth = vwidth;
146             dheight = vheight;
147             mDrawable.setBounds(0, 0, dwidth, dheight);
148         } else {
149             return;
150         }
151 
152         float scale;
153         float dx;
154         float dy;
155 
156         scale = Math.min((float) vwidth / (float) dwidth,
157                 (float) vheight / (float) dheight);
158 
159         dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
160         dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
161 
162         mDrawMatrix.setScale(scale, scale);
163         mDrawMatrix.postTranslate(dx, dy);
164         mScale = scale;
165     }
166 
167     @Override
onDraw(Canvas canvas)168     protected void onDraw(Canvas canvas) {
169         int frameColor = isActivated() ? mActiveFrameColor : mFrameColor;
170         float halfW = getWidth() / 2f;
171         float halfH = getHeight() / 2f;
172         float halfSW = Math.min(halfH, halfW);
173         if (mBitmap != null && mScale > 0) {
174             int saveCount = canvas.getSaveCount();
175             canvas.save();
176             canvas.translate(mPaddingLeft, mPaddingTop);
177             canvas.concat(mDrawMatrix);
178             float halfBW = mBitmap.getWidth() / 2f;
179             float halfBH = mBitmap.getHeight() / 2f;
180             float halfBSW = Math.min(halfBH, halfBW);
181             canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint);
182             canvas.restoreToCount(saveCount);
183         } else if (mDrawable != null && mScale > 0) {
184             int saveCount = canvas.getSaveCount();
185             canvas.save();
186             canvas.translate(mPaddingLeft, mPaddingTop);
187             canvas.translate(mFrameWidth - 1, mFrameWidth - 1);
188             canvas.concat(mDrawMatrix);
189             mDrawable.draw(canvas);
190             canvas.restoreToCount(saveCount);
191         }
192         if (frameColor != 0) {
193             mFramePaint.setColor(frameColor);
194             mFramePaint.setStrokeWidth(mFrameWidth);
195             canvas.drawCircle(halfW, halfH, halfSW + (mFramePadding - mFrameWidth) / 2f,
196                     mFramePaint);
197         }
198     }
199 
setDrawable(Drawable d)200     public void setDrawable(Drawable d) {
201         if (mDrawable != null) {
202             mDrawable.setCallback(null);
203             unscheduleDrawable(mDrawable);
204         }
205         mDrawable = d;
206         if (d != null) {
207             d.setCallback(this);
208             if (d.isStateful()) {
209                 d.setState(getDrawableState());
210             }
211             d.setLayoutDirection(getLayoutDirection());
212             configureBounds();
213         }
214         if (d != null) {
215             mBitmap = null;
216         }
217         configureBounds();
218         invalidate();
219     }
220 
221     @Override
invalidateDrawable(Drawable dr)222     public void invalidateDrawable(Drawable dr) {
223         if (dr == mDrawable) {
224             invalidate();
225         } else {
226             super.invalidateDrawable(dr);
227         }
228     }
229 
230     @Override
verifyDrawable(Drawable who)231     protected boolean verifyDrawable(Drawable who) {
232         return who == mDrawable || super.verifyDrawable(who);
233     }
234 
235     @Override
drawableStateChanged()236     protected void drawableStateChanged() {
237         super.drawableStateChanged();
238         if (mDrawable != null && mDrawable.isStateful()) {
239             mDrawable.setState(getDrawableState());
240         }
241     }
242 }
243