Plugin.FirebasePushNotifications icon indicating copy to clipboard operation
Plugin.FirebasePushNotifications copied to clipboard

How I made it working on Android with custom icon and background notification. Source code example

Open Diaver opened this issue 1 year ago • 1 comments

I hope it will be helpful to someone.

I added custom notification param OPEN_URL

Notification Service:

    public class PushNotificationService : IPushNotificationService
    {
        private readonly IFirebasePushNotification _firebasePushNotification;
        private readonly INotificationPermissions _notificationPermissions;
        private readonly IPushNotificationsApiClient _pushNotificationsApiClient;

        public PushNotificationService(
            IFirebasePushNotification firebasePushNotification,
            INotificationPermissions notificationPermissions,
            IPushNotificationsApiClient pushNotificationsApiClient)
        {
            _firebasePushNotification = firebasePushNotification;
            _notificationPermissions = notificationPermissions;
            _pushNotificationsApiClient = pushNotificationsApiClient;
        }

        public async Task StartAsync()
        {
            AuthorizationStatus authorizationStatus = await _notificationPermissions.GetAuthorizationStatusAsync();

            if (authorizationStatus != AuthorizationStatus.Granted)
            {
                await _notificationPermissions.RequestPermissionAsync();
            }

            if (authorizationStatus == AuthorizationStatus.Granted)
            {
                await _firebasePushNotification.RegisterForPushNotificationsAsync();
                _firebasePushNotification.TokenRefreshed += FirebasePushNotificationOnTokenRefreshed;
                await _pushNotificationsApiClient.MobileSubscribeAsync(_firebasePushNotification.Token);
            }
        }

        private void FirebasePushNotificationOnTokenRefreshed(object sender, FirebasePushNotificationTokenEventArgs e)
        {
           _= Task.Run(() => _pushNotificationsApiClient.MobileSubscribeAsync(_firebasePushNotification.Token));
        }
    }

FirebaseService inside Platforms/Android folder

[Service(DirectBootAware = true, Exported = true, Enabled = true)]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class FirebaseService : FirebaseMessagingService
{
    public override ComponentName StartService(Intent service)
    {
        Log.Info("GCM", $"MyFirebaseService started from intent {service}");

        return base.StartService(service);
    }

    public override void OnMessageReceived(RemoteMessage message)
    {
        base.OnMessageReceived(message);
        var notification = message.GetNotification();
        Log.Info("FirebaseService: ", "OnMessageReceived");
        SendNotification(notification.Body, notification.Title, message.Data);
    }

    private void SendNotification(string messageBody, string title, IDictionary<string, string> data)
    {
        var notificationId = Int32.Parse(DateTime.Now.ToString("MMddHHmmsss"));

        var intent = new Intent(this, typeof(MainActivity));
        intent.AddFlags(ActivityFlags.ClearTop);
        intent.AddFlags(ActivityFlags.SingleTop);

        foreach (var key in data.Keys)
        {
            string value = data[key];
            intent.PutExtra(key, value);
        }

        PendingIntent pendingIntent = PendingIntent.GetActivity(this, notificationId, intent, PendingIntentFlags.OneShot | PendingIntentFlags.Immutable);
        NotificationManager notificationManager = (NotificationManager)GetSystemService(Context.NotificationService);
        Int64 timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();

        var notificationBuilder = new NotificationCompat.Builder(this, MainActivity.NotificationChannelId.ToString())
            .SetContentTitle(title)
            .SetSmallIcon(AtlasMobile.Resource.Drawable.my_logo_white)
            .SetContentText(messageBody)
            .SetWhen(timestamp)
            .SetContentIntent(pendingIntent)
            .SetAutoCancel(true)
            .SetShowWhen(true)
            .SetPriority(NotificationCompat.PriorityHigh);

        notificationManager.Notify(notificationId, notificationBuilder.Build());
    }
}

MainActivity:

[IntentFilter(new[] { "Open_URI" }, Label = "Open_URI", Categories = new []{"android.intent.category.DEFAULT"})]
    [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
    public class MainActivity : MauiAppCompatActivity
    {
        public static int NotificationChannelId = 1152;
        public static string NotificationChannelName = "Push Notifications";
        public static string NotificationChannelDescription = "Receive notifications";

        public override void OnConfigurationChanged(Configuration newConfig)
        {
            base.OnConfigurationChanged(newConfig);
            GrialKit.NotifyConfigurationChanged(newConfig);
        }

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            if (ContextCompat.CheckSelfPermission(this, Android.Manifest.Permission.PostNotifications) == Permission.Granted && IsPlayServicesAvailable())
            {
                CreateNotificationChannel();
            }
        }

        public Boolean IsPlayServicesAvailable()
        {
            Int32 resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);

            if (resultCode != ConnectionResult.Success)
            {
                if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
                {
                    Log.Error("MAIN", GoogleApiAvailability.Instance.GetErrorString(resultCode));
                }
                else
                {
                    Log.Error("MAIN", "Device not supported.");
                    Finish();
                }

                return false;
            }

            return true;
        }

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

            if (intent != null && intent.Extras != null)
            {
                foreach (var key in intent.Extras.KeySet())
                {
                    if (key == "Open_URI")
                    {
                        string idValue = intent.Extras.GetString(key);
                        if (Preferences.ContainsKey("Open_URI"))
                            Preferences.Remove("Open_URI");

                        Preferences.Set("Open_URI", idValue);
                    }
                }
            }
        }

        public void CreateNotificationChannel()
        {
            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                var notificationManager = (NotificationManager)GetSystemService(NotificationService);

#pragma warning disable CA1416
                var notificationChannel = new NotificationChannel(NotificationChannelId.ToString(), NotificationChannelName, NotificationImportance.High)
                {
                    Description = NotificationChannelDescription
                };

                notificationManager.CreateNotificationChannel(notificationChannel);
#pragma warning restore CA1416
            }
        }
    }

AndroidManifest.xml application tag:

    <application
            android:allowBackup="true"
            android:icon="@mipmap/appicon"
            android:roundIcon="@mipmap/appicon_round" 
			android:supportsRtl="true"
            android:usesCleartextTraffic="true">

        <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>
        
        <meta-data android:name="com.google.firebase.messaging.default_notification_icon"
                   android:resource="@drawable/my_logo_white"/>

        <meta-data
                android:name="com.google.firebase.messaging.default_notification_channel_id"
                android:value="@string/default_notification_channel_id"/>

    </application>

Notification message from Server:

 Random rnd = new Random();
            int month  = rnd.Next(1, 2000);
                
            return new Message
            {
                Token = token,
                Notification = new Notification
                {
                    Body = body,
                    Title = title,
                },
                Android = new AndroidConfig
                {
                    Notification = new AndroidNotification
                    {
                        ClickAction = "Open_URI",
                    }
                },
                Apns = new ApnsConfig
                {
                    Aps = new Aps
                    {
                        Category = "General"
                    }
                },
                Data = new Dictionary<string, string>
                {
                    {"Open_URI", month.ToString()}
                }
            };

Diaver avatar May 10 '24 02:05 Diaver

Cool, thanks for sharing.

Can I ask why you‘re sending a new local notification (notificationManager.Notify) when you receive a push notification? Cant you send the notification icon as property of the notification message?

One point to remark: The plugin has built-in logic to create/update/delete notification channels at app startup. The sample app makes use of this feature.

thomasgalliker avatar May 10 '24 11:05 thomasgalliker