Files
IKAR/lib/core/calls/call_bridge.dart
T
sevenhill 0508b70732
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Tests / test (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
Release IKAR 1.0.8: fix incoming calls, cancellation and call history
2026-10-08 21:34:15 +03:00

179 lines
4.6 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io' show Platform;
import 'package:flutter/services.dart';
import '../utils/logger.dart';
import 'call_controller.dart';
class CallBridge {
CallBridge._();
static final CallBridge instance = CallBridge._();
static const _method = MethodChannel('ru.ikar.app/calls');
static const _events = EventChannel('ru.ikar.app/calls_events');
bool _started = false;
bool get _android {
try {
return Platform.isAndroid;
} catch (_) {
return false;
}
}
void init() {
if (_started || !_android) return;
_started = true;
_events.receiveBroadcastStream().listen(
_handle,
onError: (e) => logger.w('CallBridge.init: events stream error: $e'),
);
}
Future<void> checkInitialCall() async {
if (!_android) return;
try {
_handle(await _method.invokeMethod<dynamic>('consumeInitialCall'));
} catch (e) {
logger.w('CallBridge.checkInitialCall: $e');
}
}
void _handle(Object? event) {
if (event is! Map) return;
final action = event['action']?.toString();
if (action == 'hangup') {
unawaited(CallController.instance.endActive());
return;
}
if (action == 'ended') {
final id = event['conversationId']?.toString();
if (id != null) CallController.instance.cancelConversation(id);
return;
}
final dataStr = event['data'];
if (dataStr is! String) return;
Object? decoded;
try {
decoded = jsonDecode(dataStr);
} catch (e) {
logger.w('CallBridge._handle: action=$action jsonDecode failed: $e');
return;
}
if (decoded is! Map) return;
CallController.instance.injectFromNative(
decoded,
autoAccept: action == 'answer',
);
}
Future<void> showIncoming(Map<dynamic, dynamic> payload) async {
if (!_android) return;
try {
await _method.invokeMethod<void>('showIncoming', {
for (final entry in payload.entries)
entry.key.toString(): entry.value?.toString() ?? '',
'type': 'InboundCall',
'iv': payload['type'] == 'VIDEO' ? 'true' : 'false',
});
} catch (error) {
logger.w('CallBridge.showIncoming: $error');
}
}
Future<void> cancelConversation(String id) async {
if (!_android) return;
try {
await _method.invokeMethod<void>('cancelConversation', {
'conversationId': id,
});
} catch (error) {
logger.w('CallBridge.cancelConversation: $error');
}
}
Future<void> notifyAccepted({String? caller, String? conversationId}) async {
if (!_android) return;
try {
await _method.invokeMethod<void>('notifyAccepted', {
'caller': caller,
'conversationId': conversationId,
});
} catch (e) {
logger.w('CallBridge.notifyAccepted: caller=$caller $e');
}
}
Future<void> ensureOngoing({String? caller}) async {
if (!_android) return;
try {
await _method.invokeMethod<void>('ensureOngoing', {'caller': caller});
} catch (e) {
logger.w('CallBridge.ensureOngoing: $e');
}
}
Future<void> setScreenShare(bool enabled, {String? caller}) async {
if (!_android) return;
try {
await _method.invokeMethod<void>('setScreenShare', {
'enabled': enabled,
'caller': caller,
});
} catch (e) {
logger.w('CallBridge.setScreenShare: enabled=$enabled $e');
}
}
Future<void> dropOngoing() async {
if (!_android) return;
try {
await _method.invokeMethod<void>('dropOngoing');
} catch (e) {
logger.w('CallBridge.dropOngoing: $e');
}
}
Future<void> notifyEnded({String? conversationId}) async {
if (!_android) return;
try {
await _method.invokeMethod<void>('notifyEnded', {
'conversationId': conversationId,
});
} catch (e) {
logger.w('CallBridge.notifyEnded: $e');
}
}
Future<void> cancelIncoming() async {
if (!_android) return;
try {
await _method.invokeMethod<void>('cancelIncoming');
} catch (e) {
logger.w('CallBridge.cancelIncoming: $e');
}
}
Future<bool> canUseFullScreenIntent() async {
if (!_android) return true;
try {
return await _method.invokeMethod<bool>('canUseFullScreenIntent') ?? true;
} catch (e) {
logger.w('CallBridge.canUseFullScreenIntent: $e');
return true;
}
}
Future<void> openFullScreenIntentSettings() async {
if (!_android) return;
try {
await _method.invokeMethod<void>('openFullScreenIntentSettings');
} catch (e) {
logger.w('CallBridge.openFullScreenIntentSettings: $e');
}
}
}