locked
Xamarin.Forms android push notification RRS feed

  • Question

  • User383552 posted

    I want my app can pop notification when it runs on background. The function like WeChat.

    My app is a network client, but not have user account system. If my app not run on foreground or background, the server message not need to pop notification. So I don't need the cloud system to do this.

    I only want two things: 1. My app can run network receive loop on android background. 2. My app can tell the android system pop notification with my text.

    Thursday, May 2, 2019 9:33 AM

Answers

  • User382871 posted

    If you want popup a notifications on xamarin.Android, the tutorial can easily help you implement the notification on Android.

    Tutorial: https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/notifications/local-notifications Sample: https://developer.xamarin.com/samples/monodroid/LocalNotifications/

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, May 2, 2019 3:24 PM

All replies

  • User382871 posted

    1.You can use FirebasePushNotificationPlugin plugin to push notifications on background.

    Sample: https://github.com/CrossGeeks/FirebasePushNotificationPlugin

    2.Use Firebase Cloud Messaging to push notifications https://docs.microsoft.com/en-us/xamarin/android/data-cloud/google-messaging/remote-notifications-with-fcm?tabs=macos

    Thursday, May 2, 2019 12:35 PM
  • User383552 posted

    I there any solution use android API to do that?

    Thursday, May 2, 2019 2:14 PM
  • User129559 posted

    Firebase is the official Android way of doing push notifications.

    If you just want to pop up a notification on something happening while your app is open, you can manually create and pop it up using Android APIs.

    If you want remote push notification support, use Firebase.

    Thursday, May 2, 2019 2:16 PM
  • User383552 posted

    Thanks. But I am Chinese, in China user can't connect to Google server, because the government ban.

    I just want to pop up a notification on something happening while my app is open or run on background. I am new to Xamarin and android, so I don't know what android API can do this? Any sample code?

    Thursday, May 2, 2019 2:22 PM
  • User129559 posted

    If you app is running, you can use native APIs.

    https://developer.android.com/reference/android/support/v4/app/NotificationManagerCompat

    This, NotificationManager, etc, is how you build the notification.

    Thursday, May 2, 2019 2:25 PM
  • User382871 posted

    If you want popup a notifications on xamarin.Android, the tutorial can easily help you implement the notification on Android.

    Tutorial: https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/notifications/local-notifications Sample: https://developer.xamarin.com/samples/monodroid/LocalNotifications/

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, May 2, 2019 3:24 PM
  • User383552 posted

    @yelinzh
    Your answer is good. I got the sample and run, I see it use 2 Activity. I don't know how to from SecondActivity return to MainActivity. But it doesn't matter. Because I have another problem need to fix first. When my app to background, my work thread only have minutes to run, and then the thread will suspend and the socket connection will lost. If my network connection lost, my client can't get new message from my server. Even I have the method to pop notification, but there is no message can pop.

    Friday, May 3, 2019 12:23 PM
  • User72117 posted

    @lihuipeng49 For remote notifications you call the rest api https://firebase.google.com/docs/cloud-messaging/http-server-ref

    Google will handle your device receiving the notification after that. You don't need to worry about being connected for the most part.

    Friday, May 3, 2019 2:13 PM
  • User129559 posted

    @nick5454 Try to read the threads you are commenting on.

    This dev is in China, with phones without access to Google Play. You can't use Firebase with them.

    @lihuipeng49 If you want to work as a persistent connection, you may need to implement an Android service with the appropriate priority.

    I personally would recommend one of the solutions to pushes that exist in China over a persistent socket connection, as Android has really limited background services in some of its later releases.

    I have read of a few services, but have no experience with them.

    Friday, May 3, 2019 2:27 PM
  • User383552 posted

    @yelinzh I use your solution: https://developer.xamarin.com/samples/monodroid/LocalNotifications/

    I move the code and resource to my own project successfully. But my UI was create by Xamarin.Forms not by Xamarin.Android.

    My project named 'ChannelChat'.

    MainActivity.cs

        namespace ChannelChat.Droid
        {
            [Activity(Label = "????", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
            public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
            {
                protected override void OnCreate(Bundle savedInstanceState)
                {
                    TabLayoutResource = Resource.Layout.Tabbar;
                    ToolbarResource = Resource.Layout.Toolbar;
    
                    base.OnCreate(savedInstanceState);
                    global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
                    LoadApplication(new App());
                }
            }
    }
    

    App.xaml.cs

    namespace ChannelChat
    {
        public partial class App : Application
        {
            public App()
            {
                InitializeComponent();
    
                MainPage = new NavigationPage(new MainPage());
            }
        }
    }
    

    I must call the android native function from the .NET Standard project.

    I am new to Xamarin, so I don't know how to do that. Can some one tell me?

    Friday, May 3, 2019 2:45 PM
  • User382871 posted

    https://developer.xamarin.com/samples/monodroid/LocalNotifications/ The smaple is for Xamarin.Android, not Forms app.

    Push notifications on background for Xamarin.Forms, the solution is https://forums.xamarin.com/discussion/comment/373360/#Comment_373360

    Friday, May 3, 2019 3:27 PM
  • User383552 posted

    I'm in China, the country's network can't connect to Google service or Google cloud. So, I think I can't make Firebase plugin works in china.

    Friday, May 3, 2019 3:35 PM
  • User129559 posted

    You need to use Dependency Injection, the Dependency Service, or MessagingCenter to call native methods in your Forms layers.

    Friday, May 3, 2019 3:42 PM
  • User383552 posted

    I finish the work. I implement the Local Notification on Xamarin.Android. I use a interface to let the Xamarin.Forms code call the function on Xamarin.Android.

    Saturday, May 4, 2019 1:39 AM
  • User383174 posted

    @lihuipeng49 said: I finish the work. I implement the Local Notification on Xamarin.Android. I use a interface to let the Xamarin.Forms code call the function on Xamarin.Android.

    Hi. I'm in Iran and we couldn't access Firebase services also. Could you help me? I have same issue. could you put the codes?

    Thursday, October 3, 2019 4:38 AM
  • User383552 posted

    HeartbeatJob.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using Android.App;
    using Android.App.Job;
    using Android.Content;
    using Android.OS;
    using Android.Runtime;
    using Android.Util;
    using Android.Views;
    using Android.Widget;
    using ChannelChat.Models;
    
    namespace ChannelChat.Droid
    {
        [Service(Name = "com.xamarin.samples.downloadscheduler.HeartbeatJob",
                 Permission = "android.permission.BIND_JOB_SERVICE")]
        public class HeartbeatJob : JobService
        {
            public override bool OnStartJob(JobParameters jobParams)
            {
                var loopCount = jobParams.Extras.GetInt("LoopCount", 10);
    
                string tag = "myapp";
                Log.Info(tag, "HeartbeatJob");
    
                //if (MainCtrl.appOnForeground == false)
                //{
                    //MainCtrl.appOnJobCount = 30;
                //}
    
                Task.Run(() =>
                {
                    // Work is happening asynchronously
    
                    //while(MainCtrl.appOnJobCount > 0)
                    //{
                    //    Thread.Sleep(1000);
                    //}
    
                    // Have to tell the JobScheduler the work is done. 
                    JobFinished(jobParams, false);
                });
    
                // Return true because of the asynchronous work
                return true;
            }
    
            public override bool OnStopJob(JobParameters jobParams)
            {
                // we don't want to reschedule the job if it is stopped or cancelled.
                return false;
            }
        }
    
        public static class JobSchedulerHelpers
        {
            public static JobInfo.Builder CreateJobBuilderUsingJobId<T>(this Context context, int jobId) where T : JobService
            {
                var javaClass = Java.Lang.Class.FromType(typeof(T));
                var componentName = new ComponentName(context, javaClass);
                return new JobInfo.Builder(jobId, componentName);
            }
        }
    
    }
    

    LocalNotification.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using Android.App;
    using Android.Content;
    using Android.OS;
    using Android.Runtime;
    using Android.Views;
    using Android.Widget;
    using static Android.App.ActivityManager;
    
    using ChannelChat.Models;
    
    namespace ChannelChat.Droid
    {
        class LocalNotification : ILocalNotification
        {
            public MainActivity mainActivity;
    
            public void Notify()
            {
                if (IsApplicationInTheBackground())
                    mainActivity.GenerateNotification();
            }
    
            private bool IsApplicationInTheBackground()
            {
                bool isInBackground;
    
                RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
                ActivityManager.GetMyMemoryState(myProcess);
                isInBackground = myProcess.Importance != Android.App.Importance.Foreground;
    
                return isInBackground;
            }
    
        }
    }
    

    MainActivity.cs

    using System;
    
    using Android.App;
    using Android.Content.PM;
    using Android.Runtime;
    using Android.Views;
    using Android.Widget;
    using Android.OS;
    using Android.Support.V4.App;
    using TaskStackBuilder = Android.Support.V4.App.TaskStackBuilder;
    using Android.Content;
    
    using ChannelChat.Models;
    using Android.App.Job;
    using Android.Support.Design.Widget;
    
    namespace ChannelChat.Droid
    {
        [Activity(Label = "????", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
        public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
        {
            // Unique ID for our notification: 
            static readonly int NOTIFICATION_ID = 1000;
            static readonly string CHANNEL_ID = "location_notification";
            internal static readonly string COUNT_KEY = "count";
    
            // Number of times the button is tapped (starts with first tap):
            int count = 1;
    
            protected override void OnCreate(Bundle savedInstanceState)
            {
                TabLayoutResource = Resource.Layout.Tabbar;
                ToolbarResource = Resource.Layout.Toolbar;
    
                base.OnCreate(savedInstanceState);
    
                var localNotification = new LocalNotification();
                localNotification.mainActivity = this;
                MainCtrl.localNotification = localNotification;
    
                global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
    
                var width = Resources.DisplayMetrics.WidthPixels;
                var height = Resources.DisplayMetrics.HeightPixels;
                var density = Resources.DisplayMetrics.Density;
                App.ScreenWidth = (width - 0.5f) / density;
                App.ScreenHeight = (height - 0.5f) / density;
    
                LoadApplication(new App());
    
                if (MainCtrl.threadId == 0)
                {
                    CreateNotificationChannel();
                    //CreateHeartbeatJob();
                }
            }
    
            public override bool OnKeyDown([GeneratedEnum] Keycode keyCode, KeyEvent e)
            {
                if (keyCode == Keycode.Back && e.RepeatCount == 0)
                {
                    var mainPage = App.Current.MainPage;
                    if (mainPage.Navigation.ModalStack.Count == 0 && mainPage.Navigation.NavigationStack.Count == 1)
                    {
                        AlertDialog.Builder dialog = new AlertDialog.Builder(this);
                        AlertDialog alert = dialog.Create();
                        alert.SetTitle("????");
                        alert.SetMessage("??????????????");
                        alert.SetButton("??", (c, ev) =>
                        {
                            // cancel
                        });
                        alert.SetButton2("??", (c, ev) =>
                        {
                            Finish();
                        });
                        alert.Show();
    
                        return true;
                    }
                }
    
                return base.OnKeyDown(keyCode, e);
            }
    
            void CreateNotificationChannel()
            {
                if (Build.VERSION.SdkInt < BuildVersionCodes.O)
                {
                    // Notification channels are new in API 26 (and not a part of the
                    // support library). There is no need to create a notification
                    // channel on older versions of Android.
                    return;
                }
    
                var name = Resources.GetString(Resource.String.channel_name);
                var description = GetString(Resource.String.channel_description);
                var channel = new NotificationChannel(CHANNEL_ID, name, NotificationImportance.Default)
                {
                    Description = description
                };
    
                //channel.SetSound(null, null); // ?????????,?????????
                //channel.EnableLights(true); // ??????????(?? android ??????)
                //channel.EnableVibration(true); // ??????????(?? android ??????)
                //channel.SetVibrationPattern(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 });
    
                var notificationManager = (NotificationManager)GetSystemService(NotificationService);
                notificationManager.CreateNotificationChannel(channel);
            }
    
            public void GenerateNotification()
            {
                // Pass the current button press count value to the next activity:
                var valuesForActivity = new Bundle();
                valuesForActivity.PutInt(COUNT_KEY, count);
    
                // When the user clicks the notification, SecondActivity will start up.
                var resultIntent = new Intent(this, typeof(MainActivity));
    
                // Pass some values to SecondActivity:
                resultIntent.PutExtras(valuesForActivity);
    
                // Construct a back stack for cross-task navigation:
                var stackBuilder = TaskStackBuilder.Create(this);
                stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(MainActivity)));
                stackBuilder.AddNextIntent(resultIntent);
    
                // Create the PendingIntent with the back stack:
                var resultPendingIntent = stackBuilder.GetPendingIntent(0, (int)PendingIntentFlags.UpdateCurrent);
    
                // Build the notification:
                var builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                              .SetAutoCancel(true) // Dismiss the notification from the notification area when the user clicks on it
                              .SetContentIntent(resultPendingIntent) // Start up this activity when the user clicks the intent.
                              .SetContentTitle("????") // Set the title
                              .SetNumber(count) // Display the count in the Content Info
                              .SetSmallIcon(Resource.Drawable.ic_stat_button_click) // This is the icon to display
                              .SetContentText($"???? {count} ????"); // the message to display.
    
                //builder.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);
                //builder.SetPriority((int)NotificationPriority.High);
    
                // Finally, publish the notification:
                var notificationManager = NotificationManagerCompat.From(this);
                notificationManager.Notify(NOTIFICATION_ID, builder.Build());
    
                // Increment the button press count:
                count++;
            }
    
            void CreateHeartbeatJob()
            {
                var jobParameters = new PersistableBundle();
                jobParameters.PutInt("LoopCount", 11);
    
                var jobBuilder = this.CreateJobBuilderUsingJobId<HeartbeatJob>(1);
                jobBuilder.SetExtras(jobParameters);
                jobBuilder.SetRequiredNetworkType(NetworkType.Any);
                long period1 = 15 * 60 * 1000;
                jobBuilder.SetPeriodic(period1);
                var jobInfo = jobBuilder.Build();
                var period2 = jobInfo.IntervalMillis;
                System.Diagnostics.Debug.Assert(period1 == period2);
    
                var jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
                var scheduleResult = jobScheduler.Schedule(jobInfo);
    
                if (JobScheduler.ResultSuccess == scheduleResult)
                {
                    var snackBar = Snackbar.Make(FindViewById(Android.Resource.Id.Content), Resource.String.jobscheduled_success, Snackbar.LengthShort);
                    snackBar.Show();
                }
                else
                {
                    var snackBar = Snackbar.Make(FindViewById(Android.Resource.Id.Content), Resource.String.jobscheduled_failure, Snackbar.LengthShort);
                    snackBar.Show();
                }
            }
        }
    
    }
    
    Thursday, October 3, 2019 5:07 AM