Merge "Centralized user icon drawing with badges" into nyc-dev

am: 289d8a0

* commit '289d8a08501125ef1bfb5cd49245df91519b0fe6':
  Centralized user icon drawing with badges

Change-Id: I9547131f6dadb2ca384e42df52cd135f9038f1ab
This commit is contained in:
Evan Rosky
2016-04-08 15:47:34 +00:00
committed by android-build-merger
18 changed files with 614 additions and 280 deletions

View File

@@ -0,0 +1,24 @@
<!--
Copyright (C) 2016 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportWidth="36.0"
android:viewportHeight="36.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M18,0C8.06,-0 0,8.06 0,18C0,27.94 8.06,36 18,36C27.94,36 36,27.94 36,18C36,8.06 27.94,0 18,0zM15.5,10.5L20.5,10.5L21.75,11.75L21.75,13L24.66,13C25.57,13 26.34,13.74 26.34,14.66L26.34,18C26.34,18.92 25.57,19.66 24.66,19.66L19.66,19.66L19.66,18.41L16.34,18.41L16.34,19.66L11.34,19.66C10.43,19.66 9.66,18.92 9.66,18L9.66,14.66C9.66,13.74 10.43,13 11.34,13L14.25,13L14.25,11.78L15.5,10.5zM15.5,11.75L15.5,13L20.5,13L20.5,11.75L15.5,11.75zM10.5,20.5L16.34,20.5L16.34,21.75L19.66,21.75L19.66,20.5L25.5,20.5L25.5,23.84C25.5,24.76 24.76,25.5 23.84,25.5L12.16,25.5C11.24,25.5 10.5,24.76 10.5,23.84L10.5,20.5z"/>
</vector>

View File

@@ -1265,6 +1265,7 @@
<java-symbol type="drawable" name="ic_corp_badge" /> <java-symbol type="drawable" name="ic_corp_badge" />
<java-symbol type="drawable" name="ic_corp_badge_off" /> <java-symbol type="drawable" name="ic_corp_badge_off" />
<java-symbol type="drawable" name="ic_corp_icon_badge" /> <java-symbol type="drawable" name="ic_corp_icon_badge" />
<java-symbol type="drawable" name="ic_corp_user_badge" />
<java-symbol type="drawable" name="ic_corp_badge_no_background" /> <java-symbol type="drawable" name="ic_corp_badge_no_background" />
<java-symbol type="drawable" name="ic_corp_icon" /> <java-symbol type="drawable" name="ic_corp_icon" />
<java-symbol type="drawable" name="ic_corp_statusbar_icon" /> <java-symbol type="drawable" name="ic_corp_statusbar_icon" />

View File

@@ -15,7 +15,7 @@ import android.net.ConnectivityManager;
import android.os.BatteryManager; import android.os.BatteryManager;
import android.os.UserManager; import android.os.UserManager;
import com.android.internal.util.UserIcons; import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.settingslib.drawable.UserIconDrawable;
import java.text.NumberFormat; import java.text.NumberFormat;
@@ -73,21 +73,22 @@ public class Utils {
/** /**
* Returns a circular icon for a user. * Returns a circular icon for a user.
*/ */
public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) { public static UserIconDrawable getUserIcon(Context context, UserManager um, UserInfo user) {
final int iconSize = UserIconDrawable.getSizeForList(context);
if (user.isManagedProfile()) { if (user.isManagedProfile()) {
// We use predefined values for managed profiles // We use predefined values for managed profiles
Bitmap b = BitmapFactory.decodeResource(context.getResources(), Bitmap b = BitmapFactory.decodeResource(context.getResources(),
com.android.internal.R.drawable.ic_corp_icon); com.android.internal.R.drawable.ic_corp_icon);
return CircleFramedDrawable.getInstance(context, b); return new UserIconDrawable(iconSize).setIcon(b).bake();
} }
if (user.iconPath != null) { if (user.iconPath != null) {
Bitmap icon = um.getUserIcon(user.id); Bitmap icon = um.getUserIcon(user.id);
if (icon != null) { if (icon != null) {
return CircleFramedDrawable.getInstance(context, icon); return new UserIconDrawable(iconSize).setIcon(icon).bake();
} }
} }
return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap( return new UserIconDrawable(iconSize).setIconDrawable(
UserIcons.getDefaultUserIcon(user.id, /* light= */ false))); UserIcons.getDefaultUserIcon(user.id, /* light= */ false)).bake();
} }
/** Formats the ratio of amount/total as a percentage. */ /** Formats the ratio of amount/total as a percentage. */

View File

@@ -0,0 +1,428 @@
/*
* Copyright (C) 2016 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.settingslib.drawable;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import com.android.settingslib.R;
/**
* Converts the user avatar icon to a circularly clipped one with an optional badge and frame
*/
public class UserIconDrawable extends Drawable implements Drawable.Callback {
private Drawable mUserDrawable;
private Bitmap mUserIcon;
private Bitmap mBitmap; // baked representation. Required for transparent border around badge
private final Paint mIconPaint = new Paint();
private final Paint mPaint = new Paint();
private final Matrix mIconMatrix = new Matrix();
private float mIntrinsicRadius;
private float mDisplayRadius;
private float mPadding = 0;
private int mSize = 0; // custom "intrinsic" size for this drawable if non-zero
private boolean mInvalidated = true;
private ColorStateList mTintColor = null;
private PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_ATOP;
private float mFrameWidth;
private float mFramePadding;
private ColorStateList mFrameColor = null;
private Paint mFramePaint;
private Drawable mBadge;
private Paint mClearPaint;
private float mBadgeRadius;
private float mBadgeMargin;
/**
* Gets the system default managed-user badge as a drawable
* @param context
* @return drawable containing just the badge
*/
public static Drawable getManagedUserBadgeDrawable(Context context) {
int displayDensity = context.getResources().getDisplayMetrics().densityDpi;
return context.getResources().getDrawableForDensity(
com.android.internal.R.drawable.ic_corp_user_badge,
displayDensity, context.getTheme());
}
/**
* Gets the preferred list-item size of this drawable.
* @param context
* @return size in pixels
*/
public static int getSizeForList(Context context) {
return (int) context.getResources().getDimension(R.dimen.circle_avatar_size);
}
public UserIconDrawable() {
this(0);
}
/**
* Use this constructor if the drawable is intended to be placed in listviews
* @param intrinsicSize if 0, the intrinsic size will come from the icon itself
*/
public UserIconDrawable(int intrinsicSize) {
super();
mIconPaint.setAntiAlias(true);
mIconPaint.setFilterBitmap(true);
mPaint.setFilterBitmap(true);
mPaint.setAntiAlias(true);
if (intrinsicSize > 0) {
setBounds(0, 0, intrinsicSize, intrinsicSize);
setIntrinsicSize(intrinsicSize);
}
setIcon(null);
}
public UserIconDrawable setIcon(Bitmap icon) {
if (mUserDrawable != null) {
mUserDrawable.setCallback(null);
mUserDrawable = null;
}
mUserIcon = icon;
if (mUserIcon == null) {
mIconPaint.setShader(null);
mBitmap = null;
} else {
mIconPaint.setShader(new BitmapShader(icon, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP));
}
onBoundsChange(getBounds());
return this;
}
public UserIconDrawable setIconDrawable(Drawable icon) {
if (mUserDrawable != null) {
mUserDrawable.setCallback(null);
}
mUserIcon = null;
mUserDrawable = icon;
if (mUserDrawable == null) {
mBitmap = null;
} else {
mUserDrawable.setCallback(this);
}
onBoundsChange(getBounds());
return this;
}
public UserIconDrawable setBadge(Drawable badge) {
mBadge = badge;
if (mBadge != null) {
if (mClearPaint == null) {
mClearPaint = new Paint();
mClearPaint.setAntiAlias(true);
mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mClearPaint.setStyle(Paint.Style.FILL);
}
// update metrics
onBoundsChange(getBounds());
} else {
invalidateSelf();
}
return this;
}
public UserIconDrawable setBadgeIfManagedUser(Context context, int userId) {
Drawable badge = null;
boolean isManaged = context.getSystemService(DevicePolicyManager.class)
.getProfileOwnerAsUser(userId) != null;
if (isManaged) {
badge = getManagedUserBadgeDrawable(context);
}
return setBadge(badge);
}
public void setBadgeRadius(float radius) {
mBadgeRadius = radius;
onBoundsChange(getBounds());
}
public void setBadgeMargin(float margin) {
mBadgeMargin = margin;
onBoundsChange(getBounds());
}
/**
* Sets global padding of icon/frame. Doesn't effect the badge.
* @param padding
*/
public void setPadding(float padding) {
mPadding = padding;
onBoundsChange(getBounds());
}
private void initFramePaint() {
if (mFramePaint == null) {
mFramePaint = new Paint();
mFramePaint.setStyle(Paint.Style.STROKE);
mFramePaint.setAntiAlias(true);
}
}
public void setFrameWidth(float width) {
initFramePaint();
mFrameWidth = width;
mFramePaint.setStrokeWidth(width);
onBoundsChange(getBounds());
}
public void setFramePadding(float padding) {
initFramePaint();
mFramePadding = padding;
onBoundsChange(getBounds());
}
public void setFrameColor(int color) {
initFramePaint();
mFramePaint.setColor(color);
invalidateSelf();
}
public void setFrameColor(ColorStateList colorList) {
initFramePaint();
mFrameColor = colorList;
invalidateSelf();
}
/**
* This sets the "intrinsic" size of this drawable. Useful for views which use the drawable's
* intrinsic size for layout. It is independent of the bounds.
* @param size if 0, the intrinsic size will be set to the displayed icon's size
*/
public void setIntrinsicSize(int size) {
mSize = size;
}
@Override
public void draw(Canvas canvas) {
if (mInvalidated) {
rebake();
}
if (mBitmap != null) {
if (mTintColor == null) {
mPaint.setColorFilter(null);
} else {
int color = mTintColor.getColorForState(getState(), mTintColor.getDefaultColor());
if (mPaint.getColorFilter() == null) {
mPaint.setColorFilter(new PorterDuffColorFilter(color, mTintMode));
} else {
((PorterDuffColorFilter) mPaint.getColorFilter()).setMode(mTintMode);
((PorterDuffColorFilter) mPaint.getColorFilter()).setColor(color);
}
}
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
}
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
super.invalidateSelf();
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
}
@Override
public void setTintList(ColorStateList tintList) {
mTintColor = tintList;
super.invalidateSelf();
}
@Override
public void setTintMode(@NonNull PorterDuff.Mode mode) {
mTintMode = mode;
super.invalidateSelf();
}
/**
* This 'bakes' the current state of this icon into a bitmap and removes/recycles the source
* bitmap/drawable. Use this when no more changes will be made and an intrinsic size is set.
* This effectively turns this into a static drawable.
*/
public UserIconDrawable bake() {
if (mSize <= 0) {
throw new IllegalStateException("Baking requires an explicit intrinsic size");
}
onBoundsChange(new Rect(0, 0, mSize, mSize));
rebake();
mFrameColor = null;
mFramePaint = null;
mClearPaint = null;
if (mUserDrawable != null) {
mUserDrawable.setCallback(null);
mUserDrawable = null;
} else if (mUserIcon != null) {
mUserIcon.recycle();
mUserIcon = null;
}
return this;
}
private void rebake() {
mInvalidated = false;
if (mBitmap == null || (mUserDrawable == null && mUserIcon == null)) {
return;
}
final Canvas canvas = new Canvas(mBitmap);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
if(mUserDrawable != null) {
mUserDrawable.draw(canvas);
} else if (mUserIcon != null) {
int saveId = canvas.save();
canvas.concat(mIconMatrix);
canvas.drawCircle(mUserIcon.getWidth() * 0.5f, mUserIcon.getHeight() * 0.5f,
mIntrinsicRadius, mIconPaint);
canvas.restoreToCount(saveId);
}
if (mFrameColor != null) {
mFramePaint.setColor(mFrameColor.getColorForState(getState(), Color.TRANSPARENT));
}
if ((mFrameWidth + mFramePadding) > 0.001f) {
float radius = mDisplayRadius - mPadding - mFrameWidth * 0.5f;
canvas.drawCircle(getBounds().exactCenterX(), getBounds().exactCenterY(),
radius, mFramePaint);
}
if ((mBadge != null) && (mBadgeRadius > 0.001f)) {
final float badgeDiameter = mBadgeRadius * 2f;
final float badgeTop = mBitmap.getHeight() - badgeDiameter;
float badgeLeft = mBitmap.getWidth() - badgeDiameter;
mBadge.setBounds((int) badgeLeft, (int) badgeTop,
(int) (badgeLeft + badgeDiameter), (int) (badgeTop + badgeDiameter));
final float borderRadius = mBadge.getBounds().width() * 0.5f + mBadgeMargin;
canvas.drawCircle(badgeLeft + mBadgeRadius, badgeTop + mBadgeRadius,
borderRadius, mClearPaint);
mBadge.draw(canvas);
}
}
@Override
protected void onBoundsChange(Rect bounds) {
if (bounds.isEmpty() || (mUserIcon == null && mUserDrawable == null)) {
return;
}
// re-create bitmap if applicable
float newDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
int size = (int) (newDisplayRadius * 2);
if (mBitmap == null || size != ((int) (mDisplayRadius * 2))) {
mDisplayRadius = newDisplayRadius;
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
}
// update metrics
mDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
final float iconRadius = mDisplayRadius - mFrameWidth - mFramePadding - mPadding;
RectF dstRect = new RectF(bounds.exactCenterX() - iconRadius,
bounds.exactCenterY() - iconRadius,
bounds.exactCenterX() + iconRadius,
bounds.exactCenterY() + iconRadius);
if (mUserDrawable != null) {
Rect rounded = new Rect();
dstRect.round(rounded);
mIntrinsicRadius = Math.min(mUserDrawable.getIntrinsicWidth(),
mUserDrawable.getIntrinsicHeight()) * 0.5f;
mUserDrawable.setBounds(rounded);
} else if (mUserIcon != null) {
// Build square-to-square transformation matrix
final float iconCX = mUserIcon.getWidth() * 0.5f;
final float iconCY = mUserIcon.getHeight() * 0.5f;
mIntrinsicRadius = Math.min(iconCX, iconCY);
RectF srcRect = new RectF(iconCX - mIntrinsicRadius, iconCY - mIntrinsicRadius,
iconCX + mIntrinsicRadius, iconCY + mIntrinsicRadius);
mIconMatrix.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.FILL);
}
invalidateSelf();
}
@Override
public void invalidateSelf() {
super.invalidateSelf();
mInvalidated = true;
}
@Override
public boolean isStateful() {
return mFrameColor != null && mFrameColor.isStateful();
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public int getIntrinsicWidth() {
return (mSize <= 0 ? (int) mIntrinsicRadius * 2 : mSize);
}
@Override
public int getIntrinsicHeight() {
return getIntrinsicWidth();
}
@Override
public void invalidateDrawable(@NonNull Drawable who) {
invalidateSelf();
}
@Override
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
scheduleSelf(what, when);
}
@Override
public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
unscheduleSelf(what);
}
}

View File

@@ -33,7 +33,7 @@ import android.widget.ListAdapter;
import android.widget.SpinnerAdapter; import android.widget.SpinnerAdapter;
import android.widget.TextView; import android.widget.TextView;
import com.android.internal.util.UserIcons; import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.R; import com.android.settingslib.R;
@@ -71,7 +71,8 @@ public class UserAdapter implements SpinnerAdapter, ListAdapter {
} }
private static Drawable encircle(Context context, Drawable icon) { private static Drawable encircle(Context context, Drawable icon) {
return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(icon)); return new UserIconDrawable(UserIconDrawable.getSizeForList(context))
.setIconDrawable(icon).bake();
} }
} }
private ArrayList<UserDetails> data; private ArrayList<UserDetails> data;

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 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
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:color="@color/current_user_border_color" />
<item android:color="@android:color/transparent" />
</selector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 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
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
<item android:color="@android:color/transparent" />
</selector>

View File

@@ -33,14 +33,18 @@
<TextView android:id="@+id/user_name" <TextView android:id="@+id/user_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="16dp" android:layout_marginEnd="13dp"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
/> />
<com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture" <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
android:layout_width="@dimen/max_avatar_size" android:layout_width="@dimen/framed_avatar_size"
android:layout_height="@dimen/max_avatar_size" android:layout_height="@dimen/framed_avatar_size"
android:contentDescription="@null" android:contentDescription="@null"
android:backgroundTint="@color/qs_user_detail_avatar_tint"
android:backgroundTintMode="src_atop"
sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness" sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
sysui:framePadding="6dp" sysui:framePadding="2.5dp"
sysui:activeFrameColor="@color/current_user_border_color" /> sysui:badgeDiameter="18dp"
sysui:badgeMargin="1dp"
sysui:frameColor="@color/qs_user_detail_avatar_frame" />
</com.android.systemui.qs.tiles.UserDetailItemView> </com.android.systemui.qs.tiles.UserDetailItemView>

View File

@@ -33,12 +33,16 @@
<com.android.systemui.statusbar.phone.UserAvatarView <com.android.systemui.statusbar.phone.UserAvatarView
android:id="@+id/user_picture" android:id="@+id/user_picture"
android:layout_width="@dimen/max_avatar_size" android:layout_width="@dimen/framed_avatar_size"
android:layout_height="@dimen/max_avatar_size" android:layout_height="@dimen/framed_avatar_size"
android:layout_marginBottom="10dp" android:layout_marginBottom="7dp"
android:backgroundTint="@color/qs_user_detail_avatar_tint"
android:backgroundTintMode="src_atop"
systemui:frameWidth="2dp" systemui:frameWidth="2dp"
systemui:framePadding="6dp" systemui:framePadding="2.5dp"
systemui:activeFrameColor="@color/current_user_border_color"/> systemui:badgeDiameter="18dp"
systemui:badgeMargin="1dp"
systemui:frameColor="@color/qs_user_detail_avatar_frame"/>
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -53,10 +53,14 @@
<enum name="vertical" value="1" /> <enum name="vertical" value="1" />
</attr> </attr>
<declare-styleable name="UserAvatarView"> <declare-styleable name="UserAvatarView">
<attr name="avatarPadding" format="dimension" />
<attr name="frameWidth" format="dimension" /> <attr name="frameWidth" format="dimension" />
<attr name="framePadding" format="dimension" /> <attr name="framePadding" format="dimension" />
<!-- {@deprecated Use a statelist in frameColor instead.} -->
<attr name="activeFrameColor" format="color" /> <attr name="activeFrameColor" format="color" />
<attr name="frameColor" /> <attr name="frameColor" format="color" />
<attr name="badgeDiameter" format="dimension" />
<attr name="badgeMargin" format="dimension" />
</declare-styleable> </declare-styleable>
<declare-styleable name="UserDetailItemView"> <declare-styleable name="UserDetailItemView">
<attr name="regularFontFamily" format="string" /> <attr name="regularFontFamily" format="string" />

View File

@@ -388,6 +388,9 @@
quick settings header --> quick settings header -->
<dimen name="max_avatar_size">48dp</dimen> <dimen name="max_avatar_size">48dp</dimen>
<!-- Size of user icon + frame in the qs/keyguard user picker (incl. frame) -->
<dimen name="framed_avatar_size">54dp</dimen>
<!-- Margin on the left side of the carrier text on Keyguard --> <!-- Margin on the left side of the carrier text on Keyguard -->
<dimen name="keyguard_carrier_text_margin">16dp</dimen> <dimen name="keyguard_carrier_text_margin">16dp</dimen>

View File

@@ -1,54 +0,0 @@
/*
* 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;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
public class BitmapHelper {
/**
* Generate a new bitmap (width x height pixels, ARGB_8888) with the input bitmap scaled
* to fit and clipped to an inscribed circle.
* @param input Bitmap to resize and clip
* @param width Width of output bitmap (and diameter of circle)
* @param height Height of output bitmap
* @return A shiny new bitmap for you to use
*/
public static Bitmap createCircularClip(Bitmap input, int width, int height) {
if (input == null) return null;
final int inWidth = input.getWidth();
final int inHeight = input.getHeight();
final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
paint.setAntiAlias(true);
final RectF srcRect = new RectF(0, 0, inWidth, inHeight);
final RectF dstRect = new RectF(0, 0, width, height);
final Matrix m = new Matrix();
m.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER);
canvas.setMatrix(m);
canvas.drawCircle(inWidth / 2, inHeight / 2, inWidth / 2, paint);
return output;
}
}

View File

@@ -30,6 +30,7 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.systemui.FontSizeUtils; import com.android.systemui.FontSizeUtils;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.statusbar.phone.UserAvatarView; import com.android.systemui.statusbar.phone.UserAvatarView;
@@ -87,25 +88,29 @@ public class UserDetailItemView extends LinearLayout {
return (UserDetailItemView) convertView; return (UserDetailItemView) convertView;
} }
public void bind(String name, Bitmap picture) { public void bind(String name, Bitmap picture, int userId) {
mName.setText(name); mName.setText(name);
mAvatar.setBitmap(picture); mAvatar.setAvatarWithBadge(picture, userId);
} }
public void bind(String name, Drawable picture) { public void bind(String name, Drawable picture, int userId) {
mName.setText(name); mName.setText(name);
mAvatar.setDrawable(picture); mAvatar.setDrawableWithBadge(picture, userId);
}
public void setAvatarEnabled(boolean enabled) {
mAvatar.setEnabled(enabled);
} }
public void setDisabledByAdmin(boolean disabled) { public void setDisabledByAdmin(boolean disabled) {
mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE); mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE);
mName.setEnabled(!disabled); mName.setEnabled(!disabled);
mAvatar.setDisabled(disabled); mAvatar.setEnabled(!disabled);
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
mName.setEnabled(enabled); mName.setEnabled(enabled);
mAvatar.setDisabled(!enabled); mAvatar.setEnabled(enabled);
} }
@Override @Override

View File

@@ -76,9 +76,9 @@ public class UserDetailView extends PseudoGridView {
} }
String name = getName(mContext, item); String name = getName(mContext, item);
if (item.picture == null) { if (item.picture == null) {
v.bind(name, getDrawable(mContext, item)); v.bind(name, getDrawable(mContext, item), item.resolveId());
} else { } else {
v.bind(name, item.picture); v.bind(name, item.picture, item.info.id);
} }
v.setActivated(item.isCurrent); v.setActivated(item.isCurrent);
v.setDisabledByAdmin(item.isDisabledByAdmin); v.setDisabledByAdmin(item.isDisabledByAdmin);

View File

@@ -17,69 +17,57 @@
package com.android.systemui.statusbar.phone; package com.android.systemui.statusbar.phone;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Shader;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.systemui.R; import com.android.systemui.R;
/** /**
* A view that displays a user image cropped to a circle with a frame. * A view that displays a user image cropped to a circle with an optional frame.
*/ */
public class UserAvatarView extends View { public class UserAvatarView extends View {
private int mActiveFrameColor; private final UserIconDrawable mDrawable = new UserIconDrawable();
private int mFrameColor;
private float mFrameWidth;
private float mFramePadding;
private Bitmap mBitmap;
private Drawable mDrawable;
private boolean mIsDisabled;
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, public UserAvatarView(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleAttr,
int defStyleRes) { int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes( final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes); attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
final int N = a.getIndexCount(); final int N = a.getIndexCount();
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
int attr = a.getIndex(i); int attr = a.getIndex(i);
switch (attr) { switch (attr) {
case R.styleable.UserAvatarView_avatarPadding:
setAvatarPadding(a.getDimension(attr, 0));
break;
case R.styleable.UserAvatarView_frameWidth: case R.styleable.UserAvatarView_frameWidth:
setFrameWidth(a.getDimension(attr, 0)); setFrameWidth(a.getDimension(attr, 0));
break; break;
case R.styleable.UserAvatarView_framePadding: case R.styleable.UserAvatarView_framePadding:
setFramePadding(a.getDimension(attr, 0)); setFramePadding(a.getDimension(attr, 0));
break; break;
case R.styleable.UserAvatarView_activeFrameColor:
setActiveFrameColor(a.getColor(attr, 0));
break;
case R.styleable.UserAvatarView_frameColor: case R.styleable.UserAvatarView_frameColor:
setFrameColor(a.getColor(attr, 0)); setFrameColor(a.getColorStateList(attr));
break;
case R.styleable.UserAvatarView_badgeDiameter:
setBadgeDiameter(a.getDimension(attr, 0));
break;
case R.styleable.UserAvatarView_badgeMargin:
setBadgeMargin(a.getDimension(attr, 0));
break; break;
} }
} }
a.recycle(); a.recycle();
setBackground(mDrawable);
mFramePaint.setAntiAlias(true);
mFramePaint.setStyle(Paint.Style.STROKE);
mBitmapPaint.setAntiAlias(true);
} }
public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) { public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -94,180 +82,61 @@ public class UserAvatarView extends View {
this(context, null); this(context, null);
} }
/**
* @deprecated use {@link #setAvatar(Bitmap)} instead.
*/
@Deprecated
public void setBitmap(Bitmap bitmap) { public void setBitmap(Bitmap bitmap) {
setDrawable(null); setAvatar(bitmap);
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) { public void setFrameColor(ColorStateList color) {
mFrameColor = frameColor; mDrawable.setFrameColor(color);
invalidate();
}
public void setActiveFrameColor(int activeFrameColor) {
mActiveFrameColor = activeFrameColor;
invalidate();
} }
public void setFrameWidth(float frameWidth) { public void setFrameWidth(float frameWidth) {
mFrameWidth = frameWidth; mDrawable.setFrameWidth(frameWidth);
invalidate();
} }
public void setFramePadding(float framePadding) { public void setFramePadding(float framePadding) {
mFramePadding = framePadding; mDrawable.setFramePadding(framePadding);
invalidate();
} }
@Override public void setAvatarPadding(float avatarPadding) {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { mDrawable.setPadding(avatarPadding);
super.onLayout(changed, left, top, right, bottom);
configureBounds();
} }
public void configureBounds() { public void setBadgeDiameter(float diameter) {
int vwidth = getWidth() - mPaddingLeft - mPaddingRight; mDrawable.setBadgeRadius(diameter * 0.5f);
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 public void setBadgeMargin(float margin) {
protected void onDraw(Canvas canvas) { mDrawable.setBadgeMargin(margin);
int frameColor = isActivated() ? mActiveFrameColor : mFrameColor; }
float halfW = getWidth() / 2f;
float halfH = getHeight() / 2f; public void setAvatar(Bitmap avatar) {
float halfSW = Math.min(halfH, halfW); mDrawable.setIcon(avatar);
updateDrawableIfDisabled(); mDrawable.setBadge(null);
if (mBitmap != null && mScale > 0) { }
int saveCount = canvas.getSaveCount();
canvas.save(); public void setAvatarWithBadge(Bitmap avatar, int userId) {
canvas.translate(mPaddingLeft, mPaddingTop); mDrawable.setIcon(avatar);
canvas.concat(mDrawMatrix); mDrawable.setBadgeIfManagedUser(getContext(), userId);
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) { public void setDrawable(Drawable d) {
if (mDrawable != null) { if (d instanceof UserIconDrawable) {
mDrawable.setCallback(null); throw new RuntimeException("Recursively adding UserIconDrawable");
unscheduleDrawable(mDrawable);
} }
mDrawable = d; mDrawable.setIconDrawable(d);
if (d != null) { mDrawable.setBadge(null);
d.setCallback(this);
if (d.isStateful()) {
d.setState(getDrawableState());
}
d.setLayoutDirection(getLayoutDirection());
configureBounds();
}
if (d != null) {
mBitmap = null;
}
configureBounds();
invalidate();
} }
@Override public void setDrawableWithBadge(Drawable d, int userId) {
public void invalidateDrawable(Drawable dr) { if (d instanceof UserIconDrawable) {
if (dr == mDrawable) { throw new RuntimeException("Recursively adding UserIconDrawable");
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());
}
}
public void setDisabled(boolean disabled) {
if (mIsDisabled == disabled) {
return;
}
mIsDisabled = disabled;
invalidate();
}
private void updateDrawableIfDisabled() {
int disabledColor = getContext().getColor(R.color.qs_tile_disabled_color);
PorterDuffColorFilter filter = new PorterDuffColorFilter(disabledColor,
PorterDuff.Mode.SRC_ATOP);
if (mBitmap != null) {
if (mIsDisabled) {
mBitmapPaint.setColorFilter(filter);
} else {
mBitmapPaint.setColorFilter(null);
}
} else if (mDrawable != null) {
if (mIsDisabled) {
mDrawable.setColorFilter(filter);
} else {
mDrawable.setColorFilter(null);
}
} }
mDrawable.setIconDrawable(d);
mDrawable.setBadgeIfManagedUser(getContext(), userId);
} }
} }

View File

@@ -253,17 +253,13 @@ public class KeyguardUserSwitcher {
UserDetailItemView v = (UserDetailItemView) convertView; UserDetailItemView v = (UserDetailItemView) convertView;
String name = getName(mContext, item); String name = getName(mContext, item);
Drawable drawable;
if (item.picture == null) { if (item.picture == null) {
drawable = getDrawable(mContext, item).mutate(); v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
} else { } else {
drawable = new BitmapDrawable(mContext.getResources(), item.picture); v.bind(name, item.picture, item.info.id);
} }
// Disable the icon if switching is disabled // Disable the icon if switching is disabled
if (!item.isSwitchToEnabled) { v.setAvatarEnabled(item.isSwitchToEnabled);
drawable.setTint(mContext.getColor(R.color.qs_tile_disabled_color));
}
v.bind(name, drawable);
convertView.setActivated(item.isCurrent); convertView.setActivated(item.isCurrent);
convertView.setTag(item); convertView.setTag(item);
return convertView; return convertView;

View File

@@ -26,7 +26,6 @@ import android.content.pm.UserInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.RemoteException; import android.os.RemoteException;
@@ -37,7 +36,7 @@ import android.util.Log;
import android.util.Pair; import android.util.Pair;
import com.android.internal.util.UserIcons; import com.android.internal.util.UserIcons;
import com.android.systemui.BitmapHelper; import com.android.settingslib.drawable.UserIconDrawable;
import com.android.systemui.R; import com.android.systemui.R;
import java.util.ArrayList; import java.util.ArrayList;
@@ -155,8 +154,8 @@ public final class UserInfoController {
Drawable avatar = null; Drawable avatar = null;
Bitmap rawAvatar = um.getUserIcon(userId); Bitmap rawAvatar = um.getUserIcon(userId);
if (rawAvatar != null) { if (rawAvatar != null) {
avatar = new BitmapDrawable(mContext.getResources(), avatar = new UserIconDrawable(avatarSize)
BitmapHelper.createCircularClip(rawAvatar, avatarSize, avatarSize)); .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
} else { } else {
avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId, avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
/* light= */ true); /* light= */ true);

View File

@@ -50,7 +50,6 @@ import android.widget.BaseAdapter;
import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.internal.util.UserIcons; import com.android.internal.util.UserIcons;
import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.BitmapHelper;
import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.SystemUISecondaryUserService; import com.android.systemui.SystemUISecondaryUserService;
@@ -197,8 +196,6 @@ public class UserSwitcherController {
boolean canSwitchUsers = mUserManager.canSwitchUsers(); boolean canSwitchUsers = mUserManager.canSwitchUsers();
UserInfo currentUserInfo = null; UserInfo currentUserInfo = null;
UserRecord guestRecord = null; UserRecord guestRecord = null;
int avatarSize = mContext.getResources()
.getDimensionPixelSize(R.dimen.max_avatar_size);
for (UserInfo info : infos) { for (UserInfo info : infos) {
boolean isCurrent = currentId == info.id; boolean isCurrent = currentId == info.id;
@@ -219,8 +216,10 @@ public class UserSwitcherController {
picture = mUserManager.getUserIcon(info.id); picture = mUserManager.getUserIcon(info.id);
if (picture != null) { if (picture != null) {
picture = BitmapHelper.createCircularClip( int avatarSize = mContext.getResources()
picture, avatarSize, avatarSize); .getDimensionPixelSize(R.dimen.max_avatar_size);
picture = Bitmap.createScaledBitmap(
picture, avatarSize, avatarSize, true);
} }
} }
int index = isCurrent ? 0 : records.size(); int index = isCurrent ? 0 : records.size();
@@ -664,8 +663,7 @@ public class UserSwitcherController {
if (item.isAddUser) { if (item.isAddUser) {
return context.getDrawable(R.drawable.ic_add_circle_qs); return context.getDrawable(R.drawable.ic_add_circle_qs);
} }
return UserIcons.getDefaultUserIcon(item.isGuest ? UserHandle.USER_NULL : item.info.id, return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
/* light= */ true);
} }
public void refresh() { public void refresh() {
@@ -718,6 +716,13 @@ public class UserSwitcherController {
isSwitchToEnabled); isSwitchToEnabled);
} }
public int resolveId() {
if (isGuest || info == null) {
return UserHandle.USER_NULL;
}
return info.id;
}
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("UserRecord("); sb.append("UserRecord(");