Xamarin Forms: Load a contentpage when tap push notification android

SreeeeSreeee INMember ✭✭✭✭✭
edited March 28 in Xamarin.Forms

I completed receiving test notification from FCM console. Now I am trying to open a page when tapping the notification. Any ideas about how to achieve this? I have searched the internet but can't find a working solution. I am also able to send the notification through the postman.

Best Answer

  • SreeeeSreeee IN ✭✭✭✭✭
    Accepted Answer

    @LandLu, @chetanrawat, @PatrickJelitto I handled the notification tapping in following way. The page loading is handled in App.xaml.cs.

    On OnCreate():

    //Background or killed mode
                if (Intent.Extras != null)
                {
                    foreach (var key in Intent.Extras.KeySet())
                    {
                        var value = Intent.Extras.GetString(key);
                        if (key == "webContentList") 
                        {
                            if (value?.Length > 0)
                            {
                                isNotification = true;
                                LoadApplication(new App(domainname, value));
                            }
                        }
                    }
                }
                //Foreground mode
                if (FirebaseNotificationService.webContentList.ToString() != "")
                {
                    isNotification = true;
                    LoadApplication(new App(domainname, FirebaseNotificationService.webContentList.ToString()));
                    FirebaseNotificationService.webContentList = "";
                }
    
                //Normal loading
                if (!isNotification)
                {
                    LoadApplication(new App(domainname, string.Empty));
                }
    

    On FirebaseNotificationService:

    [Service]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    public class FirebaseNotificationService : FirebaseMessagingService
    {
        public static string webContentList = "";
        public override void OnMessageReceived(RemoteMessage message)
        {
            base.OnMessageReceived(message);
            webContentList = message.Data["webContentList"];
    
            try
            {
                SendNotificatios(message.GetNotification().Body, message.GetNotification().Title);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error:>>" + ex);
            }
        }
    
        public void SendNotificatios(string body, string Header)
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.O)
            {
                var intent = new Intent(this, typeof(MainActivity));
                intent.AddFlags(ActivityFlags.ClearTop);
                var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
    
                var notificationBuilder = new Android.App.Notification.Builder(this, Utils.CHANNEL_ID)
                            .SetContentTitle(Header)
                            .SetSmallIcon(Resource.Drawable.icon)
                            .SetContentText(body)
                            .SetAutoCancel(true)
                            .SetContentIntent(pendingIntent);
    
                var notificationManager = NotificationManager.FromContext(this);
    
                notificationManager.Notify(0, notificationBuilder.Build());
            }
            else
            {
                var intent = new Intent(this, typeof(MainActivity));
                intent.AddFlags(ActivityFlags.ClearTop);
                var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
    
                var notificationBuilder = new Android.App.Notification.Builder(this, Utils.CHANNEL_ID)
                            .SetContentTitle(Header)
                            .SetSmallIcon(Resource.Drawable.icon)
                            .SetContentText(body)
                            .SetAutoCancel(true)
                            .SetContentIntent(pendingIntent)
                            .SetChannelId(Utils.CHANNEL_ID);
    
                if (Build.VERSION.SdkInt < BuildVersionCodes.O)
                {
                    return;
                }
    
                var channel = new NotificationChannel(Utils.CHANNEL_ID, "FCM Notifications", NotificationImportance.High)
                {
                    Description = "Firebase Cloud Messages appear in this channel"
                };
    
                var notificationManager = (NotificationManager)GetSystemService(NotificationService);
                notificationManager.CreateNotificationChannel(channel);
    
                notificationManager.Notify(0, notificationBuilder.Build());
            }
        }
    

    Thanks @LandLu for your support.

Answers

  • SreeeeSreeee INMember ✭✭✭✭✭
    edited March 28

    I already go through this and didn't get some steps. What is AppGcmListenerService.cs? Is that invoke when we tap a notification? What is jobId and NotificationId?

    Did you implement this feature?

  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee If you are using FCM to implement the notifications, you should create your own notification service class inheriting from FirebaseMessagingService to handle the message receiving event. Take a look at this documentation for more details: https://docs.microsoft.com/en-us/azure/notification-hubs/xamarin-notification-hubs-push-notifications-android-gcm#registering-with-firebase-cloud-messaging.
    Then you could create your own intent when messages come following the step 18. Therefore, you could add your parameters there:

    intent.PutExtra("JobID", jobId); // Passed parameters to MainActivity.cs
    intent.PutExtra("NotificationId", NotificationId);
    

    At last follow the thread: https://forums.xamarin.com/discussion/100056/how-to-open-a-different-xaml-page-on-push-notification-click-event to open a new page on Forms.

  • SreeeeSreeee INMember ✭✭✭✭✭

    Hi @LandLu I already implemented message receiving event using FirebaseMessagingService like below and pass the message using MessagingCenter

    public override void OnMessageReceived(RemoteMessage message)
            {
                base.OnMessageReceived(message);
                try
                {
                    var msg = message.GetNotification().Body;
                    MessagingCenter.Send<object, string>(this, MyProject.App.NotificationReceivedKey, msg);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Error:>>" + ex);
                }
            }
    

    I am looking for a function like this which invokes when tapping the notification in android. I go through this thread but have some doubts.

    What is AppGcmListenerService.cs? Is that class inherit from any other class? Is that invoke when we tap a notification? From where I get the value of jobId and NotificationId?

  • LandLuLandLu Member, Xamarin Team Xamurai

    AppGcmListenerService is his own class inherited from the FirebaseMessagingService where you placed the OnMessageReceived method.
    You should focus on the SendNotification, it can't be omitted. When your notification comes the OnMessageReceived will fire immediately without clicking the notification.

    void SendNotification(string messageBody)
    {
    
        //string chanName = GetString(Resource.String.noti_chan_urgent);
        var importance = NotificationImportance.High;
        NotificationChannel chan = new NotificationChannel(URGENT_CHANNEL, "Urgent", importance);
        chan.EnableVibration(true);
        chan.LockscreenVisibility = NotificationVisibility.Public;
    
        var intent = new Intent(this, typeof(MainActivity));
        intent.AddFlags(ActivityFlags.ClearTop);
    
        intent.PutExtra("JobID", jobId); // Passed parameters to MainActivity.cs
        intent.PutExtra("NotificationId", NotificationId);
    
        var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
    
        var notificationBuilder = new NotificationCompat.Builder(this)
            .SetSmallIcon(Resource.Drawable.ic_stat_ic_notification)
            .SetContentTitle("New Todo Item")
            .SetContentText(messageBody)
            .SetContentIntent(pendingIntent)
            .SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
            .SetAutoCancel(true)
            .SetChannelId(URGENT_CHANNEL);
    
        NotificationManager notificationManager =
    (NotificationManager)GetSystemService(NotificationService);
        notificationManager.CreateNotificationChannel(chan);
    
        notificationManager.Notify(NOTIFY_ID, notificationBuilder.Build());
    }
    

    This tells the system what should be shown on the notification and what activity should be opened when the user clicks the notification. But this is configured on the Android side, we still choose the MainActivity as the initialized activity and pass the parameters there.
    At last in the MainActivity's OnCreate event. We could get the parameters through

    string JobId = Intent.GetStringExtra("JobID");
    string NotificationId = Intent.GetStringExtra("NotificationId");
    

    If they are not null, it means the user open the app by clicking the notification. Then we could tell the App class in Forms to load a new page. Otherwise, do the default setup codes.

  • chetanrawatchetanrawat USMember ✭✭✭
    edited April 1

    Hi @Sreeee you need to set LaunchMode of the app is SingleInstance

    [Activity(Label = "ICLDC", LaunchMode = LaunchMode.SingleInstance, Icon = "@drawable/Logo", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]

    and then

    protected override void OnNewIntent(Intent intent)
            {
    
                    var nav = ((App.Current.MainPage as MenuPage).Detail as NavigationPage).Navigation;
                    if (!(nav.NavigationStack[nav.NavigationStack.Count - 1] is OpenPage))
                    {
                        nav.PushAsync(new OpenPage());
                    }
              }
    
  • SreeeeSreeee INMember ✭✭✭✭✭

    @chetanrawat What is MenuPage and OpenPage?

  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu I added all the codes but when tap a notification (when the app is in the background) nothing is happening, code execution not entering to OnMessageReceived or SendNotification().

    OnMessageReceived fired only when the app is running, but in that case no notification is showing in the device.

  • chetanrawatchetanrawat USMember ✭✭✭

    Hi, @Sreeee MenuPage is my Master-detail page and Open page which page you want open

  • SreeeeSreeee INMember ✭✭✭✭✭

    @chetanrawat said:
    Hi, @Sreeee MenuPage is my Master-detail page and Open page which page you want open

    I have no Master-detail page all are content pages.

  • chetanrawatchetanrawat USMember ✭✭✭

    Hi, @Sreeee
    try this

    var nav = ((App.Current.MainPage as YourMainPage) as NavigationPage).Navigation;`

  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee If your app is on quit state. The notification can't be received on Android.
    However, if it is on foreground and background but it can't show the notification correctly. It means you haven't configured the SendNotification correctly.
    You should call my code above in the OnMessageReceived method:

    public override void OnMessageReceived(RemoteMessage message)
    {
        Log.Debug(TAG, "From: " + message.From);
    
        // Analyze the message.Data, get the messageBody here
        // Call the SendNotification here
        Log.Debug(TAG, "Notification message body: " + messageBody);
        SendNotification(messageBody);
    }
    

    You have to create a notificationBuilder to show a notification on the window.

  • SreeeeSreeee INMember ✭✭✭✭✭
    edited April 11

    Hi @LandLu

    OnMessageReceived method and SendNotification methods are invoked only when the app is in the foreground. When the app is in the background code execution not entering to OnMessageReceived or SendNotification().

  • SreeeeSreeee INMember ✭✭✭✭✭

    Hi @LandLu Is it possible to do this feature using FirebasePushNotificationPlugin?

  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee This may be because you are using a notification message.
    Notification messages will only trigger the OnMessageReceived() method when your app is on foreground state.
    Data messages will call OnMessageReceived() method no matter whether it is on foreground, background or killed.
    So please check your notification payload.

  • SreeeeSreeee INMember ✭✭✭✭✭
    edited April 18

    @LandLu Following is my notification payload.

    {
     "to" : "my_device_key",
     "collapse_key" : "type_a",
     "notification" : {
         "body" : "Body of Your Notification",
         "title": "Title of Your Notification"
     },
     "data" : {
         "body" : "Body of Your Notification in Data",
         "title": "Title of Your Notification in Title",
         "key_1" : "Value for key_1",
         "key_2" : "Value for key_2"
     }
    }
    
  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee Remove the items under notification and make it like:

    {
     "to" : "my_device_key",
     "collapse_key" : "type_a",
     "data" : {
         "body" : "Body of Your Notification in Data",
         "title": "Title of Your Notification in Title",
         "key_1" : "Value for key_1",
         "key_2" : "Value for key_2"
     }
    }
    
  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu I tried removing the notification. But after that, no notifications are delivering to the device. I am testing from postman.

  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee Then you have to use my SendNotification above to create a notification by yourself.

  • SreeeeSreeee INMember ✭✭✭✭✭
    edited April 18

    OK @LandLu

    On where we are passing the model data details, inside of data or notification? Is there any problem if we pass model class like below inside of data?

    {
     "to" : "My device id",
     "collapse_key" : "type_a",
     "notification" : {
         "body" : "Body of Your Notification",
         "title": "Title of Your Notification"
     },
     "data" : {
        "MyModel": [
            {
                "webContentDefinitionId": 799994,
                "pageTitle": "10AM",
                "pageKwd": "10AM",
                "pageCreatedTime": 1553488305073,
                "pageUpdatedDate": 1554181005667,
                "creator": {
                    "userId": 10282,
                    "applicationId": 28,
                    "username": "[email protected]",
                    "email": "[email protected]",
                    "firstName": "Sreejith",
                    "lastName": "T"
              }
                 }
        ],
         "body" : "Body of Your Notification in Data",
         "title": "Title of Your Notification in Data",
         "key_1" : "key_1",
         "key_2" : "key_2"
     }
    }   
    

    I am able to read model data like below.

    var MyModel= message.Data["MyModel"];
    
  • SreeeeSreeee INMember ✭✭✭✭✭
    Accepted Answer

    @LandLu, @chetanrawat, @PatrickJelitto I handled the notification tapping in following way. The page loading is handled in App.xaml.cs.

    On OnCreate():

    //Background or killed mode
                if (Intent.Extras != null)
                {
                    foreach (var key in Intent.Extras.KeySet())
                    {
                        var value = Intent.Extras.GetString(key);
                        if (key == "webContentList") 
                        {
                            if (value?.Length > 0)
                            {
                                isNotification = true;
                                LoadApplication(new App(domainname, value));
                            }
                        }
                    }
                }
                //Foreground mode
                if (FirebaseNotificationService.webContentList.ToString() != "")
                {
                    isNotification = true;
                    LoadApplication(new App(domainname, FirebaseNotificationService.webContentList.ToString()));
                    FirebaseNotificationService.webContentList = "";
                }
    
                //Normal loading
                if (!isNotification)
                {
                    LoadApplication(new App(domainname, string.Empty));
                }
    

    On FirebaseNotificationService:

    [Service]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    public class FirebaseNotificationService : FirebaseMessagingService
    {
        public static string webContentList = "";
        public override void OnMessageReceived(RemoteMessage message)
        {
            base.OnMessageReceived(message);
            webContentList = message.Data["webContentList"];
    
            try
            {
                SendNotificatios(message.GetNotification().Body, message.GetNotification().Title);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error:>>" + ex);
            }
        }
    
        public void SendNotificatios(string body, string Header)
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.O)
            {
                var intent = new Intent(this, typeof(MainActivity));
                intent.AddFlags(ActivityFlags.ClearTop);
                var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
    
                var notificationBuilder = new Android.App.Notification.Builder(this, Utils.CHANNEL_ID)
                            .SetContentTitle(Header)
                            .SetSmallIcon(Resource.Drawable.icon)
                            .SetContentText(body)
                            .SetAutoCancel(true)
                            .SetContentIntent(pendingIntent);
    
                var notificationManager = NotificationManager.FromContext(this);
    
                notificationManager.Notify(0, notificationBuilder.Build());
            }
            else
            {
                var intent = new Intent(this, typeof(MainActivity));
                intent.AddFlags(ActivityFlags.ClearTop);
                var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
    
                var notificationBuilder = new Android.App.Notification.Builder(this, Utils.CHANNEL_ID)
                            .SetContentTitle(Header)
                            .SetSmallIcon(Resource.Drawable.icon)
                            .SetContentText(body)
                            .SetAutoCancel(true)
                            .SetContentIntent(pendingIntent)
                            .SetChannelId(Utils.CHANNEL_ID);
    
                if (Build.VERSION.SdkInt < BuildVersionCodes.O)
                {
                    return;
                }
    
                var channel = new NotificationChannel(Utils.CHANNEL_ID, "FCM Notifications", NotificationImportance.High)
                {
                    Description = "Firebase Cloud Messages appear in this channel"
                };
    
                var notificationManager = (NotificationManager)GetSystemService(NotificationService);
                notificationManager.CreateNotificationChannel(channel);
    
                notificationManager.Notify(0, notificationBuilder.Build());
            }
        }
    

    Thanks @LandLu for your support.

  • Suneelkumar_BSuneelkumar_B USMember ✭✭

    @LandLu said:
    @Sreeee This may be because you are using a notification message.
    Notification messages will only trigger the OnMessageReceived() method when your app is on foreground state.
    Data messages will call OnMessageReceived() method no matter whether it is on foreground, background or killed.
    So please check your notification payload.

    @Landu, Thanks for the information.

    It is working as expected.

    Thanks
    Suneel Kumar Biyyapu

Sign In or Register to comment.