diff options
| author | 2024-03-06 18:51:19 +0000 | |
|---|---|---|
| committer | 2024-03-06 18:51:19 +0000 | |
| commit | f5f650fe8d86ce2cce33f473a62b93f4500152cd (patch) | |
| tree | 589f36aec0453f2d417926c0eb53da8f0f1a3eb5 | |
| parent | 347dede41c1013c6967ec59a369c7921b9cb248d (diff) | |
| parent | 0daea65bf49fc1b40cab9b34a8536d32b9d26f49 (diff) | |
Merge changes I581ec376,Ic8b15286,I5ad19ac9 into main
* changes:
[Battery] RTL support
[Battery] Define layout rects as insets
[Sb] Properly inflate the estimate view
7 files changed, 173 insertions, 61 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index 31698a35c811..01c2cc4fe874 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -345,11 +345,25 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { } } - private TextView loadPercentView() { + private TextView inflatePercentView() { return (TextView) LayoutInflater.from(getContext()) .inflate(R.layout.battery_percentage_view, null); } + private void addPercentView(TextView inflatedPercentView) { + mBatteryPercentView = inflatedPercentView; + + if (mPercentageStyleId != 0) { // Only set if specified as attribute + mBatteryPercentView.setTextAppearance(mPercentageStyleId); + } + float fontHeight = mBatteryPercentView.getPaint().getFontMetricsInt(null); + mBatteryPercentView.setLineHeight(TypedValue.COMPLEX_UNIT_PX, fontHeight); + if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor); + addView(mBatteryPercentView, new LayoutParams( + LayoutParams.WRAP_CONTENT, + (int) Math.ceil(fontHeight))); + } + /** * Updates percent view by removing old one and reinflating if necessary */ @@ -388,7 +402,9 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { mBatteryEstimateFetcher.fetchBatteryTimeRemainingEstimate( (String estimate) -> { if (mBatteryPercentView == null) { - mBatteryPercentView = loadPercentView(); + // Similar to the legacy behavior, inflate and add the view. We will + // only use it for the estimate text + addPercentView(inflatePercentView()); } if (estimate != null && mShowPercentMode == MODE_ESTIMATE) { mEstimateText = estimate; @@ -401,6 +417,10 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { } }); } else { + if (mBatteryPercentView != null) { + mEstimateText = null; + mBatteryPercentView.setText(null); + } updateContentDescription(); } } @@ -485,21 +505,18 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { return; } - if (mUnifiedBattery == null) { - return; - } + if (!mShowPercentAvailable || mUnifiedBattery == null) return; - // TODO(b/140051051) - final boolean systemSetting = 0 != whitelistIpcs(() -> Settings.System - .getIntForUser(getContext().getContentResolver(), - SHOW_BATTERY_PERCENT, getContext().getResources().getBoolean( - com.android.internal.R.bool.config_defaultBatteryPercentageSetting) - ? 1 : 0, UserHandle.USER_CURRENT)); - - boolean shouldShow = - (mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF) - || mShowPercentMode == MODE_ON; - shouldShow = shouldShow && !mBatteryStateUnknown; + boolean shouldShow = mShowPercentMode == MODE_ON || mShowPercentMode == MODE_ESTIMATE; + if (!mBatteryStateUnknown && !shouldShow && (mShowPercentMode != MODE_OFF)) { + // Slow case: fall back to the system setting + // TODO(b/140051051) + shouldShow = 0 != whitelistIpcs(() -> Settings.System + .getIntForUser(getContext().getContentResolver(), + SHOW_BATTERY_PERCENT, getContext().getResources().getBoolean( + com.android.internal.R.bool.config_defaultBatteryPercentageSetting) + ? 1 : 0, UserHandle.USER_CURRENT)); + } setBatteryDrawableState( new BatteryDrawableState( @@ -534,17 +551,8 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { if (shouldShow) { if (!showing) { - mBatteryPercentView = loadPercentView(); - if (mPercentageStyleId != 0) { // Only set if specified as attribute - mBatteryPercentView.setTextAppearance(mPercentageStyleId); - } - float fontHeight = mBatteryPercentView.getPaint().getFontMetricsInt(null); - mBatteryPercentView.setLineHeight(TypedValue.COMPLEX_UNIT_PX, fontHeight); - if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor); + addPercentView(inflatePercentView()); updatePercentText(); - addView(mBatteryPercentView, new LayoutParams( - LayoutParams.WRAP_CONTENT, - (int) Math.ceil(fontHeight))); } } else { if (showing) { diff --git a/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryAttributionDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryAttributionDrawable.kt index 1b8495ace243..f3652b89cc50 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryAttributionDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryAttributionDrawable.kt @@ -23,6 +23,7 @@ import android.graphics.Rect import android.graphics.drawable.Drawable import android.graphics.drawable.DrawableWrapper import android.view.Gravity +import kotlin.math.ceil import kotlin.math.min import kotlin.math.roundToInt @@ -36,7 +37,7 @@ import kotlin.math.roundToInt */ @Suppress("RtlHardcoded") class BatteryAttributionDrawable(dr: Drawable?) : DrawableWrapper(dr) { - /** One of [CENTER, LEFT]. Note that RTL is handled in the parent */ + /** One of [CENTER, LEFT]. Note that number text does not RTL. */ var gravity = Gravity.CENTER set(value) { field = value @@ -67,8 +68,8 @@ class BatteryAttributionDrawable(dr: Drawable?) : DrawableWrapper(dr) { dr.setBounds( bounds.left, bounds.top, - (bounds.left + dw).roundToInt(), - (bounds.top + dh).roundToInt() + ceil(bounds.left + dw).toInt(), + ceil(bounds.top + dh).toInt() ) } } diff --git a/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryFillDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryFillDrawable.kt index 6d3206767d2b..5e34d2909d81 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryFillDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryFillDrawable.kt @@ -26,6 +26,7 @@ import android.graphics.PixelFormat import android.graphics.Rect import android.graphics.RectF import android.graphics.drawable.Drawable +import android.view.View import com.android.systemui.battery.unified.BatteryLayersDrawable.Companion.Metrics import kotlin.math.floor import kotlin.math.roundToInt @@ -103,6 +104,11 @@ class BatteryFillDrawable(private val framePath: Path) : Drawable() { // saveLayer is needed here so we don't clip the other layers of our drawable canvas.saveLayer(null, null) + // Fill from the opposite direction in rtl mode + if (layoutDirection == View.LAYOUT_DIRECTION_RTL) { + canvas.scale(-1f, 1f, bounds.width() / 2f, bounds.height() / 2f) + } + // We need to use 3 draw commands: // 1. Clip to the current level // 2. Clip anything outside of the path diff --git a/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryLayersDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryLayersDrawable.kt index 199dd1f18a42..706b9ec563c9 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryLayersDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryLayersDrawable.kt @@ -26,7 +26,10 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.util.PathParser import android.view.Gravity +import android.view.View import com.android.systemui.res.R +import kotlin.math.ceil +import kotlin.math.floor import kotlin.math.roundToInt /** @@ -69,8 +72,11 @@ class BatteryLayersDrawable( ) : LayerDrawable(arrayOf(frameBg, frame, fill, textOnly, spaceSharingText, attribution)) { private val scaleMatrix = Matrix().also { it.setScale(1f, 1f) } - private val scaledAttrFullCanvas = RectF(Metrics.AttrFullCanvas) - private val scaledAttrRightCanvas = RectF(Metrics.AttrRightCanvas) + + private val attrFullCanvas = RectF() + private val attrRightCanvas = RectF() + private val scaledAttrFullCanvas = RectF() + private val scaledAttrRightCanvas = RectF() var batteryState = batteryState set(value) { @@ -88,6 +94,12 @@ class BatteryLayersDrawable( updateColors(batteryState.showErrorState, value) } + init { + isAutoMirrored = true + // Initialize the canvas rects since they are not static + setAttrRects(layoutDirection == View.LAYOUT_DIRECTION_RTL) + } + private fun handleUpdateState(old: BatteryDrawableState, new: BatteryDrawableState) { if (new.showErrorState != old.showErrorState) { updateColors(new.showErrorState, colors) @@ -144,9 +156,42 @@ class BatteryLayersDrawable( bounds.height() / Metrics.ViewportHeight ) - // Scale the attribution bounds - scaleMatrix.mapRect(scaledAttrFullCanvas, Metrics.AttrFullCanvas) - scaleMatrix.mapRect(scaledAttrRightCanvas, Metrics.AttrRightCanvas) + scaleAttributionBounds() + } + + override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean { + setAttrRects(layoutDirection == View.LAYOUT_DIRECTION_RTL) + scaleAttributionBounds() + + return super.onLayoutDirectionChanged(layoutDirection) + } + + private fun setAttrRects(rtl: Boolean) { + // Local refs make the math easier to parse + val full = Metrics.AttrFullCanvasInsets + val side = Metrics.AttrRightCanvasInsets + val sideRtl = Metrics.AttrRightCanvasInsetsRtl + val vh = Metrics.ViewportHeight + val vw = Metrics.ViewportWidth + + attrFullCanvas.set( + if (rtl) full.right else full.left, + full.top, + vw - if (rtl) full.left else full.right, + vh - full.bottom, + ) + attrRightCanvas.set( + if (rtl) sideRtl.left else side.left, + side.top, + vw - (if (rtl) sideRtl.right else side.right), + vh - side.bottom, + ) + } + + /** If bounds (i.e., scale), or RTL properties change, we have to recalculate the attr bounds */ + private fun scaleAttributionBounds() { + scaleMatrix.mapRect(scaledAttrFullCanvas, attrFullCanvas) + scaleMatrix.mapRect(scaledAttrRightCanvas, attrRightCanvas) } override fun draw(canvas: Canvas) { @@ -163,13 +208,14 @@ class BatteryLayersDrawable( if (batteryState.showPercent && batteryState.attribution != null) { // 4a. percent & attribution. Implies space-sharing - // Configure the attribute to draw in a smaller bounding box and align left + // Configure the attribute to draw in a smaller bounding box and align left and use + // floor/ceil math to make sure we get every available pixel attribution.gravity = Gravity.LEFT attribution.setBounds( - scaledAttrRightCanvas.left.roundToInt(), - scaledAttrRightCanvas.top.roundToInt(), - scaledAttrRightCanvas.right.roundToInt(), - scaledAttrRightCanvas.bottom.roundToInt(), + floor(scaledAttrRightCanvas.left).toInt(), + floor(scaledAttrRightCanvas.top).toInt(), + ceil(scaledAttrRightCanvas.right).toInt(), + ceil(scaledAttrRightCanvas.bottom).toInt(), ) attribution.draw(canvas) @@ -196,16 +242,44 @@ class BatteryLayersDrawable( */ override fun setAlpha(alpha: Int) {} + /** + * Interface that describes relevant top-level metrics for the proper rendering of this icon. + * The overall canvas is defined as ViewportWidth x ViewportHeight, which is hard coded to 24x14 + * points. + * + * The attr canvas insets are rect inset definitions. That is, they are defined as l,t,r,b + * points from the nearest edge. Note that for RTL, we don't actually flip the text since + * numbers do not reverse for RTL locales. + */ interface M { val ViewportWidth: Float val ViewportHeight: Float - // Bounds, oriented in the above viewport, where we will fit-center and center-align - // an attribution that is the sole foreground element - val AttrFullCanvas: RectF - // Bounds, oriented in the above viewport, where we will fit-center and left-align - // an attribution that is sharing space with the percent text of the drawable - val AttrRightCanvas: RectF + /** + * Insets, oriented in the above viewport in LTR, that define the full canvas for a single + * foreground element. The element will be fit-center and center-aligned on this canvas + * + * 18x8 point size + */ + val AttrFullCanvasInsets: RectF + + /** + * Insets, oriented in the above viewport in LTR, that define the partial canvas for a + * foreground element that shares space with the percent text. The element will be + * fit-center and left-aligned on this canvas. + * + * 6x6 point size + */ + val AttrRightCanvasInsets: RectF + + /** + * Insets, oriented in the above viewport in RTL, that define the partial canvas for a + * foreground element that shares space with the percent text. The element will be + * fit-center and left-aligned on this canvas. + * + * 6x6 point size + */ + val AttrRightCanvasInsetsRtl: RectF } companion object { @@ -220,20 +294,9 @@ class BatteryLayersDrawable( override val ViewportWidth: Float = 24f override val ViewportHeight: Float = 14f - /** - * Bounds, oriented in the above viewport, where we will fit-center and center-align - * an attribution that is the sole foreground element - * - * 18x8 point size - */ - override val AttrFullCanvas: RectF = RectF(4f, 3f, 22f, 11f) - /** - * Bounds, oriented in the above viewport, where we will fit-center and left-align - * an attribution that is sharing space with the percent text of the drawable - * - * 6x6 point size - */ - override val AttrRightCanvas: RectF = RectF(16f, 4f, 22f, 10f) + override val AttrFullCanvasInsets = RectF(4f, 3f, 2f, 3f) + override val AttrRightCanvasInsets = RectF(16f, 4f, 2f, 4f) + override val AttrRightCanvasInsetsRtl = RectF(14f, 4f, 4f, 4f) } /** diff --git a/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryPercentTextOnlyDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryPercentTextOnlyDrawable.kt index 123d6ba57900..aa0e37348f1e 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryPercentTextOnlyDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/unified/BatteryPercentTextOnlyDrawable.kt @@ -23,6 +23,7 @@ import android.graphics.PixelFormat import android.graphics.Rect import android.graphics.Typeface import android.graphics.drawable.Drawable +import android.view.View import com.android.systemui.battery.unified.BatteryLayersDrawable.Companion.Metrics /** @@ -71,6 +72,7 @@ class BatteryPercentTextOnlyDrawable(font: Typeface) : Drawable() { } override fun draw(canvas: Canvas) { + val rtl = layoutDirection == View.LAYOUT_DIRECTION_RTL val totalAvailableHeight = CanvasHeight * vScale // Distribute the vertical whitespace around the text. This is a simplified version of @@ -81,11 +83,12 @@ class BatteryPercentTextOnlyDrawable(font: Typeface) : Drawable() { val totalAvailableWidth = CanvasWidth * hScale val textWidth = textPaint.measureText(percentText) val offsetX = (totalAvailableWidth - textWidth) / 2 + val startOffset = if (rtl) ViewportInsetRight else ViewportInsetLeft // Draw the text centered in the available area canvas.drawText( percentText, - (ViewportInsetLeft * hScale) + offsetX, + (startOffset * hScale) + offsetX, (ViewportInsetTop * vScale) + offsetY, textPaint ) diff --git a/packages/SystemUI/src/com/android/systemui/battery/unified/BatterySpaceSharingPercentTextDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/unified/BatterySpaceSharingPercentTextDrawable.kt index 0c418b9caa7d..3b4c77936cff 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/unified/BatterySpaceSharingPercentTextDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/unified/BatterySpaceSharingPercentTextDrawable.kt @@ -23,6 +23,7 @@ import android.graphics.PixelFormat import android.graphics.Rect import android.graphics.Typeface import android.graphics.drawable.Drawable +import android.view.View import com.android.systemui.battery.unified.BatteryLayersDrawable.Companion.Metrics /** @@ -94,6 +95,7 @@ class BatterySpaceSharingPercentTextDrawable(font: Typeface) : Drawable() { } override fun draw(canvas: Canvas) { + val rtl = layoutDirection == View.LAYOUT_DIRECTION_RTL val totalAvailableHeight = CanvasHeight * vScale // Distribute the vertical whitespace around the text. This is a simplified version of @@ -107,7 +109,7 @@ class BatterySpaceSharingPercentTextDrawable(font: Typeface) : Drawable() { canvas.drawText( percentText, - (ViewportInsetLeft * hScale) + offsetX, + ((if (rtl) ViewportInsetLeftRtl else ViewportInsetLeft) * hScale) + offsetX, (ViewportInsetTop * vScale) + offsetY, textPaint ) @@ -128,6 +130,7 @@ class BatterySpaceSharingPercentTextDrawable(font: Typeface) : Drawable() { companion object { private const val ViewportInsetLeft = 4f + private const val ViewportInsetLeftRtl = 2f private const val ViewportInsetTop = 2f private const val CanvasWidth = 12f diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt index 043dcaa0d919..3c073d5e7a3b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt @@ -193,6 +193,34 @@ class BatteryMeterViewTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_NEW_STATUS_BAR_ICONS) + fun modeEstimate_batteryPercentView_isNotNull_flagOn() { + mBatteryMeterView.onBatteryLevelChanged(15, false) + mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE) + mBatteryMeterView.setBatteryEstimateFetcher(Fetcher()) + + mBatteryMeterView.updatePercentText() + + // New battery icon only uses the percent view for the estimate text + assertThat(mBatteryMeterView.batteryPercentView).isNotNull() + // Make sure that it was added to the view hierarchy + assertThat(mBatteryMeterView.batteryPercentView.parent).isNotNull() + } + + @Test + @EnableFlags(FLAG_NEW_STATUS_BAR_ICONS) + fun modePercent_batteryPercentView_isNull_flagOn() { + mBatteryMeterView.onBatteryLevelChanged(15, false) + mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ON) + mBatteryMeterView.setBatteryEstimateFetcher(Fetcher()) + + mBatteryMeterView.updatePercentText() + + // New battery icon only uses the percent view for the estimate text + assertThat(mBatteryMeterView.batteryPercentView).isNull() + } + + @Test fun contentDescription_manyUpdates_alwaysUpdated() { // BatteryDefender mBatteryMeterView.onBatteryLevelChanged(90, false) |