Public Access
Record the keep-alive corrections in the port notes and the manual checks
The port doc's backgrounding decision now carries the four corrections rather than describing a wiring that was not true, and Phase 14 gains the checks a phone can actually run: a backgrounded shell surviving, an idle Files connection surviving, the permission ask arriving at the first thing worth showing, and a refusal costing the notification and nothing else.
This commit is contained in:
+38
-3
@@ -30,7 +30,10 @@ verified is that it compiles, links, packages, and carries the right natives.
|
||||
transfers protected by a **foreground service**. File transfer is not in the first scope; when it arrives it
|
||||
is **one remote pane** with Android's document picker for moving files in and out. *It has since arrived,
|
||||
both ways:* the pane, the queue, `ACTION_OPEN_DOCUMENT` going in and `ACTION_CREATE_DOCUMENT` coming out,
|
||||
with the foreground service now counting transfers as well as shells.
|
||||
with the foreground service now counting transfers as well as shells — and, since, an idle-but-connected
|
||||
Files session as well, which a transfer count alone was blind to. *Corrected the same round:* the service's
|
||||
other half — a shell's own opening — had never been wired to anything at all, so a shell survived only for
|
||||
as long as the app stayed foreground; see [Sessions survive backgrounding](#sessions-survive-backgrounding-via-a-foreground-service).
|
||||
|
||||
**What was actually checked**, so the rest can be read with the right amount of trust:
|
||||
|
||||
@@ -280,13 +283,40 @@ What is desktop-only is the *left* pane — `LocalDirectory`, the drive list, th
|
||||
|
||||
### Sessions survive backgrounding, via a foreground service
|
||||
|
||||
A persistent notification for as long as a shell or a transfer is live.
|
||||
A persistent notification for as long as a shell, a transfer, or an idle-but-connected Files session is
|
||||
live.
|
||||
|
||||
It costs the user a notification and some battery. It buys the behaviour the desktop client already promises
|
||||
and documents — that a shell outlives a vault lock, and that a transfer finishes — and the alternative was
|
||||
to make `TerminalWorkspace`'s guarantee desktop-only, which is a worse thing to have to write down than a
|
||||
notification is to look at.
|
||||
|
||||
**Three corrections found after the first cut shipped, all in the wiring rather than the design:**
|
||||
|
||||
- **A shell opening never started the service.** `SessionKeepAlive` heard `TerminalWorkspace.SessionEnded`
|
||||
and refreshed on that, but nothing announced the opposite event — so a user who opened a shell and
|
||||
backgrounded the app immediately had no foreground service at all, and Android was free to kill the
|
||||
process holding it. `MainWindowViewModel.TerminalSessionOpened` is now wired the same way in
|
||||
`App.axaml.cs`'s `ComposeKeepAlive`.
|
||||
- **A connected-but-idle Files session counted as nothing.** A host open on the Files screen with no
|
||||
transfer moving is a live SFTP connection a dying process would sever, and the old two-argument
|
||||
`Reconcile(liveSessions, activeTransfers)` had no way to hear about it. `TransfersViewModel.HasLiveFileSession`
|
||||
— `IsConnected` with a real `ConnectedCipher`, which a bucket never has — is the third fact `Reconcile` now
|
||||
takes.
|
||||
- **Refreshing the notification restarted the service, which throws when backgrounded.** `Reconcile` called
|
||||
`StartForegroundService` on every refresh, including the common case of a service that was already
|
||||
running. On API 31+ that throws `ForegroundServiceStartNotAllowedException` the instant the app is
|
||||
backgrounded — a transfer finishing in the pocket, one of two shells dying — which crashed the process and
|
||||
took every session with it. `SessionForegroundService` now tracks whether it is already running and, when
|
||||
it is, posts the updated notification through `NotificationManager.Notify` instead of asking Android to
|
||||
start anything.
|
||||
|
||||
**The notification permission is requested, not just declared.** API 33+ requires `POST_NOTIFICATIONS` at
|
||||
runtime or the receipt is silently invisible — the service still runs, but nothing on screen says so.
|
||||
`SessionForegroundService.Reconcile` asks for it the first time in this process there is actually something
|
||||
to show, at most once, with no result read back: a refusal costs the notification and nothing else, which is
|
||||
what the manifest's own comment on the permission says.
|
||||
|
||||
### Phone first
|
||||
|
||||
About 360dp wide. The tablet route was cheaper — a landscape tablet is close to the existing 880×560 minimum
|
||||
@@ -523,7 +553,12 @@ go at 360dp:
|
||||
stopping it from a count rather than a lifecycle. `TerminalWorkspace.LiveSessionCount` is the source of
|
||||
truth deliberately: it already knows that a session whose shell exited is not live, which a counter
|
||||
incremented on open would not, and a phone showing "1 shell connected" over nothing would be exactly the
|
||||
dishonesty the unlock screen's count exists to prevent.
|
||||
dishonesty the unlock screen's count exists to prevent. *Corrected since:* the opened half of a shell's
|
||||
lifecycle was never wired in, so the service could never come up for a shell at all; an idle-but-connected
|
||||
Files session now counts as a third live fact rather than nothing; a refresh while backgrounded updates
|
||||
the notification in place instead of restarting the service, which the API throws on; and
|
||||
`POST_NOTIFICATIONS` is now actually requested rather than merely declared. See
|
||||
[Sessions survive backgrounding](#sessions-survive-backgrounding-via-a-foreground-service) for all four.
|
||||
7. ~~**The interface**, phone-first.~~ **Done for the decided scope** — all seven screens of the design,
|
||||
plus the two states the design does not draw because it starts at an enrolled phone (naming a server, and
|
||||
choosing a passphrase).
|
||||
|
||||
Reference in New Issue
Block a user