Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When I click the Accept button while the application is closed, I want to send it to a page but it doesn't work. #631

Closed
MrAzimzadeh opened this issue Jan 13, 2025 · 3 comments

Comments

@MrAzimzadeh
Copy link

Hello, When I click the Accept button while the application is "Closed", the application opens but does not direct me to the page I want. What could be the reason for this error?

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<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

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

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

  // Initialize navigatorKey at the start
  navigatorKey = GlobalKey<NavigatorState>();

// 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<void> 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<void> _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<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {  
  @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<LocalizationProvider>(
        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(),
            },
          );
        },
      ),
    );
  }
}
@owl-madness
Copy link

@MrAzimzadeh
Does the incoming call showing in iOS when the app is terminated??

@MrAzimzadeh
Copy link
Author

No

@MrAzimzadeh Does the incoming call showing in iOS when the app is terminated??

@moahmmadjaber
Copy link

on android you need to use isolated channel or sharedPreferences

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants