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

[ANDROID] Handling background messages from Dart code #464

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

efraespada
Copy link

@efraespada efraespada commented Aug 18, 2021

Test this PR with this:

onesignal_flutter:
    git:
      url: https://github.com/efraespada/OneSignal-Flutter-SDK.git

I'm implementing OneSignal because of its reliability. Previously I used FCM where all the data push messages could be handled since the same (Dart) point.

Background notifications handling is not possible with OneSignal in Flutter right now.

I added the background handling functionality in Android (even if the app was swiped away).

/// Android: Foreground and background
/// iOS: Only foreground
Future<void> _backgroundHandler(OSNotificationReceivedEvent event) async {
  print('HANDLER CALLED WITH: $event');

  /// Display Notification, send null to not display
  event.complete(event.notification);
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  void initState() {
    super.initState();
    // before --> OneSignal.shared.setNotificationWillShowInForegroundHandler(_backgroundHandler);
    OneSignal.shared.setNotificationWillShowHandler(_backgroundHandler);
  }
}

Create a NotificationServiceExtension with XNotificationServiceExtension:

package com.your.app;

import com.onesignal.flutter.XNotificationServiceExtension

class NotificationServiceExtension : XNotificationServiceExtension()

And implement it in the AndroidManifest.xml:

<application>
   <meta-data
      android:name="com.onesignal.NotificationServiceExtension"
      android:value="com.your.app.NotificationServiceExtension" />
</application>

No current functionality has changed.

iOS (not completed)

The needed logic is the same as Android:

  • A Dart isolated callback function (already implemented):
void _callbackDispatcher() {
  // Initialize state necessary for MethodChannels.
  WidgetsFlutterBinding.ensureInitialized();

  const MethodChannel _channel = MethodChannel('OneSignalBackground');

  // This is where we handle background events from the native portion of the plugin.
  _channel.setMethodCallHandler((MethodCall call) async {
    if (call.method == 'OneSignal#onBackgroundNotification') {
      final CallbackHandle notificationCallbackHandle =
          CallbackHandle.fromRawHandle(
              call.arguments['notificationCallbackHandle']);

      // PluginUtilities.getCallbackFromHandle performs a lookup based on the
      // callback handle and returns a tear-off of the original callback.
      final closure =
          PluginUtilities.getCallbackFromHandle(notificationCallbackHandle)!
              as Future<void> Function(OSNotificationReceivedEvent);

      try {
        Map<String, dynamic> messageMap =
            Map<String, dynamic>.from(call.arguments['message']);
        final notification = OSNotificationReceivedEvent(messageMap);
        await closure(notification);
      } catch (e) {
        // ignore: avoid_print
        print(
            'OneSignal: An error occurred in your background messaging handler:');
        // ignore: avoid_print
        print(e);
      }
    } else {
      throw UnimplementedError('${call.method} has not been implemented');
    }
  });

  // Once we've finished initializing, let the native portion of the plugin
  // know that it can start scheduling alarms.
  _channel.invokeMethod<void>('OneSignal#backgroundHandlerInitialized');
}
  • A NotificationServiceExtension that runs our plugin callback function and the user callback function (where developers handle notifications) which is sent to the OneSignalBackground channel for executing the notification handling.
 final CallbackHandle notificationCallbackHandle =
          CallbackHandle.fromRawHandle(call.arguments['notificationCallbackHandle']);

// PluginUtilities.getCallbackFromHandle performs a lookup based on the
// callback handle and returns a tear-off of the original callback.
final closure = PluginUtilities.getCallbackFromHandle(notificationCallbackHandle)!
              as Future<void> Function(OSNotificationReceivedEvent);

Map<String, dynamic> messageMap = Map<String, dynamic>.from(call.arguments['message']);
final notification = OSNotificationReceivedEvent(messageMap);
await closure(notification);

After some days I could create a FlutterEngine in the OneSignalNotificationServiceExtension but there is a problem:

  • FlutterEngine needs some seconds to start the plugin callback function.
  • Before launching our plugin callback function, the iOS system kills the OneSignalNotificationServiceExtension in less than a second.

Then the OneSignalBackground channel won't start.

All I'm talking about has been coded and tested but not committed just to keep this PR clean with the Android changes.


This change is Reviewable

@apoorvpandey0
Copy link

I'm frustrated that this feature is not implemented in SDK already!

@apoorvpandey0
Copy link

apoorvpandey0 commented Mar 5, 2022

The plugin `onesignal_flutter` uses a deprecated version of the Android embedding.
To avoid unexpected runtime failures, or future build failures, try to see if this plugin supports the Android V2 embedding. Otherwise, consider removing it since a future release of Flutter will remove these deprecated APIs.
If you are plugin author, take a look at the docs for migrating the plugin to the V2 embedding: https://flutter.dev/go/android-plugin-migration.

Getting this warning with Flutter v2.10.1

@iAmWillShepherd
Copy link
Contributor

iAmWillShepherd commented Dec 4, 2022

I'm frustrated that this feature is not implemented in SDK already!

I've just tried to reproducing this, and the event handler fires. Could you share some info about your environment 👇🏽 ?

  • Share the output of executing flutter --version in your CLI
  • What version of Android are you targeting
  • Which version of the SDK you used
The plugin `onesignal_flutter` uses a deprecated version of the Android embedding.
To avoid unexpected runtime failures or future build failures, try to see if this plugin supports the Android V2 embedding. Otherwise, consider removing it since a future release of Flutter will remove these deprecated APIs.
If you are plugin author, take a look at the docs for migrating the plugin to the V2 embedding: https://flutter.dev/go/android-plugin-migration.

Getting this warning with Flutter v2.10.1

@apoorvpandey0 we shipped a fix for the issue you mentioned in PR #486

@apoorvpandey0
Copy link

@iAmWillShepherd pl look into this issue for understanding my original question #557

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

Successfully merging this pull request may close these issues.

3 participants