package com.badoo.chateau.extras.widgets; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.NinePatch; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.drawable.NinePatchDrawable; import android.os.Build; import android.support.annotation.DrawableRes; import android.util.AttributeSet; import android.widget.FrameLayout; import com.badoo.chateau.utils.R; /** * Layout providing chrome around a message bubble. This view draws the message chrome over the views so it's important to have the correct * padding for items such as text messages where losing the corner to chrome would cause and issue. */ public class ChatBubbleLayout extends FrameLayout { private final static PorterDuffXfermode DST_IN = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private NinePatchDrawable mTailMask; private NinePatchDrawable mNoTailMask; private NinePatchDrawable mMask; private boolean mShowTail = true; private boolean mReverseLayout; public ChatBubbleLayout(Context context) { this(context, null); } public ChatBubbleLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ChatBubbleLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public ChatBubbleLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { setWillNotDraw(false); setLayerType(LAYER_TYPE_HARDWARE, mPaint); mMask = mTailMask = createMask(R.drawable.ic_mess_bubble_right); mNoTailMask = createMask(R.drawable.ic_mess_bubble_circle); } private NinePatchDrawable createMask(@DrawableRes int res) { final Bitmap maskBitmap = BitmapFactory.decodeResource(getResources(), res); final NinePatch patch = new NinePatch(maskBitmap, maskBitmap.getNinePatchChunk(), "BubbleMask"); return new NinePatchDrawable(getResources(), patch); } /** * By default the tail is on the rhs, set reverse layout to <code>true</code> to have the tail on the other side. */ public void reverseLayout(boolean reverseLayout) { if (mReverseLayout == reverseLayout) { return; } mReverseLayout = reverseLayout; invalidate(); } public void showTail(boolean showTail) { if (mShowTail == showTail) { return; } mShowTail = showTail; mMask = mShowTail ? mTailMask : mNoTailMask; invalidate(); } @Override protected int getSuggestedMinimumWidth() { return Math.max(mMask.getMinimumWidth(), super.getSuggestedMinimumWidth()); } @Override protected int getSuggestedMinimumHeight() { return Math.max(mMask.getMinimumHeight(), super.getSuggestedMinimumHeight()); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (w != oldw || h != oldh) { mTailMask.setBounds(0, 0, w, h); mNoTailMask.setBounds(0, 0, w, h); } } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); mMask.getPaint().setXfermode(DST_IN); if (mReverseLayout && mShowTail) { canvas.save(); canvas.scale(-1.0f, 1.0f); canvas.translate(-getMeasuredWidth(), 0); } mMask.draw(canvas); if (mReverseLayout && mShowTail) { canvas.restore(); } mMask.getPaint().setXfermode(null); } }