You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			178 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Dart
		
	
			
		
		
	
	
			178 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Dart
		
	
import 'dart:core';
 | 
						|
 | 
						|
import 'package:flutter/foundation.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
 | 
						|
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
 | 
						|
import 'package:flutter_webrtc/flutter_webrtc.dart' as webrtc_impl;
 | 
						|
import 'package:matrix/matrix.dart';
 | 
						|
import 'package:webrtc_interface/webrtc_interface.dart' hide Navigator;
 | 
						|
 | 
						|
import 'package:fluffychat/pages/chat_list/chat_list.dart';
 | 
						|
import 'package:fluffychat/pages/dialer/dialer.dart';
 | 
						|
import 'package:fluffychat/utils/platform_infos.dart';
 | 
						|
import '../../utils/voip/user_media_manager.dart';
 | 
						|
import '../widgets/matrix.dart';
 | 
						|
 | 
						|
class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate {
 | 
						|
  final MatrixState matrix;
 | 
						|
  Client get client => matrix.client;
 | 
						|
  VoipPlugin(this.matrix) {
 | 
						|
    voip = VoIP(client, this);
 | 
						|
    if (!kIsWeb) {
 | 
						|
      final wb = WidgetsBinding.instance;
 | 
						|
      wb.addObserver(this);
 | 
						|
      didChangeAppLifecycleState(wb.lifecycleState);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  bool background = false;
 | 
						|
  bool speakerOn = false;
 | 
						|
  late VoIP voip;
 | 
						|
  OverlayEntry? overlayEntry;
 | 
						|
  BuildContext get context => matrix.context;
 | 
						|
 | 
						|
  @override
 | 
						|
  void didChangeAppLifecycleState(AppLifecycleState? state) {
 | 
						|
    background = (state == AppLifecycleState.detached ||
 | 
						|
        state == AppLifecycleState.paused);
 | 
						|
  }
 | 
						|
 | 
						|
  void addCallingOverlay(String callId, CallSession call) {
 | 
						|
    final context =
 | 
						|
        kIsWeb ? ChatList.contextForVoip! : this.context; // web is weird
 | 
						|
 | 
						|
    if (overlayEntry != null) {
 | 
						|
      Logs().e('[VOIP] addCallingOverlay: The call session already exists?');
 | 
						|
      overlayEntry!.remove();
 | 
						|
    }
 | 
						|
    // Overlay.of(context) is broken on web
 | 
						|
    // falling back on a dialog
 | 
						|
    if (kIsWeb) {
 | 
						|
      showDialog(
 | 
						|
        context: context,
 | 
						|
        builder: (context) => Calling(
 | 
						|
          context: context,
 | 
						|
          client: client,
 | 
						|
          callId: callId,
 | 
						|
          call: call,
 | 
						|
          onClear: () => Navigator.of(context).pop(),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
    } else {
 | 
						|
      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 isWeb => kIsWeb;
 | 
						|
 | 
						|
  @override
 | 
						|
  Future<RTCPeerConnection> createPeerConnection(
 | 
						|
    Map<String, dynamic> configuration, [
 | 
						|
    Map<String, dynamic> constraints = const {},
 | 
						|
  ]) =>
 | 
						|
      webrtc_impl.createPeerConnection(configuration, constraints);
 | 
						|
 | 
						|
  Future<bool> get hasCallingAccount async => false;
 | 
						|
 | 
						|
  @override
 | 
						|
  Future<void> playRingtone() async {
 | 
						|
    if (!background && !await hasCallingAccount) {
 | 
						|
      try {
 | 
						|
        await UserMediaManager().startRingingTone();
 | 
						|
      } catch (_) {}
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Future<void> stopRingtone() async {
 | 
						|
    if (!background && !await hasCallingAccount) {
 | 
						|
      try {
 | 
						|
        await UserMediaManager().stopRingingTone();
 | 
						|
      } catch (_) {}
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Future<void> handleNewCall(CallSession call) async {
 | 
						|
    if (PlatformInfos.isAndroid) {
 | 
						|
      try {
 | 
						|
        final wasForeground = await FlutterForegroundTask.isAppOnForeground;
 | 
						|
 | 
						|
        await matrix.store.setString(
 | 
						|
          'wasForeground',
 | 
						|
          wasForeground == true ? 'true' : 'false',
 | 
						|
        );
 | 
						|
        FlutterForegroundTask.setOnLockScreenVisibility(true);
 | 
						|
        FlutterForegroundTask.wakeUpScreen();
 | 
						|
        FlutterForegroundTask.launchApp();
 | 
						|
      } catch (e) {
 | 
						|
        Logs().e('VOIP foreground failed $e');
 | 
						|
      }
 | 
						|
      // use fallback flutter call pages for outgoing and video calls.
 | 
						|
      addCallingOverlay(call.callId, call);
 | 
						|
    } else {
 | 
						|
      addCallingOverlay(call.callId, call);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Future<void> handleCallEnded(CallSession session) async {
 | 
						|
    if (overlayEntry != null) {
 | 
						|
      overlayEntry!.remove();
 | 
						|
      overlayEntry = null;
 | 
						|
      if (PlatformInfos.isAndroid) {
 | 
						|
        FlutterForegroundTask.setOnLockScreenVisibility(false);
 | 
						|
        FlutterForegroundTask.stopService();
 | 
						|
        final wasForeground = matrix.store.getString('wasForeground');
 | 
						|
        wasForeground == 'false' ? FlutterForegroundTask.minimizeApp() : null;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Future<void> handleGroupCallEnded(GroupCallSession groupCall) async {
 | 
						|
    // TODO: implement handleGroupCallEnded
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Future<void> handleNewGroupCall(GroupCallSession groupCall) async {
 | 
						|
    // TODO: implement handleNewGroupCall
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  // TODO: implement canHandleNewCall
 | 
						|
  bool get canHandleNewCall =>
 | 
						|
      voip.currentCID == null && voip.currentGroupCID == null;
 | 
						|
 | 
						|
  @override
 | 
						|
  Future<void> handleMissedCall(CallSession session) async {
 | 
						|
    // TODO: implement handleMissedCall
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  // TODO: implement keyProvider
 | 
						|
  EncryptionKeyProvider? get keyProvider => throw UnimplementedError();
 | 
						|
 | 
						|
  @override
 | 
						|
  Future<void> registerListeners(CallSession session) {
 | 
						|
    // TODO: implement registerListeners
 | 
						|
    throw UnimplementedError();
 | 
						|
  }
 | 
						|
}
 |