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