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>
This commit is contained in:
1 parent
ded6df559d
commit
adaeb0c0ad
6 files changed
+449
-212
No files matched your search
@@ -897,6 +897,44 @@ mutable at runtime from the phone.
|
||||
caught, and the next thing the run reports asks again. Stops go through
|
||||
`stopSelf(lastStart)` rather than `stopSelf()`, so work starting in the
|
||||
moment between deciding to stop and stopping is not taken away with it.
|
||||
**A build that finishes while nobody is looking keeps its notification,
|
||||
and that notification carries the install.** Handing an APK to the
|
||||
system installer is starting an activity, which Android refuses from the
|
||||
background -- fired anyway it does nothing at all, so a download that
|
||||
worked read as an update that never landed. So `updateComponent` stops
|
||||
at `ComponentState.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 a `WorkKind.Waiting` row: 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; a broadcast back into this
|
||||
app would have to start the installer itself, which is the thing that
|
||||
cannot be done from there.
|
||||
Which is why the drawing lives in `WorkNotice` rather than in the
|
||||
service: that row outlives the last running thing, and something still
|
||||
alive has to take it down. The list screen is that something, since the
|
||||
states being drawn are its own.
|
||||
**Two things had to be true for it to come down again**, and neither was.
|
||||
`continuePendingInstalls` now begins by forgetting every download whose
|
||||
install has already happened -- compared as the installed copy's
|
||||
`lastUpdateTime` against the downloaded file's mtime, rather than by
|
||||
listening for an install, because the same broadcast reports removals
|
||||
and a removal is what 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 REMOVED half of a replace, the
|
||||
ADDED half, *and* the REPLACED that follows, so testing it without the
|
||||
action threw the lot away and an in-place install reached nothing here
|
||||
until the screen next resumed. Invisible while the resume was the only
|
||||
thing waiting on it; the moment a notification depended on it, it was
|
||||
the difference between the row coming down by itself and sitting there
|
||||
after the install it was for.
|
||||
What is left is a row that outlives the process: nothing cancels it if
|
||||
the install happens with this app long since reclaimed. It is
|
||||
`autoCancel`, so the tap that opens the app takes it away, and the app
|
||||
is where the truth about that build is -- which is the cheap answer to a
|
||||
case that costs a stale line in the shade.
|
||||
`POST_NOTIFICATIONS` is asked for at startup beside the local-network
|
||||
one, and refusing it costs the watching rather than the work: the
|
||||
service still runs, and there is simply nothing to draw. The type is
|
||||
|
||||
Reference in new issue
Block a user