From 1e841e03f65142573cd931b75283cebcf5c07d04 Mon Sep 17 00:00:00 2001
From: cogwheel0 <172976095+cogwheel0@users.noreply.github.com>
Date: Wed, 26 Nov 2025 20:09:34 +0530
Subject: [PATCH] feat(voip): add CallKit availability check for iOS devices
---
ios/Podfile.lock | 2 +-
ios/Runner/Info.plist | 1 +
lib/core/services/callkit_service.dart | 63 ++++++++++++++++++-
.../chat/services/voice_call_service.dart | 11 +++-
4 files changed, 72 insertions(+), 5 deletions(-)
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 8c338a7..1365e15 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -225,6 +225,6 @@ SPEC CHECKSUMS:
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
webview_flutter_wkwebview: 8ebf4fded22593026f7dbff1fbff31ea98573c8d
-PODFILE CHECKSUM: 1f8874f58051a502e2f506dc2d4737781aa1edde
+PODFILE CHECKSUM: 32adf4606dae7e9ca2351c13b9e3ce1df6ad7ebf
COCOAPODS: 1.16.2
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index ff92f4b..7b72d23 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -91,6 +91,7 @@
audio
processing
+ voip
BGTaskSchedulerPermittedIdentifiers
diff --git a/lib/core/services/callkit_service.dart b/lib/core/services/callkit_service.dart
index 3334a8b..61c452f 100644
--- a/lib/core/services/callkit_service.dart
+++ b/lib/core/services/callkit_service.dart
@@ -1,4 +1,6 @@
import 'dart:developer' as developer;
+import 'dart:io';
+import 'dart:ui' as ui;
import 'package:flutter_callkit_incoming/entities/entities.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
@@ -9,13 +11,22 @@ part 'callkit_service.g.dart';
/// Thin wrapper around `flutter_callkit_incoming` for voice calls.
class CallKitService {
- CallKitService({Uuid? uuid}) : _uuid = uuid ?? const Uuid();
+ CallKitService({Uuid? uuid})
+ : _uuid = uuid ?? const Uuid(),
+ _callKitAllowed = _computeCallKitAllowed();
final Uuid _uuid;
+ final bool _callKitAllowed;
+ bool _loggedCallKitDisabled = false;
static const int _defaultCallDurationMs = 2 * 60 * 60 * 1000; // 2 hours
+ /// Returns whether CallKit can be used on this device/region.
+ bool get isAvailable => _callKitAllowed;
+
/// Requests the notification/full-screen intent permissions needed on Android.
Future requestPermissions() async {
+ if (!_shouldUseCallKit('request permissions')) return;
+
await _safe(
() => FlutterCallkitIncoming.requestNotificationPermission(
{
@@ -37,6 +48,8 @@ class CallKitService {
String? avatar,
int? durationMs,
}) async {
+ if (!_shouldUseCallKit('start call')) return null;
+
final id = _uuid.v4();
final params = _buildParams(
id: id,
@@ -61,6 +74,8 @@ class CallKitService {
/// Marks the current call as connected so iOS shows an incrementing timer.
Future markCallConnected(String id) async {
+ if (!_shouldUseCallKit('mark call connected')) return;
+
try {
await FlutterCallkitIncoming.setCallConnected(id);
} catch (error, stackTrace) {
@@ -75,16 +90,22 @@ class CallKitService {
/// Ends a specific call id.
Future endCall(String id) async {
+ if (!_shouldUseCallKit('end call')) return;
+
await _safe(() => FlutterCallkitIncoming.endCall(id));
}
/// Clears all ongoing/missed calls.
Future endAllCalls() async {
+ if (!_shouldUseCallKit('end all calls')) return;
+
await _safe(FlutterCallkitIncoming.endAllCalls);
}
/// Returns the platform VOIP token (iOS PushKit) when available.
Future getVoipToken() async {
+ if (!_shouldUseCallKit('fetch VoIP token')) return null;
+
final token = await _safe(
() => FlutterCallkitIncoming.getDevicePushTokenVoIP(),
);
@@ -95,6 +116,10 @@ class CallKitService {
/// Returns the raw active call list from the plugin.
Future>> activeCalls() async {
+ if (!_shouldUseCallKit('fetch active calls')) {
+ return