forked from eatvio/eatvio_app
feat(video_calls): fixed call ended push handling
This commit is contained in:
@@ -25,8 +25,10 @@ class MainActivity: FlutterActivity() {
|
||||
|
||||
private val channelName = "com.eatvioapp/hms_push"
|
||||
private val videoCallPipChannelName = "com.eatvioapp/video_call_pip"
|
||||
private val videoCallPushChannelName = "com.eatvioapp/video_call_push"
|
||||
private var methodChannel: MethodChannel? = null
|
||||
private var videoCallPipChannel: MethodChannel? = null
|
||||
private var videoCallPushChannel: MethodChannel? = null
|
||||
private var videoCallPipEnabled: Boolean = false
|
||||
|
||||
override fun onResume() {
|
||||
@@ -44,6 +46,20 @@ class MainActivity: FlutterActivity() {
|
||||
|
||||
methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channelName)
|
||||
videoCallPipChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, videoCallPipChannelName)
|
||||
videoCallPushChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, videoCallPushChannelName)
|
||||
videoCallPushChannel?.setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"dismissIncomingCalls" -> {
|
||||
try {
|
||||
VideoCallPushBridge.dismissAllIncomingCalls(applicationContext)
|
||||
result.success(null)
|
||||
} catch (e: Exception) {
|
||||
result.error("DISMISS_INCOMING_CALLS_FAILED", e.message, null)
|
||||
}
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
videoCallPipChannel?.setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"setPipEnabled" -> {
|
||||
|
||||
@@ -2,12 +2,17 @@ package com.eatvioapp.app
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.hiennv.flutter_callkit_incoming.CallkitConstants
|
||||
import com.hiennv.flutter_callkit_incoming.CallkitIncomingBroadcastReceiver
|
||||
import com.hiennv.flutter_callkit_incoming.CallkitNotificationManager
|
||||
import com.hiennv.flutter_callkit_incoming.CallkitNotificationService
|
||||
import com.hiennv.flutter_callkit_incoming.CallkitSoundPlayerManager
|
||||
import com.hiennv.flutter_callkit_incoming.Data
|
||||
import com.hiennv.flutter_callkit_incoming.FlutterCallkitIncomingPlugin
|
||||
import com.hiennv.flutter_callkit_incoming.addCall
|
||||
import com.hiennv.flutter_callkit_incoming.getDataActiveCalls
|
||||
import com.hiennv.flutter_callkit_incoming.removeAllCalls
|
||||
@@ -16,6 +21,10 @@ import java.util.HashMap
|
||||
object VideoCallPushBridge {
|
||||
private const val TAG = "VideoCallPushBridge"
|
||||
private const val ANDROID_ACCENT_COLOR = "#2787F5"
|
||||
private const val MISSED_CALL_CLEANUP_DELAY_MS = 250L
|
||||
|
||||
private var activeSoundManager: CallkitSoundPlayerManager? = null
|
||||
private var activeNotificationManager: CallkitNotificationManager? = null
|
||||
|
||||
fun isIncomingVideoCall(data: Map<String, String>): Boolean {
|
||||
val rawType = data["type"]
|
||||
@@ -90,6 +99,8 @@ object VideoCallPushBridge {
|
||||
|
||||
val soundManager = CallkitSoundPlayerManager(appContext)
|
||||
val notificationManager = CallkitNotificationManager(appContext, soundManager)
|
||||
activeSoundManager = soundManager
|
||||
activeNotificationManager = notificationManager
|
||||
notificationManager.createNotificationChanel(callData)
|
||||
notificationManager.showIncomingNotification(callData)
|
||||
|
||||
@@ -105,28 +116,108 @@ object VideoCallPushBridge {
|
||||
val calls = getDataActiveCalls(appContext)
|
||||
Log.i(TAG, "dismissAllIncomingCalls: active calls=${calls.size}")
|
||||
|
||||
val soundManager = CallkitSoundPlayerManager(appContext)
|
||||
val notificationManager = CallkitNotificationManager(appContext, soundManager)
|
||||
stopIncomingRingtone(appContext)
|
||||
|
||||
for (call in calls) {
|
||||
val bundle = call.toBundle()
|
||||
val notificationManager = resolveNotificationManager(appContext)
|
||||
val bundles = calls.map { it.toBundle() }
|
||||
|
||||
for (bundle in bundles) {
|
||||
try {
|
||||
notificationManager.clearIncomingNotification(bundle, isAccepted = false)
|
||||
appContext.sendBroadcast(
|
||||
CallkitIncomingBroadcastReceiver.getIntentEnded(appContext, bundle),
|
||||
)
|
||||
dismissIncomingCallNotification(appContext, notificationManager, bundle)
|
||||
} catch (error: Exception) {
|
||||
Log.w(TAG, "dismissAllIncomingCalls: failed for call ${call.id}", error)
|
||||
Log.w(
|
||||
TAG,
|
||||
"dismissAllIncomingCalls: failed for call ${bundle.getString(CallkitConstants.EXTRA_CALLKIT_ID)}",
|
||||
error,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
CallkitNotificationService.stopService(appContext)
|
||||
} catch (error: Exception) {
|
||||
Log.w(TAG, "dismissAllIncomingCalls: failed to stop callkit service", error)
|
||||
}
|
||||
|
||||
try {
|
||||
removeAllCalls(appContext)
|
||||
} catch (error: Exception) {
|
||||
Log.w(TAG, "dismissAllIncomingCalls: failed to remove persisted calls", error)
|
||||
}
|
||||
|
||||
soundManager.stop()
|
||||
stopIncomingRingtone(appContext)
|
||||
scheduleMissedCallCleanup(notificationManager, bundles)
|
||||
activeSoundManager = null
|
||||
activeNotificationManager = null
|
||||
}
|
||||
|
||||
private fun resolveNotificationManager(context: Context): CallkitNotificationManager {
|
||||
activeNotificationManager?.let { return it }
|
||||
FlutterCallkitIncomingPlugin.getInstance()
|
||||
?.getCallkitNotificationManager()
|
||||
?.let { return it }
|
||||
|
||||
val soundManager = CallkitSoundPlayerManager(context)
|
||||
return CallkitNotificationManager(context, soundManager)
|
||||
}
|
||||
|
||||
private fun stopIncomingRingtone(context: Context) {
|
||||
activeSoundManager?.stop()
|
||||
FlutterCallkitIncomingPlugin.getInstance()
|
||||
?.getCallkitSoundPlayerManager()
|
||||
?.stop()
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the incoming notification before cancel so Android does not fire the
|
||||
* timeout delete intent (which would show a missed-call notification).
|
||||
*/
|
||||
private fun dismissIncomingCallNotification(
|
||||
context: Context,
|
||||
notificationManager: CallkitNotificationManager,
|
||||
bundle: Bundle,
|
||||
) {
|
||||
val notificationId = incomingNotificationId(bundle)
|
||||
|
||||
val silentNotification = NotificationCompat.Builder(
|
||||
context,
|
||||
CallkitNotificationManager.NOTIFICATION_CHANNEL_ID_INCOMING,
|
||||
)
|
||||
.setSmallIcon(context.applicationInfo.icon)
|
||||
.setContentTitle("")
|
||||
.setSilent(true)
|
||||
.setOngoing(false)
|
||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||
.build()
|
||||
|
||||
val notificationManagerCompat = NotificationManagerCompat.from(context)
|
||||
notificationManagerCompat.notify(notificationId, silentNotification)
|
||||
notificationManagerCompat.cancel(notificationId)
|
||||
|
||||
bundle.putBoolean(CallkitConstants.EXTRA_CALLKIT_MISSED_CALL_SHOW, false)
|
||||
notificationManager.clearIncomingNotification(bundle, isAccepted = true)
|
||||
notificationManager.clearMissCallNotification(bundle)
|
||||
}
|
||||
|
||||
private fun scheduleMissedCallCleanup(
|
||||
notificationManager: CallkitNotificationManager,
|
||||
bundles: List<Bundle>,
|
||||
) {
|
||||
if (bundles.isEmpty()) return
|
||||
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
for (bundle in bundles) {
|
||||
try {
|
||||
notificationManager.clearMissCallNotification(bundle)
|
||||
} catch (error: Exception) {
|
||||
Log.w(TAG, "dismissAllIncomingCalls: delayed missed-call cleanup failed", error)
|
||||
}
|
||||
}
|
||||
}, MISSED_CALL_CLEANUP_DELAY_MS)
|
||||
}
|
||||
|
||||
private fun incomingNotificationId(bundle: Bundle): Int {
|
||||
return bundle.getString(CallkitConstants.EXTRA_CALLKIT_ID, "callkit_incoming").hashCode()
|
||||
}
|
||||
|
||||
private fun buildCallDataBundle(data: Map<String, String>): Bundle? {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,8 +1,26 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
|
||||
|
||||
/// Dismisses incoming call UI after a remote `video_call_ended` push.
|
||||
abstract final class VideoCallEndedPushHandler {
|
||||
static const MethodChannel _androidPushChannel = MethodChannel(
|
||||
'com.eatvioapp/video_call_push',
|
||||
);
|
||||
|
||||
static Future<void> dismissIncomingCallUi() async {
|
||||
if (Platform.isAndroid) {
|
||||
try {
|
||||
await _androidPushChannel.invokeMethod<void>('dismissIncomingCalls');
|
||||
return;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
await _dismissViaCallKit();
|
||||
}
|
||||
|
||||
static Future<void> _dismissViaCallKit() async {
|
||||
try {
|
||||
final activeCalls = await FlutterCallkitIncoming.activeCalls();
|
||||
for (final call in activeCalls) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: eatvio_app
|
||||
description: A new Flutter project.
|
||||
version: 1.3.32+121
|
||||
version: 1.3.33+122
|
||||
publish_to: 'none'
|
||||
|
||||
environment:
|
||||
|
||||
Reference in New Issue
Block a user