Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 289d8a08 authored by Evan Rosky's avatar Evan Rosky Committed by Android (Google) Code Review
Browse files

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

parents c724dcf2 aa7f51fe
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
<!--
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>
+1 −0
Original line number Diff line number Diff line
@@ -1265,6 +1265,7 @@
  <java-symbol type="drawable" name="ic_corp_badge" />
  <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_user_badge" />
  <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_statusbar_icon" />
+7 −6
Original line number Diff line number Diff line
@@ -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. */
+428 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -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<UserDetails> data;
Loading