callkeep icon indicating copy to clipboard operation
callkeep copied to clipboard

How to take control to the flutter application after answering the call

Open aravindhkumar23 opened this issue 4 years ago • 23 comments

Hi, I am using Fcm background message handler(android) to receive call notification from user-A. when notification is received my background message handler is called and I can able to see the answer or decline view buttons like a normal phone call(I think this is using phone accounts).

my question is how to take control to my application once the user answered the call (can be audio/video call)?

or please do suggest me to get this done. struck here for a long time.

aravindhkumar23 avatar Dec 10 '20 07:12 aravindhkumar23

Try this :

_callKeep.backToForeground();
_callKeep.endAllCalls(); 
or 
_callKeep.endCall("your uuid");

Then push to your screen

NavigationService.instance.navigateToRoute(MaterialPageRoute(
        builder: (context) => YourScreen(),
      ));

MaheshPeri19 avatar Dec 13 '20 08:12 MaheshPeri19

@MaheshPeri19 Thanks for the reply, will try it and let you know.

aravindhkumar23 avatar Dec 14 '20 06:12 aravindhkumar23

@MaheshPeri19

same error , The method 'push' was called on null.

I have tried accessing global state but no luck, In the static method we cannot able to use these context/store context/service context.

I hope there should be a way to get this thing working or any workaround will be there, but I don't have that much knowledge in it. can you guys suggest me to achieve this or any workaround to give a try. thanks

aravindhkumar23 avatar Dec 14 '20 08:12 aravindhkumar23

Check with below link for NavigationService class. When you come from background to forground, context will be null. So need to use navigator key.

https://stackoverflow.com/questions/63324364/navigation-in-flutter-without-context

And also if you want to pass any data to pushed screen, get from firebase if you already use. Shared preferences and sqlite are not working properly for me at background to foreground.

MaheshPeri19 avatar Dec 14 '20 08:12 MaheshPeri19

@MaheshPeri19

Implemented the same way which you are suggesting, but no luck attaching my screen let me know if anything missed out, if needed I can share the code snippet. Screenshot 2020-12-14 at 6 40 01 PM

and my background listener

Screenshot 2020-12-14 at 6 40 26 PM

aravindhkumar23 avatar Dec 14 '20 13:12 aravindhkumar23

what is the exact error after implementing navigationservice class ?

try to remove return statement at end of CallKeepPerformAnswerCallAction event and also try _callKeep.endAllCalls() instead of endCall(-) method.

MaheshPeri19 avatar Dec 14 '20 14:12 MaheshPeri19

Call gets ended by after answering the call and executing .endCall(id); and my-app opens as expected.

but not pushing to new page (VideoCall in my case). Getting error in catch block with message The method 'push' was called on null.

aravindhkumar23 avatar Dec 14 '20 14:12 aravindhkumar23

So when the app opens as expected,

  1. is it opening the home screen ? and all code releated to firebase or callkeep functionality in homescreen itself, right ?
  2. any parameters passing to VideoCall() ? if missing data also causes null exception.

Otherwise in between, it looses state. Please check the logs

MaheshPeri19 avatar Dec 14 '20 14:12 MaheshPeri19

@MaheshPeri19 here is my reply

1.It opens up the app with screen which is put background to test call (not tested by killing the app bcz I need to see log). All code are included in main.dart file. 2.just pushing to new route no params is passing , if it works then I will pass the params to achieve my functionality.

Attaching my log may be it helps in identifying the cause I/flutter (18517): backgroundMessage: message => {data: {body: Test call, key_1: Data for key one, key_2: Hellowww, title: ALT App Testing}} I/flutter (18517): backgroundMessage: displayIncomingCall (Test call) D/RNCK:VoiceConnectionService(18517): setAvailable: false D/RNCK:VoiceConnectionService(18517): setAvailable: true D/FLT:CallKeepModule(18517): displayIncomingCall number: Test call, callerName: D/FLT:CallKeepModule(18517): backToForeground, app isOpened ?true E/RNCK:VoiceConnectionService(18517): Constructor V/PhoneWindow(18517): DecorView setVisiblity: visibility = 0, Parent = ViewRoot{6b44733 com.example/com.example.MainActivity,ident = 0}, this = DecorView@594d49f[MainActivity] I/flutter (18517): ---state --AppLifecycleState.resumed I/flutter (18517): --------agter update Instance of 'Store<AppState>' D/OpenGLRenderer(18517): CanvasContext() 0x774edd9280 initialize window=0x775fff4000 D/Surface (18517): Surface::connect(this=0x775fff4000,api=1) D/mali_winsys(18517): EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, egl_color_buffer_format *, EGLBoolean) returns 0x3000 D/Surface (18517): Surface::connect(this=0x774ea3f000,api=1) D/mali_winsys(18517): EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, egl_color_buffer_format *, EGLBoolean) returns 0x3000 D/Surface (18517): Surface::disconnect(this=0x7751636000,api=1) E/BpSurfaceComposerClient(18517): Failed to transact (-1) E/BpSurfaceComposerClient(18517): Failed to transact (-1) D/RNCK:VoiceConnectionService(18517): setAvailable: false D/RNCK:VoiceConnectionService(18517): setAvailable: true I/TelecomFramework(18517): VoiceConnectionService: notifyCreateConnectionComplete TC@6_1: (...->CSW.hCCC)->CS.crCoC->H.CS.crCoC@E-E-E-BbE I/zygote64(18517): Do partial code cache collection, code=57KB, data=58KB I/zygote64(18517): After code cache collection, code=57KB, data=58KB I/zygote64(18517): Increasing code cache capacity to 256KB D/RNCK:VoiceConnection(18517): onAnswer called D/RNCK:VoiceConnection(18517): onAnswer executed I/flutter (18517): ---state --AppLifecycleState.inactive I/flutter (18517): --------agter update Instance of 'Store<AppState>' I/flutter (18517): [CallKeep] INFO: received event "CallKeepPerformAnswerCallAction" {callUUID: a0d150b6-0842-4aca-8677-a60943f90c86} I/flutter (18517): backgroundMessage: CallKeepPerformAnswerCallAction a0d150b6-0842-4aca-8677-a60943f90c86 D/FLT:CallKeepModule(18517): backToForeground, app isOpened ?true I/flutter (18517): backgroundMessage global state <<<<--- null I/flutter (18517): ---state --AppLifecycleState.resumed I/flutter (18517): --------agter update Instance of 'Store<AppState>' I/flutter (18517): ---state --AppLifecycleState.inactive I/flutter (18517): --------agter update Instance of 'Store<AppState>' D/FLT:CallKeepModule(18517): endCall called D/RNCK:VoiceConnection(18517): onDisconnect executed D/RNCK:VoiceConnectionService(18517): deinitConnection:a0d150b6-0842-4aca-8677-a60943f90c86 D/FLT:CallKeepModule(18517): endCall executed I/flutter (18517): push to new route error NoSuchMethodError: The method 'push' was called on null. I/flutter (18517): Receiver: null I/flutter (18517): Tried calling: push<Object>(Instance of 'MaterialPageRoute<dynamic>') I/flutter (18517): [CallKeep] INFO: received event "CallKeepDidActivateAudioSession" {} I/flutter (18517): [CallKeep] INFO: received event "CallKeepPerformEndCallAction" {callUUID: a0d150b6-0842-4aca-8677-a60943f90c86} I/flutter (18517): backgroundMessage: CallKeepPerformEndCallAction a0d150b6-0842-4aca-8677-a60943f90c86 I/flutter (18517): ---state --AppLifecycleState.resumed I/flutter (18517): --------agter update Instance of 'Store<AppState>' V/PhoneWindow(18517): DecorView setVisiblity: visibility = 0, Parent = ViewRoot{6b44733 com.example/com.example.MainActivity,ident = 0}, this = DecorView@594d49f[MainActivity]

aravindhkumar23 avatar Dec 15 '20 07:12 aravindhkumar23

@aravindhkumar23

Have you implemented CallKeepDidActivateAudioSession callback method ?

Please copy paste below method

 Future<void> didActivateAudioSession(
      CallKeepDidActivateAudioSession event) async {
    print('[didActivateAudioSession] calling...');
    final String callUUID = newUUID();
    _callKeep.backToForeground();
    _callKeep.endCall(callUUID);
  }

If this one also not working, you only can do R & D and solve this issue.

MaheshPeri19 avatar Dec 15 '20 11:12 MaheshPeri19

If you make you functions static it might work. Here is my navigation service.

class NavigationService {
  static final navigatorKey = GlobalKey<NavigatorState>();

  static Future<void> pushVideoView() {
    return navigatorKey.currentState.push(VideoCallView.route());
  }
}

VictorUvarov avatar Dec 15 '20 20:12 VictorUvarov

From what I've noticed working with FCM backgroundMessageHandler, what happens is the bgMsgHandler is running on a separate isolate than the main flutter UI thread so it has no way of accessing the navigator. One way to mitigate this is to use the flutter_local_notifications package to instantiate a local notif from within the bgmsghandler and place the navigator code inside the local notif's callback for full-screen intent. I'm not sure if I was able to get my idea across but that was how I worked around it. Hope it helps!

SirJAKfromSpace avatar Dec 23 '20 10:12 SirJAKfromSpace

@SirJAKfromSpace @VictorUvarov @MaheshPeri19 Thanks for the suggestion, will give a try for the above suggestions by this weekend and update you guys with the status of the proposed solution.

aravindhkumar23 avatar Dec 23 '20 15:12 aravindhkumar23

@SirJAKfromSpace Are you creating a notification that is displayed to the user and they have to tap on the notification? Or is there a way to instantly trigger a silent notification and handle that immediately? Or are you not starting the call until the notification is tapped?

VictorUvarov avatar Jan 04 '21 19:01 VictorUvarov

@VictorUvarov i am indeed creating a notification using flutter_local_notifications which has to be tapped by the user (when the device is unlocked). But if the device is locked the onSelectNotification callback runs automatically.

If on Android and you set showWhenLocked and android:turnScreenOn to "true" in the AndroidManifest, then it launches a fullscreen intent from the lockscreen (like an alarm or a call would). flutter_local_notifs FullscreenIntent

Also, I used the lastest prerelease version of the Firebase Cloud Messaging package, since thats the one that properly supports BgMsgHandler.

I myself learnt all this recently so it may not be the definitive answer. Just sharing my workarounds thats all. Hope it helps.

SirJAKfromSpace avatar Jan 06 '21 07:01 SirJAKfromSpace

Hi! Have you found solution? Have the same issue(

rekonvald avatar Mar 02 '21 19:03 rekonvald

From what I've noticed working with FCM backgroundMessageHandler, what happens is the bgMsgHandler is running on a separate isolate than the main flutter UI thread so it has no way of accessing the navigator. One way to mitigate this is to use the flutter_local_notifications package to instantiate a local notif from within the bgmsghandler and place the navigator code inside the local notif's callback for full-screen intent. I'm not sure if I was able to get my idea across but that was how I worked around it. Hope it helps!

Hi, is it possible to share code how to call local notification and local notification callback. I couldnt success to run my app from backgroundmessagehandler function

xyzbilal avatar Mar 03 '21 11:03 xyzbilal

@xyzbilal @rekonvald

Here's what I did:

  1. pub get the latest prerelease FCM package and the local_flutter_notifs package
  2. edit AndroidManifest and set android:showWhenLocked and android:turnScreenOn to "true"
  3. implement FCM into Flutter app properly according to FlutterFire documentation
  4. look at "fullscreen intents" in the local_flutter_notifs documentation for how they behave
  5. initialize flutterlocalnotifs and set callback function for onSelectNotif
  6. initialize fcm and pass onBgMsg a callback function
  7. instantiate a flutterLocalNotif from within fcm onBgMsg callback
  8. add navigator logic in localNotif callback (if using fullscreen intent and screen is locked, app will open up regardless)

Hope this helps. Sorry, I couldn’t comment bits of code because it would result in long confusing and perhaps misleading code that is sure to throw errors upon copy pasting.

If you want, you could look into the dart file in my repo linked here to figure out what I did. Please, feel free to leave a star if it helps :p

SirJAKfromSpace avatar Mar 03 '21 14:03 SirJAKfromSpace

Thanks you! so, you did not use callkeep at all? I looked at local_flutter_notifs package. But there is no possibility to add action buttons there to answer call, also, on IOS this notification disappears in 3 sec(It is not ok for me because I am implementing calling notification)

rekonvald avatar Mar 03 '21 14:03 rekonvald

so, you did not use callkeep at all?

Nope, I found using fcm (android) and apn (ios) push notifications to work best for me rather than callkeep (it didnt have enough documentation to be usable).

If you're working on iOS look into CallKit APN.

For iOS the localnotifs wont have any action buttons since Apple requires you use their native APN system for call pushes.

SirJAKfromSpace avatar Mar 03 '21 15:03 SirJAKfromSpace

Thank you, I researched CallKit, they also don't have functionality to take control to the fluter app on answering call(

rekonvald avatar Mar 03 '21 15:03 rekonvald

@xyzbilal @rekonvald

Here's what I did:

  1. pub get the latest prerelease FCM package and the local_flutter_notifs package
  2. edit AndroidManifest and set android:showWhenLocked and android:turnScreenOn to "true"
  3. implement FCM into Flutter app properly according to FlutterFire documentation
  4. look at "fullscreen intents" in the local_flutter_notifs documentation for how they behave
  5. initialize flutterlocalnotifs and set callback function for onSelectNotif
  6. initialize fcm and pass onBgMsg a callback function
  7. instantiate a flutterLocalNotif from within fcm onBgMsg callback
  8. add navigator logic in localNotif callback (if using fullscreen intent and screen is locked, app will open up regardless)

Hope this helps. Sorry, I couldn’t comment bits of code because it would result in long confusing and perhaps misleading code that is sure to throw errors upon copy pasting.

If you want, you could look into the dart file in my repo linked here to figure out what I did. Please, feel free to leave a star if it helps :p

this looks fine, but the code you referred to is not available on github, can you share it here

muhammedrashidm avatar Mar 29 '21 12:03 muhammedrashidm

For navigation to specific screen when your app is terminated you can use Shared Preferences. Save the call notification data with a key in prefs and when you open the app check this key to see if any data is available and if it is then navigate to call screen with that data and remove it from prefs. Of course you need the delete the notification from prefs when user did't accept call or you get a end call notification. good luck!

benyaminbeyzaie avatar Sep 20 '21 21:09 benyaminbeyzaie