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