diff --git a/core/res/res/drawable/ic_corp_user_badge.xml b/core/res/res/drawable/ic_corp_user_badge.xml new file mode 100644 index 0000000000000..23809d5a33101 --- /dev/null +++ b/core/res/res/drawable/ic_corp_user_badge.xml @@ -0,0 +1,24 @@ + + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d0414c36c4941..592904e47b5a5 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1265,6 +1265,7 @@ + diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 586f2690ba1fa..ca0b86a52e4f6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -15,7 +15,7 @@ import android.net.ConnectivityManager; import android.os.BatteryManager; import android.os.UserManager; import com.android.internal.util.UserIcons; -import com.android.settingslib.drawable.CircleFramedDrawable; +import com.android.settingslib.drawable.UserIconDrawable; import java.text.NumberFormat; @@ -73,21 +73,22 @@ public class Utils { /** * 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()) { // We use predefined values for managed profiles Bitmap b = BitmapFactory.decodeResource(context.getResources(), com.android.internal.R.drawable.ic_corp_icon); - return CircleFramedDrawable.getInstance(context, b); + return new UserIconDrawable(iconSize).setIcon(b).bake(); } if (user.iconPath != null) { Bitmap icon = um.getUserIcon(user.id); if (icon != null) { - return CircleFramedDrawable.getInstance(context, icon); + return new UserIconDrawable(iconSize).setIcon(icon).bake(); } } - return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap( - UserIcons.getDefaultUserIcon(user.id, /* light= */ false))); + return new UserIconDrawable(iconSize).setIconDrawable( + UserIcons.getDefaultUserIcon(user.id, /* light= */ false)).bake(); } /** Formats the ratio of amount/total as a percentage. */ diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java new file mode 100644 index 0000000000000..32478a7582fbd --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java @@ -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); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java index f9fa805f808d4..750601d82ce5e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java @@ -33,7 +33,7 @@ import android.widget.ListAdapter; import android.widget.SpinnerAdapter; import android.widget.TextView; import com.android.internal.util.UserIcons; -import com.android.settingslib.drawable.CircleFramedDrawable; +import com.android.settingslib.drawable.UserIconDrawable; import com.android.settingslib.R; @@ -71,7 +71,8 @@ public class UserAdapter implements SpinnerAdapter, ListAdapter { } 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 data; diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml new file mode 100644 index 0000000000000..20251c2fedc02 --- /dev/null +++ b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml new file mode 100644 index 0000000000000..2b75c369c2e0a --- /dev/null +++ b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml index c6e453a5d991f..d6855286142b7 100644 --- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml +++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml @@ -33,14 +33,18 @@ + sysui:framePadding="2.5dp" + sysui:badgeDiameter="18dp" + sysui:badgeMargin="1dp" + sysui:frameColor="@color/qs_user_detail_avatar_frame" /> diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml index 661d74a468e47..58fc0694c72c4 100644 --- a/packages/SystemUI/res/layout/qs_user_detail_item.xml +++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml @@ -33,12 +33,16 @@ + systemui:framePadding="2.5dp" + systemui:badgeDiameter="18dp" + systemui:badgeMargin="1dp" + systemui:frameColor="@color/qs_user_detail_avatar_frame"/> + + - + + + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a4eadbfb56011..cf2e3384de084 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -388,6 +388,9 @@ quick settings header --> 48dp + + 54dp + 16dp diff --git a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java b/packages/SystemUI/src/com/android/systemui/BitmapHelper.java deleted file mode 100644 index 1933bbce6e3e8..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java +++ /dev/null @@ -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; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java index fcf758b458686..99eae024a7c79 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java @@ -30,6 +30,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.util.ArrayUtils; +import com.android.settingslib.drawable.UserIconDrawable; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.statusbar.phone.UserAvatarView; @@ -87,25 +88,29 @@ public class UserDetailItemView extends LinearLayout { return (UserDetailItemView) convertView; } - public void bind(String name, Bitmap picture) { + public void bind(String name, Bitmap picture, int userId) { 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); - mAvatar.setDrawable(picture); + mAvatar.setDrawableWithBadge(picture, userId); + } + + public void setAvatarEnabled(boolean enabled) { + mAvatar.setEnabled(enabled); } public void setDisabledByAdmin(boolean disabled) { mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE); mName.setEnabled(!disabled); - mAvatar.setDisabled(disabled); + mAvatar.setEnabled(!disabled); } public void setEnabled(boolean enabled) { mName.setEnabled(enabled); - mAvatar.setDisabled(!enabled); + mAvatar.setEnabled(enabled); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java index da98762352631..d4fa765cbefbd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -76,9 +76,9 @@ public class UserDetailView extends PseudoGridView { } String name = getName(mContext, item); if (item.picture == null) { - v.bind(name, getDrawable(mContext, item)); + v.bind(name, getDrawable(mContext, item), item.resolveId()); } else { - v.bind(name, item.picture); + v.bind(name, item.picture, item.info.id); } v.setActivated(item.isCurrent); v.setDisabledByAdmin(item.isDisabledByAdmin); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java index 093a827272b85..dc1b35d6c701b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java @@ -17,69 +17,57 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.content.res.ColorStateList; 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.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; +import com.android.settingslib.drawable.UserIconDrawable; 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 { - private int mActiveFrameColor; - 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; + private final UserIconDrawable mDrawable = new UserIconDrawable(); 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_avatarPadding: + setAvatarPadding(a.getDimension(attr, 0)); + break; 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)); + 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; } } a.recycle(); - - mFramePaint.setAntiAlias(true); - mFramePaint.setStyle(Paint.Style.STROKE); - mBitmapPaint.setAntiAlias(true); + setBackground(mDrawable); } public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) { @@ -94,180 +82,61 @@ public class UserAvatarView extends View { this(context, null); } + /** + * @deprecated use {@link #setAvatar(Bitmap)} instead. + */ + @Deprecated 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(); + setAvatar(bitmap); } - public void setFrameColor(int frameColor) { - mFrameColor = frameColor; - invalidate(); - } - - public void setActiveFrameColor(int activeFrameColor) { - mActiveFrameColor = activeFrameColor; - invalidate(); + public void setFrameColor(ColorStateList color) { + mDrawable.setFrameColor(color); } public void setFrameWidth(float frameWidth) { - mFrameWidth = frameWidth; - invalidate(); + mDrawable.setFrameWidth(frameWidth); } public void setFramePadding(float framePadding) { - mFramePadding = framePadding; - invalidate(); + mDrawable.setFramePadding(framePadding); } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - configureBounds(); + public void setAvatarPadding(float avatarPadding) { + mDrawable.setPadding(avatarPadding); } - 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; + public void setBadgeDiameter(float diameter) { + mDrawable.setBadgeRadius(diameter * 0.5f); } - @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); - updateDrawableIfDisabled(); - 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 setBadgeMargin(float margin) { + mDrawable.setBadgeMargin(margin); + } + + public void setAvatar(Bitmap avatar) { + mDrawable.setIcon(avatar); + mDrawable.setBadge(null); + } + + public void setAvatarWithBadge(Bitmap avatar, int userId) { + mDrawable.setIcon(avatar); + mDrawable.setBadgeIfManagedUser(getContext(), userId); } public void setDrawable(Drawable d) { - if (mDrawable != null) { - mDrawable.setCallback(null); - unscheduleDrawable(mDrawable); + if (d instanceof UserIconDrawable) { + throw new RuntimeException("Recursively adding UserIconDrawable"); } - 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(); + mDrawable.setIconDrawable(d); + mDrawable.setBadge(null); } - @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()); - } - } - - 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); - } + public void setDrawableWithBadge(Drawable d, int userId) { + if (d instanceof UserIconDrawable) { + throw new RuntimeException("Recursively adding UserIconDrawable"); } + mDrawable.setIconDrawable(d); + mDrawable.setBadgeIfManagedUser(getContext(), userId); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java index fb310a64a3585..c39d718a44f48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java @@ -253,17 +253,13 @@ public class KeyguardUserSwitcher { UserDetailItemView v = (UserDetailItemView) convertView; String name = getName(mContext, item); - Drawable drawable; if (item.picture == null) { - drawable = getDrawable(mContext, item).mutate(); + v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId()); } else { - drawable = new BitmapDrawable(mContext.getResources(), item.picture); + v.bind(name, item.picture, item.info.id); } // Disable the icon if switching is disabled - if (!item.isSwitchToEnabled) { - drawable.setTint(mContext.getColor(R.color.qs_tile_disabled_color)); - } - v.bind(name, drawable); + v.setAvatarEnabled(item.isSwitchToEnabled); convertView.setActivated(item.isCurrent); convertView.setTag(item); return convertView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java index 85ac755e6cc37..bae5bdab07bcc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java @@ -26,7 +26,6 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.RemoteException; @@ -37,7 +36,7 @@ import android.util.Log; import android.util.Pair; import com.android.internal.util.UserIcons; -import com.android.systemui.BitmapHelper; +import com.android.settingslib.drawable.UserIconDrawable; import com.android.systemui.R; import java.util.ArrayList; @@ -155,8 +154,8 @@ public final class UserInfoController { Drawable avatar = null; Bitmap rawAvatar = um.getUserIcon(userId); if (rawAvatar != null) { - avatar = new BitmapDrawable(mContext.getResources(), - BitmapHelper.createCircularClip(rawAvatar, avatarSize, avatarSize)); + avatar = new UserIconDrawable(avatarSize) + .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake(); } else { avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId, /* light= */ true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index ea0bdf29c25c2..c82ba3b168902 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -50,7 +50,6 @@ import android.widget.BaseAdapter; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.internal.util.UserIcons; import com.android.settingslib.RestrictedLockUtils; -import com.android.systemui.BitmapHelper; import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; @@ -197,8 +196,6 @@ public class UserSwitcherController { boolean canSwitchUsers = mUserManager.canSwitchUsers(); UserInfo currentUserInfo = null; UserRecord guestRecord = null; - int avatarSize = mContext.getResources() - .getDimensionPixelSize(R.dimen.max_avatar_size); for (UserInfo info : infos) { boolean isCurrent = currentId == info.id; @@ -219,8 +216,10 @@ public class UserSwitcherController { picture = mUserManager.getUserIcon(info.id); if (picture != null) { - picture = BitmapHelper.createCircularClip( - picture, avatarSize, avatarSize); + int avatarSize = mContext.getResources() + .getDimensionPixelSize(R.dimen.max_avatar_size); + picture = Bitmap.createScaledBitmap( + picture, avatarSize, avatarSize, true); } } int index = isCurrent ? 0 : records.size(); @@ -664,8 +663,7 @@ public class UserSwitcherController { if (item.isAddUser) { return context.getDrawable(R.drawable.ic_add_circle_qs); } - return UserIcons.getDefaultUserIcon(item.isGuest ? UserHandle.USER_NULL : item.info.id, - /* light= */ true); + return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true); } public void refresh() { @@ -718,6 +716,13 @@ public class UserSwitcherController { isSwitchToEnabled); } + public int resolveId() { + if (isGuest || info == null) { + return UserHandle.USER_NULL; + } + return info.id; + } + public String toString() { StringBuilder sb = new StringBuilder(); sb.append("UserRecord(");