1 /*
2  * Copyright (C) 2018 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.settings.wifi.qrcode;
18 
19 import android.content.Context;
20 import android.graphics.Bitmap;
21 import android.graphics.Canvas;
22 import android.graphics.Paint;
23 import android.graphics.PorterDuff;
24 import android.graphics.PorterDuffXfermode;
25 import android.graphics.RectF;
26 import android.util.AttributeSet;
27 import android.util.TypedValue;
28 import android.view.View;
29 
30 import com.android.settings.R;
31 
32 /**
33  * Draws the lines at the corner of the inner frame.
34  */
35 public class QrDecorateView extends View {
36     private static final float CORNER_STROKE_WIDTH = 4f;    // 4dp
37     private static final float CORNER_LINE_LENGTH = 264f;   // 264dp
38     private static final float CORNER_RADIUS = 16f;         // 16dp
39 
40     final private int mCornerColor;
41     final private int mFocusedCornerColor;
42     final private int mBackgroundColor;
43 
44     final private Paint mStrokePaint;
45     final private Paint mTransparentPaint;
46     final private Paint mBackgroundPaint;
47 
48     final private float mRadius;
49     final private float mInnerRidus;
50 
51     private Bitmap mMaskBitmap;
52     private Canvas mMaskCanvas;
53 
54     private RectF mOuterFrame;
55     private RectF mInnerFrame;
56 
57     private boolean mFocused;
58 
QrDecorateView(Context context)59     public QrDecorateView(Context context) {
60         this(context, null);
61     }
62 
QrDecorateView(Context context, AttributeSet attrs)63     public QrDecorateView(Context context, AttributeSet attrs) {
64         this(context, attrs, 0);
65     }
66 
QrDecorateView(Context context, AttributeSet attrs, int defStyleAttr)67     public QrDecorateView(Context context, AttributeSet attrs, int defStyleAttr) {
68         this(context, attrs, defStyleAttr, 0);
69     }
70 
QrDecorateView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)71     public QrDecorateView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
72         super(context, attrs, defStyleAttr, defStyleRes);
73 
74         mFocused = false;
75         mRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS,
76                 getResources().getDisplayMetrics());
77         // Inner radius needs to minus stroke width for keeping the width of border consistent.
78         mInnerRidus = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
79                 CORNER_RADIUS - CORNER_STROKE_WIDTH, getResources().getDisplayMetrics());
80 
81         mCornerColor = context.getResources().getColor(R.color.qr_corner_line_color);
82         mFocusedCornerColor = context.getResources().getColor(R.color.qr_focused_corner_line_color);
83         mBackgroundColor = context.getResources().getColor(R.color.qr_background_color);
84 
85         mStrokePaint = new Paint();
86         mStrokePaint.setAntiAlias(true);
87 
88         mTransparentPaint = new Paint();
89         mTransparentPaint.setAntiAlias(true);
90         mTransparentPaint.setColor(getResources().getColor(android.R.color.transparent));
91         mTransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
92 
93         mBackgroundPaint = new Paint();
94         mBackgroundPaint.setColor(mBackgroundColor);
95     }
96 
97     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)98     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
99         super.onLayout(changed, left, top, right, bottom);
100 
101         if(mMaskBitmap == null) {
102             mMaskBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
103             mMaskCanvas = new Canvas(mMaskBitmap);
104         }
105 
106         calculateFramePos();
107     }
108 
109     @Override
onDraw(Canvas canvas)110     protected void onDraw(Canvas canvas) {
111         // Set frame line color.
112         mStrokePaint.setColor(mFocused ? mFocusedCornerColor : mCornerColor);
113         // Draw background color.
114         mMaskCanvas.drawColor(mBackgroundColor);
115         // Draw outer corner.
116         mMaskCanvas.drawRoundRect(mOuterFrame, mRadius, mRadius, mStrokePaint);
117         // Draw inner transparent corner.
118         mMaskCanvas.drawRoundRect(mInnerFrame, mInnerRidus, mInnerRidus, mTransparentPaint);
119 
120         canvas.drawBitmap(mMaskBitmap, 0, 0, mBackgroundPaint);
121         super.onDraw(canvas);
122     }
123 
calculateFramePos()124     private void calculateFramePos() {
125         final int centralX = getWidth() / 2;
126         final int centralY = getHeight() / 2;
127         final float cornerLineLength = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
128                 CORNER_LINE_LENGTH, getResources().getDisplayMetrics()) / 2;
129         final float strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
130                 CORNER_STROKE_WIDTH, getResources().getDisplayMetrics());
131 
132         mOuterFrame = new RectF(centralX - cornerLineLength, centralY - cornerLineLength,
133                 centralX + cornerLineLength, centralY + cornerLineLength);
134         mInnerFrame = new RectF(mOuterFrame.left + strokeWidth, mOuterFrame.top + strokeWidth,
135                 mOuterFrame.right - strokeWidth, mOuterFrame.bottom - strokeWidth);
136     }
137 
138     // Draws green lines if focused. Otherwise, draws white lines.
setFocused(boolean focused)139     public void setFocused(boolean focused) {
140         mFocused = focused;
141         invalidate();
142     }
143 }
144