Почему CallKit не работает, когда приложение закрыто? Как я могу правильно запустить его с помощью push-уведомлений VoIPAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Почему CallKit не работает, когда приложение закрыто? Как я могу правильно запустить его с помощью push-уведомлений VoIP

Сообщение Anonymous »

Я реализую CallKit в своем приложении Flutter, но столкнулся с проблемой обработки входящих вызовов, когда приложение полностью закрыто (не работает в фоновом режиме). Приложение работает нормально, когда оно находится на переднем плане или в фоновом режиме: при приеме входящего вызова срабатывает CallKit, и появляется экран вызова. Однако, когда приложение закрыто (полностью не работает в фоновом режиме), даже если я получаю входящий звонок через push-уведомление VoIP, CallKit не срабатывает, и приложение не переходит на экран вызова. Я настроил push-уведомления VoIP для пробуждения приложения, но CallKit не работает должным образом, когда приложение полностью закрыто. Что я пробовал? 1. Включение фоновых режимов. Я включил фоновую выборку и push-уведомления в Xcode, чтобы приложение могло обрабатывать push-уведомления VoIP, даже когда приложение закрыто. 2. Обработка push-уведомлений VoIP. Я убедился, что приложение может получать push-уведомления VoIP и что обработчик уведомлений правильно настроен для пробуждения приложения. 3. Использование CallKit с push-уведомлениями. Я внедрил CallKit для обработки входящих вызовов, но он работает только тогда, когда приложение находится в фоновом или переднем режиме.

Ожидаемые результаты:
Я ожидал, что когда будет получено Push-уведомление VoIP, когда приложение закрыто, приложение проснется и отобразит экран CallKit. > и переход на страницу звонка где я могу принять звонок.
Фактические результаты:
Когда приложение закрыто, CallKit< /strong> не срабатывает, и приложение не переходит к экрану вызова даже после получения Push-уведомления VoIP.
Дополнительная информация :
Я пытался следовать документации для CallKit и Push-уведомления VoIP на iOS, но мне все равно не удается заставить приложение работать должным образом, когда оно полностью закрыто. Если у кого-то есть опыт обработки вызовов VoIP и CallKit в Flutter (или даже с собственным кодом iOS), я был бы признателен за любые рекомендации или предложения по как решить эту проблему.
import 'dart:async';
import 'dart:developer' as dev;
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_callkit_incoming/entities/entities.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:legalis/AppUtil.dart';
import 'package:legalis/l10n/support_locale.dart';
import 'package:legalis/notify_controller.dart';
import 'package:legalis/provider/auth/auth_provider.dart';
import 'package:legalis/provider/call/call_provider.dart';
import 'package:legalis/provider/category/category_provider.dart';
import 'package:legalis/provider/chat/chat_detail_provider.dart';
import 'package:legalis/provider/communication/communication_provider.dart';
import 'package:legalis/provider/home/lawyer_report_provider.dart';
import 'package:legalis/provider/language_provider.dart';
import 'package:legalis/provider/localization/localization_provider.dart';
import 'package:legalis/provider/message/message_list_provider.dart';
import 'package:legalis/provider/navigation/navigation_provider.dart';
import 'package:legalis/provider/service/user_service_provider.dart';
import 'package:legalis/provider/user/user_provider.dart';
import 'package:legalis/screens/call/call_page.dart';
import 'package:legalis/screens/pages/home/home_page.dart';
import 'package:legalis/static/call_accept_observer.dart';
import 'package:logger/logger.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
import 'firebase_options.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; // Bu dosya tüm providerları içeriyor.
import 'package:legalis/screens/splash_screen.dart';
import 'package:legalis/screens/auth/login_screen.dart';
import 'package:legalis/screens/pages/message/message_page.dart';
import 'package:legalis/screens/pages/profile/profile_page.dart';
import 'package:legalis/screens/pages/call/call_history.dart';
import 'package:legalis/wrapper/main_wrapper.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

int getUniqueNotificationId() {
var num = Random().nextInt(2000);
AppUtil.setNotification(num);
return num;
}

GlobalKey navigatorKey = GlobalKey();

void main() async {
WidgetsFlutterBinding.ensureInitialized();

await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);

// Initialize navigatorKey at the start
navigatorKey = GlobalKey();

// Initialize CallKit event handler
Widget? initialRoute;

// Add CallKit listener initialization here
await _initCallKitListener();

FirebaseMessaging messaging = FirebaseMessaging.instance;

// Set background message handler only once
FirebaseMessaging.onBackgroundMessage(handleNotification);

// Configure foreground message handling
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
dev.log('Foreground message received');
await handleNotification(message);
});

// Configure message handling when app is opened from terminated state
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
dev.log('App opened from terminated state with message');
await handleNotification(message);
});

await Future.delayed(const Duration(seconds: 1));
if (Platform.isIOS) {
NotificationSettings settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);

if (settings.authorizationStatus == AuthorizationStatus.authorized) {
String? apnsToken = await messaging.getAPNSToken();
Logger().t('APNS Token: $apnsToken');
if (apnsToken != null) {
print('APNS Token: $apnsToken');
}
}
}

// Get FCM token only once
String? fcmToken = await messaging.getToken();
Logger().t('FCM Token: $fcmToken');

// Remove duplicate call
// FirebaseMessaging.onBackgroundMessage(handleNotification);

AwesomeNotifications().initialize(
null,
[
NotificationChannel(
channelKey: 'basic_channel',
channelName: 'Basic notifications',
channelDescription: 'Notification channel for basic tests',
defaultColor: const Color(0xFF9D50DD),
ledColor: Colors.white,
),
NotificationChannel(
channelKey: 'call_channel',
channelName: 'Call notifications',
channelDescription: 'Notification channel for call notifications',
defaultColor: Colors.orange,
ledColor: Colors.white,
importance: NotificationImportance.Max,
channelShowBadge: true,
locked: false,
playSound: true,
defaultRingtoneType: DefaultRingtoneType.Ringtone,
),
NotificationChannel(
channelKey: 'message_channel',
channelName: 'Message notifications',
channelDescription: 'Notification channel for message notifications',
defaultColor: Colors.blue,
ledColor: Colors.white,
importance: NotificationImportance.High,
channelShowBadge: true,
),
],
);
await FirebaseMessaging.instance.setAutoInitEnabled(true);
// Firebase Dynamic Links

runApp(MyApp(page: initialRoute));
}

@pragma('vm:entry-point')
Future handleNotification(RemoteMessage message) async {
Logger().d('Remote Message ${message.toString()}');
Logger().d('Remote Message ${message.data.toString()}');
String? title = message.data['sender_name'] ?? 'Yeni Mesaj';
String? body = message.data['body'];
String channelKey = message.data['channel_key'] ?? 'default_channel';
String? id = message.data['id'] ?? '0';

if (channelKey == 'call_channel') {
// Clear any existing calls first
await FlutterCallkitIncoming.endAllCalls();

final uuid = const Uuid().v4();
final params = CallKitParams(
id: uuid,
nameCaller: title,
appName: 'Legalis',
avatar: message.data['caller_avatar'] ?? '',
handle: body ?? '',
type: 0,
duration: 30000,
textAccept: Platform.isIOS ? 'Accept' : 'Qəbul et',
textDecline: Platform.isIOS ? 'Decline' : 'Rədd et',
extra: {
...message.data,
'timestamp': DateTime.now().toIso8601String(),
'uuid': uuid, // Add unique identifier
},
headers: {},
android: const AndroidParams(
isCustomNotification: true,
isShowLogo: false,
ringtonePath: 'system_ringtone_default',
backgroundColor: '#0955fa',
backgroundUrl: '',
actionColor: '#4CAF50',
incomingCallNotificationChannelName: "Incoming Call",
missedCallNotificationChannelName: "Missed Call"),
ios: const IOSParams(
iconName: 'CallKitLogo',
handleType: 'generic',
supportsVideo: false,
maximumCallGroups: 1, // Limit to 1 call group
maximumCallsPerCallGroup: 1,
audioSessionMode: 'voicechat',
audioSessionActive: true,
audioSessionPreferredSampleRate: 44100.0,
audioSessionPreferredIOBufferDuration: 0.005,
supportsDTMF: true,
supportsHolding: true,
supportsGrouping: false,
supportsUngrouping: false,
),
);

try {
await FlutterCallkitIncoming.showCallkitIncoming(params);
} catch (e) {
Logger().e('Error showing CallKit: $e');
}
} else if (channelKey == 'message_channel') {
if (AppUtil.connectionUserId != id) {
await AwesomeNotifications().createNotification(
content: NotificationContent(
id: getUniqueNotificationId(),
channelKey: channelKey,
color: Colors.blue,
title: title,
body: body,
category: NotificationCategory.Message,
backgroundColor: Colors.blue,
payload: {'message-api-id': id, 'username': title}),
actionButtons: [
NotificationActionButton(
key: 'READ',
label: 'Read Message',
color: Colors.green,
),
NotificationActionButton(
key: 'DISMISS',
label: 'Dismiss',
color: Colors.red,
),
],
localizations: {
// Azərbaycanca
'az': NotificationLocalization(buttonLabels: {
'READ': 'Mesajı oxu',
'DISMISS': 'İmtina et',
}),
// EN
'en': NotificationLocalization(
buttonLabels: {
'READ': 'Read Message',
'DISMISS': 'Dismiss',
},
),
// Rus
'ru': NotificationLocalization(
buttonLabels: {
'READ': 'Прочитать сообщение',
'DISMISS': 'Отклонить',
},
),
},
);
}
}

AwesomeNotifications().setListeners(
onActionReceivedMethod: (ReceivedAction receivedAction) async {
AppUtil.init();
AppUtil.setNotification(receivedAction.id);
NotificationController.onActionReceivedMethod(receivedAction);
},
onNotificationCreatedMethod:
(ReceivedNotification receivedNotification) async {
AppUtil.setNotification(receivedNotification.id);
NotificationController.onNotificationCreatedMethod(receivedNotification);
},
onNotificationDisplayedMethod:
(ReceivedNotification receivedNotification) async {
NotificationController.onNotificationDisplayedMethod(
receivedNotification);
},
onDismissActionReceivedMethod: (ReceivedAction receivedAction) async {
NotificationController.onDismissActionReceivedMethod(receivedAction);
},
);
}

@pragma('vm:entry-point')
Future _initCallKitListener() async {
try {
FlutterCallkitIncoming.onEvent.listen((event) async {
if (event?.event == null || event?.body == null) return;

Logger().i('CallKit Event: ${event?.event}, Body: ${event?.body}');
final data = event?.body;
if (data == null) return;

switch (event?.event) {
case Event.actionCallAccept:
if (AppUtil.activeCallUser) {
Logger().d('Call already active, ignoring accept action');
return;
}
AppUtil.activeCallUser = true;

if (navigatorKey.currentState == null) {
await Future.delayed(const Duration(seconds: 2));
}

// Navigate to CallPage and remove all previous routes
await navigatorKey.currentState?.pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => CallPage(
userName: data['nameCaller'],
callId: data['extra']?['callId'],
recieverId: data['extra']?['userId'] ?? '',
accep: true,
),
),
(route) => false, // Remove all previous routes
);
break;

case Event.actionCallDecline:
await AppUtil.endCallAsync(callId: data['extra']?['callId']);
await FlutterCallkitIncoming.endAllCalls();
break;

case Event.actionCallEnded:
if (AppUtil.activeCallUser) {
await AppUtil.endCallAsync(callId: data['extra']?['callId']);
await FlutterCallkitIncoming.endAllCalls();
AppUtil.activeCallUser = false;
}
break;

case Event.actionCallIncoming:
AppUtil.activeCallUser = false;
break;

default:
break;
}
});
} catch (e, stackTrace) {
Logger().e("Error in call listener", error: e, stackTrace: stackTrace);
}
}

// ignore: must_be_immutable
class MyApp extends StatefulWidget {
final Widget? page;
const MyApp({super.key, this.page});

@override
State createState() => _MyAppState();
}

class _MyAppState extends State {
@override
void initState() {
super.initState();
// Remove _listenerCallKit call since we already initialized it in main()
// _listenerCallKit();

AwesomeNotifications().setListeners(
onActionReceivedMethod: NotificationController.onActionReceivedMethod,
onNotificationCreatedMethod:
NotificationController.onNotificationCreatedMethod,
onNotificationDisplayedMethod:
NotificationController.onNotificationDisplayedMethod,
onDismissActionReceivedMethod:
NotificationController.onDismissActionReceivedMethod);
}

@override
Widget build(BuildContext context) {
// Fbs
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => MessageListProvider()),
ChangeNotifierProvider(create: (_) => ChatDetailProvider()),
ChangeNotifierProvider(create: (_) => LawyerReportProvider()),
ChangeNotifierProvider(create: (_) => UserServiceProvider()),
ChangeNotifierProvider(create: (_) => CallProvider()),
ChangeNotifierProvider(create: (_) => CallAcceptProvider()),
ChangeNotifierProvider(create: (_) => NavigationProvider()),
ChangeNotifierProvider(create: (_) => LocalizationProvider()),
ChangeNotifierProvider(create: (_) => CommunicationProvider()),
ChangeNotifierProvider(create: (_) => AuthProvider()),
ChangeNotifierProvider(create: (_) => CategoryProvider()),
ChangeNotifierProvider(create: (_) => LanguageProvider()),
ChangeNotifierProvider(create: (_) => UserProvider()),
],
child: Consumer(
builder: (context, provider, child) {
return MaterialApp(
onGenerateRoute: (settings) {
Logger().i('Route: ${settings}');
},
title: 'Legalis',
navigatorKey: navigatorKey,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
locale: provider.locale,
supportedLocales: L10n.support,
theme: ThemeData(
bottomSheetTheme: const BottomSheetThemeData(
backgroundColor: Color(0xFF000E2B)),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
},
),
fontFamily: 'SF-Pro-Display',
primarySwatch: Colors.green,
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF000E2B),
elevation: 0,
iconTheme: IconThemeData(color: Colors.white),
titleTextStyle: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w700,
),
),
scaffoldBackgroundColor: Colors.white,
),
home: widget.page ?? const SplashPage(),
routes: {
'/login': (context) => const LoginScreen(),
'/home': (context) => const HomePageView(),
'/messagePage': (context) => const MessagePage(),
'/profilePage': (context) => const ProfilePage(),
'/callHistory': (context) => const CallHistory(),
'/mainWrapper': (context) => MainWrapper(),
},
);
},
),
);
}
}


Подробнее здесь: https://stackoverflow.com/questions/793 ... -correctly
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»