1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.widget; 18 19 import android.annotation.ColorInt; 20 import android.content.res.Resources; 21 import android.graphics.Bitmap; 22 import android.graphics.Canvas; 23 import android.graphics.Color; 24 import android.graphics.Paint; 25 import android.graphics.Path; 26 import android.graphics.Rect; 27 import android.graphics.drawable.AdaptiveIconDrawable; 28 import android.graphics.drawable.DrawableWrapper; 29 import android.os.RemoteException; 30 import android.util.PathParser; 31 import android.view.IWindowManager; 32 import android.view.WindowManagerGlobal; 33 34 import androidx.annotation.IntDef; 35 import androidx.annotation.VisibleForTesting; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 40 /** 41 * Adaptive outline drawable with white plain background color and black outline 42 */ 43 public class AdaptiveOutlineDrawable extends DrawableWrapper { 44 45 private static final float ADVANCED_ICON_CENTER = 50f; 46 private static final float ADVANCED_ICON_RADIUS = 48f; 47 48 public static final int ICON_TYPE_DEFAULT = 0; 49 public static final int ICON_TYPE_ADVANCED = 1; 50 51 @Retention(RetentionPolicy.SOURCE) 52 @IntDef({ICON_TYPE_DEFAULT, ICON_TYPE_ADVANCED}) 53 public @interface AdaptiveOutlineIconType { 54 } 55 56 @VisibleForTesting 57 Paint mOutlinePaint; 58 private Path mPath; 59 private int mInsetPx; 60 private int mStrokeWidth; 61 private Bitmap mBitmap; 62 private int mType; 63 AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap)64 public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) { 65 super(new AdaptiveIconShapeDrawable(resources)); 66 67 init(resources, bitmap, ICON_TYPE_DEFAULT); 68 } 69 AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap, @AdaptiveOutlineIconType int type)70 public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap, 71 @AdaptiveOutlineIconType int type) { 72 super(new AdaptiveIconShapeDrawable(resources)); 73 74 init(resources, bitmap, type); 75 } 76 init(Resources resources, Bitmap bitmap, @AdaptiveOutlineIconType int type)77 private void init(Resources resources, Bitmap bitmap, 78 @AdaptiveOutlineIconType int type) { 79 mType = type; 80 getDrawable().setTint(Color.WHITE); 81 mPath = new Path(PathParser.createPathFromPathData( 82 resources.getString(com.android.internal.R.string.config_icon_mask))); 83 mStrokeWidth = resources.getDimensionPixelSize(R.dimen.adaptive_outline_stroke); 84 mOutlinePaint = new Paint(); 85 mOutlinePaint.setColor(getColor(resources, type)); 86 mOutlinePaint.setStyle(Paint.Style.STROKE); 87 mOutlinePaint.setStrokeWidth(mStrokeWidth); 88 mOutlinePaint.setAntiAlias(true); 89 90 mInsetPx = getDimensionPixelSize(resources, type); 91 mBitmap = bitmap; 92 } 93 getColor(Resources resources, @AdaptiveOutlineIconType int type)94 private @ColorInt int getColor(Resources resources, @AdaptiveOutlineIconType int type) { 95 int resId; 96 switch (type) { 97 case ICON_TYPE_ADVANCED: 98 resId = R.color.advanced_outline_color; 99 break; 100 case ICON_TYPE_DEFAULT: 101 default: 102 resId = R.color.bt_outline_color; 103 break; 104 } 105 return resources.getColor(resId, /* theme */ null); 106 } 107 getDimensionPixelSize(Resources resources, @AdaptiveOutlineIconType int type)108 private int getDimensionPixelSize(Resources resources, @AdaptiveOutlineIconType int type) { 109 int resId; 110 switch (type) { 111 case ICON_TYPE_ADVANCED: 112 resId = R.dimen.advanced_dashboard_tile_foreground_image_inset; 113 break; 114 case ICON_TYPE_DEFAULT: 115 default: 116 resId = R.dimen.dashboard_tile_foreground_image_inset; 117 break; 118 } 119 return resources.getDimensionPixelSize(resId); 120 } 121 122 @Override draw(Canvas canvas)123 public void draw(Canvas canvas) { 124 super.draw(canvas); 125 final Rect bounds = getBounds(); 126 final float pathSize = AdaptiveIconDrawable.MASK_SIZE; 127 128 final float scaleX = (bounds.right - bounds.left) / pathSize; 129 final float scaleY = (bounds.bottom - bounds.top) / pathSize; 130 131 final int count = canvas.save(); 132 canvas.scale(scaleX, scaleY); 133 // Draw outline 134 if (mType == ICON_TYPE_DEFAULT) { 135 canvas.drawPath(mPath, mOutlinePaint); 136 } else { 137 canvas.drawCircle(ADVANCED_ICON_CENTER, ADVANCED_ICON_CENTER, ADVANCED_ICON_RADIUS, 138 mOutlinePaint); 139 } 140 canvas.restoreToCount(count); 141 142 // Draw the foreground icon 143 canvas.drawBitmap(mBitmap, bounds.left + mInsetPx, bounds.top + mInsetPx, null); 144 } 145 getDefaultDisplayDensity(int displayId)146 private static int getDefaultDisplayDensity(int displayId) { 147 try { 148 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 149 return wm.getInitialDisplayDensity(displayId); 150 } catch (RemoteException exc) { 151 return -1; 152 } 153 } 154 155 @Override getIntrinsicHeight()156 public int getIntrinsicHeight() { 157 return mBitmap.getHeight() + 2 * mInsetPx; 158 } 159 160 @Override getIntrinsicWidth()161 public int getIntrinsicWidth() { 162 return mBitmap.getWidth() + 2 * mInsetPx; 163 } 164 } 165