forked from eatvio/eatvio_app
feat(video_calls): ios calle mic fix, infinite loading after accepting call on an oppened app fix, incaming call declining after declining on a different device
This commit is contained in:
@@ -13,14 +13,12 @@ object CallKitAcceptIntentMapper {
|
||||
return null
|
||||
}
|
||||
|
||||
return hashMapOf(
|
||||
"action" to action,
|
||||
"id" to data.getString(CallkitConstants.EXTRA_CALLKIT_ID, ""),
|
||||
"handle" to data.getString(CallkitConstants.EXTRA_CALLKIT_HANDLE, ""),
|
||||
"nameCaller" to data.getString(CallkitConstants.EXTRA_CALLKIT_NAME_CALLER, ""),
|
||||
"avatar" to data.getString(CallkitConstants.EXTRA_CALLKIT_AVATAR, ""),
|
||||
"extra" to data.getSerializable(CallkitConstants.EXTRA_CALLKIT_EXTRA),
|
||||
)
|
||||
return payloadMap(data, action)
|
||||
}
|
||||
|
||||
fun declinePayloadFromBundle(data: Bundle, action: String): Map<String, Any?>? {
|
||||
if (!action.contains(CallkitConstants.ACTION_CALL_DECLINE)) return null
|
||||
return payloadMap(data, action)
|
||||
}
|
||||
|
||||
fun payloadFromIntent(intent: Intent?): Map<String, Any?>? {
|
||||
@@ -35,6 +33,16 @@ object CallKitAcceptIntentMapper {
|
||||
return null
|
||||
}
|
||||
|
||||
return payloadMap(data, action)
|
||||
}
|
||||
|
||||
fun clearCallKitLaunchIntent(intent: Intent?) {
|
||||
if (intent == null) return
|
||||
intent.removeExtra(FlutterCallkitIncomingPlugin.EXTRA_CALLKIT_CALL_DATA)
|
||||
intent.action = null
|
||||
}
|
||||
|
||||
private fun payloadMap(data: Bundle, action: String): Map<String, Any?> {
|
||||
return hashMapOf(
|
||||
"action" to action,
|
||||
"id" to data.getString(CallkitConstants.EXTRA_CALLKIT_ID, ""),
|
||||
@@ -44,10 +52,4 @@ object CallKitAcceptIntentMapper {
|
||||
"extra" to data.getSerializable(CallkitConstants.EXTRA_CALLKIT_EXTRA),
|
||||
)
|
||||
}
|
||||
|
||||
fun clearCallKitLaunchIntent(intent: Intent?) {
|
||||
if (intent == null) return
|
||||
intent.removeExtra(FlutterCallkitIncomingPlugin.EXTRA_CALLKIT_CALL_DATA)
|
||||
intent.action = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ object CallKitIntentBridge {
|
||||
CallKitAcceptIntentMapper.payloadFromBundle(data, action)?.let { payload ->
|
||||
PendingCallKitAcceptBridge.store(payload)
|
||||
}
|
||||
CallKitAcceptIntentMapper.declinePayloadFromBundle(data, action)?.let { payload ->
|
||||
PendingCallKitDeclineBridge.store(payload)
|
||||
if (MainActivity.isInForeground) {
|
||||
MainActivity.onPendingDecline?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val broadcastIntent = CallkitIncomingBroadcastReceiver.getIntent(context, action, data)
|
||||
|
||||
@@ -25,6 +25,9 @@ class MainActivity: FlutterActivity() {
|
||||
@JvmStatic
|
||||
var isInForeground: Boolean = false
|
||||
private set
|
||||
|
||||
@JvmStatic
|
||||
var onPendingDecline: (() -> Unit)? = null
|
||||
}
|
||||
|
||||
private val channelName = "com.eatvioapp/hms_push"
|
||||
@@ -38,13 +41,18 @@ class MainActivity: FlutterActivity() {
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
isInForeground = true
|
||||
onPendingDecline = { scheduleDeliverPendingCallKitDecline() }
|
||||
captureCallKitLaunchIntent(intent)
|
||||
if (PendingCallKitAcceptBridge.peek() != null) {
|
||||
scheduleDeliverPendingCallKitAccept()
|
||||
}
|
||||
if (PendingCallKitDeclineBridge.peek() != null) {
|
||||
scheduleDeliverPendingCallKitDecline()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
onPendingDecline = null
|
||||
isInForeground = false
|
||||
super.onPause()
|
||||
}
|
||||
@@ -55,6 +63,7 @@ class MainActivity: FlutterActivity() {
|
||||
methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channelName)
|
||||
videoCallPipChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, videoCallPipChannelName)
|
||||
videoCallPushChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, videoCallPushChannelName)
|
||||
onPendingDecline = { scheduleDeliverPendingCallKitDecline() }
|
||||
videoCallPushChannel?.setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"dismissIncomingCalls" -> {
|
||||
@@ -95,6 +104,9 @@ class MainActivity: FlutterActivity() {
|
||||
"peekPendingCallKitAccept" -> {
|
||||
result.success(PendingCallKitAcceptBridge.peekPending())
|
||||
}
|
||||
"drainPendingCallKitDecline" -> {
|
||||
result.success(PendingCallKitDeclineBridge.drain())
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
@@ -250,6 +262,30 @@ class MainActivity: FlutterActivity() {
|
||||
channel.invokeMethod("onCallKitLaunchIntent", payload)
|
||||
}
|
||||
|
||||
private fun scheduleDeliverPendingCallKitDecline() {
|
||||
PendingCallKitDeclineBridge.scheduleDeliverToFlutter(
|
||||
deliver = { deliverPendingCallKitDeclineToFlutter() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun deliverPendingCallKitDeclineToFlutter() {
|
||||
val payload = PendingCallKitDeclineBridge.peek() ?: return
|
||||
val channel = videoCallPushChannel
|
||||
if (channel == null) {
|
||||
android.util.Log.w(
|
||||
"VideoCallPush",
|
||||
"deliverPendingCallKitDeclineToFlutter: channel not ready",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
android.util.Log.i(
|
||||
"VideoCallPush",
|
||||
"deliverPendingCallKitDeclineToFlutter: id=${payload["id"]}",
|
||||
)
|
||||
channel.invokeMethod("onCallKitDecline", payload)
|
||||
}
|
||||
|
||||
private fun handleIntentForActionUrl(intent: Intent?) {
|
||||
if (intent == null) return
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.eatvioapp.app
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
|
||||
/// Stores a decline until Flutter can POST /video-call/decline.
|
||||
///
|
||||
/// The plugin broadcasts the decline. If the Flutter engine is not attached,
|
||||
/// that broadcast never reaches the backend and the call keeps ringing on the
|
||||
/// user's other devices.
|
||||
object PendingCallKitDeclineBridge {
|
||||
private const val TAG = "PendingCallKitDecline"
|
||||
|
||||
@Volatile
|
||||
private var pendingPayload: Map<String, Any?>? = null
|
||||
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
fun store(payload: Map<String, Any?>) {
|
||||
pendingPayload = payload
|
||||
Log.i(TAG, "stored pending decline id=${payload["id"]}")
|
||||
}
|
||||
|
||||
fun peek(): Map<String, Any?>? = pendingPayload
|
||||
|
||||
fun drain(): Map<String, Any?>? {
|
||||
val payload = pendingPayload
|
||||
pendingPayload = null
|
||||
if (payload != null) {
|
||||
Log.i(TAG, "drained pending decline id=${payload["id"]}")
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
fun scheduleDeliverToFlutter(
|
||||
deliver: () -> Unit,
|
||||
delaysMs: LongArray = longArrayOf(0L, 250L, 700L, 1500L, 2800L, 4200L, 6000L),
|
||||
) {
|
||||
for (delayMs in delaysMs) {
|
||||
mainHandler.postDelayed({ deliver() }, delayMs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -558,7 +558,10 @@ object VideoCallPushBridge {
|
||||
if (extraCallId != null && extraCallId.lowercase() == normalizedCallId) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
// A ring that already stores its own call id must not be closed by
|
||||
// another call in the same chat. Legacy rings have no call id and
|
||||
// still match the conversation.
|
||||
if (!extraCallId.isNullOrBlank()) return false
|
||||
}
|
||||
|
||||
val normalizedConversation = conversationUuid?.trim()?.lowercase() ?: return false
|
||||
|
||||
Reference in New Issue
Block a user