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