在终止的flutter应用程序中接收firebase消息时注册SIP

问题描述 投票:0回答:1

我正在尝试在我的 flutter 应用程序上发出来电通知。除了下面的事情之外,这几乎是有效的。当应用程序终止时,我收到 firebase 消息,并且可以显示我的通知,但我的 SIPController 未注册,因此在用户应答之前服务器会取消呼叫。有人知道如何处理这个问题吗? 这是我的 main.dart 和我的 SIPController 类:

import 'dart:io';

import 'package:callkeep/callkeep.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_callkit_incoming/entities/android_params.dart';
import 'package:flutter_callkit_incoming/entities/call_event.dart';
import 'package:flutter_callkit_incoming/entities/call_kit_params.dart';
import 'package:flutter_callkit_incoming/entities/ios_params.dart';
import 'package:flutter_callkit_incoming/entities/notification_params.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:mvc_pattern/mvc_pattern.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sip_ua/sip_ua.dart';
import 'package:smart_diese/business_logic/services/firebase/diesetel_firebase.dart';
import 'package:smart_diese/business_logic/services/sip/sip_controller.dart';
import 'package:smart_diese/business_logic/services/sip/sip_listener.dart';
import 'package:smart_diese/db/model/utilisateur.dart';
import 'package:smart_diese/db/smart_diese_database.dart';
import 'package:smart_diese/firebase_options.dart';
import 'package:smart_diese/views/app.dart';

import 'business_logic/utils/assets_manager.dart';

/* Top main variables */
int? initScreen = 0;
final FlutterCallkeep callKeep = FlutterCallkeep();
bool callKeepStarted = false;
final SipUaHelperListener listener = SIPListener();
bool notificationAnswered = false;


class PostHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return super.createHttpClient(context)
      ..badCertificateCallback = (
          X509Certificate cert,
          String host,
          int port,
          ) =>
      true;
  }
}

bool isFlutterLocalNotificationsInitialized = false;
late AndroidNotificationChannel channel;
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;

@pragma("vm:entry-point")
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  if (kDebugMode) {
    print('Message title: ${message.notification?.title}, body: ${message.notification?.body}, data: ${message.data}');
  }
  await setupFlutterNotifications();
  await showIncomingCall(message, Uuid().generateV4());
}

Future<void> showIncomingCall(RemoteMessage message, String uuid) async{
  final callerNum = message.data['callerNum'];
  final equipement = await SmartDieseDatabase.instance.readEquipementByNumero(int.parse(callerNum));

  final params = CallKitParams(
    id: uuid,
    nameCaller: equipement.nom,
    avatar: null,
    handle: equipement.numero.toString(),
    textAccept: 'Accept',
    textDecline: 'Decline',
    duration: 30000,
    missedCallNotification: const NotificationParams(
      showNotification: true,
      isShowCallback: false,
      subtitle: 'Missed call',
    ),
    android: const AndroidParams(
      isCustomNotification: true,
      isShowLogo: false,
      incomingCallNotificationChannelName: 'call_channel',
      isShowFullLockedScreen: true,
    ),
    ios: const IOSParams(
      iconName: 'CallKitLogo',
      handleType: '',
      supportsVideo: true,
      maximumCallGroups: 1,
      maximumCallsPerCallGroup: 1,
      audioSessionMode: 'default',
      audioSessionActive: true,
      audioSessionPreferredSampleRate: 44100.0,
      audioSessionPreferredIOBufferDuration: 0.005,
      supportsDTMF: true,
      supportsHolding: true,
      supportsGrouping: false,
      supportsUngrouping: false,
    ),
  );

  await FlutterCallkitIncoming.showCallkitIncoming(params);
}

Future<void> setupFlutterNotifications() async {
  if (isFlutterLocalNotificationsInitialized) {
    return;
  }

  channel = const AndroidNotificationChannel(
      'call_channel',
      'Calls',
      description:
      'This channel is used incoming calls.',
      importance: Importance.high,
      playSound: true
  );

  flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

  await flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation<
      AndroidFlutterLocalNotificationsPlugin>()
      ?.createNotificationChannel(channel);

  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
    alert: true,
    badge: true,
    sound: true,
  );

  isFlutterLocalNotificationsInitialized = true;
}

Future<void> initFirebase() async {
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);

  RemoteMessage? initialMessage =
  await FirebaseMessaging.instance.getInitialMessage();

  if(initialMessage != null){
    firebaseMessagingBackgroundHandler(initialMessage);
  }

  FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
    if(event!.event == Event.actionCallAccept){
      SIPController.controller.signalAnswer();
    }
    if(event.event == Event.actionCallDecline){
      SIPController.controller.signalHangup();
    }
  });
}

Future<void> main() async {
  HttpOverrides.global = PostHttpOverrides();
  WidgetsFlutterBinding.ensureInitialized();

  final List<Utilisateur> utilisateurs =
  await SmartDieseDatabase.instance.readAllUtilisateur();

  if (utilisateurs.isNotEmpty && utilisateurs[0].role == 0) {
    await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
    await initFirebase();

    final SharedPreferences preferences = await SharedPreferences.getInstance();
    initScreen = preferences.getInt('initScreen');
    await preferences.setInt('initScreen', 1);

    await DiesetelFirebase.initFirebase();
    FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
  }

  await AssetsManager.initAssets();

  await dotenv.load();

  runApp(const App());
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:mvc_pattern/mvc_pattern.dart';
import 'package:sip_ua/sip_ua.dart';
import 'package:smart_diese/business_logic/model/sip_model.dart';

class SIPController extends ControllerMVC {
  /* Singleton */
  SIPController._privateConstructor();

  static final SIPController _instance = SIPController._privateConstructor();

  static SIPController get controller => _instance;
  final SIPModel _model = SIPModel();

  void callStateChanged(Call call, CallStateEnum callState) {
    _model.call = call;
    _model.callState = callState;
  }

  String getSipAdress() {
    return _model.call.remote_identity.toString();
  }

  void updateView() {
    if (kDebugMode) {
      print("REFRESH");
    }
    for (StateMVC<StatefulWidget> element in states) {
      if (element.mounted) element.refresh();
      if (kDebugMode) {
        print(element.toString());
      }
    }
  }

  void testcall(String contact) {
    _model.testcall(contact);
  }

  void stopHelper() {
    _model.stopHelper();
  }

  void removeSIPListener(SipUaHelperListener l) {
    _model.removeSIPListener(l);
  }

  RegistrationStateEnum? get registerState => _model.registerStatus;
  void register(SipUaHelperListener listener) => _model.register(listener);
  CallStateEnum? get callState => _model.callState;

  void signalAnswer() {
    _model.answerCall();
  }

  void makeCall(String number) {
    _model.makeCall(number);
  }

  void sendREGISTER() {
    _model.sendREGISTER();
  }

  bool isReachable() => _model.isReachable();

  void signalDTMF(String dtmf) {
    _model.sendDTMF(dtmf);
  }

  void signalHangup() {
    _model.hangupCall();
  }

  void signalMuted() {
    _model.call.mute();
  }

  void signalUnmuted() {
    _model.call.unmute();
  }

  void setRegisterState(RegistrationState s) {
    _model.setRegisterStatus = s;
    updateView();
  }
}
flutter firebase-cloud-messaging call sip
1个回答
0
投票

当应用程序终止时,它已经失去了与 SIP 服务器的连接,并且 SIPController 未运行。

当前实现仅显示通知,但不会恢复 SIP 连接,也无法接听电话。

当收到推送通知时,其处理程序应首先启动应用程序(它将进行常规初始化,连接到 SIP 服务器并接收 SIP 呼叫),然后才显示一些通知。

iOS 应用程序只能通过 PushKit + VoiP 推送令牌启动,并且必须在收到推送(启动)后立即显示 CallKit UI。

还检查服务器端的超时,发送推送后和取消呼叫之前等待的时间。

© www.soinside.com 2019 - 2024. All rights reserved.