fix: notification Accept opens CallActivity directly (Android 12+)
Android 12+ blocks starting activities from BroadcastReceivers used as notification trampolines. The Accept action now uses PendingIntent.getActivity to launch CallActivity directly with EXTRA_ACCEPT_CALL. CallActivity.onCreate/onNewIntent checks this extra and accepts the incoming call. The Reject action remains as a BroadcastReceiver since it doesn't need to start an activity. https://claude.ai/code/session_01Ak5tTkujpjNG1r5ASuPipZ
This commit is contained in:
+6
-3
@@ -561,13 +561,16 @@ object NotificationUtils {
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
)
|
||||
|
||||
// Accept launches CallActivity directly (not via BroadcastReceiver)
|
||||
// to comply with Android 12+ notification trampoline restrictions.
|
||||
val acceptIntent =
|
||||
Intent(applicationContext, com.vitorpamplona.amethyst.ui.call.CallNotificationReceiver::class.java).apply {
|
||||
action = com.vitorpamplona.amethyst.ui.call.CallNotificationReceiver.ACTION_ACCEPT_CALL
|
||||
Intent(applicationContext, com.vitorpamplona.amethyst.ui.call.CallActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
putExtra(com.vitorpamplona.amethyst.ui.call.CallActivity.EXTRA_ACCEPT_CALL, true)
|
||||
}
|
||||
|
||||
val acceptPendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
PendingIntent.getActivity(
|
||||
applicationContext,
|
||||
CALL_NOTIFICATION_ID + 1,
|
||||
acceptIntent,
|
||||
|
||||
@@ -97,6 +97,7 @@ class CallActivity : AppCompatActivity() {
|
||||
|
||||
registerPipReceiver()
|
||||
observeVideoStateForPip(callController)
|
||||
handleAcceptCallIntent(intent)
|
||||
|
||||
setContent {
|
||||
AmethystTheme {
|
||||
@@ -111,6 +112,27 @@ class CallActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
handleAcceptCallIntent(intent)
|
||||
}
|
||||
|
||||
private fun handleAcceptCallIntent(intent: Intent?) {
|
||||
if (intent?.getBooleanExtra(EXTRA_ACCEPT_CALL, false) != true) return
|
||||
// Clear the extra so it doesn't re-trigger on config changes
|
||||
intent.removeExtra(EXTRA_ACCEPT_CALL)
|
||||
|
||||
com.vitorpamplona.amethyst.service.notifications.NotificationUtils
|
||||
.cancelCallNotification(this)
|
||||
|
||||
val callController = ActiveCallHolder.callController ?: return
|
||||
val callManager = ActiveCallHolder.callManager ?: return
|
||||
val state = callManager.state.value
|
||||
if (state is CallState.IncomingCall) {
|
||||
callController.acceptIncomingCall(state.sdpOffer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUserLeaveHint() {
|
||||
super.onUserLeaveHint()
|
||||
enterPipIfActive()
|
||||
@@ -274,6 +296,7 @@ class CallActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_ACCEPT_CALL = "com.vitorpamplona.amethyst.EXTRA_ACCEPT_CALL"
|
||||
private const val ACTION_PIP_HANGUP = "com.vitorpamplona.amethyst.PIP_HANGUP"
|
||||
private const val ACTION_PIP_TOGGLE_MUTE = "com.vitorpamplona.amethyst.PIP_TOGGLE_MUTE"
|
||||
private const val PIP_HANGUP_REQUEST_CODE = 0x60001
|
||||
|
||||
+6
-16
@@ -23,12 +23,17 @@ package com.vitorpamplona.amethyst.ui.call
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.vitorpamplona.amethyst.commons.call.CallState
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Handles the Reject action from the incoming call notification.
|
||||
*
|
||||
* The Accept action launches [CallActivity] directly via PendingIntent.getActivity
|
||||
* to comply with Android 12+ notification trampoline restrictions.
|
||||
*/
|
||||
class CallNotificationReceiver : BroadcastReceiver() {
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
override fun onReceive(
|
||||
@@ -36,20 +41,6 @@ class CallNotificationReceiver : BroadcastReceiver() {
|
||||
intent: Intent,
|
||||
) {
|
||||
when (intent.action) {
|
||||
ACTION_ACCEPT_CALL -> {
|
||||
NotificationUtils.cancelCallNotification(context)
|
||||
|
||||
val callController = ActiveCallHolder.callController
|
||||
val callManager = ActiveCallHolder.callManager
|
||||
if (callController == null || callManager == null) return
|
||||
|
||||
val state = callManager.state.value
|
||||
if (state is CallState.IncomingCall) {
|
||||
callController.acceptIncomingCall(state.sdpOffer)
|
||||
CallActivity.launch(context)
|
||||
}
|
||||
}
|
||||
|
||||
ACTION_REJECT_CALL -> {
|
||||
NotificationUtils.cancelCallNotification(context)
|
||||
|
||||
@@ -62,7 +53,6 @@ class CallNotificationReceiver : BroadcastReceiver() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ACTION_ACCEPT_CALL = "com.vitorpamplona.amethyst.ACCEPT_CALL"
|
||||
const val ACTION_REJECT_CALL = "com.vitorpamplona.amethyst.REJECT_CALL"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user