Commit Graph
3 Commits
Author SHA1 Message Date
iris-aiandClaude Opus 5 adaeb0c0ad A finished build keeps its notification, with an Install button on it
Handing an APK to the system installer means starting an activity, and
Android refuses one from the background: fired anyway it does nothing at
all, so a download that finished while the phone was in a pocket read as
an update that never landed. `updateComponent` now stops at
ReadyToInstall whenever this screen is not in front of anybody -- exactly
as it already did for the second APK of a project-wide update -- and that
state is drawn as a row of its own: no bar, dismissible, out of the
group, and carrying an Install action whose PendingIntent is the install
intent itself. An activity is the one press a notification may make from
any state.

Which is why the drawing moves from the service into WorkNotice: that
row outlives the last running thing, and something still alive has to
take it down. The list screen is that something. The service is now the
keep-alive and the one notification it is held up by, nothing else.

Two things had to be true for the row to come down again, and neither
was. continuePendingInstalls now begins by forgetting a download whose
install has already happened -- the installed copy's lastUpdateTime
against the downloaded file's mtime, rather than listening for an
install, since the same broadcast reports the removal the wrong-key case
is waiting for. Without it, pressing Install on the notification and then
returning to the app ran the whole install a second time; measured, not
feared. And registerPackageChangeReceiver dropped every arrival that was
an update: EXTRA_REPLACING marks the ADDED half of a replace and the
REPLACED that follows it, not only the REMOVED half the guard was written
for, so an in-place install reached nothing here until the screen next
resumed.

Verified on the emulator: Install pressed, app sent to the home screen,
build and download finish in the background, the row turns into
"Downloaded, waiting to install..." with the button, the service stops,
Install from the shade puts up the system installer, and the row goes
away by itself once the install lands -- with the app still in the
background, and with no second installer when it is opened again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 21:37:49 -04:00
iris-aiandClaude Opus 5 ded6df559d One notification per thing running, grouped, and an em dash between the two names
Two builds at once were two lines inside one notification; they are now
two notifications in one group, each with its own title and its own bar,
which is what the shade is for -- and what lets a component that
finishes take its own row down while its sibling carries on. Rows are
keyed by a tag (<key>/<component>) rather than an id, so an update
replaces the row it is about and two components cannot collide, and
`drawn` is the path out for one whose work is over.

A foreground service needs one notification that outlives every row, so
the service's own is the row itself while one thing is running and the
group's summary once there are more. Measured on API 36: a group of one
is collapsed to its header, which is the line of text with the bar left
out of it -- so grouping a single row would cost it the progress it had.
Verified on the emulator through all four states: one row with its
count, two grouped rows with a bar each, the fast one finishing and
leaving the other as a single row again, and everything gone when the
last build lands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 20:30:35 -04:00
iris-aiandClaude Opus 5 bb65526230 Show an ongoing notification while the app is working
Everything this app asks for is minutes of the build machine's time
followed by a download, and all of it runs in the list screen's own
coroutines -- so with the app in the background the process is an
ordinary cached one, and Android is free to take it away halfway
through a build. A foreground service is the only way to say otherwise,
and its notification is both what that costs and what it is for: the
work is visible from the shade while it runs, and one tap comes back to
the card that started it.

The work itself stays where it is. WorkNoticeService runs nothing --
it collects a list of what is happening and reposts one notification,
stopping as soon as the list is empty -- and that list is *derived* by
workItems from the same projectStates/componentStates the cards are
drawn from, so there is nothing to forget to post and nothing to forget
to take back. The words are shared rather than written twice:
componentWorkLine and buildLine are now read by the card's own bars and
by the notification alike.

Measured on API 36 rather than assumed: the collapsed row drops the
content text the moment there is a progress bar, so one thing running
puts both the component and what is happening to it in the title
("Test Tablet / tablet: building  14/30"); several get a line each in
the expanded view and an indeterminate bar, there being no honest
single number for two builds at once. With the app at the home screen
the process sits at fg-service-act rather than cached, and the builds
it started landed while it was there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 19:36:03 -04:00