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