OSDN Git Service

Merge "docs: Add documentation for equals() method" into qt-dev am: 732a127636
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / CornerHandleView.java
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.systemui;
18
19 import android.animation.ArgbEvaluator;
20 import android.content.Context;
21 import android.graphics.Canvas;
22 import android.graphics.Paint;
23 import android.graphics.Path;
24 import android.graphics.RectF;
25 import android.util.AttributeSet;
26 import android.util.DisplayMetrics;
27 import android.view.ContextThemeWrapper;
28 import android.view.View;
29
30 import com.android.settingslib.Utils;
31
32 /**
33  * CornerHandleView draws an inset arc intended to be displayed within the screen decoration
34  * corners.
35  */
36 public class CornerHandleView extends View {
37     private static final float STROKE_DP_LARGE = 2f;
38     private static final float STROKE_DP_SMALL = 1.95f;
39     // Radius to use if none is available.
40     private static final int FALLBACK_RADIUS_DP = 15;
41     private static final float MARGIN_DP = 8;
42     private static final int MAX_ARC_DEGREES = 90;
43     // Arc length along the phone's perimeter used to measure the desired angle.
44     private static final float ARC_LENGTH_DP = 31f;
45
46     private Paint mPaint;
47     private int mLightColor;
48     private int mDarkColor;
49     private Path mPath;
50
51     public CornerHandleView(Context context, AttributeSet attrs) {
52         super(context, attrs);
53
54         mPaint = new Paint();
55         mPaint.setAntiAlias(true);
56         mPaint.setStyle(Paint.Style.STROKE);
57         mPaint.setStrokeCap(Paint.Cap.ROUND);
58         mPaint.setStrokeWidth(getStrokePx());
59
60         final int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme);
61         final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme);
62         Context lightContext = new ContextThemeWrapper(mContext, dualToneLightTheme);
63         Context darkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme);
64         mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor);
65         mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor);
66
67         updatePath();
68     }
69
70     private void updatePath() {
71         mPath = new Path();
72
73         float marginPx = getMarginPx();
74         float radiusPx = getInnerRadiusPx();
75         float halfStrokePx = getStrokePx() / 2f;
76         float angle = getAngle();
77         float startAngle = 180 + ((90 - angle) / 2);
78         RectF circle = new RectF(marginPx + halfStrokePx,
79                 marginPx + halfStrokePx,
80                 marginPx + 2 * radiusPx - halfStrokePx,
81                 marginPx + 2 * radiusPx - halfStrokePx);
82
83         if (angle >= 90f) {
84             float innerCircumferenceDp = convertPixelToDp(radiusPx * 2 * (float) Math.PI,
85                     mContext);
86             float arcDp = innerCircumferenceDp * getAngle() / 360f;
87             // Add additional "arms" to the two ends of the arc. The length computation is
88             // hand-tuned.
89             float lineLengthPx = convertDpToPixel((ARC_LENGTH_DP - arcDp - MARGIN_DP) / 2,
90                     mContext);
91
92             mPath.moveTo(marginPx + halfStrokePx, marginPx + radiusPx + lineLengthPx);
93             mPath.lineTo(marginPx + halfStrokePx, marginPx + radiusPx);
94             mPath.arcTo(circle, startAngle, angle);
95             mPath.moveTo(marginPx + radiusPx, marginPx + halfStrokePx);
96             mPath.lineTo(marginPx + radiusPx + lineLengthPx, marginPx + halfStrokePx);
97         } else {
98             mPath.arcTo(circle, startAngle, angle);
99         }
100     }
101
102     /**
103      * Receives an intensity from 0 (lightest) to 1 (darkest) and sets the handle color
104      * appropriately. Intention is to match the home handle color.
105      */
106     public void updateDarkness(float darkIntensity) {
107         mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
108                 mLightColor,
109                 mDarkColor));
110         if (getVisibility() == VISIBLE) {
111             invalidate();
112         }
113     }
114
115     @Override
116     public void onDraw(Canvas canvas) {
117         super.onDraw(canvas);
118         canvas.drawPath(mPath, mPaint);
119     }
120
121     private static float convertDpToPixel(float dp, Context context) {
122         return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
123                 / DisplayMetrics.DENSITY_DEFAULT);
124     }
125
126     private static float convertPixelToDp(float px, Context context) {
127         return px * DisplayMetrics.DENSITY_DEFAULT
128                 / ((float) context.getResources().getDisplayMetrics().densityDpi);
129     }
130
131     private float getAngle() {
132         // Measure a length of ARC_LENGTH_DP along the *screen's* perimeter, get the angle and cap
133         // it at 90.
134         float circumferenceDp = convertPixelToDp((
135                 getOuterRadiusPx()) * 2 * (float) Math.PI, mContext);
136         float angleDeg = (ARC_LENGTH_DP / circumferenceDp) * 360;
137         if (angleDeg > MAX_ARC_DEGREES) {
138             angleDeg = MAX_ARC_DEGREES;
139         }
140         return angleDeg;
141     }
142
143     private float getMarginPx() {
144         return convertDpToPixel(MARGIN_DP, mContext);
145     }
146
147     private float getInnerRadiusPx() {
148         return getOuterRadiusPx() - getMarginPx();
149     }
150
151     private float getOuterRadiusPx() {
152         // Attempt to get the bottom corner radius, otherwise fall back on the generic or top
153         // values. If none are available, use the FALLBACK_RADIUS_DP.
154         int radius = getResources().getDimensionPixelSize(
155                 com.android.internal.R.dimen.rounded_corner_radius_bottom);
156         if (radius == 0) {
157             radius = getResources().getDimensionPixelSize(
158                     com.android.internal.R.dimen.rounded_corner_radius);
159         }
160         if (radius == 0) {
161             radius = getResources().getDimensionPixelSize(
162                     com.android.internal.R.dimen.rounded_corner_radius_top);
163         }
164         if (radius == 0) {
165             radius = (int) convertDpToPixel(FALLBACK_RADIUS_DP, mContext);
166         }
167         return radius;
168     }
169
170     private float getStrokePx() {
171         // Use a slightly smaller stroke if we need to cover the full corner angle.
172         return convertDpToPixel((getAngle() < 90) ? STROKE_DP_LARGE : STROKE_DP_SMALL,
173                 getContext());
174     }
175 }