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

backToForeground() does not work when app is closed on Android #21

Open
jfaltis opened this issue Oct 27, 2020 · 29 comments
Open

backToForeground() does not work when app is closed on Android #21

jfaltis opened this issue Oct 27, 2020 · 29 comments

Comments

@jfaltis
Copy link

jfaltis commented Oct 27, 2020

First of all: This is a very nice library! I am using callkeep to display an incoming call notification when a message is received via firebase_messaging in onBackgroundMessage. When the user answers the incoming call I call backToForeground() and endCall() to just open the app when answering the call. This works pretty good when the app is running and in the background.

But when the app is closed and a message is received it shows the incoming call notification but when you click on answer, it doesn't launch the app.

I think this is a critical feature for such use case since it can't be guaranteed that the app is alive. I don't know how easy it is to extend backToForeground() so it handles this case properly or if this is even possible at all.

I have yet to test the behavior on iOS if it launches the app by default because backToForeground is not implemented for iOS according to the source code of callkeep.

If you know an alternative solution, please let me know.

@ghenry
Copy link

ghenry commented Oct 27, 2020

I've read your issue and I have not tried it that way. I am answering
the call and then answering the SIP INVITE in dart-sip-ua and staying
in the native dialler screen and using all the callbacks for hold/dtmf
etc.

Once a successful call is ended I am about to use backtoforeground,
but I haven't tested that yet.

If you end the call, what are you doing about the native dialler call
logs? Isn't the call recorded there? What is the architecture of your
app like? SIP?

Regarding iOS, I understand true background messages do not work
with FCM, just onLaunch and onResume, so I am using what apple say to
use for VoIP:

firebase/flutterfire#116 (comment)
https://github.com/masashi-sutou/flutter_ios_voip_kit

I may move to this though for a custom inbound call screen or copy it to this project:

https://github.com/doneservices/flutter_callkeep/blob/01e683d16be6d6675cf678d85be683718382a96a/lib/flutter_callkeep.dart#L248

doneservices/flutter_callkeep#16

@ghenry
Copy link

ghenry commented Oct 28, 2020

Actually, callKeep.backToForeground(); is working for me. It is there

public void backToForeground(@NonNull MethodChannel.Result result) {

@jfaltis
Copy link
Author

jfaltis commented Oct 28, 2020

If you end the call, what are you doing about the native dialler call
logs? Isn't the call recorded there? What is the architecture of your
app like? SIP?

I planned to use it only for the incoming call notification and I didn't thought alot about call logs. Can you prevent it from being written to the call logs? If it's not possible then I think it is not that dramatic. I want to use Agora for Video Calling with custom UI and therefore I just need the incoming call notification to open the app. This library is the most promising thing I found which could work on iOS and Android with Flutter.

Regarding iOS, I understand true background messages do not work
with FCM, just onLaunch and onResume, so I am using what apple say to

I know that onBackgroundMessage is not supported by firebase_messaging yet but there is a PR (firebase/flutterfire#2016) and a Google dev commented "Hold on to your hats... New dev release coming soon." so I think this is going to be merged soon. I also read somewhere that CallKit which is used for iOS always opens the app. So if onBackgroundMessage is working reliably on iOS this might be a good solution if Android behaved the same.

use for VoIP:
FirebaseExtended/flutterfire#116 (comment)
https://github.com/masashi-sutou/flutter_ios_voip_kit

This also looks interesting if the PR with onBackgroundMessage won't work reliably on iOS.

I may move to this though for a custom inbound call screen or copy it to this project:

https://github.com/doneservices/flutter_callkeep/blob/01e683d16be6d6675cf678d85be683718382a96a/lib/flutter_callkeep.dart#L248

doneservices/flutter_callkeep#16

That also looks interesting

Actually, callKeep.backToForeground(); is working for me. It is there

public void backToForeground(@NonNull MethodChannel.Result result) {

It is working for you when the app is killed? It is working for me only when the app is in background but not when it was killed. What Android version?

@ghenry
Copy link

ghenry commented Oct 28, 2020 via email

@jfaltis
Copy link
Author

jfaltis commented Oct 28, 2020

I wanted to test if the implementation of backToForeground() of the other library (https://github.com/doneservices/flutter_callkeep) can open the app when it was killed but I could not get it running in the background, only foreground. It's the same problem another person was facing: doneservices/flutter_callkeep#19

@ghenry
Copy link

ghenry commented Oct 28, 2020 via email

@jfaltis
Copy link
Author

jfaltis commented Oct 28, 2020

I read up on the flag used to do this on the android native side, and the app needs to be running as it's moved up the activity stack. Read the source and have a Google to confirm yourself. That's my understanding anyway.

That's what I picked up too but I made an interesting observation: When I kill the app after the incoming call notification is shown and then click on answer it works despite when backToForeground() is called when the app is closed. This seems contradicting to me.

I am also initializing CallKeep and setting the CallKeepPerformAnswerCallAction "listener" when receciving a notification. I tried initializing and setting the listener with a static instance when the app is started but that's not working. I guess because onBackgroundMessage is running as it's own service.

@aponski
Copy link

aponski commented Oct 29, 2020

Hi @jfaltis it looks like we have the same use case :). I also want to use callkeep plugin to inform user about incoming call and then redirect to app with webrtc call. I also tried the other flutter_callkeep plugin with the same error in background mode as described here.

One question for you, how did you make callkeep plugin to work when app is terminated? Other plugins that used (flutter_local_notifications, flutter_ringtone_player) are working ok in background mode when app is terminated, but callkeep is not, nothing is shown. Any suggestions?

It would be nice if you could share your implementation of backgroundMessageHandler

@jfaltis
Copy link
Author

jfaltis commented Oct 29, 2020

Hi @aponski, I am doing all the initialization of callkeep in the onBackgroundMessage because doing it somewhere else did not properly work for me.

static Future<dynamic> onBackgroundMessage(
    Map<String, dynamic> message,
  ) async {
      final FlutterCallkeep _callKeep = FlutterCallkeep();
      _callKeep.setup(<String, dynamic>{
        'ios': {
          'appName': 'CallKeepDemo',
        },
        'android': {
          'alertTitle': 'Permissions required',
          'alertDescription':
              'This application needs to access your phone accounts',
          'cancelButton': 'Cancel',
          'okButton': 'ok',
        },
      });
      final String callUUID = Uuid().v4();
      _callKeep.on(CallKeepPerformAnswerCallAction(),
          (CallKeepPerformAnswerCallAction event) {
        _callKeep.backToForeground();
        _callKeep.endCall(event.callUUID);
      });
      _callKeep.displayIncomingCall(callUUID, "Caller Name");
  }

Maybe you haven't registered the Plugin properly?

My Application.kt

package your.package.name

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
import com.github.cloudwebrtc.flutter_callkeep.FlutterCallkeepPlugin

class Application : FlutterApplication(), PluginRegistrantCallback {
  override fun onCreate() {
    super.onCreate()
    FlutterFirebaseMessagingService.setPluginRegistrant(this)
  }

  override fun registerWith(registry: PluginRegistry?) {
    io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin.registerWith(registry?.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
    FlutterCallkeepPlugin.registerWith(registry?.registrarFor("com.github.cloudwebrtc.flutter_callkeep"));
  }
}

@aponski
Copy link

aponski commented Oct 29, 2020

@jfaltis thanks a lot, initialization in background message handler did the job, it's working. Now I guess we have to figure out hot to make backToForeground() work when app is terminated. Any ideas?

@jfaltis
Copy link
Author

jfaltis commented Oct 29, 2020

@aponski I am happy to help you :) Yes I guess that is the only thing we have to figure out to make it work reliably on Android. But I don't know if this will also work reliably on iOS with the onBackgroundMessage PR for iOS (firebase/flutterfire#2016). I currently don't have an iOS Device nor an Apple Developer Account to test this. Could you maybe test that?

@aponski
Copy link

aponski commented Oct 29, 2020

@jfaltis sure, I will test it on iOS and let you know

@aponski
Copy link

aponski commented Oct 29, 2020

@jfaltis Here is a nice summary of what’s working regarding notifications on Android and iOS: link. On iOS I think the only way is to use voip notifications (Apple PushKit) with an additional flutter plugin flutter_voip_push_notification. I will try it but currently my main concern is to make it fully work on Android first.

@ghenry
Copy link

ghenry commented Oct 29, 2020 via email

@aponski
Copy link

aponski commented Oct 29, 2020

Also found this stackoverflow topic which I think is worth checking.

@ghenry
Copy link

ghenry commented Oct 29, 2020

Also found this stackoverflow topic which I think is worth checking.

Yep, this is the solution and something I've known now for a long while. We can put iOS to bed: :-)

The only exception for that are calls, which were was my case. I had to use apple VoIP notifications, There are two separate flutter packages to deal with that flutter_voip_push_notification and flutter_call_kit

I also knew about https://pub.dev/packages/bringtoforeground and there is this too:

https://github.com/doneservices/flutter_callkeep/wiki/Native-android-code-that-handles-incoming-calls

@jfaltis
Copy link
Author

jfaltis commented Oct 30, 2020

@jfaltis Here is a nice summary of what’s working regarding notifications on Android and iOS: link. On iOS I think the only way is to use voip notifications (Apple PushKit) with an additional flutter plugin flutter_voip_push_notification. I will try it but currently my main concern is to make it fully work on Android first.

This looks interesting and could be one solution for iOS. @aponski Can you verify that my idea with the new PR on iOS is definitely not working?

I also knew about https://pub.dev/packages/bringtoforeground and there is this too:

https://github.com/doneservices/flutter_callkeep/wiki/Native-android-code-that-handles-incoming-calls

@ghenry I also tried to get this working for bringing the Android App to foreground when the app is killed but for me it did not work. When I had the plugin in my pubspec.yml somehow none of my messages were caught by onBackgroundMessage even though I wasn't calling any code of that plugin. But I am not 100 percent sure if it was the fault of the plugin. If you get it working please let me know.

Another problem I see with the library is that it only works up to Android 9 and that some vendors don't allow bringing activites into foreground.

Some android vendors like Xaiomi will prevent your app's services from bringing activities to the foreground, so you have to tell the user: go to settings, enable some setting, which differs by vendor

The proposed solution is unfortunately bad user experience in my opinion.

If we get https://pub.dev/packages/bringtoforeground working for Android 9 and lower the problems left are no support for Android 10 and some vendors will disallow it.

@payam-zahedi
Copy link

@jfaltis @aponski I have the Same Scenario and the same issue. did you find out any solution for this issue?

@jfaltis
Copy link
Author

jfaltis commented Dec 1, 2020

@payam-zahedi I have not found a solution but I also did not investigate alot further and don't plan to in the future.

@ahmedJD
Copy link

ahmedJD commented Dec 15, 2020

@jfaltis i have the same problem when app is killed _callKeep.backToForeground(); is not working and an exception is shown Unhandled Exception: PlatformException(error, Attempt to invoke virtual method 'int android.content.Context.checkPermission(java.lang.String, int, int)' on a null object reference, null, java.lang.NullPointerException: Attempt to invoke virtual method 'int android.content.Context.checkPermission(java.lang.String, int, int)' on a null
object reference

@MaheshPeri19
Copy link

@jfaltis thanks a lot, initialization in background message handler did the job, it's working. Now I guess we have to figure out hot to make backToForeground() work when app is terminated. Any ideas?

Any workaround for this issue for backToForeground() ?

@aponski
Copy link

aponski commented Dec 18, 2020

@payam-zahedi I haven't found a solution yet and I had to park this project for a while.

@cloudwebrtc
Copy link
Member

cloudwebrtc commented Jan 10, 2021

@jfaltis @ghenry @MaheshPeri19 @ahmedJD @payam-zahedi

fixed b09080e

you can use it like this:

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
  print('backgroundMessage: message => ${message.toString()}');
  Map<dynamic, dynamic> payload = message['data'] as Map<dynamic, dynamic>;
  String callerId = payload['caller_id'] as String;
  String callerName = payload['caller_name'] as String;

  bool appIsOpened = await _callKeep.backToForeground();
  if (appIsOpened) {
    print('APP is opened, use callkeep display incoming call now.');
    await displayIncomingCall(callerId, callerName);
  } else {
    print('APP is closed, wake it up and use callkeep to display incoming calls.');
  }
}

@cloudwebrtc
Copy link
Member

@ghenry I did a test and can wake up the app to the foreground. The logic code you used in the background can be moved to the app after startup, so there is no need to perform registration in the background.

@ghenry
Copy link

ghenry commented Jan 10, 2021

Thanks. I'll try just waking it up on receipt of a push like iOS does.

@ghenry
Copy link

ghenry commented Mar 28, 2021

@jfaltis @ghenry @MaheshPeri19 @ahmedJD @payam-zahedi

fixed b09080e

you can use it like this:

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
  print('backgroundMessage: message => ${message.toString()}');
  Map<dynamic, dynamic> payload = message['data'] as Map<dynamic, dynamic>;
  String callerId = payload['caller_id'] as String;
  String callerName = payload['caller_name'] as String;

  bool appIsOpened = await _callKeep.backToForeground();
  if (appIsOpened) {
    print('APP is opened, use callkeep display incoming call now.');
    await displayIncomingCall(callerId, callerName);
  } else {
    print('APP is closed, wake it up and use callkeep to display incoming calls.');
  }
}

You can't use await in the background handler :-(

@ghenry
Copy link

ghenry commented Apr 1, 2021

Stupid me. It returns a future so can use async in the definition.

@xaviersydney48
Copy link

Hello i have the same problem it cant ork in android 10,,
_callKeep.backToForeground(),,seems not working on android 10 but in android 9 works

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

8 participants