amplify-flutter icon indicating copy to clipboard operation
amplify-flutter copied to clipboard

Amplify.Notifications.Push.launchNotification returns null

Open szymondobrzanski opened this issue 2 years ago • 18 comments

Description

Hello, Amplify.Notifications.Push.launchNotification returns null each time I sent push notification to terminated app. Both foreground and background notifications are delivered and read properly. I tested this behavior on both Android and iOS devices (in release mode for iOS). App is killed (by swipe - not from editor), I sent push notification, it is delivered to phone, after clicking on it app is opened but launchNotification returns null. Here is log example from Android device in release mode:

W/FlutterJNI(29654): FlutterJNI.loadLibrary called more than once
W/FlutterJNI(29654): FlutterJNI.init called more than once
I/flutter (29654): 🚀 launchNotificationFromTerminated: null

I checked and launchNotification is not consumed anywhere before.

I attach code of my configuration and notifications service:

Configuration:

Future<void> main() async {
  await configureApp();
  runMain();
}

void runMain() {
  runApp(MyApp());
}

Future<void> configureApp() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  const environment =
      String.fromEnvironment('ENVIRONMENT_F', defaultValue: Environment.prod);
  if (kDebugMode) {
    await firebaseCrashlyticsInstance.setCrashlyticsCollectionEnabled(false);
  }
  FlutterError.onError = (errorDetails) {
    firebaseCrashlyticsInstance.recordFlutterFatalError(errorDetails);
  };
  PlatformDispatcher.instance.onError = (error, stack) {
    firebaseCrashlyticsInstance.recordError(error, stack, fatal: true);
    return true;
  };
  await configureInjection(environment);
  try {
    if (Amplify.isConfigured) {
      safePrint('Amplify is already configured. Skipping configuration.');
    } else {
      final auth = AmplifyAuthCognito();
      final dataStorePlugin =
          AmplifyDataStore(modelProvider: ModelProvider.instance);
      final api = AmplifyAPI(modelProvider: ModelProvider.instance);
      final pushPlugin = AmplifyPushNotificationsPinpoint();
      PushNotificationsService.initalize(getIt<INotificationsRepository>());
      pushPlugin.onNotificationReceivedInBackground(
          PushNotificationsService.backgroundNotificationReceivedHandler);
      await Amplify.addPlugins([dataStorePlugin, api, auth, pushPlugin]);
      await Amplify.configure(amplifyconfig);
      PushNotificationsService.launchNotificationFromTerminated();
PushNotificationsService.startListeningToForeground();
          PushNotificationsService.onNotificationOpened();
    }
  } catch (e) {
    safePrint('An error occurred while configuring Amplify: $e');
    reportError(
        exception: 'Aws Amplify framework could not be initialised',
        reason: 'An error occurred while configuring Amplify: $e');
  }
}

Service:

@injectable
@pragma('vm:entry-point')
class PushNotificationsService {
  static late final PushNotificationsService _self;
  static late final INotificationsRepository _notificationsRepository;
  static late final StreamSubscription<PushNotificationMessage>
      _foregroundStream;
  static late final StreamSubscription<PushNotificationMessage>
      _notificationsOpenedStream;
  static late final StreamSubscription<String> _tokenStream;

  PushNotificationsService._internal(
      INotificationsRepository notificationsRepository) {
    _notificationsRepository = notificationsRepository;
  }

  @factoryMethod
  static PushNotificationsService initalize(
      INotificationsRepository notificationsRepository) {
    _self = PushNotificationsService._internal(notificationsRepository);
    return _self;
  }

  static Future<bool> requestPermission() async {
    final status = await Amplify.Notifications.Push.getPermissionStatus();
    if (status == PushNotificationPermissionStatus.granted) {
      return true;
    }
    if (status == PushNotificationPermissionStatus.shouldRequest ||
        status == PushNotificationPermissionStatus.shouldExplainThenRequest) {
      final status = await Amplify.Notifications.Push.requestPermissions();

      await _notificationsRepository.saveNotifications(status);
      return status;
    }
    return false;
  }

  static void startListeningToForeground() {
    _foregroundStream = Amplify
        .Notifications.Push.onNotificationReceivedInForeground
        .listen(_foregroundNotificationReceivedHandler);
  }

  static void onNotificationOpened() {
    _notificationsOpenedStream = Amplify.Notifications.Push.onNotificationOpened
        .listen(_notificationOpenedHandler);
  }

  ///Used to get device id in order to test notifications
  static void onTokenReceived() {
    _tokenStream = Amplify.Notifications.Push.onTokenReceived
        .listen(_tokenReceivedHandler);
  }

  static void launchNotificationFromTerminated() {
    final notification = Amplify.Notifications.Push.launchNotification;
    print('🚀 launchNotificationFromTerminated: $notification');
    if (notification != null) {
       //do sth
    }
  }

  static void _foregroundNotificationReceivedHandler(
      PushNotificationMessage notification) {
    print('🚀 foregroundNotificationReceivedHandler: $notification');
    if (notification.title != null && notification.body != null) {
      //display local notification
    }
  }

  static Future<void> backgroundNotificationReceivedHandler(
      PushNotificationMessage notification) async {
    print('🚀 backgroundNotificationReceivedHandler: $notification');

    ///onNotificationOpened won't work on iOS because of flutter_local_notifications config
    ///https://github.com/aws-amplify/amplify-flutter/issues/3273 -> that's why navigation is done here
    if (Platform.isIOS) {
        //do sth
    }
  }

  static void _notificationOpenedHandler(PushNotificationMessage notification) {
    ///Works only on Android as per comment above
    print('🚀 _notificationOpenedHandler: $notification');
  }


  static void cancelStreams() {
    _foregroundStream.cancel();
    _notificationsOpenedStream.cancel();
    _tokenStream.cancel();
  }
}

Categories

  • [ ] Analytics
  • [ ] API (REST)
  • [ ] API (GraphQL)
  • [ ] Auth
  • [ ] Authenticator
  • [ ] DataStore
  • [X] Notifications (Push)
  • [ ] Storage

Steps to Reproduce

No response

Screenshots

No response

Platforms

  • [X] iOS
  • [X] Android
  • [ ] Web
  • [ ] macOS
  • [ ] Windows
  • [ ] Linux

Flutter Version

3.13.0

Amplify Flutter Version

1.4.0

Deployment Method

Amplify CLI

Schema

No response

szymondobrzanski avatar Sep 08 '23 11:09 szymondobrzanski

Hi @szymondobrzanski

For iOS, if you enabled the background mode for your app, when a PN arrives on the phone after the app is killed, it will launch the app in the background. In this case, the message will be emitted via onNotificationReceivedInBackground. And then, when you click on the PN to open the app, as the app has already launched in the background, the message will be emitted via onNotificationOpened. If the background mode is not enabled, when you click the PN to launch the app, the message should be accessible via Amplify.Notifications.Push.launchNotification.

For android, while you are seeing Amplify.Notifications.Push.launchNotification returns null, do you receive any message event from other channels (onNotificationReceivedInBackground etc.)?

HuiSF avatar Sep 08 '23 23:09 HuiSF

Hi @HuiSF, nope the only log I'm getting is from Amplify.Notifications.Push.launchNotification as stated above, other channels don't invoke any notifications.

szymondobrzanski avatar Sep 09 '23 06:09 szymondobrzanski

@HuiSF any update from your side? Did you try to reproduce it?

szymondobrzanski avatar Sep 14 '23 08:09 szymondobrzanski

Hey @HuiSF @dnys1 any update?

szymondobrzanski avatar Sep 21 '23 11:09 szymondobrzanski

@szymondobrzanski, apologies on the delayed response. Can you share details of the android environment that you're using? Is it an emulator or a real, physical device? There was a similar issue (#3143) that we couldn't reproduce, and has since been closed tied to Android behavior .

On the iOS side, I believe it's expected behavior if the you have enabled background mode. We're still investigating this, so any further context/answers would be helpful! Thank you.

cwomack avatar Oct 12 '23 18:10 cwomack

Hey @cwomack , thanks for response. I was using Android physical device with API Level 26. Why returning null after opening app from terminated state in iOS device would be expected behavior? Enabling background modes for iOS is one of configuration steps in Amplify documentation and there is none information that getLaunchNotification will return null for iOS by default.

szymondobrzanski avatar Oct 13 '23 06:10 szymondobrzanski

@cwomack I am experiencing the same behavior on iOS. I have tried with both with background notification mode on and off based on your comment above. I have my notification code register with onNotificationOpened and check Amplify.Notifications.Push.launchNotification for notifications after the app has initialized with no luck.

Foreground notifications and background notifications both work as expected 100% of the time but when I kill the app trigger a notification, tap it to launch the app, it is not received by the launchNotification or onNotificationOpened.

chrispypatt avatar Dec 13 '23 08:12 chrispypatt

Same issue on iOS, launchNotification and onNotificationOpened are both not trigged from terminated state.

rest950 avatar Feb 15 '24 07:02 rest950

@rest950 Do you have firebase messaging as part of the project?

If you are using firebase_messaging in any way in the project, iOS will stop behaving as expected, the two libraries are currently incompatible.

see #4237

bitsplash-andy avatar Feb 16 '24 15:02 bitsplash-andy

Just to add to this, I am also experiencing this issue, but only on Android. I receive tokens, foreground and background notifications as expected but am unable to get access to the notification that launched the app. iOS works fine providing the firebase_messaging library isn't used in conjunction with Amplify. Frustratingly, I didn't experience this issue using Firebase, so we've swapped one problem for another.

bitsplash-andy avatar Feb 16 '24 16:02 bitsplash-andy

@bitsplash-andy

My project no longer utilizes Firebase Messaging; it only relies on other Firebase services. However, on iOS in the terminated state, I am unable to trigger launchNotification or onNotificationOpened.

Regarding Android's ability to obtain launchNotification, in my tests, I found that it can only be trigged in a release build; it is not trigged in a debug build. Please give it a try and see if you encounter the same issue.

rest950 avatar Feb 17 '24 06:02 rest950

Hi @rest950

However, on iOS in the terminated state, I am unable to trigger launchNotification

Have you enabled the background mode for you iOS app?

If you have enabled background mode for your iOS app, when a push notification arrives on the device and your app is in the terminated state - the notification wakes your app up in the background. In this case, the notification will be sent to the callback of onNotificationReceivedInBackground - so there won't be a launchNotification.

I am unable to trigger onNotificationOpened.

Is this happening when you tap on a notification in the notification center or the PN banner to open the app?

HuiSF avatar Feb 19 '24 18:02 HuiSF

I am still seeing this on iOS and we do not use any Firebase dependencies.

chrispypatt avatar Feb 25 '24 23:02 chrispypatt

In our case this didn’t work on Android because we were using google-services 4.4.1 in our build.gradle.

Downgrading google-services to 4.3.15 resolved the issue. Not sure if the library is incompatible with 4.4.x versions at this time?

bitsplash-andy avatar Feb 25 '24 23:02 bitsplash-andy

I am unable to trigger onNotificationOpened.

Is this happening when you tap on a notification in the notification center or the PN banner to open the app?

@HuiSF

After my attempts, I found that tap from notification center successfully triggered onNotificationOpened. However, tap from the PN Banner did not trigger onNotificationOpened. Is this the expected behavior?

rest950 avatar Mar 19 '24 06:03 rest950

Apologies for the delay. Thank you for the additional info. We will attempt to reproduce this with the additional info provided.

Jordan-Nelson avatar May 02 '24 19:05 Jordan-Nelson