import 'dart:core'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart' as webrtc_impl; import 'package:matrix/matrix.dart'; import 'package:webrtc_interface/webrtc_interface.dart'; import 'package:fluffychat/pages/dialer/dialer.dart'; import '../../utils/voip/user_media_manager.dart'; class VoipPlugin extends WidgetsBindingObserver implements WebRTCDelegate { VoipPlugin({required this.client, required this.context}) { voip = VoIP(client, this); Connectivity() .onConnectivityChanged .listen(_handleNetworkChanged) .onError((e) => _currentConnectivity = ConnectivityResult.none); Connectivity() .checkConnectivity() .then((result) => _currentConnectivity = result) .catchError((e) => _currentConnectivity = ConnectivityResult.none); if (!kIsWeb) { final wb = WidgetsBinding.instance; wb?.addObserver(this); if (wb != null) { didChangeAppLifecycleState(wb.lifecycleState!); } } } final Client client; bool background = false; bool speakerOn = false; late VoIP voip; ConnectivityResult? _currentConnectivity; ValueChanged? onIncomingCall; OverlayEntry? overlayEntry; final BuildContext context; void _handleNetworkChanged(ConnectivityResult result) async { /// Got a new connectivity status! if (_currentConnectivity != result) { voip.calls.forEach((_, sess) { sess.restartIce(); }); } _currentConnectivity = result; } @override void didChangeAppLifecycleState(AppLifecycleState state) { Logs().v('AppLifecycleState = $state'); background = !(state != AppLifecycleState.detached && state != AppLifecycleState.paused); } void addCallingOverlay( BuildContext context, String callId, CallSession call) { if (overlayEntry != null) { Logs().w('[VOIP] addCallingOverlay: The call session already exists?'); overlayEntry?.remove(); } overlayEntry = OverlayEntry( builder: (_) => Calling( context: context, client: client, callId: callId, call: call, onClear: () { overlayEntry?.remove(); overlayEntry = null; }), ); Overlay.of(context)!.insert(overlayEntry!); } @override MediaDevices get mediaDevices => webrtc_impl.navigator.mediaDevices; @override bool get isBackgroud => background; @override bool get isWeb => kIsWeb; @override Future createPeerConnection( Map configuration, [Map constraints = const {}]) => webrtc_impl.createPeerConnection(configuration, constraints); @override VideoRenderer createRenderer() { return webrtc_impl.RTCVideoRenderer(); } @override void playRingtone() async { if (!background) { try { await UserMediaManager().startRingingTone(); } catch (_) {} } } @override void stopRingtone() async { if (!background) { try { await UserMediaManager().stopRingingTone(); } catch (_) {} } } @override void handleNewCall(CallSession call) async { /// Popup CallingPage for incoming call. if (!background) { addCallingOverlay(context, call.callId, call); } else { onIncomingCall?.call(call); } } @override void handleCallEnded(CallSession session) async { if (overlayEntry != null) { overlayEntry?.remove(); overlayEntry = null; } } }