Skip to content

Commit 8d94d56

Browse files
committed
Dashboard: Show the adapter's standing capability note on supported devices
The adapter_detail_*_ready strings were dead copy: AdapterSupport.detail only ever reached the UI through the Unsupported/NeedsSetup observation, so a healthy device had no per-adapter hint surface at all. ChargingState now carries the detail as adapterDetail - populated only while control is enabled, which by every probe's when-cascade construction is exactly when detail holds the ready string, so a gate-failure reason can never print twice - and the dashboard's provenance block renders it between the readback line and the device line. The six strings are reworded from probe-log phrasing ("...detected; ...") into standing facts (write latency on Pixel, immediate application on Samsung/Xiaomi, the Shizuku requirement on Oplus/LineageOS, replug semantics on GrapheneOS).
1 parent 2b1db02 commit 8d94d56

4 files changed

Lines changed: 40 additions & 7 deletions

File tree

app/src/debug/java/eu/darken/amply/screenshots/ScreenshotContent.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ private fun readyState() = DashboardUiState(
206206
device = pixelDevice(),
207207
adapterName = "Pixel Charge Control".toCaString(),
208208
adapterId = "pixel",
209+
adapterDetail = "Charging changes take about 15 seconds to reach the hardware".toCaString(),
209210
supportedPolicies = pixelPolicies(),
210211
reconnectSupported = true,
211212
controlEnabled = true,

app/src/main/java/eu/darken/amply/charging/core/ChargingRepository.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ data class ChargingState(
5353
val device: DeviceInfo = DeviceInfo.current(),
5454
val adapterName: CaString = R.string.adapter_name_detecting.toCaString(),
5555
val adapterId: String? = null,
56+
/**
57+
* Standing capability note for a control-enabled adapter (write latency, Shizuku requirement,
58+
* replug semantics — the `adapter_detail_*_ready` strings). Null whenever control is unavailable:
59+
* the gate-failure text already reaches the UI as the observation's reason, and populating both
60+
* would print the same sentence twice.
61+
*/
62+
val adapterDetail: CaString? = null,
5663
val supportedPolicies: List<ChargePolicy> = emptyList(),
5764
/**
5865
* The selected adapter's protective default (e.g. FixedLimit(80) on Pixel, Adaptive on Xiaomi), or
@@ -276,7 +283,11 @@ class ChargingRepository @Inject constructor(
276283
if (adapter == null || !selection.support.controlEnabled) {
277284
val detail = selection.support.detail.toCaString()
278285
val observation = ChargeObservation.Unsupported(detail)
279-
mutableState.value = state.value.copy(observation = observation, message = detail)
286+
// Also drop the standing ready note: this branch means the gate just failed on a fresh
287+
// selection (a capability can vanish between refresh and tap), and the field's contract
288+
// is "null whenever control is unavailable" — keeping a stale "changes apply
289+
// immediately" under the failure reason would contradict it.
290+
mutableState.value = state.value.copy(observation = observation, message = detail, adapterDetail = null)
280291
return ApplyResult(false, observation, context.getString(selection.support.detail))
281292
}
282293
if (policy !in adapter.supportedPolicies) {
@@ -500,6 +511,13 @@ class ChargingRepository @Inject constructor(
500511
controlEnabled = selection.support.controlEnabled,
501512
contributionWanted = selection.support.contributionWanted,
502513
guidedCaptureUseful = selection.support.guidedCaptureUseful,
514+
// controlEnabled implies detail is the adapter's *_ready string (every probe's when-cascade
515+
// pairs them), so this can never carry a gate-failure reason.
516+
adapterDetail = if (adapter != null && selection.support.controlEnabled) {
517+
selection.support.detail.toCaString()
518+
} else {
519+
null
520+
},
503521
adapterResolved = true,
504522
access = access,
505523
observation = observation,

app/src/main/java/eu/darken/amply/main/ui/dashboard/DashboardScreen.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,18 @@ private fun StatusCard(
483483
style = MaterialTheme.typography.bodySmall,
484484
color = MaterialTheme.colorScheme.onSurfaceVariant,
485485
)
486+
// Standing adapter fact (write latency, Shizuku requirement, replug semantics) — only set
487+
// while control is enabled, so it never repeats a gate-failure reason shown above. Hidden
488+
// while a replug is pending: the transient hint below states the same fact as an instruction,
489+
// and printing both would say "reconnect the charger" twice on latched adapters.
490+
state.charging.adapterDetail?.takeIf { !state.charging.isAwaitingReplug() }?.let {
491+
Spacer(Modifier.height(4.dp))
492+
Text(
493+
it.asComposable(),
494+
style = MaterialTheme.typography.labelMedium,
495+
color = MaterialTheme.colorScheme.onSurfaceVariant,
496+
)
497+
}
486498
Spacer(Modifier.height(4.dp))
487499
Text(
488500
stringResource(
@@ -889,6 +901,7 @@ private fun DashboardScreenPreview() = PreviewWrapper {
889901
device = DeviceInfo("Google", "Pixel 8", 36, "preview"),
890902
adapterName = "Pixel Charge Control".toCaString(),
891903
adapterId = "pixel",
904+
adapterDetail = "Charging changes take about 15 seconds to reach the hardware".toCaString(),
892905
supportedPolicies = listOf(
893906
ChargePolicy.FixedLimit(80),
894907
ChargePolicy.Adaptive,
@@ -1125,6 +1138,7 @@ private fun DashboardScreenAwaitingReplugPreview() = PreviewWrapper {
11251138
device = DeviceInfo("Google", "Pixel 9 Pro XL", 37, "preview"),
11261139
adapterName = "GrapheneOS charge limit".toCaString(),
11271140
adapterId = "grapheneos-chargelimit-v1",
1141+
adapterDetail = "Changes take effect the next time the charger is reconnected".toCaString(),
11281142
supportedPolicies = listOf(
11291143
ChargePolicy.FixedLimit(80),
11301144
ChargePolicy.Unrestricted,

app/src/main/res/values/strings.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,22 +126,22 @@
126126
<string name="adapter_detail_no_tablet">Pixel tablets are not supported</string>
127127
<string name="adapter_detail_no_controller">Google\'s charging-optimization controller is not available</string>
128128
<string name="adapter_detail_disabled_build">Adapter disabled in this build</string>
129-
<string name="adapter_detail_pixel_ready">Supported Pixel capability detected; charging hardware may take about 15 seconds to react</string>
129+
<string name="adapter_detail_pixel_ready">Charging changes take about 15 seconds to reach the hardware</string>
130130
<string name="adapter_detail_lab_diagnostics">Detected for diagnostics only; no unverified writes are exposed</string>
131131
<string name="adapter_detail_requires_samsung">Requires a Samsung device on a verified One UI version</string>
132132
<string name="adapter_detail_samsung_no_key">The battery-protection setting is not present on this device</string>
133133
<string name="adapter_detail_secondary_user">Charging protection is device-wide; control is limited to the main user</string>
134-
<string name="adapter_detail_samsung_ready">Samsung battery protection detected; changes apply immediately</string>
134+
<string name="adapter_detail_samsung_ready">Changes apply to the hardware immediately</string>
135135
<string name="adapter_detail_requires_xiaomi">Requires a verified Xiaomi device on HyperOS 2</string>
136-
<string name="adapter_detail_xiaomi_ready">Xiaomi charging protection detected; changes apply immediately</string>
136+
<string name="adapter_detail_xiaomi_ready">Changes apply to the hardware immediately</string>
137137
<string name="adapter_detail_requires_oplus">Requires a OnePlus, Oppo, or Realme device on ColorOS 15</string>
138-
<string name="adapter_detail_oplus_ready">ColorOS charging protection detected; control requires Shizuku</string>
138+
<string name="adapter_detail_oplus_ready">Charging control requires Shizuku on this device</string>
139139
<string name="adapter_detail_requires_grapheneos">Requires GrapheneOS</string>
140140
<string name="adapter_detail_grapheneos_no_key">GrapheneOS detected, but its charge-limit setting is not present on this build</string>
141-
<string name="adapter_detail_grapheneos_ready">GrapheneOS charge limit detected — changes take effect when the charger is reconnected</string>
141+
<string name="adapter_detail_grapheneos_ready">Changes take effect the next time the charger is reconnected</string>
142142
<string name="adapter_detail_requires_lineageos">Requires a qualified LineageOS device</string>
143143
<string name="adapter_detail_lineageos_no_provider">LineageOS charging control is not available on this build</string>
144-
<string name="adapter_detail_lineageos_ready">LineageOS charging control detected; control requires Shizuku</string>
144+
<string name="adapter_detail_lineageos_ready">Charging control requires Shizuku on this device</string>
145145
<string name="adapter_detail_none">No charging adapter is known for this device</string>
146146

147147
<!-- Access backend detail + summary labels -->

0 commit comments

Comments
 (0)