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
17package com.android.systemui.statusbar.phone;
18
19import com.android.systemui.R;
20
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.Bitmap;
24import android.graphics.BitmapShader;
25import android.graphics.Canvas;
26import android.graphics.Matrix;
27import android.graphics.Paint;
28import android.graphics.Shader;
29import android.graphics.drawable.Drawable;
30import android.util.AttributeSet;
31import android.view.View;
32
33/**
34 * A view that displays a user image cropped to a circle with a frame.
35 */
36public 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
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
82    public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
83        this(context, attrs, defStyleAttr, 0);
84    }
85
86    public UserAvatarView(Context context, AttributeSet attrs) {
87        this(context, attrs, 0);
88    }
89
90    public UserAvatarView(Context context) {
91        this(context, null);
92    }
93
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
107    public void setFrameColor(int frameColor) {
108        mFrameColor = frameColor;
109        invalidate();
110    }
111
112    public void setActiveFrameColor(int activeFrameColor) {
113        mActiveFrameColor = activeFrameColor;
114        invalidate();
115    }
116
117    public void setFrameWidth(float frameWidth) {
118        mFrameWidth = frameWidth;
119        invalidate();
120    }
121
122    public void setFramePadding(float framePadding) {
123        mFramePadding = framePadding;
124        invalidate();
125    }
126
127    @Override
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
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
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
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
222    public void invalidateDrawable(Drawable dr) {
223        if (dr == mDrawable) {
224            invalidate();
225        } else {
226            super.invalidateDrawable(dr);
227        }
228    }
229
230    @Override
231    protected boolean verifyDrawable(Drawable who) {
232        return who == mDrawable || super.verifyDrawable(who);
233    }
234
235    @Override
236    protected void drawableStateChanged() {
237        super.drawableStateChanged();
238        if (mDrawable != null && mDrawable.isStateful()) {
239            mDrawable.setState(getDrawableState());
240        }
241    }
242}
243