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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
93d7eaa980
commit
63f2758d46
@@ -1,5 +1,7 @@
|
|||||||
package io.flamenet.messenger.app
|
package io.flamenet.messenger.app
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.viewModels
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
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.
|
// The handover: exactly one poller at a time.
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
MessengerService.stop(this)
|
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() {
|
override fun onStop() {
|
||||||
|
|||||||
@@ -310,6 +310,12 @@ class MessengerService : Service() {
|
|||||||
// accident while reaching for the torch.
|
// accident while reaching for the torch.
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setAutoCancel(false)
|
.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)
|
.setFullScreenIntent(full, true)
|
||||||
.setStyle(NotificationCompat.CallStyle.forIncomingCall(caller, decline, full))
|
.setStyle(NotificationCompat.CallStyle.forIncomingCall(caller, decline, full))
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -3430,6 +3430,32 @@ class MessengerViewModel(
|
|||||||
* arrived, `CallManager` moved to RINGING, and the only notification posted
|
* arrived, `CallManager` moved to RINGING, and the only notification posted
|
||||||
* was the service's own "running" notice.
|
* 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() {
|
fun pausePolling() {
|
||||||
polling?.cancel()
|
polling?.cancel()
|
||||||
polling = null
|
polling = null
|
||||||
|
|||||||
Reference in New Issue
Block a user