Thin the app's comments

The same pass the server had, on the Kotlin side: comments restating what
the code says are gone, and the ones recording a measurement, a constraint
or an incident are kept but cut to a few lines each. 6540 comment lines to
5674, and 920 lines off the app.

Two doc comments had drifted onto the item above the one they describe --
`contextAfter`'s onto `sessionWorking` in Events.kt, and `UsageMonitor`'s
equivalent on the server was fixed in the previous commit. Each is back on
its own item, which is the only non-comment line this diff moves.

The comments are reflowed to the column limit at their own indentation:
several were written wide, and ktfmt re-wrapped them into lines holding a
single orphan word. `/tmp` script, not kept -- ktfmt is idempotent over the
result, which is the check.

Left alone deliberately: this codebase's remaining comment density is high
because the comments carry things the code cannot say -- what a null means,
what a number was measured against, which bug a guard exists for. Of the
238 one-line doc comments in the app, five were pure restatement of the
name and were removed; the rest each say something the signature does not.

ktfmtFormat, compileDebugKotlin, lintDebug and testDebugUnitTest pass;
cargo test (127), clippy --all-targets and fmt still clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-04 16:20:16 -04:00
1 parent 79682f03a7
commit edc39c7371
68 files changed
+2077 -2997

No files matched your search

@@ -34,12 +34,11 @@ import org.json.JSONObject
* gets a push from Google's servers, which would mean this backend talking to Google about
* somebody's coding sessions, and the whole point of the tunnel is that it does not.
*
* The cost Android charges for it is a notification of its own that cannot be dismissed. That is
* made as quiet as the platform allows: [ONGOING_CHANNEL] is `IMPORTANCE_MIN`, so it makes no
* sound, shows no status-bar icon, and sits at the bottom of the shade -- the same arrangement
* Syncthing's "hide the persistent notification" option produces. It is not hidden outright,
* because it cannot be and because it should not be: it is the honest indicator that something is
* holding a connection open.
* The cost Android charges is a notification of its own that cannot be dismissed. That is made as
* quiet as the platform allows: [ONGOING_CHANNEL] is `IMPORTANCE_MIN`, so it makes no sound, shows
* no status-bar icon, and sits at the bottom of the shade. It is not hidden outright, because it
* cannot be and because it should not be: it is the honest indicator that something is holding a
* connection open.
*/
class NotificationService : Service() {
@Volatile private var stream: HttpURLConnection? = null
@@ -50,18 +49,18 @@ class NotificationService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val settings = loadServerSettings(this)
if (settings == null) {
// Nothing to connect to. Stopping rather than idling: a service
// holding no connection still costs the ongoing notification,
// which would then be announcing work that is not happening.
// Nothing to connect to. Stopping rather than idling: a service holding no connection
// still costs the ongoing notification, which would be announcing work that is not
// happening.
stopSelf()
return START_NOT_STICKY
}
// Through ServiceCompat so the type is stated once and ignored on
// the versions that predate types, rather than branching here.
// Through ServiceCompat so the type is stated once and ignored on the versions that predate
// types, rather than branching here.
ServiceCompat.startForeground(this, ONGOING_ID, ongoingNotification(), foregroundType())
thread(isDaemon = true, name = "ai-app-notifications") { follow(settings) }
// Restarted if Android kills it, which is the whole point: the
// window this covers is exactly the one where nobody is watching.
// Restarted if Android kills it, which is the whole point: the window this covers is
// exactly the one where nobody is watching.
return START_STICKY
}
@@ -73,11 +72,10 @@ class NotificationService : Service() {
/**
* Follows the backend's notification stream, reconnecting until stopped.
*
* A dropped connection is the ordinary case here rather than an error -- a phone changes
* networks, the tunnel comes and goes, the backend restarts -- so it retries quietly and
* forever. Nothing is shown when it cannot connect: a notification saying "I could not tell you
* whether anything happened" on a phone in somebody's pocket is noise about a condition they
* cannot act on, and the session list already says what is waiting when they next look.
* A dropped connection is the ordinary case here rather than an error, so it retries quietly
* and forever. Nothing is shown when it cannot connect: a notification saying "I could not tell
* you whether anything happened" is noise about a condition nobody can act on, and the session
* list already says what is waiting when they next look.
*/
private fun follow(settings: ServerSettings) {
while (!stopping) {
@@ -102,8 +100,8 @@ class NotificationService : Service() {
try {
connection.applyPinnedTls()
connection.connectTimeout = CONNECT_TIMEOUT_MS
// No read timeout, for the reason EventStream gives: between
// notifications there is nothing to read, possibly for hours.
// No read timeout, for the reason EventStream gives: between notifications there is
// nothing to read, possibly for hours.
connection.readTimeout = 0
connection.setRequestProperty("Authorization", "Bearer ${settings.token}")
connection.setRequestProperty("Accept", "text/event-stream")
@@ -134,28 +132,23 @@ class NotificationService : Service() {
*
* Keyed by session id rather than accumulating: two sessions wanting attention are two things
* to know about, but one session that finished and then asked a question is one thing -- the
* question. A stack of stale rows for the same conversation is how a notification drawer
* becomes something to clear rather than read.
* question. A stack of stale rows is how a drawer becomes something to clear rather than read.
*/
private fun show(notification: SessionNotification) {
// Nothing to tell somebody about the session they are reading. The transcript in front of
// them is already saying it, and a sound over the top of it would be this app announcing
// what the screen is showing.
// them is already saying it.
if (isOnScreen(notification.sessionId)) return
// The app is up: it says this itself, as a banner over whatever screen they are on. See
// [forTheScreen]. Never both -- one thing happened, and a drawer filling up behind an
// app that already showed you each one is a drawer nobody reads.
// The app is up: it says this itself, as a banner over whatever screen they are on. Never
// both -- one thing happened, and a drawer filling up behind an app that already showed you
// each one is a drawer nobody reads.
if (handOver(notification)) return
val manager = NotificationManagerCompat.from(this)
// Two different noes, and both are answers rather than faults: the runtime permission
// refused, and notifications switched off for the app in Android's own settings. Neither
// is reported anywhere -- the person said no, and saying it back to them through the
// channel they closed is not available anyway.
// refused, and notifications switched off for the app in Android's own settings.
//
// The permission only exists from Android 13. Asking an older version about it gets
// "denied" for a name it does not know, which read as the person having said no -- so
// every notification on Android 12 and below was silently dropped. Before 13 the
// switch in Android's own settings, checked below, is the whole of the answer.
// "denied" for a name it does not know, which read as the person having said no -- so every
// notification on Android 12 and below was silently dropped.
val allowed =
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) ==
@@ -187,9 +180,8 @@ class NotificationService : Service() {
* The type Android 14+ requires a foreground service to declare, and nothing before it.
*
* Named behind a version check rather than passed as a constant: the value is inlined at
* compile time and would be handed to platforms that have no concept of it, which is exactly
* the case lint's InlinedApi exists to catch. Zero is what ServiceCompat wants where types do
* not apply.
* compile time and would be handed to platforms that have no concept of it, which is what
* lint's InlinedApi exists to catch.
*/
private fun foregroundType(): Int =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -226,11 +218,10 @@ class NotificationService : Service() {
/**
* Two channels, because they are two different things to be told.
*
* The alerts are what somebody turned this on for, so they get the default importance and
* whatever sound and heads-up display the person has chosen for the app. The ongoing one is
* the platform's tax for staying connected, so it takes the lowest importance that exists.
* Both are created before the service starts, since posting to a channel that does not
* exist is silently dropped.
* The alerts are what somebody turned this on for, so they get the default importance. The
* ongoing one is the platform's tax for staying connected, so it takes the lowest
* importance that exists. Both are created before the service starts, since posting to a
* channel that does not exist is silently dropped.
*/
private fun createChannels(context: Context) {
val manager = NotificationManagerCompat.from(context)
@@ -256,12 +247,10 @@ class NotificationService : Service() {
* The session somebody is looking at, or null when no screen is showing one.
*
* Process-wide state, which the rest of this app does without: Android constructs the
* service and the composition draws the screen, so the two have no common owner a value
* could be passed through. [showing] and [stoppedShowing] are the pair, both called from
* the one composable that shows a session. Clearing names the session rather than setting
* null outright, because moving from one session to another composes the new screen before
* the old one's coroutine is cancelled -- an unconditional clear would then throw away the
* new screen's claim and start notifying about what is on it.
* service and the composition draws the screen, so the two have no common owner. Clearing
* names the session rather than setting null outright, because moving from one session to
* another composes the new screen before the old one's coroutine is cancelled -- an
* unconditional clear would throw away the new screen's claim.
*/
@Volatile private var onScreen: String? = null
@@ -271,10 +260,9 @@ class NotificationService : Service() {
* The way a notification reaches the app instead of Android's drawer.
*
* Whether there is an app to reach is the subscriber count rather than a flag of its own:
* [SessionAlerts] collects this exactly while it is on screen, so there is nothing that
* could be left saying the app is up after it has gone. `tryEmit` neither suspends nor
* blocks the thread reading the stream, and the buffer is there so a handful of sessions
* finishing together all land rather than the last one winning.
* [SessionAlerts] collects this exactly while it is on screen. `tryEmit` neither suspends
* nor blocks the thread reading the stream, and the buffer is there so a handful of
* sessions finishing together all land rather than the last one winning.
*/
private val toApp = MutableSharedFlow<SessionNotification>(extraBufferCapacity = 8)
@@ -287,9 +275,8 @@ class NotificationService : Service() {
/** Somebody is looking at [sessionId]; nothing is posted about it until they stop. */
fun showing(context: Context, sessionId: String) {
onScreen = sessionId
// Whatever was posted about it before is about to be read, so it has nothing left
// to say -- and a row in the drawer for the conversation on screen is the same
// duplication this whole rule is about.
// Whatever was posted about it before is about to be read, so it has nothing left to
// say.
NotificationManagerCompat.from(context).cancel(sessionId, ALERT_ID)
}
@@ -311,8 +298,8 @@ class NotificationService : Service() {
* The intent that opens one session, and the id it carries back out.
*
* The two halves are written together so neither can be changed without the other, and the scheme
* is enrollment's `aiapp://` under a different host so that [MainActivity] has one thing to look at
* when an intent arrives rather than two.
* is enrollment's `aiapp://` under a different host so that [MainActivity] has one thing to look
* at.
*
* The id rides in the intent's **data** rather than in an extra, which is not a style choice:
* PendingIntent identity is `Intent.filterEquals`, and that compares the data while ignoring
@@ -345,9 +332,8 @@ data class SessionNotification(
* What a notification asks of the reader, in the words they see.
*
* What they have to do, not what the session did: "awaitingInput" is the wire's word and says
* nothing to somebody reading a lock screen. One function because the same fact is now shown in two
* places -- Android's drawer and the app's own banner -- and two mappings of one word drift. The
* banner colours the line as well, which is its own decision and stays with the drawing.
* nothing to somebody reading a lock screen. One function because the same fact is shown in two
* places -- Android's drawer and the app's own banner -- and two mappings of one word drift.
*/
fun attentionLine(kind: String): String =
when (kind) {