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

Not able to decline the call when app in Background Mode #113

Open
1 of 9 tasks
Kushagra-Esmagico opened this issue Feb 25, 2025 · 3 comments
Open
1 of 9 tasks

Not able to decline the call when app in Background Mode #113

Kushagra-Esmagico opened this issue Feb 25, 2025 · 3 comments
Assignees
Labels
bug Something isn't working

Comments

@Kushagra-Esmagico
Copy link

Bug Category**

  • Credential Login
  • Token Login
  • Local Audio Issue
  • Remote Audio Issue
  • Audio Device Switching
  • Mute / Unmute
  • Hold / Unhold
  • Performance issues
  • Other
    SDK Version
    Which version of the SDK have you added from pub.dev? Feel free to add the whole dependency implementation:
    eg. telnyx_webrtc: ^1.0.2
    Describe the bug
    When App is in background and got the call notification , try to decline the call , it does not decline the call , and when call is time out , getting another call notification
    Expected behaviour
    When user click on Decline call, It should end the call and on other end , the call should be disconnected . Duplicate call notification should not be there
    Observation
    When app is in background and user decline it , it is invoking both FlutterCallkitIncoming.onEvent.listen from initialize method and firebase background handler , if it declines the call , on next inbound call , that is automatically ended , as we have called the endcall method two times from both listeners .
    To Reproduce
    Steps to reproduce the behaviour:
  1. (Log on with connectWithToken )
    2 Make the app in background , it disconnects the socket.
  2. Make an Inbound call , and try to decline it .
    ** Device (please complete the following information):**
  • Emulator: (true)
  • Device/Browser: [Android Realme ]
  • OS Version: [13]
    Logs
@Kushagra-Esmagico Kushagra-Esmagico added the bug Something isn't working label Feb 25, 2025
@Oliver-Zimmerman
Copy link
Collaborator

@Kushagra-Esmagico

In order to decline a notification you need to log into the socket and send a bye message for that invite with the call ID.

This means to actually decline a call from a push notification you need to reconnect to the socket, log in with a stored credential, send a bye message, and disconnect again.

You can see a sample implementation of this here in main.dart, however there are definite improvements that can be made here for a production application (remember the sample is just a simple usage example)

      case Event.actionCallDecline:
        /*
        * When the user declines the call from the push notification, the app will no longer be visible, and we have to
        * handle the endCall user here.
        *
        * */
        logger.i('actionCallDecline :: call declined');
        String? token;
        PushMetaData? pushMetaData;
        final telnyxClient = TelnyxClient();

        telnyxClient.onSocketMessageReceived = (TelnyxMessage message) {
          switch (message.socketMethod) {
            case SocketMethod.bye:
              {
                // make sure to disconnect the telnyxclient on Bye for Decline
                // Only disconnect the socket when the call was ended from push notifications
                logger.i('TelnyxClient :: onSocketMessageReceived :: BYE');
                telnyxClient.disconnect();
                break;
              }
            default:
              logger.i('TelnyxClient :: onSocketMessageReceived   $message');
          }
          logger.i('TelnyxClient :: onSocketMessageReceived : $message');
        };

        pushMetaData =
            PushMetaData.fromJson(jsonDecode(message.data['metadata']!));
        // Set the pushMetaData to decline
        pushMetaData.isDecline = true;

        if (defaultTargetPlatform == TargetPlatform.android) {
          token = (await FirebaseMessaging.instance.getToken())!;

          logger.i('Android notification token :: $token');
        } else if (defaultTargetPlatform == TargetPlatform.iOS) {
          token = await FlutterCallkitIncoming.getDevicePushTokenVoIP();

          logger.i('iOS notification token :: $token');
        }
        final config = await txClientViewModel.getConfig();
        telnyxClient.handlePushNotification(
          pushMetaData,
          config is CredentialConfig ? config : null,
          config is TokenConfig ? config : null,
        );
        break;

@Kushagra-Esmagico
Copy link
Author

@Oliver-Zimmerman This method I can see in firebase Background handler , but when app is in background ,
Future initialize() async {}
and
@pragma('vm:entry-point')
Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
both are invoking , to decline the call , and sometime it works , but two methods called for declining the call , On next call it is declining the call itself from _pendingDeclineFromPush is true .
from Future initialize method
case Event.actionCallDecline:
logger.i("action call deline here app initialization");
final metadata = event.body['extra']['metadata'];
if (metadata == null) {
logger.i('Decline Call Directly');
txClientViewModel.endCall();
} else {
logger.i('Received push Call for iOS $metadata');
final data = metadata as Map<dynamic, dynamic>;
data['isDecline'] = true;
await handlePush(data);
}
break;

AND from Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {}
case Event.actionCallDecline:
/*
* When the user declines the call from the push notification, the app will no longer be visible, and we have to
* handle the endCall user here.
*
* */
logger.i('actionCallDecline :: call declined');
String? token;
PushMetaData? pushMetaData;
final telnyxClient = TelnyxClient();

    telnyxClient.onSocketMessageReceived = (TelnyxMessage message) {
      switch (message.socketMethod) {
        case SocketMethod.bye:
          {
            // make sure to disconnect the telnyxclient on Bye for Decline
            // Only disconnect the socket when the call was ended from push notifications
            logger.i('TelnyxClient :: onSocketMessageReceived :: BYE');
            telnyxClient.disconnect();
            break;
          }
        default:
          logger.i('TelnyxClient :: onSocketMessageReceived   $message');
      }
      logger.i('TelnyxClient :: onSocketMessageReceived : $message');
    };

    pushMetaData =
        PushMetaData.fromJson(jsonDecode(message.data['metadata']!));
    // Set the pushMetaData to decline
    pushMetaData.isDecline = true;

    if (defaultTargetPlatform == TargetPlatform.android) {
      token = (await FirebaseMessaging.instance.getToken())!;

      logger.i('Android notification token :: $token');
    } else if (defaultTargetPlatform == TargetPlatform.iOS) {
      token = await FlutterCallkitIncoming.getDevicePushTokenVoIP();

      logger.i('iOS notification token :: $token');
    }
    final config = await txClientViewModel.getConfig();
    telnyxClient.handlePushNotification(
      pushMetaData,
      config is CredentialConfig ? config : null,
      config is TokenConfig ? config : null,
    );
    break;

You can see a sample implementation of this here in main.dart, however there are definite improvements that can be made here for a production application (remember the sample is just a simple usage example)
I know demo code is not for production level implementation .
For this specific use case , can you suggest some way ?

@Oliver-Zimmerman
Copy link
Collaborator

@Kushagra-Esmagico I cannot reproduce your issue on our sample app. Here is another demonstration video of being able to decline a call when the app isn't open or when it is in the background...

You can see we receive the User Busy state which is only emitted when a user declines a call.

Screen.Recording.2025-02-26.at.09.52.29.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

No branches or pull requests

2 participants