Patrick Trunkl
Patrick Trunkl

Reputation: 13

How can I run a Task every Day at the same Time in Xamarin Forms Android?

Im new to Xamarin and Android programming. I'm currently try to create an App which checks the position of the device every night at around 1 am. The time will be set in a Settings Page with TimePicker. The only challenge im facing at the moment is how to schedule this task so that it runs even if the app is closed or the device is in Dooze Mode. The target Platform is Android 9(Pie).

So far i created a Foreground Service. That foreground service sets an AlarmManager with SetRepeading method and mode Rtc_Wakeup. But this does not work the way i hoped! The problem is that the Alarm gets triggert when the App is set to Background or Closed. But it schould only be triggert if the time to trigger is reached. Or am i getting something wrong?

You can get the whole code from my Git Repository

What is the best way to achieve this? Thank you! :)

This is what i tried so far :)

Here is my ForegroundService class:


using Android.App;
using Android.Content;
using Android.OS;
using AnwesenheitsApp.Droid.Alarm;

namespace AnwesenheitsApp.Droid
{
    [Service]
    class PositionServiceDroid : Service
    {
        private Logging.Logging _logger = new Logging.Logging();
        private static AlarmHandler alarm = new AlarmHandler();

        public override StartCommandResult OnStartCommand(Intent intent,
            StartCommandFlags flags, int startId)
        {
            int messageID = 90000;

            var notifMngr = new NotificationManagerDroid();
            Notification notification = notifMngr.ReturnNotification(
                "Positions Service", "Die überwachung der Position für die" +
                " automatische Prüfung der Anwesenheit läuft!");

            StartForeground(messageID, notification);

            alarm.SetAlarm();

            return StartCommandResult.Sticky;
        }

        public override bool StopService(Intent name)
        {
            alarm.UnsetAlarm();
            return base.StopService(name);
        }

        public override void OnDestroy()
        {
            alarm.UnsetAlarm();
            base.OnDestroy();
        }

        public override IBinder OnBind(Intent intent)
        {
            return null;
        }  
    }
}

This is my AlarmHandler class:

using System;

using Android.App;
using Android.Content;
using Android.Runtime;

namespace AnwesenheitsApp.Droid.Alarm
{
    class AlarmHandler
    {
        public void SetAlarm()
        {
            var alarmIntent = new Intent(Application.Context, typeof(AlarmReciver));
            var pending = PendingIntent.GetBroadcast(Application.Context,
                0, alarmIntent, PendingIntentFlags.UpdateCurrent);
            var alarmManager = Application.Context.GetSystemService(Application.AlarmService)
                .JavaCast<AlarmManager>();

            //For Testing
            var now = DateTime.Now;

            var dt = new DateTime(now.Year, now.Month, now.Day,
                19, 5, 0, 0).Ticks / 10000;

            long millis = dt - now.Ticks / 10000;

            alarmManager.SetRepeating(AlarmType.RtcWakeup, millis,
                1000 * 60 * 60 * 4, pending);
        }

        public void UnsetAlarm()
        {
            var alarmIntent = new Intent(Application.Context, typeof(AlarmReciver));
            var pending = PendingIntent.GetBroadcast(Application.Context,
                0, alarmIntent, PendingIntentFlags.UpdateCurrent);
            var alarmManager = Application.Context.GetSystemService(Application.AlarmService)
                .JavaCast<AlarmManager>();

            alarmManager.Cancel(pending);
        }
    }
}

and this is the BroadcastReceiver class:

using System;
using System.Linq;
using AnwesenheitsApp.DbModels;
using Xamarin.Essentials;

using Android.Content;

namespace AnwesenheitsApp.Droid.Alarm
{
    [BroadcastReceiver]
    class AlarmReciver : BroadcastReceiver
    {
        private LocationData _data;
        private Logging.Logging _logger = new Logging.Logging();

        public override void OnReceive(Context context, Intent intent)
        {
            GetLocationData();
        }

        private void SaveDataToDb(LocationData data)
        {
            App.Database.SaveLocationDataToDbAsync(data);
        }

        private async void GetLocationData()
        {
            if (this._data == null)
                this._data = new LocationData();


            try
            {
                var location = await Geolocation.GetLocationAsync();
                if (location == null)
                    location = await Geolocation.GetLastKnownLocationAsync();

                if (location != null)
                {
                    this._data.Latitude = location.Latitude;
                    this._data.Longitude = location.Longitude;

                    var placemark = (await Geocoding.GetPlacemarksAsync(
                        location.Latitude, location.Longitude))?.FirstOrDefault();
                    if (placemark != null)
                    {
                        this._data.Locality = placemark.Locality;
                        this._data.ZipCode = placemark.PostalCode;
                        this._data.AdminArea = placemark.AdminArea;
                        this._data.Country = placemark.CountryName;
                    }
                }
                this._data.CreationDate = DateTime.Now;
                SaveDataToDb(this._data);
            }
            catch (Exception ex)
            {
                this._logger.WriteLogEntry(Logging.LoggingType.ERROR,
                    ex.Message + " GetLocationData() in class PositionServiceDroid");
            }
        }
    }
}

Upvotes: 1

Views: 754

Answers (1)

habib salimi
habib salimi

Reputation: 116

You can use Shiny , it is a real problem solver in realm of background and foreground service in xamarin . shiny getting started

Upvotes: 2

Related Questions