1 /* 2 * 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.PixelFormat 22 import android.graphics.Rect 23 import android.graphics.drawable.Drawable 24 import android.graphics.drawable.DrawableWrapper 25 import android.view.Gravity 26 import kotlin.math.ceil 27 import kotlin.math.min 28 import kotlin.math.roundToInt 29 30 /** 31 * A battery attribution is defined as a drawable that can display either alongside the percent text 32 * or solely in the center of the battery frame. 33 * 34 * Attributions are given an explicit canvas of 18x8, or 6x6 depending on the display mode (centered 35 * or right-aligned). The size is configured in [BatteryLayersDrawable] by changing this drawable 36 * wrapper's bounds, and optionally setting the [gravity] 37 */ 38 @Suppress("RtlHardcoded") 39 class BatteryAttributionDrawable(dr: Drawable?) : DrawableWrapper(dr) { 40 /** One of [CENTER, LEFT]. Note that number text does not RTL. */ 41 var gravity = Gravity.CENTER 42 set(value) { 43 field = value 44 updateBoundsInner() 45 } 46 47 // Must be called if bounds change, gravity changes, or the wrapped drawable changes updateBoundsInnernull48 private fun updateBoundsInner() { 49 val dr = drawable ?: return 50 51 val hScale = bounds.width().toFloat() / dr.intrinsicWidth.toFloat() 52 val vScale = bounds.height().toFloat() / dr.intrinsicHeight.toFloat() 53 val scale = min(hScale, vScale) 54 55 val dw = scale * dr.intrinsicWidth 56 val dh = scale * dr.intrinsicHeight 57 58 if (gravity == Gravity.CENTER) { 59 val padLeft = (bounds.width() - dw) / 2 60 val padTop = (bounds.height() - dh) / 2 61 dr.setBounds( 62 (bounds.left + padLeft).roundToInt(), 63 (bounds.top + padTop).roundToInt(), 64 (bounds.left + padLeft + dw).roundToInt(), 65 (bounds.top + padTop + dh).roundToInt() 66 ) 67 } else if (gravity == Gravity.LEFT) { 68 dr.setBounds( 69 bounds.left, 70 bounds.top, 71 ceil(bounds.left + dw).toInt(), 72 ceil(bounds.top + dh).toInt() 73 ) 74 } 75 } 76 setDrawablenull77 override fun setDrawable(dr: Drawable?) { 78 super.setDrawable(dr) 79 updateBoundsInner() 80 } 81 onBoundsChangenull82 override fun onBoundsChange(bounds: Rect) { 83 updateBoundsInner() 84 } 85 86 /** 87 * DrawableWrapper allows for a null constructor, but this method assumes that the drawable is 88 * non-null. It is called by LayerDrawable on init, so we have to handle null here specifically 89 */ getChangingConfigurationsnull90 override fun getChangingConfigurations(): Int = drawable?.changingConfigurations ?: 0 91 92 override fun draw(canvas: Canvas) { 93 drawable?.draw(canvas) 94 } 95 96 // Deprecated, but needed for Drawable implementation getOpacitynull97 override fun getOpacity() = PixelFormat.OPAQUE 98 99 // We don't use this 100 override fun setAlpha(alpha: Int) {} 101 setColorFilternull102 override fun setColorFilter(colorFilter: ColorFilter?) {} 103 } 104