Search code examples
c#androidxamarinxamarin.formsbackground-service

Not able to show notifications when app is closed in xamarin forms


I am trying to implement a background service which notifies me every continuously. So for that I'm using Alarm manager and broadcast receiver. This is my broadcast receiver code

[BroadcastReceiver(Enabled = true)]
    [IntentFilter(new string[] {PeriodicService.AlarmService, "android.intent.action.BOOT_COMPLETED" }, Priority = (int)IntentFilterPriority.LowPriority)]
    public class BackgroundReceiver : BroadcastReceiver
    {
        public override void OnReceive(Context context, Intent intent)
        {
            PowerManager pm = (PowerManager)context.GetSystemService(Context.PowerService);
            PowerManager.WakeLock wakeLock = pm.NewWakeLock(WakeLockFlags.Partial, "BackgroundReceiver");
            wakeLock.Acquire();
            Device.StartTimer(new TimeSpan(0, 0, 10), () =>
            {
                // do something every 60 seconds
                Device.BeginInvokeOnMainThread(() =>
                {

                    Toast.MakeText(Android.App.Application.Context, "Hi when app opened", ToastLength.Short).Show();
                    NotificationCenter.NotifyNotificationTapped(intent);
                   
                    var alarmAttributes = new AudioAttributes.Builder()
                                      .SetContentType(AudioContentType.Sonification)
                                      .SetUsage(AudioUsageKind.Alarm)
                                      .Build();

                   // var alarmUri = Android.Net.Uri.Parse("Resources/Audio/AlarmSound.wav");
                    NotificationCenter.Current.Show((notification) => notification
                           .WithScheduleOptions((schedule) => schedule
                           .Build())
                           
                           .WithAndroidOptions((android) => android
                                .WithAutoCancel(true)
                                .WithChannelId("General")
                                
                                .WithPriority(Plugin.LocalNotification.NotificationPriority.High)
                                .Build())
                           .WithiOSOptions((ios) => ios
                               .ShouldPlayForegroundSound(true)
                               .Build())
                           .WithReturningData("Dummy Data")
                           .WithTitle("Test Title")
                           .WithDescription("Test Description")
                           .WithNotificationId(100)
                           .Create());
                    Toast.MakeText(Android.App.Application.Context, "HI", ToastLength.Short).Show();
                    MessagingCenter.Send<object, string>(this, "UpdateLabel", "Hello from Android");
                });
                return true; // runs again, or false to stop
            });

            wakeLock.Release();
        }
    }

This is my background service code

[Service]
    public class PeriodicService : Service
    {
        public override IBinder OnBind(Intent intent)
        {
            return null;
        }

        public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
        {
            // From shared code or in your PCL
            MessagingCenter.Send<object, string>(this, "UpdateLabel", "Hello from Android");
       
            Device.StartTimer(new TimeSpan(0, 0, 10), () =>
            {
                // do something every 60 seconds
                Device.BeginInvokeOnMainThread(() =>
                {
                    Toast.MakeText(Android.App.Application.Context, "Hi when app closed", ToastLength.Short).Show();
                  
                });
                return true; // runs again, or false to stop
            });
            return StartCommandResult.Sticky;
        }

    }

This is my Main activity code

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
       protected override void OnCreate(Bundle savedInstanceState)
    {
      

        base.OnCreate(savedInstanceState);


        global::Xamarin.Essentials.Platform.Init(this, savedInstanceState);

        ZXing.Net.Mobile.Forms.Android.Platform.Init();
        

        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
        global::ZXing.Net.Mobile.Forms.Android.Platform.Init();

        LoadApplication(new App());
        Intent alarmIntent = new Intent(Application.Context, typeof(BackgroundReceiver));
        BackgroundReceiver myreceiver = new BackgroundReceiver();
        RegisterReceiver(myreceiver, new IntentFilter("android.intent.action.BOOT_COMPLETED"));
        //StartService(new Intent(this, typeof(PeriodicService)));
        // CreateNotificationChannel();
        if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
        {
            //DispatchNotificationThatServiceIsRunning();
            //StartForegroundService(Intent);
            StartService(new Intent(this, typeof(PeriodicService)));
        }
        else
        {
            StartService(new Intent(this, typeof(PeriodicService)));
        }

       
        //alarmIntent.PutExtra("message", "This is my test message!");
        //alarmIntent.PutExtra("title", "This is my test title!");
        //PendingIntent pendingIntent = PendingIntent.GetBroadcast(Application.Context, 0, alarmIntent, PendingIntentFlags.UpdateCurrent);
        //AlarmManager alarmManager = (AlarmManager)Application.Context.GetSystemService(Context.AlarmService);
        //alarmManager.SetAndAllowWhileIdle(AlarmType.RtcWakeup, DateTime.Now.Millisecond + 5000, pendingIntent);

    }

This is my code in manifest

<application android:theme="@style/MainTheme" android:usesCleartextTraffic="true" android:allowBackup="false" android:label="myproject">
        <receiver android:name=".BackgroundReceiver" android:enabled="true" android:exported="true" android:process=":remote" android:label="myproject" />
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>

This is my code where I request for permission.

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            global::Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            global::ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

I have gone through many solutions but nothing helped me. These are the links I have gone through

Android - AlarmManager is not working after app is closed

Alarm Manager not working when app closed

I tried to use foreground services as well this is my service code

  public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
        {
            // From shared code or in your PCL
            MessagingCenter.Send<object, string>(this, "UpdateLabel", "Hello from Android");
            
            
                    CreateNotificationChannel();
                    DispatchNotificationThatServiceIsRunning();

            return StartCommandResult.Sticky;
        }
        public void DispatchNotificationThatServiceIsRunning()
        {
            
            NotificationCompat.Builder mBuilder =
                new NotificationCompat.Builder(this)
            .SetDefaults((int)NotificationDefaults.All)

            .SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
            .SetSound(null)

            .SetPriority(NotificationCompat.PriorityHigh)
            .SetAutoCancel(false)
            .SetSmallIcon(Resource.Drawable.notification_icon_background)
            .SetContentTitle("test")
            .SetContentText("service started")
            .SetOngoing(true);
            
            Notification bui = mBuilder.Build();
            NotificationManager notificationManager = (NotificationManager)GetSystemService(NotificationService);
            notificationManager.Notify(0, bui);
            StartForeground(121, bui);
            
        }

        public void CreateNotificationChannel()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.O)
            {

                return;
            }

            var channelName = "Notification_Channel";
            var channelDescription = "Foreground Notification";
            var channel = new NotificationChannel("10121", channelName, NotificationImportance.High)
            {
                Description = channelDescription
            };

            var notificationManager = (NotificationManager)GetSystemService(NotificationService);
            notificationManager.CreateNotificationChannel(channel);
        }

Till now I'm getting the notification channel in the app settings but notification is not showing for me. I am using a Redmi note 8 device and my target android version is Android 11.0(API level 30). I have gone through app which meets my requirement that is StepSetGo you can find that in playstore you can see that application continuously shows notification even if the app is killed and it records the number of steps without internet . I have given the same permission for this application also like autostart and no battery restrictions but still it doesn't work. I have no clue how to fix this any suggestions?


Solution

  • The broadcast receiver will die with the app. I would use the service to start the broadcast receiver and have it talk directly to the service.

    In your service: MyReceiver myreceiver = new MyReceiver(this); RegisterReceiver(myreceiver, new IntentFilter("com.example.MyReceiver");

    In your receiver:

    service.Notify("IT'S WAKE UP TIME!");

    Also, I noticed your service doesn't have a notification running permanently. It will be killed by the OS.

    In your Service: StartForeground(intID, notification);