feat(core/eckhart): extend fuel gauge batt icons
What changed, and why it matters
This commit is a user-interface polish change for the Trezor hardware wallet's battery indicator. It adds finer battery-level icons (splitting one 'mid' icon into 'mid-plus' and 'mid-minus') and adjusts the percentage thresholds at which each icon appears. It also includes automated tests to verify the new thresholds. There is no security-relevant change here.
No security action required. Treat as normal UI/UX maintenance.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors FuelGauge::battery_indication() in the Eckhart UI layout to use four charge thresholds (90/40/20/10%) instead of three (80/25/9%), and introduces ICON_BATTERY_MID_PLUS and ICON_BATTERY_MID_MINUS assets to replace the previous single ICON_BATTERY_MID. The logic is rewritten as match expressions and unit tests are added to assert icon and color selection at boundary values. Several PNG/TOIF icon files are updated or converted to greyscale for consistency. No cryptographic, input-validation, memory-safety, or privilege changes are present.
Changed components
core/embed/rust/src/ui/layout_eckhart/component/fuel_gauge.rscore/embed/rust/src/ui/layout_eckhart/theme/mod.rscore/embed/rust/src/ui/layout_eckhart/res/battery/*Inspect captured patch +65 / −15
diff --git a/core/embed/rust/src/ui/layout_eckhart/component/fuel_gauge.rs b/core/embed/rust/src/ui/layout_eckhart/component/fuel_gauge.rs
index ef6bfa6c3..ce35aa8e5 100644
--- a/core/embed/rust/src/ui/layout_eckhart/component/fuel_gauge.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/component/fuel_gauge.rs
@@ -14,8 +14,8 @@ use crate::ui::{component::Timer, util::animation_disabled};
use super::super::{
fonts,
theme::{
- GREY_LIGHT, ICON_BATTERY_EMPTY, ICON_BATTERY_FULL, ICON_BATTERY_LOW, ICON_BATTERY_MID,
- ICON_BATTERY_ZAP, RED, YELLOW,
+ GREY_LIGHT, ICON_BATTERY_EMPTY, ICON_BATTERY_FULL, ICON_BATTERY_LOW,
+ ICON_BATTERY_MID_MINUS, ICON_BATTERY_MID_PLUS, ICON_BATTERY_ZAP, RED, YELLOW,
},
};
@@ -117,21 +117,26 @@ impl FuelGauge {
/// Returns the icon, color for the icon, and color for the text based on
/// the charging state and state of charge (soc).
fn battery_indication(&self, charging_state: ChargingState, soc: u8) -> (Icon, Color, Color) {
- const SOC_THRESHOLD_FULL: u8 = 80;
- const SOC_THRESHOLD_MID: u8 = 25;
- const SOC_THRESHOLD_LOW: u8 = 9;
+ const SOC_THRESHOLD_FULL: u8 = 90;
+ const SOC_THRESHOLD_MID_PLUS: u8 = 40;
+ const SOC_THRESHOLD_MID_MINUS: u8 = 20;
+ const SOC_THRESHOLD_LOW: u8 = 10;
match charging_state {
ChargingState::Charging => (ICON_BATTERY_ZAP, YELLOW, GREY_LIGHT),
ChargingState::Discharging | ChargingState::Idle => {
- if soc > SOC_THRESHOLD_FULL {
- (ICON_BATTERY_FULL, GREY_LIGHT, GREY_LIGHT)
- } else if soc > SOC_THRESHOLD_MID {
- (ICON_BATTERY_MID, GREY_LIGHT, GREY_LIGHT)
- } else if soc > SOC_THRESHOLD_LOW {
- (ICON_BATTERY_LOW, YELLOW, GREY_LIGHT)
- } else {
- (ICON_BATTERY_EMPTY, RED, RED)
- }
+ let icon = match soc {
+ x if x >= SOC_THRESHOLD_FULL => ICON_BATTERY_FULL,
+ x if x >= SOC_THRESHOLD_MID_PLUS => ICON_BATTERY_MID_PLUS,
+ x if x >= SOC_THRESHOLD_MID_MINUS => ICON_BATTERY_MID_MINUS,
+ x if x >= SOC_THRESHOLD_LOW => ICON_BATTERY_LOW,
+ _ => ICON_BATTERY_EMPTY,
+ };
+ let (icon_color, text_color) = match soc {
+ x if x >= SOC_THRESHOLD_MID_MINUS => (GREY_LIGHT, GREY_LIGHT),
+ x if x >= SOC_THRESHOLD_LOW => (YELLOW, GREY_LIGHT),
+ _ => (RED, RED),
+ };
+ (icon, icon_color, text_color)
}
}
}
@@ -262,3 +267,41 @@ impl crate::trace::Trace for FuelGauge {
t.int("soc", self.soc.unwrap_or(0) as i64);
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn charging_icon_constant() {
+ let gauge = FuelGauge::always();
+ for soc in [0u8, 1, 50, 100] {
+ let (icon, ic, tc) = gauge.battery_indication(ChargingState::Charging, soc);
+ assert!(icon == ICON_BATTERY_ZAP);
+ assert!(ic == YELLOW);
+ assert!(tc == GREY_LIGHT);
+ }
+ }
+
+ #[test]
+ fn discharging_threshold_boundaries() {
+ let gauge = FuelGauge::always();
+ // soc, expected icon, icon color, text color
+ let cases = [
+ (9, ICON_BATTERY_EMPTY, RED, RED),
+ (10, ICON_BATTERY_LOW, YELLOW, GREY_LIGHT),
+ (19, ICON_BATTERY_LOW, YELLOW, GREY_LIGHT),
+ (20, ICON_BATTERY_MID_MINUS, GREY_LIGHT, GREY_LIGHT),
+ (39, ICON_BATTERY_MID_MINUS, GREY_LIGHT, GREY_LIGHT),
+ (40, ICON_BATTERY_MID_PLUS, GREY_LIGHT, GREY_LIGHT),
+ (89, ICON_BATTERY_MID_PLUS, GREY_LIGHT, GREY_LIGHT),
+ (90, ICON_BATTERY_FULL, GREY_LIGHT, GREY_LIGHT),
+ ];
+ for (soc, exp_icon, exp_ic, exp_tc) in cases {
+ let (icon, ic, tc) = gauge.battery_indication(ChargingState::Discharging, soc);
+ assert!(icon == exp_icon, "icon soc={}", soc);
+ assert!(ic == exp_ic, "icon color soc={}", soc);
+ assert!(tc == exp_tc, "text color soc={}", soc);
+ }
+ }
+}
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/empty.png b/core/embed/rust/src/ui/layout_eckhart/res/battery/empty.png
index d083e7e50..cfc80f66c 100644
Binary files a/core/embed/rust/src/ui/layout_eckhart/res/battery/empty.png and b/core/embed/rust/src/ui/layout_eckhart/res/battery/empty.png differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/full.png b/core/embed/rust/src/ui/layout_eckhart/res/battery/full.png
index 7d40d33c6..432d38eb4 100644
Binary files a/core/embed/rust/src/ui/layout_eckhart/res/battery/full.png and b/core/embed/rust/src/ui/layout_eckhart/res/battery/full.png differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/low.png b/core/embed/rust/src/ui/layout_eckhart/res/battery/low.png
index cb29ed950..ca4eeb847 100644
Binary files a/core/embed/rust/src/ui/layout_eckhart/res/battery/low.png and b/core/embed/rust/src/ui/layout_eckhart/res/battery/low.png differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/mid.png b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid.png
deleted file mode 100644
index 0b32137e0..000000000
Binary files a/core/embed/rust/src/ui/layout_eckhart/res/battery/mid.png and /dev/null differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/mid.toif b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid.toif
deleted file mode 100644
index 08cf7997b..000000000
Binary files a/core/embed/rust/src/ui/layout_eckhart/res/battery/mid.toif and /dev/null differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_minus.png b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_minus.png
new file mode 100644
index 000000000..05293d68d
Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_minus.png differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_minus.toif b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_minus.toif
new file mode 100644
index 000000000..780da39a0
Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_minus.toif differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_plus.png b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_plus.png
new file mode 100644
index 000000000..3ca6fbdd5
Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_plus.png differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_plus.toif b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_plus.toif
new file mode 100644
index 000000000..08cf7997b
Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/battery/mid_plus.toif differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/battery/zap.png b/core/embed/rust/src/ui/layout_eckhart/res/battery/zap.png
index 1ae22400b..60a43aa43 100644
Binary files a/core/embed/rust/src/ui/layout_eckhart/res/battery/zap.png and b/core/embed/rust/src/ui/layout_eckhart/res/battery/zap.png differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/chevron_down_mini.png b/core/embed/rust/src/ui/layout_eckhart/res/chevron_down_mini.png
index 0a0edbcf2..e2e9af7bf 100644
Binary files a/core/embed/rust/src/ui/layout_eckhart/res/chevron_down_mini.png and b/core/embed/rust/src/ui/layout_eckhart/res/chevron_down_mini.png differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/close.png b/core/embed/rust/src/ui/layout_eckhart/res/close.png
index 86a7705f5..9c34cd7ac 100644
Binary files a/core/embed/rust/src/ui/layout_eckhart/res/close.png and b/core/embed/rust/src/ui/layout_eckhart/res/close.png differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/res/secured.png b/core/embed/rust/src/ui/layout_eckhart/res/secured.png
index 32eb486f3..9206a9ef3 100644
Binary files a/core/embed/rust/src/ui/layout_eckhart/res/secured.png and b/core/embed/rust/src/ui/layout_eckhart/res/secured.png differ
diff --git a/core/embed/rust/src/ui/layout_eckhart/theme/mod.rs b/core/embed/rust/src/ui/layout_eckhart/theme/mod.rs
index 3c1b9d4f0..ae6a5c5da 100644
--- a/core/embed/rust/src/ui/layout_eckhart/theme/mod.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/theme/mod.rs
@@ -143,7 +143,14 @@ include_icon!(
// Battery icons
include_icon!(ICON_BATTERY_ZAP, "layout_eckhart/res/battery/zap.toif");
include_icon!(ICON_BATTERY_FULL, "layout_eckhart/res/battery/full.toif");
-include_icon!(ICON_BATTERY_MID, "layout_eckhart/res/battery/mid.toif");
+include_icon!(
+ ICON_BATTERY_MID_PLUS,
+ "layout_eckhart/res/battery/mid_plus.toif"
+);
+include_icon!(
+ ICON_BATTERY_MID_MINUS,
+ "layout_eckhart/res/battery/mid_minus.toif"
+);
include_icon!(ICON_BATTERY_LOW, "layout_eckhart/res/battery/low.toif");
include_icon!(ICON_BATTERY_EMPTY, "layout_eckhart/res/battery/empty.toif");
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.