The phone runner says what it runs, on its card and notification (refs #8); pins OxygenFlow main 0b152017dee3
- Author
- Andy McCall @andymccall <[email protected]>
- via
- agent
agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key 86de9233aaf1
- When
- · 2026-10-29 01:14 UTC
- Signature
- ◈ sealed by agent:claude@andymccall
- Hash
0b152017dee3d4d6b9fb9a0aa6e0932a49f5006376dfc745e320ec65bed483e6- Depends on
- 26530d9758ee 41e2e862abce 4aee5293461d 57c0824300db 8440d51a2275 aa21eb5e0e5b c53e328b2db4
- Depended on by
- aa627197ec18 6823c243eaac
- In bookmarks
- main
- In tags
- v0.1.2 v0.1.3 v0.1.4 v0.1.5 v0.1.6 v0.1.7 v0.1.8 v0.1.9
- Edits
- 17 atoms, 0 tree operations, 8 files
Browse the tree at this change
modified CHANGELOG.md +1 −0
| … | 1 unchanged lines |
||
| 2 | 2 | ||
| 3 | 3 | ## Unreleased |
|
| 4 | 4 | ||
| 5 | + | - **The phone runner says what it runs (#8):** while it serves, the runner card lists the jobs it is running (owner/name #run job), and its notification says "Running owner/name #7 test" instead of waiting, from OxygenFlow's `runner::running()` (pins OxygenFlow main). |
|
| 5 | 6 | - **The phone runner rests when warm (#8):** besides charging and Wi-Fi, the runner now needs the phone cool: when the system says it is throttling (moderate or worse) or the battery is above 40 °C it takes nothing more, finishes a job due within a minute and hands back any other, as off charge, and serves again once it has cooled. The card and the notification say why it waits. |
|
| 6 | 7 | - **A proposal's description is Markdown:** it showed as plain text, `##`, `**`, backticks and table pipes included, with the source's line breaks; it now renders as an issue's does (headings, emphasis, code, lists and tables, lines joined into paragraphs). |
|
| 7 | 8 | ||
| … | 31 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/runner/PhoneRunner.kt +15 −0
| … | 51 unchanged lines |
||
| 52 | 52 | private val _state = MutableStateFlow<State>(State.Unregistered) |
|
| 53 | 53 | val state: StateFlow<State> = _state.asStateFlow() |
|
| 54 | 54 | ||
| 55 | + | /** The jobs it is running now, "owner/name #run job" (#8), while it serves. */ |
|
| 56 | + | private val _jobs = MutableStateFlow<List<String>>(emptyList()) |
|
| 57 | + | val jobs: StateFlow<List<String>> = _jobs.asStateFlow() |
|
| 58 | + | ||
| 59 | + | internal fun setJobs(jobs: List<String>) { |
|
| 60 | + | _jobs.value = jobs |
|
| 61 | + | } |
|
| 62 | + | ||
| 63 | + | /** The notification's line for [jobs]: what it runs, or that it waits for work. */ |
|
| 64 | + | fun jobsLine(jobs: List<String>): String = when (jobs.size) { |
|
| 65 | + | 0 -> "Waiting for WASM jobs, on charge and Wi-Fi" |
|
| 66 | + | 1 -> "Running ${jobs[0]}" |
|
| 67 | + | else -> "Running ${jobs.size} jobs: ${jobs.joinToString(", ")}" |
|
| 68 | + | } |
|
| 69 | + | ||
| 55 | 70 | internal fun set(state: State) { |
|
| 56 | 71 | _state.value = state |
|
| 57 | 72 | } |
|
| … | 112 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/runner/RunnerCard.kt +3 −0
| … | 41 unchanged lines |
||
| 42 | 42 | fun RunnerCard(api: HubApi, hub: String, canWrite: Boolean) { |
|
| 43 | 43 | val context = LocalContext.current |
|
| 44 | 44 | val state by PhoneRunner.state.collectAsStateWithLifecycle() |
|
| 45 | + | val jobs by PhoneRunner.jobs.collectAsStateWithLifecycle() |
|
| 45 | 46 | var busy by remember { mutableStateOf(false) } |
|
| 46 | 47 | var error by remember { mutableStateOf<String?>(null) } |
|
| 47 | 48 | val scope = rememberCoroutineScope() |
|
| … | 23 unchanged lines |
||
| 71 | 72 | is PhoneRunner.State.Failed -> "Stopped: ${s.message}" to status.bad |
|
| 72 | 73 | } |
|
| 73 | 74 | Text(line, style = MaterialTheme.typography.bodyMedium, color = colour) |
|
| 75 | + | // What it runs now (#8). |
|
| 76 | + | jobs.forEach { Text("▸ $it", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant) } |
|
| 74 | 77 | error?.let { Text(it, style = MaterialTheme.typography.bodyMedium, color = status.bad) } |
|
| 75 | 78 | Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { |
|
| 76 | 79 | when (state) { |
|
| … | 30 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/runner/RunnerService.kt +24 −1
| … | 18 unchanged lines |
||
| 19 | 19 | import android.os.PowerManager |
|
| 20 | 20 | import io.oxygenhub.android.MainActivity |
|
| 21 | 21 | import io.oxygenhub.android.R |
|
| 22 | + | import uniffi.rust_core.runnerRunning |
|
| 22 | 23 | import uniffi.rust_core.runnerServe |
|
| 23 | 24 | import uniffi.rust_core.runnerStop |
|
| 24 | 25 | ||
| … | 88 unchanged lines |
||
| 113 | 114 | } |
|
| 114 | 115 | val home = PhoneRunner.home(this).path |
|
| 115 | 116 | PhoneRunner.set(PhoneRunner.State.Running) |
|
| 116 | - | notify("Waiting for WASM jobs, on charge and Wi-Fi") |
|
| 117 | + | notify(PhoneRunner.jobsLine(emptyList())) |
|
| 118 | + | watchJobs() |
|
| 117 | 119 | serving = Thread { |
|
| 118 | 120 | val failed = try { |
|
| 119 | 121 | runnerServe(home) |
|
| … | 8 unchanged lines |
||
| 128 | 130 | } |
|
| 129 | 131 | } |
|
| 130 | 132 | ||
| 133 | + | /** While serving, what it runs (#8): on the card, and in the notification when it changes. */ |
|
| 134 | + | private fun watchJobs() { |
|
| 135 | + | Thread { |
|
| 136 | + | var shown: List<String>? = null |
|
| 137 | + | while (serving != null) { |
|
| 138 | + | val now = runnerRunning() |
|
| 139 | + | if (now != shown) { |
|
| 140 | + | PhoneRunner.setJobs(now) |
|
| 141 | + | if (PhoneRunner.state.value == PhoneRunner.State.Running) notify(PhoneRunner.jobsLine(now)) |
|
| 142 | + | shown = now |
|
| 143 | + | } |
|
| 144 | + | Thread.sleep(3_000) |
|
| 145 | + | } |
|
| 146 | + | PhoneRunner.setJobs(emptyList()) |
|
| 147 | + | }.apply { |
|
| 148 | + | name = "oxygenflow-runner-jobs" |
|
| 149 | + | isDaemon = true |
|
| 150 | + | start() |
|
| 151 | + | } |
|
| 152 | + | } |
|
| 153 | + | ||
| 131 | 154 | /** Serving ended: wait for the charger and Wi-Fi again while the owner wants it, else stop. */ |
|
| 132 | 155 | @Synchronized |
|
| 133 | 156 | private fun served(failed: String?) { |
|
| … | 73 unchanged lines |
modified app/src/test/java/io/oxygenhub/android/runner/PhoneRunnerTest.kt +7 −0
| … | 16 unchanged lines |
||
| 17 | 17 | PhoneRunner.tooWarm(PowerManager.THERMAL_STATUS_NONE, 415), |
|
| 18 | 18 | ) |
|
| 19 | 19 | } |
|
| 20 | + | ||
| 21 | + | @Test |
|
| 22 | + | fun whatItRunsReadsPlainly() { |
|
| 23 | + | assertEquals("Waiting for WASM jobs, on charge and Wi-Fi", PhoneRunner.jobsLine(emptyList())) |
|
| 24 | + | assertEquals("Running andy/demo #7 test", PhoneRunner.jobsLine(listOf("andy/demo #7 test"))) |
|
| 25 | + | assertEquals("Running 2 jobs: a/b #1 x, a/b #1 y", PhoneRunner.jobsLine(listOf("a/b #1 x", "a/b #1 y"))) |
|
| 26 | + | } |
|
| 20 | 27 | } |
replaced oxygenflow +0 −0
No line changes.
modified rust-core/Cargo.lock +4 −4
| … | 895 unchanged lines |
||
| 896 | 896 | ||
| 897 | 897 | [[package]] |
|
| 898 | 898 | name = "flow" |
|
| 899 | - | version = "0.5.0" |
|
| 899 | + | version = "0.5.2" |
|
| 900 | 900 | dependencies = [ |
|
| 901 | 901 | "anyhow", |
|
| 902 | 902 | "blake3", |
|
| … | 18 unchanged lines |
||
| 921 | 921 | ||
| 922 | 922 | [[package]] |
|
| 923 | 923 | name = "flow-core" |
|
| 924 | - | version = "0.5.0" |
|
| 924 | + | version = "0.5.2" |
|
| 925 | 925 | dependencies = [ |
|
| 926 | 926 | "blake3", |
|
| 927 | 927 | "curve25519-dalek", |
|
| … | 10 unchanged lines |
||
| 938 | 938 | ||
| 939 | 939 | [[package]] |
|
| 940 | 940 | name = "flow-init" |
|
| 941 | - | version = "0.5.0" |
|
| 941 | + | version = "0.5.2" |
|
| 942 | 942 | dependencies = [ |
|
| 943 | 943 | "libc", |
|
| 944 | 944 | "serde", |
|
| … | 408 unchanged lines |
||
| 1353 | 1353 | ||
| 1354 | 1354 | [[package]] |
|
| 1355 | 1355 | name = "jet" |
|
| 1356 | - | version = "0.5.0" |
|
| 1356 | + | version = "0.5.2" |
|
| 1357 | 1357 | dependencies = [ |
|
| 1358 | 1358 | "blake3", |
|
| 1359 | 1359 | "hex", |
|
| … | 2107 unchanged lines |
modified rust-core/src/lib.rs +6 −0
| … | 76 unchanged lines |
||
| 77 | 77 | } |
|
| 78 | 78 | ||
| 79 | 79 | /// The tiers this phone offers (the WASM tier), and why not the others. |
|
| 80 | + | /// What the runner is running now: "owner/name #run job" for each job (#8). |
|
| 80 | 81 | #[uniffi::export] |
|
| 82 | + | pub fn runner_running() -> Vec<String> { |
|
| 83 | + | flow::runner::running() |
|
| 84 | + | } |
|
| 85 | + | ||
| 86 | + | #[uniffi::export] |
|
| 81 | 87 | pub fn runner_tiers(home: String) -> Vec<String> { |
|
| 82 | 88 | let Ok(h) = flow::home::Home::locate(Some(Path::new(&home))) else { return vec![] }; |
|
| 83 | 89 | match flow::runner::tiers(&h) { |
|
| … | 26 unchanged lines |