/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.systemui.statusbar.phone; import com.android.systemui.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; /** * A view that displays a user image cropped to a circle with a frame. */ public class UserAvatarView extends View { private int mActiveFrameColor; private int mFrameColor; private float mFrameWidth; private float mFramePadding; private Bitmap mBitmap; private Drawable mDrawable; private final Paint mFramePaint = new Paint(); private final Paint mBitmapPaint = new Paint(); private final Matrix mDrawMatrix = new Matrix(); private float mScale = 1; public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes); final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); switch (attr) { case R.styleable.UserAvatarView_frameWidth: setFrameWidth(a.getDimension(attr, 0)); break; case R.styleable.UserAvatarView_framePadding: setFramePadding(a.getDimension(attr, 0)); break; case R.styleable.UserAvatarView_activeFrameColor: setActiveFrameColor(a.getColor(attr, 0)); break; case R.styleable.UserAvatarView_frameColor: setFrameColor(a.getColor(attr, 0)); break; } } a.recycle(); mFramePaint.setAntiAlias(true); mFramePaint.setStyle(Paint.Style.STROKE); mBitmapPaint.setAntiAlias(true); } public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public UserAvatarView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public UserAvatarView(Context context) { this(context, null); } public void setBitmap(Bitmap bitmap) { setDrawable(null); mBitmap = bitmap; if (mBitmap != null) { mBitmapPaint.setShader(new BitmapShader( bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); } else { mBitmapPaint.setShader(null); } configureBounds(); invalidate(); } public void setFrameColor(int frameColor) { mFrameColor = frameColor; invalidate(); } public void setActiveFrameColor(int activeFrameColor) { mActiveFrameColor = activeFrameColor; invalidate(); } public void setFrameWidth(float frameWidth) { mFrameWidth = frameWidth; invalidate(); } public void setFramePadding(float framePadding) { mFramePadding = framePadding; invalidate(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); configureBounds(); } public void configureBounds() { int vwidth = getWidth() - mPaddingLeft - mPaddingRight; int vheight = getHeight() - mPaddingTop - mPaddingBottom; int dwidth; int dheight; if (mBitmap != null) { dwidth = mBitmap.getWidth(); dheight = mBitmap.getHeight(); } else if (mDrawable != null) { vwidth -= 2 * (mFrameWidth - 1); vheight -= 2 * (mFrameWidth - 1); dwidth = vwidth; dheight = vheight; mDrawable.setBounds(0, 0, dwidth, dheight); } else { return; } float scale; float dx; float dy; scale = Math.min((float) vwidth / (float) dwidth, (float) vheight / (float) dheight); dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f); dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f); mDrawMatrix.setScale(scale, scale); mDrawMatrix.postTranslate(dx, dy); mScale = scale; } @Override protected void onDraw(Canvas canvas) { int frameColor = isActivated() ? mActiveFrameColor : mFrameColor; float halfW = getWidth() / 2f; float halfH = getHeight() / 2f; float halfSW = Math.min(halfH, halfW); if (mBitmap != null && mScale > 0) { int saveCount = canvas.getSaveCount(); canvas.save(); canvas.translate(mPaddingLeft, mPaddingTop); canvas.concat(mDrawMatrix); float halfBW = mBitmap.getWidth() / 2f; float halfBH = mBitmap.getHeight() / 2f; float halfBSW = Math.min(halfBH, halfBW); canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint); canvas.restoreToCount(saveCount); } else if (mDrawable != null && mScale > 0) { int saveCount = canvas.getSaveCount(); canvas.save(); canvas.translate(mPaddingLeft, mPaddingTop); canvas.translate(mFrameWidth - 1, mFrameWidth - 1); canvas.concat(mDrawMatrix); mDrawable.draw(canvas); canvas.restoreToCount(saveCount); } if (frameColor != 0) { mFramePaint.setColor(frameColor); mFramePaint.setStrokeWidth(mFrameWidth); canvas.drawCircle(halfW, halfH, halfSW + (mFramePadding - mFrameWidth) / 2f, mFramePaint); } } public void setDrawable(Drawable d) { if (mDrawable != null) { mDrawable.setCallback(null); unscheduleDrawable(mDrawable); } mDrawable = d; if (d != null) { d.setCallback(this); if (d.isStateful()) { d.setState(getDrawableState()); } d.setLayoutDirection(getLayoutDirection()); configureBounds(); } if (d != null) { mBitmap = null; } configureBounds(); invalidate(); } @Override public void invalidateDrawable(Drawable dr) { if (dr == mDrawable) { invalidate(); } else { super.invalidateDrawable(dr); } } @Override protected boolean verifyDrawable(Drawable who) { return who == mDrawable || super.verifyDrawable(who); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mDrawable != null && mDrawable.isStateful()) { mDrawable.setState(getDrawableState()); } } }