TobiasBuchholz / Plugin.Firebase

Wrapper around the native Android and iOS Firebase Xamarin SDKs
MIT License
220 stars 49 forks source link

Notification support for closed app? #145

Closed andyzukunft closed 1 year ago

andyzukunft commented 1 year ago

Hey all,

I migrated my in-development app from Xamarin to MAUI and moved from Plugin.FirebasePushNotification to Plugin.Firebase. With the Plugin.FirebaasePushNotification the notifications are shown when the app is opened, in background and closed. I did not change the test notification (message directed to fcm token) being sent from my backend from the last time I tested this feature (as far as I can tell).

During testing (atm on Android) I realised that the notification is received and displayed just fine when the app is opened or in background. However if the app is closed the notification is not displayed. Did I miss something or is this a known limitation?

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowBackup="true" android:icon="@mipmap/app_icon" android:supportsRtl="true">
        <!-- firebase cloud message -->
        <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@mipmap/app_icon" />
        <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
        <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="${applicationId}" />
            </intent-filter>
        </receiver>

    </application>

    <!-- permissions -->
</manifest>

MainActivity.cs

[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTask, ScreenOrientation = ScreenOrientation.Portrait,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
    public class MainActivity : MauiAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            HandleIntent(Intent);
            CreateNotificationChannelIfNeeded();
        }

        protected override void OnNewIntent(Intent intent)
        {
            base.OnNewIntent(intent);
            HandleIntent(intent);
        }

        private static void HandleIntent(Intent intent)
        {
            FirebaseCloudMessagingImplementation.OnNewIntent(intent);
        }

        private void CreateNotificationChannelIfNeeded()
        {
            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                CreateNotificationChannel();
            }
        }

        private void CreateNotificationChannel()
        {
            var channelId = $"{PackageName}.general";
            var notificationManager = (NotificationManager)GetSystemService(NotificationService);
            var channel = new NotificationChannel(channelId, "General", NotificationImportance.Default);
            notificationManager.CreateNotificationChannel(channel);
            FirebaseCloudMessagingImplementation.ChannelId = channelId;
            //FirebaseCloudMessagingImplementation.SmallIconRef = Resource.Drawable.ic_push_small;
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
        {
            Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

Fcm Message

{
    "token": "token",
    "Data": {
        "Title": "Message",
        "Body": "Test",
        "Icon": "app_icon",
        "Color": "#bb2231"
    },
    "Priority": "high",
    "TimeToLive": 86400
}

{
    "token": "token",
    "Notification": {
        "Title": "Test",
        "Body": "Test"
    },
    "Data": {
        "Title": "Test",
        "Body": "Test",
        "Icon": "app_icon",
        "Color": "#bb2231"
    },
    "Priority": "high",
    "TimeToLive": 86400
}

I tried both variations. My regular variation is the one without Notification because as far as I know only messages without notification are displayed when the app is closed.

andyzukunft commented 1 year ago

I have good news!

The problem has been identified. There are actually several:

  1. I think Plugin.Firebase has the same "bug" as Plugin.FirebasePushNotifcation in regard to debugging the FCM feature. It seems one has to startup the app WITHOUT debugging. Close it, start it again and from now on it should be working properly.
  2. I believe after problem 1) is fixed I get the error message popup Developer warning for package "***" Failed to post notification to channel "null". See log for more details in Android when I send a notification to the app. I fixed it by removing the Build.VERSION.SdkInt check (Build.VERSION.SdkInt >= BuildVersionCodes.O) for before the call to CreateNotificationChannel() in MainActivity.cs. Android 8.1 is currently my minimum so this check is always fulfilled however the CreateNotificationChannel-code was not executed on an Android 11 emulator. Have no clue why this happens. Super weird.
  3. After Android 11 works just fine we seem to have to add something starting with Android 13. For Android 13+ the permission android.permission.POST_NOTIFICATION (more information) has to be added to the AndroidManifest.xml and has to be requested during startup. Current MAUI implementation does not support this permission. So I suggest to add the following to the Android Activity (e.g. where/before the channel is created).
    if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
    {
    #pragma warning disable CA1416 // Validate platform compatibility
    if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.PostNotifications) != Permission.Granted)
    {
        ActivityCompat.RequestPermissions(this, new[] { Manifest.Permission.PostNotifications }, 0);
    }
    #pragma warning restore CA1416 // Validate platform compatibility
    }

All of this has been tested with the following FCM message (I upgraded my backend to support the new Google REST v1 backend):

"message": {
    "token": "token",
      "android": {
        "notification": {
          "title": "Message",
          "body": "Test1"
        },
      "priority": "high",
      "ttl": "86400s"
    }
  }

@TobiasBuchholz Can you please add the information (Problem 1: how to test FCM, Problem 2: possible solution if FCM notifications are not working on closed app, Problem 3: Android 13 additions, examle FCM message for testing) to your documentation?

I will keep the issue open. Please feel free to close it at your convenience.

angelru commented 1 year ago

@andyzukunft thanks for this information, you may want to add this information and request a pull.

angelru commented 1 year ago

any news about this?

TobiasBuchholz commented 1 year ago

I've made a reference to @andyzukunft's helpful comment in the troubleshooting section of the cloud messaging documentation.