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  * A variant of [BatteryPercentTextOnlyDrawable] with the following differences:
31  * 1. It is defined on a canvas of 12x10 (shortened by 6 points horizontally)
32  * 2. Because of this, we scale the font according to the number of characters
33  *
34  * Note that these drawing metrics are only tested to work with google-sans BOLD
35  */
36 class BatterySpaceSharingPercentTextDrawable(font: Typeface) : Drawable() {
37     private var verticalNudge = 0f
38     private var hScale = 1f
39     private var vScale = 1f
40 
41     // range 0-100
42     var batteryLevel: Int = 88
43         set(value) {
44             field = value
45             percentText = "$value"
46             invalidateSelf()
47         }
48 
49     private var percentText = "$batteryLevel"
50         set(value) {
51             field = value
52             numberOfCharacters = percentText.length
53         }
54 
55     private var numberOfCharacters = percentText.length
56         set(value) {
57             if (field != value) {
58                 field = value
59                 updateFontSize()
60             }
61         }
62 
63     private val textPaint =
64         Paint().also { p ->
65             p.textSize = 10f
66             p.typeface = font
67         }
68 
69     private fun updateFontSize() {
70         // These values are determined experimentally
71         when (numberOfCharacters) {
72             3 -> {
73                 verticalNudge = 1f
74                 textPaint.textSize = 6f * hScale
75             }
76             // 1, 2
77             else -> {
78                 verticalNudge = 1.25f
79                 textPaint.textSize = 9f * hScale
80             }
81         }
82     }
83 
84     private fun updateScale() {
85         updateFontSize()
86     }
87 
88     override fun onBoundsChange(bounds: Rect) {
89         super.onBoundsChange(bounds)
90 
91         hScale = bounds.right / Metrics.ViewportWidth
92         vScale = bounds.bottom / Metrics.ViewportHeight
93 
94         updateScale()
95     }
96 
97     override fun draw(canvas: Canvas) {
98         val rtl = layoutDirection == View.LAYOUT_DIRECTION_RTL
99         val totalAvailableHeight = CanvasHeight * vScale
100 
101         // Distribute the vertical whitespace around the text. This is a simplified version of
102         // the equation ((C - T) / 2) + T - V, where C == canvas height, T == text height, and V
103         // is the vertical nudge.
104         val offsetY = (totalAvailableHeight + textPaint.textSize) / 2 - (verticalNudge * vScale)
105 
106         val totalAvailableWidth = CanvasWidth * hScale
107         val textWidth = textPaint.measureText(percentText)
108         val offsetX = (totalAvailableWidth - textWidth) / 2
109 
110         canvas.drawText(
111             percentText,
112             ((if (rtl) ViewportInsetLeftRtl else ViewportInsetLeft) * hScale) + offsetX,
113             (ViewportInsetTop * vScale) + offsetY,
114             textPaint
115         )
116     }
117 
118     override fun setTint(tintColor: Int) {
119         textPaint.color = tintColor
120         super.setTint(tintColor)
121     }
122 
123     override fun getOpacity() = PixelFormat.OPAQUE
124 
125     override fun setAlpha(p0: Int) {}
126 
127     override fun setColorFilter(colorFilter: ColorFilter?) {
128         textPaint.colorFilter = colorFilter
129     }
130 
131     companion object {
132         private const val ViewportInsetLeft = 4f
133         private const val ViewportInsetLeftRtl = 2f
134         private const val ViewportInsetTop = 2f
135 
136         private const val CanvasWidth = 12f
137         private const val CanvasHeight = 10f
138     }
139 }
140