summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Evan Laird <evanlaird@google.com> 2024-03-06 18:51:19 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-03-06 18:51:19 +0000
commitf5f650fe8d86ce2cce33f473a62b93f4500152cd (patch)
tree589f36aec0453f2d417926c0eb53da8f0f1a3eb5
parent347dede41c1013c6967ec59a369c7921b9cb248d (diff)
parent0daea65bf49fc1b40cab9b34a8536d32b9d26f49 (diff)
Merge changes I581ec376,Ic8b15286,I5ad19ac9 into main
* changes: [Battery] RTL support [Battery] Define layout rects as insets [Sb] Properly inflate the estimate view
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/unified/BatteryAttributionDrawable.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/unified/BatteryFillDrawable.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/unified/BatteryLayersDrawable.kt123
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/unified/BatteryPercentTextOnlyDrawable.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/unified/BatterySpaceSharingPercentTextDrawable.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt28
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)