From 63f2758d4648ea72f63bf3ceb001daa9593c1a79 Mon Sep 17 00:00:00 2001 From: Paul Hitt Date: Thu, 10 Sep 2026 13:03:04 -0400 Subject: [PATCH] Accept on the call notification answers the call The notification's Accept button has carried EXTRA_ANSWER_CALL since it was written, and nothing ever read it. Pressing Accept did what any other tap did: it opened the app. Answering was left to the next turn of the polling loop, which refuses a parked offer older than the caller's patience, so a call taken a little late opened the app onto nothing and looked like it had dropped the moment you answered it. MainActivity reads the extra now, on a cold start and on a warm one, and answers at once rather than waiting for a tick. The notification also goes away on its own when the offer it shows has expired. Before, it could outlive the offer by the gap between service sweeps, which is longer since the loop started holding its poll open, and during that gap it was a button that could only disappoint. Seen on the emulator: Accept pressed, app opened, nothing on screen. Co-Authored-By: Claude Fable 5.1 --- .../io/flamenet/messenger/app/MainActivity.kt | 28 +++++++++++++++++++ .../messenger/app/MessengerService.kt | 6 ++++ .../messenger/app/MessengerViewModel.kt | 26 +++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/app/src/main/kotlin/io/flamenet/messenger/app/MainActivity.kt b/app/src/main/kotlin/io/flamenet/messenger/app/MainActivity.kt index 93818c1..5d6eb5a 100644 --- a/app/src/main/kotlin/io/flamenet/messenger/app/MainActivity.kt +++ b/app/src/main/kotlin/io/flamenet/messenger/app/MainActivity.kt @@ -1,5 +1,7 @@ package io.flamenet.messenger.app +import android.content.Intent +import androidx.activity.viewModels import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -152,10 +154,36 @@ class MainActivity : androidx.fragment.app.FragmentActivity() { } } + private val model: MessengerViewModel by viewModels() + + /** + * Accept, pressed on the notification, has to answer the call. + * + * The intent has carried `EXTRA_ANSWER_CALL` since the notification was + * written and nothing read it, so pressing Accept only opened the app and + * left the answering to a polling tick that refuses a stale offer. The call + * then looked like it had dropped the moment you took it. + */ + private fun answerIfAsked(intent: Intent?) { + if (intent?.getBooleanExtra(MessengerService.EXTRA_ANSWER_CALL, false) == true) { + intent.removeExtra(MessengerService.EXTRA_ANSWER_CALL) + model.answerParkedCall() + } + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + setIntent(intent) + answerIfAsked(intent) + } + // The handover: exactly one poller at a time. override fun onStart() { super.onStart() MessengerService.stop(this) + // Also on the way in: a cold start from the notification arrives here + // rather than through onNewIntent. + answerIfAsked(intent) } override fun onStop() { diff --git a/app/src/main/kotlin/io/flamenet/messenger/app/MessengerService.kt b/app/src/main/kotlin/io/flamenet/messenger/app/MessengerService.kt index 1f1e50d..6ccbb26 100644 --- a/app/src/main/kotlin/io/flamenet/messenger/app/MessengerService.kt +++ b/app/src/main/kotlin/io/flamenet/messenger/app/MessengerService.kt @@ -310,6 +310,12 @@ class MessengerService : Service() { // accident while reaching for the torch. .setOngoing(true) .setAutoCancel(false) + // Goes away by itself when the offer it is showing has expired. + // `take` refuses a parked offer older than the caller's own + // patience, so a notification that outlived that was a button + // which opened the app onto nothing and read as a dropped call. + // Android cancels this without needing the service to notice. + .setTimeoutAfter(CallManager.DIAL_TIMEOUT_MS) .setFullScreenIntent(full, true) .setStyle(NotificationCompat.CallStyle.forIncomingCall(caller, decline, full)) .build() diff --git a/app/src/main/kotlin/io/flamenet/messenger/app/MessengerViewModel.kt b/app/src/main/kotlin/io/flamenet/messenger/app/MessengerViewModel.kt index d0c8f51..e222c9f 100644 --- a/app/src/main/kotlin/io/flamenet/messenger/app/MessengerViewModel.kt +++ b/app/src/main/kotlin/io/flamenet/messenger/app/MessengerViewModel.kt @@ -3430,6 +3430,32 @@ class MessengerViewModel( * arrived, `CallManager` moved to RINGING, and the only notification posted * was the service's own "running" notice. */ + /** + * Answer the call the notification is showing, now. + * + * The notification's Accept button has always carried + * `EXTRA_ANSWER_CALL`, and nothing ever read it. Tapping Accept therefore + * did what any other tap did: it opened the app. Answering was left to the + * next turn of the polling loop, which refuses a parked offer older than + * the caller's patience, so a call answered a little late opened the app + * onto nothing and looked like it had dropped. + */ + fun answerParkedCall() { + val parked = PendingIncomingCall.take() ?: return + runCatching { + androidx.core.app.NotificationManagerCompat + .from(getApplication()) + .cancel(3) + } + viewModelScope.launch(Dispatchers.IO) { + val manager = callManager() + val turn = session?.turnCredentials() + manager.onSignal(parked.fromUser, parked.signal, turn, parked.at) + // The offer only sets it ringing; this is the answer itself. + manager.accept(turn) + } + } + fun pausePolling() { polling?.cancel() polling = null