1 /* 2 * Copyright (C) 2017 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.launcher3.popup; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.Canvas; 22 import android.graphics.Matrix; 23 import android.graphics.Paint; 24 import android.graphics.PorterDuff; 25 import android.graphics.PorterDuffXfermode; 26 import android.graphics.Rect; 27 import android.graphics.drawable.ShapeDrawable; 28 import android.graphics.drawable.shapes.RoundRectShape; 29 import android.util.AttributeSet; 30 import android.view.View; 31 import android.widget.FrameLayout; 32 33 import com.android.launcher3.R; 34 import com.android.launcher3.Utilities; 35 import com.android.launcher3.popup.PopupContainerWithArrow.RoundedCornerFlags; 36 37 import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_BOTTOM_CORNERS; 38 import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_TOP_CORNERS; 39 40 /** 41 * An abstract {@link FrameLayout} that contains content for {@link PopupContainerWithArrow}. 42 */ 43 public abstract class PopupItemView extends FrameLayout { 44 45 protected final Rect mPillRect; 46 protected @RoundedCornerFlags int mRoundedCorners; 47 protected final boolean mIsRtl; 48 protected View mIconView; 49 50 private final Paint mBackgroundClipPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); 51 private final Matrix mMatrix = new Matrix(); 52 private Bitmap mRoundedCornerBitmap; 53 PopupItemView(Context context)54 public PopupItemView(Context context) { 55 this(context, null, 0); 56 } 57 PopupItemView(Context context, AttributeSet attrs)58 public PopupItemView(Context context, AttributeSet attrs) { 59 this(context, attrs, 0); 60 } 61 PopupItemView(Context context, AttributeSet attrs, int defStyle)62 public PopupItemView(Context context, AttributeSet attrs, int defStyle) { 63 super(context, attrs, defStyle); 64 65 mPillRect = new Rect(); 66 67 // Initialize corner clipping Bitmap and Paint. 68 int radius = (int) getBackgroundRadius(); 69 mRoundedCornerBitmap = Bitmap.createBitmap(radius, radius, Bitmap.Config.ALPHA_8); 70 Canvas canvas = new Canvas(); 71 canvas.setBitmap(mRoundedCornerBitmap); 72 canvas.drawArc(0, 0, radius*2, radius*2, 180, 90, true, mBackgroundClipPaint); 73 mBackgroundClipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 74 75 mIsRtl = Utilities.isRtl(getResources()); 76 } 77 78 @Override onFinishInflate()79 protected void onFinishInflate() { 80 super.onFinishInflate(); 81 mIconView = findViewById(R.id.popup_item_icon); 82 } 83 84 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)85 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 86 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 87 mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); 88 } 89 90 @Override dispatchDraw(Canvas canvas)91 protected void dispatchDraw(Canvas canvas) { 92 if (mRoundedCorners == 0) { 93 super.dispatchDraw(canvas); 94 return; 95 } 96 97 int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null); 98 super.dispatchDraw(canvas); 99 100 // Clip children to this item's rounded corners. 101 int cornerWidth = mRoundedCornerBitmap.getWidth(); 102 int cornerHeight = mRoundedCornerBitmap.getHeight(); 103 int cornerCenterX = Math.round(cornerWidth / 2f); 104 int cornerCenterY = Math.round(cornerHeight / 2f); 105 if ((mRoundedCorners & ROUNDED_TOP_CORNERS) != 0) { 106 // Clip top left corner. 107 mMatrix.reset(); 108 canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint); 109 // Clip top right corner. 110 mMatrix.setRotate(90, cornerCenterX, cornerCenterY); 111 mMatrix.postTranslate(canvas.getWidth() - cornerWidth, 0); 112 canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint); 113 } 114 if ((mRoundedCorners & ROUNDED_BOTTOM_CORNERS) != 0) { 115 // Clip bottom right corner. 116 mMatrix.setRotate(180, cornerCenterX, cornerCenterY); 117 mMatrix.postTranslate(canvas.getWidth() - cornerWidth, canvas.getHeight() - cornerHeight); 118 canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint); 119 // Clip bottom left corner. 120 mMatrix.setRotate(270, cornerCenterX, cornerCenterY); 121 mMatrix.postTranslate(0, canvas.getHeight() - cornerHeight); 122 canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint); 123 } 124 125 canvas.restoreToCount(saveCount); 126 } 127 128 /** 129 * Creates a round rect drawable (with the specified corners unrounded) 130 * and sets it as this View's background. 131 */ setBackgroundWithCorners(int color, @RoundedCornerFlags int roundedCorners)132 public void setBackgroundWithCorners(int color, @RoundedCornerFlags int roundedCorners) { 133 mRoundedCorners = roundedCorners; 134 float rTop = (roundedCorners & ROUNDED_TOP_CORNERS) == 0 ? 0 : getBackgroundRadius(); 135 float rBot = (roundedCorners & ROUNDED_BOTTOM_CORNERS) == 0 ? 0 : getBackgroundRadius(); 136 float[] radii = new float[] {rTop, rTop, rTop, rTop, rBot, rBot, rBot, rBot}; 137 ShapeDrawable roundRectBackground = new ShapeDrawable(new RoundRectShape(radii, null, null)); 138 roundRectBackground.getPaint().setColor(color); 139 setBackground(roundRectBackground); 140 } 141 getBackgroundRadius()142 protected float getBackgroundRadius() { 143 return getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius); 144 } 145 } 146