1 /*
<lambda>null2  * Copyright (C) 2024 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.battery.unified
18 
19 import android.graphics.Canvas
20 import android.graphics.ColorFilter
21 import android.graphics.Paint
22 import android.graphics.PixelFormat
23 import android.graphics.Rect
24 import android.graphics.Typeface
25 import android.graphics.drawable.Drawable
26 import android.view.View
27 import com.android.systemui.battery.unified.BatteryLayersDrawable.Companion.Metrics
28 
29 /**
30  * (Names are hard) this drawable calculates the percent text for inside of the
31  * [BatteryLayersDrawable], assuming that there is no other attribution in the foreground. In this
32  * case, we can use the maximum font size and center the text in the full render area inside of the
33  * frame. After accounting for the stroke width and the insets from there, our rendering area is
34  * 18x10 points.
35  *
36  * See [BatterySpaceSharingPercentTextDrawable] (names are still hard) for the space-sharing
37  * approach.
38  *
39  * Note that these drawing metrics are only tested to work with google-sans BOLD
40  */
41 class BatteryPercentTextOnlyDrawable(font: Typeface) : Drawable() {
42     private var hScale = 1f
43     private var vScale = 1f
44 
45     // range 0-100
46     var batteryLevel: Int = 100
47         set(value) {
48             field = value
49             percentText = "$value"
50             invalidateSelf()
51         }
52 
53     private var percentText = "$batteryLevel"
54 
55     private val textPaint =
56         Paint().also { p ->
57             p.textSize = 10f
58             p.typeface = font
59         }
60 
61     override fun onBoundsChange(bounds: Rect) {
62         super.onBoundsChange(bounds)
63 
64         vScale = bounds.bottom / Metrics.ViewportHeight
65         hScale = bounds.right / Metrics.ViewportWidth
66 
67         updateScale()
68     }
69 
70     private fun updateScale() {
71         textPaint.textSize = TextSize * vScale
72     }
73 
74     override fun draw(canvas: Canvas) {
75         val rtl = layoutDirection == View.LAYOUT_DIRECTION_RTL
76         val totalAvailableHeight = CanvasHeight * vScale
77 
78         // Distribute the vertical whitespace around the text. This is a simplified version of
79         // the equation ((C - T) / 2) + T - V, where C == canvas height, T == text height, and V
80         // is the vertical nudge.
81         val offsetY = (totalAvailableHeight + textPaint.textSize) / 2 - (VerticalNudge * vScale)
82 
83         val totalAvailableWidth = CanvasWidth * hScale
84         val textWidth = textPaint.measureText(percentText)
85         val offsetX = (totalAvailableWidth - textWidth) / 2
86         val startOffset = if (rtl) ViewportInsetRight else ViewportInsetLeft
87 
88         // Draw the text centered in the available area
89         canvas.drawText(
90             percentText,
91             (startOffset * hScale) + offsetX,
92             (ViewportInsetTop * vScale) + offsetY,
93             textPaint
94         )
95     }
96 
97     override fun setTint(tintColor: Int) {
98         textPaint.color = tintColor
99         super.setTint(tintColor)
100     }
101 
102     override fun getOpacity() = PixelFormat.OPAQUE
103 
104     override fun setAlpha(alpha: Int) {}
105 
106     override fun setColorFilter(colorFilter: ColorFilter?) {}
107 
108     companion object {
109         // Based on the 24x14 canvas, we can render in an 18x10 canvas, inset like so:
110         const val ViewportInsetLeft = 4f
111         const val ViewportInsetRight = 2f
112         const val ViewportInsetTop = 2f
113         const val CanvasHeight = 10f
114         const val CanvasWidth = 18f
115 
116         // raise the text up by a smidgen so that it is more centered. Experimentally determined
117         const val VerticalNudge = 1.5f
118 
119         // Experimentally-determined value
120         const val TextSize = 10f
121     }
122 }
123