Skip to content

Commit 400486a

Browse files
refactor: type DeviceInfo battery status/health and counts
1 parent f38f0dd commit 400486a

6 files changed

Lines changed: 130 additions & 75 deletions

File tree

internal/adb/adb_device_info.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ type DeviceInfo struct {
2929

3030
BatteryPct float64 // [0,1] when known, negative when unknown
3131

32-
BatteryStatus string
32+
BatteryStatus BatteryStatus
3333
BatteryTemp string
34-
BatteryHealth string
34+
BatteryHealth BatteryHealth
3535

3636
StorageTotal string
3737
StorageUsed string
@@ -41,13 +41,13 @@ type DeviceInfo struct {
4141

4242
RAM string
4343
CPUPlatform string
44-
CPUCores string
44+
CPUCores int // -1 when unknown
4545

4646
WifiSSID string
4747
IPAddress string
4848

4949
Uptime string
50-
AppCount string
50+
AppCount int // -1 when unknown
5151
AndroidDisplay string
5252
DensityDisplay string
5353
}
@@ -180,15 +180,15 @@ func (c *Client) GetDeviceInfo(ctx context.Context, serial string) (DeviceInfo,
180180
}
181181
}
182182

183-
appCount := fieldUnknown
183+
appCount := -1
184184
if len(appOut) > 0 {
185185
count := 0
186186
for _, line := range strings.Split(string(appOut), "\n") {
187187
if strings.HasPrefix(strings.TrimSpace(line), "package:") {
188188
count++
189189
}
190190
}
191-
appCount = strconv.Itoa(count)
191+
appCount = count
192192
}
193193

194194
prop := func(key string) string {

internal/adb/fixture_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ func TestFixtureDumpsysBattery(t *testing.T) {
3131
if level != 73 {
3232
t.Errorf("level = %d, want 73", level)
3333
}
34-
if got := parseBatteryStatus(status); got != "Charging" {
35-
t.Errorf("battery status = %q, want %q", got, "Charging")
34+
if got := parseBatteryStatus(status); got != BatteryStatusCharging {
35+
t.Errorf("battery status = %v, want %v", got, BatteryStatusCharging)
3636
}
37-
if got := parseBatteryHealth(health); got != "Good" {
38-
t.Errorf("battery health = %q, want %q", got, "Good")
37+
if got := parseBatteryHealth(health); got != BatteryHealthGood {
38+
t.Errorf("battery health = %v, want %v", got, BatteryHealthGood)
3939
}
4040
if got := parseBatteryTemp(temp); got != "31.2°C" {
4141
t.Errorf("battery temp = %q, want %q", got, "31.2°C")
@@ -73,8 +73,8 @@ func TestFixtureMeminfo(t *testing.T) {
7373
func TestFixtureCpuinfoArm(t *testing.T) {
7474
out := loadFixture(t, "cpuinfo_arm.txt")
7575
// 8 per-core "processor" entries; the ARM "Processor" header line must NOT count
76-
if got := parseCPUCores(out); got != "8" {
77-
t.Errorf("parseCPUCores = %q, want %q", got, "8")
76+
if got := parseCPUCores(out); got != 8 {
77+
t.Errorf("parseCPUCores = %d, want 8", got)
7878
}
7979
}
8080

internal/adb/parse_helpers.go

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,44 +33,78 @@ func parseDumpsysBattery(output string) (level int, status, temp, health string)
3333
return
3434
}
3535

36-
func parseBatteryStatus(code string) string {
37-
switch code {
38-
case "1":
39-
return "Unknown"
40-
case "2":
36+
// BatteryStatus mirrors android.os.BatteryManager.BATTERY_STATUS_*.
37+
type BatteryStatus int
38+
39+
const (
40+
BatteryStatusUnknown BatteryStatus = 1
41+
BatteryStatusCharging BatteryStatus = 2
42+
BatteryStatusDischarging BatteryStatus = 3
43+
BatteryStatusNotCharging BatteryStatus = 4
44+
BatteryStatusFull BatteryStatus = 5
45+
)
46+
47+
func (s BatteryStatus) String() string {
48+
switch s {
49+
case BatteryStatusCharging:
4150
return "Charging"
42-
case "3":
51+
case BatteryStatusDischarging:
4352
return "Discharging"
44-
case "4":
53+
case BatteryStatusNotCharging:
4554
return "Not charging"
46-
case "5":
55+
case BatteryStatusFull:
4756
return "Full"
4857
default:
4958
return "Unknown"
5059
}
5160
}
5261

53-
func parseBatteryHealth(code string) string {
54-
switch code {
55-
case "1":
56-
return "Unknown"
57-
case "2":
62+
// BatteryHealth mirrors android.os.BatteryManager.BATTERY_HEALTH_*.
63+
type BatteryHealth int
64+
65+
const (
66+
BatteryHealthUnknown BatteryHealth = 1
67+
BatteryHealthGood BatteryHealth = 2
68+
BatteryHealthOverheat BatteryHealth = 3
69+
BatteryHealthDead BatteryHealth = 4
70+
BatteryHealthOverVoltage BatteryHealth = 5
71+
BatteryHealthFailure BatteryHealth = 6
72+
BatteryHealthCold BatteryHealth = 7
73+
)
74+
75+
func (h BatteryHealth) String() string {
76+
switch h {
77+
case BatteryHealthGood:
5878
return "Good"
59-
case "3":
79+
case BatteryHealthOverheat:
6080
return "Overheat"
61-
case "4":
81+
case BatteryHealthDead:
6282
return "Dead"
63-
case "5":
83+
case BatteryHealthOverVoltage:
6484
return "Over voltage"
65-
case "6":
85+
case BatteryHealthFailure:
6686
return "Failure"
67-
case "7":
87+
case BatteryHealthCold:
6888
return "Cold"
6989
default:
7090
return "Unknown"
7191
}
7292
}
7393

94+
func parseBatteryStatus(code string) BatteryStatus {
95+
if n, err := strconv.Atoi(strings.TrimSpace(code)); err == nil {
96+
return BatteryStatus(n)
97+
}
98+
return BatteryStatusUnknown
99+
}
100+
101+
func parseBatteryHealth(code string) BatteryHealth {
102+
if n, err := strconv.Atoi(strings.TrimSpace(code)); err == nil {
103+
return BatteryHealth(n)
104+
}
105+
return BatteryHealthUnknown
106+
}
107+
74108
func parseBatteryTemp(raw string) string {
75109
val, err := strconv.Atoi(strings.TrimSpace(raw))
76110
if err != nil {
@@ -172,7 +206,8 @@ func parseMemTotal(memInfoOutput string) string {
172206
}
173207

174208
// case-sensitive "processor" prefix + numeric value excludes the ARM header line "Processor : AArch64 ...".
175-
func parseCPUCores(cpuInfoOutput string) string {
209+
// returns -1 when no per-core entries are found.
210+
func parseCPUCores(cpuInfoOutput string) int {
176211
count := 0
177212
for _, line := range strings.Split(cpuInfoOutput, "\n") {
178213
trimmed := strings.TrimSpace(line)
@@ -189,9 +224,9 @@ func parseCPUCores(cpuInfoOutput string) string {
189224
count++
190225
}
191226
if count == 0 {
192-
return "-"
227+
return -1
193228
}
194-
return strconv.Itoa(count)
229+
return count
195230
}
196231

197232
// returns the index of the value after "SSID:" in s, rejecting matches inside "BSSID:". -1 if not found.

internal/adb/parse_helpers_test.go

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,54 @@ import (
77

88
func TestParseBatteryStatus(t *testing.T) {
99
tests := []struct {
10-
input string
11-
want string
10+
input string
11+
want BatteryStatus
12+
wantText string
1213
}{
13-
{"2", "Charging"},
14-
{"3", "Discharging"},
15-
{"4", "Not charging"},
16-
{"5", "Full"},
17-
{"1", "Unknown"},
18-
{"", "Unknown"},
19-
{"garbage", "Unknown"},
20-
{"99", "Unknown"},
14+
{"2", BatteryStatusCharging, "Charging"},
15+
{"3", BatteryStatusDischarging, "Discharging"},
16+
{"4", BatteryStatusNotCharging, "Not charging"},
17+
{"5", BatteryStatusFull, "Full"},
18+
{"1", BatteryStatusUnknown, "Unknown"},
19+
{"", BatteryStatusUnknown, "Unknown"},
20+
{"garbage", BatteryStatusUnknown, "Unknown"},
21+
{"99", BatteryStatus(99), "Unknown"}, // unknown int code falls back to "Unknown" text
2122
}
2223
for _, tt := range tests {
23-
if got := parseBatteryStatus(tt.input); got != tt.want {
24-
t.Errorf("parseBatteryStatus(%q) = %q, want %q", tt.input, got, tt.want)
24+
got := parseBatteryStatus(tt.input)
25+
if got != tt.want {
26+
t.Errorf("parseBatteryStatus(%q) = %d, want %d", tt.input, got, tt.want)
27+
}
28+
if got.String() != tt.wantText {
29+
t.Errorf("parseBatteryStatus(%q).String() = %q, want %q", tt.input, got.String(), tt.wantText)
2530
}
2631
}
2732
}
2833

2934
func TestParseBatteryHealth(t *testing.T) {
3035
tests := []struct {
31-
input string
32-
want string
36+
input string
37+
want BatteryHealth
38+
wantText string
3339
}{
34-
{"1", "Unknown"},
35-
{"2", "Good"},
36-
{"3", "Overheat"},
37-
{"4", "Dead"},
38-
{"5", "Over voltage"},
39-
{"6", "Failure"},
40-
{"7", "Cold"},
41-
{"", "Unknown"},
42-
{"garbage", "Unknown"},
43-
{"99", "Unknown"},
40+
{"1", BatteryHealthUnknown, "Unknown"},
41+
{"2", BatteryHealthGood, "Good"},
42+
{"3", BatteryHealthOverheat, "Overheat"},
43+
{"4", BatteryHealthDead, "Dead"},
44+
{"5", BatteryHealthOverVoltage, "Over voltage"},
45+
{"6", BatteryHealthFailure, "Failure"},
46+
{"7", BatteryHealthCold, "Cold"},
47+
{"", BatteryHealthUnknown, "Unknown"},
48+
{"garbage", BatteryHealthUnknown, "Unknown"},
49+
{"99", BatteryHealth(99), "Unknown"},
4450
}
4551
for _, tt := range tests {
46-
if got := parseBatteryHealth(tt.input); got != tt.want {
47-
t.Errorf("parseBatteryHealth(%q) = %q, want %q", tt.input, got, tt.want)
52+
got := parseBatteryHealth(tt.input)
53+
if got != tt.want {
54+
t.Errorf("parseBatteryHealth(%q) = %d, want %d", tt.input, got, tt.want)
55+
}
56+
if got.String() != tt.wantText {
57+
t.Errorf("parseBatteryHealth(%q).String() = %q, want %q", tt.input, got.String(), tt.wantText)
4858
}
4959
}
5060
}
@@ -148,21 +158,21 @@ func TestParseCPUCores(t *testing.T) {
148158
tests := []struct {
149159
name string
150160
input string
151-
want string
161+
want int
152162
}{
153-
{"two cores", "processor\t: 0\nBogoMIPS\t: 52.00\nprocessor\t: 1\n", "2"},
163+
{"two cores", "processor\t: 0\nBogoMIPS\t: 52.00\nprocessor\t: 1\n", 2},
154164
// ARM header line "Processor : AArch64 ..." must NOT be counted —
155165
// the device has 2 cores, not 3.
156-
{"arm header excluded", "Processor\t: AArch64 Processor rev 4 (aarch64)\nprocessor\t: 0\nprocessor\t: 1\n", "2"},
157-
{"empty", "", "-"},
158-
{"no matches", "no processor lines here", "-"},
166+
{"arm header excluded", "Processor\t: AArch64 Processor rev 4 (aarch64)\nprocessor\t: 0\nprocessor\t: 1\n", 2},
167+
{"empty", "", -1},
168+
{"no matches", "no processor lines here", -1},
159169
// Non-numeric value should be ignored.
160-
{"non-numeric value", "processor\t: foo\nprocessor\t: 0\n", "1"},
161-
{"eight cores", "processor\t: 0\nprocessor\t: 1\nprocessor\t: 2\nprocessor\t: 3\nprocessor\t: 4\nprocessor\t: 5\nprocessor\t: 6\nprocessor\t: 7\n", "8"},
170+
{"non-numeric value", "processor\t: foo\nprocessor\t: 0\n", 1},
171+
{"eight cores", "processor\t: 0\nprocessor\t: 1\nprocessor\t: 2\nprocessor\t: 3\nprocessor\t: 4\nprocessor\t: 5\nprocessor\t: 6\nprocessor\t: 7\n", 8},
162172
}
163173
for _, tt := range tests {
164174
if got := parseCPUCores(tt.input); got != tt.want {
165-
t.Errorf("parseCPUCores(%q) = %q, want %q", tt.input, got, tt.want)
175+
t.Errorf("parseCPUCores(%q) = %d, want %d", tt.input, got, tt.want)
166176
}
167177
}
168178
}

internal/ui/device_info_panel.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"image/color"
88
"log"
9+
"strconv"
910

1011
"fyne.io/fyne/v2"
1112
"fyne.io/fyne/v2/canvas"
@@ -427,9 +428,9 @@ func (v *infoView) apply(info adb.DeviceInfo) {
427428
v.batteryBar.Refresh()
428429

429430
v.batteryPills.Objects = []fyne.CanvasObject{
430-
buildInfoPill(icons.ZapIcon, info.BatteryStatus, batteryStatusColor(info.BatteryStatus)),
431+
buildInfoPill(icons.ZapIcon, info.BatteryStatus.String(), batteryStatusColor(info.BatteryStatus)),
431432
buildInfoPill(icons.ThermometerIcon, info.BatteryTemp, pillTeal),
432-
buildInfoPill(icons.HeartIcon, info.BatteryHealth, pillTeal),
433+
buildInfoPill(icons.HeartIcon, info.BatteryHealth.String(), pillTeal),
433434
}
434435
v.batteryPills.Refresh()
435436

@@ -444,18 +445,26 @@ func (v *infoView) apply(info adb.DeviceInfo) {
444445
v.density.Set(info.DensityDisplay)
445446

446447
v.cpuPlatform.Set(info.CPUPlatform)
447-
v.cpuCores.Set(info.CPUCores)
448+
v.cpuCores.Set(formatCount(info.CPUCores))
448449
v.ram.Set(info.RAM)
449450

450451
v.wifiSSID.Set(info.WifiSSID)
451452
v.ip.Set(info.IPAddress)
452453

453454
v.androidVer.Set(info.AndroidDisplay)
454455
v.uptime.Set(info.Uptime)
455-
v.appCount.Set(info.AppCount)
456+
v.appCount.Set(formatCount(info.AppCount))
456457
v.buildID.Set(info.BuildID)
457458
}
458459

460+
// formatCount renders a possibly-unknown count: "-" for negative, decimal otherwise.
461+
func formatCount(n int) string {
462+
if n < 0 {
463+
return "-"
464+
}
465+
return strconv.Itoa(n)
466+
}
467+
459468
func (dip *DeviceInfoPanel) buildDisconnectedView(serial string) fyne.CanvasObject {
460469
name := dip.deviceDisplayName(serial)
461470
heroArea := buildSmallHero(name, "● Disconnected", pillGray)

internal/ui/device_info_widgets.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fyne.io/fyne/v2/theme"
1111
"fyne.io/fyne/v2/widget"
1212

13+
"mirroid/internal/adb"
1314
"mirroid/internal/icons"
1415
)
1516

@@ -151,11 +152,11 @@ func buildInfoPill(icon fyne.Resource, text string, pillColor color.Color) fyne.
151152
return container.New(&badgeLayout{padX: pillPadX, padY: badgePadY}, bg, row)
152153
}
153154

154-
func batteryStatusColor(status string) color.Color {
155-
switch strings.ToLower(strings.TrimSpace(status)) {
156-
case "charging", "full":
155+
func batteryStatusColor(status adb.BatteryStatus) color.Color {
156+
switch status {
157+
case adb.BatteryStatusCharging, adb.BatteryStatusFull:
157158
return pillGreen
158-
case "discharging":
159+
case adb.BatteryStatusDischarging:
159160
return pillRed
160161
default:
161162
return pillGray

0 commit comments

Comments
 (0)