How to convert some functions for running in background? - Xamarin Forms

I'd like to know how can I convert this functions for running in the background (Running even if the app is closed or the phone was restarted)

This is the code which I'd like to convert:

private async void OnTimerElapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                #region TaskHere

                // Implement your task here
                var locales = await TextToSpeech.GetLocalesAsync();

                // Grab the first locale
                var locale = locales.FirstOrDefault();

                var settings = new SpeechOptions()
                {
                    Volume = 1.0f,
                    Pitch = 0.70f,
                    Locale = locale
                };


                await TextToSpeech.SpeakAsync(_Item.Description, settings);
                await Task.Delay(2000);

                await TextToSpeech.SpeakAsync(_Item.Description, settings);
                await Task.Delay(2000);

                await TextToSpeech.SpeakAsync(_Item.Description, settings);

                #endregion
            }
            catch (Exception ex)
            {
                Console.Write(ex.ToString());
                throw;
            }
            // Task to run every 5 hours on each target day of the week

            

        }

The function is called in a CRUD, everything work well, only when the app is closed or the phone is shutdown or restarted the app doesn't work.

I Hope you can help me.

The complete code would be this:

CRUD:

using Android.Content;
using Android.Widget;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using ToDo.App.Models;
using Xamarin.Essentials;

namespace ToDo.App.Services
{
    public class SchedulerTaskService
    {
        ToDoItem _Item;
        Android.Content.Context _context;
        public SchedulerTaskService()
        {
            
        }
        public SchedulerTaskService(ToDoItem Item, Android.Content.Context context)
        {
            _Item = Item;
            _context = context;
        }

        public async Task<ToDoItem> ScheduleTask()
        {
            var lstDeserializeListOfWeek = JsonConvert.DeserializeObject<List<DayOfWeekModel>>(_Item.ObjDaysToRepeat);
            List<DayOfWeek> targetDays = await GetDaysFromModel(lstDeserializeListOfWeek);

            // Define the target days of the week
            //List<DayOfWeek> targetDays = JsonConvert.DeserializeObject<List<DayOfWeek>>(_Item.ObjDaysToRepeat);
            List<System.Timers.Timer> lstTimers = new List<System.Timers.Timer>();

            await Task.Run(() =>
            {
                // Schedule timer for each target day of the week
                foreach (DayOfWeek targetDay in targetDays)
                {
                    // Get the next target day
                    DateTime today = DateTime.Today;
                    int daysUntilNextTargetDay = ((int)targetDay - (int)today.DayOfWeek + 7) % 7;
                    DateTime nextTargetDay = today.AddDays(daysUntilNextTargetDay);

                    // Set the start time for the timer
                    TimeSpan startTime = new TimeSpan(_Item.Hour.Hours, _Item.Hour.Minutes, _Item.Hour.Seconds);
                    DateTime startDateTime = nextTargetDay + startTime;

                    // Calculate the initial delay until the first timer elapses
                    TimeSpan initialDelay = startDateTime - DateTime.Now;

                    // Set timer properties

                    var timer = new System.Timers.Timer(initialDelay.TotalMilliseconds > 0 ? initialDelay.TotalMilliseconds : initialDelay.TotalMilliseconds * -1);
                    //var timer = new System.Timers.Timer();

                    if (_Item.WithInterval != 0)
                    {
                        timer.Interval = TimeSpan.FromHours(_Item.WithInterval).TotalMilliseconds;
                    }

                    //timer.AutoReset = true;

                    // Start the timer
                    timer.Elapsed += OnTimerElapsed;
                    timer.Start();

                    lstTimers.Add(timer);

                }

            });

            _Item.ObjTimer = JsonConvert.SerializeObject(lstTimers.ToList());
            int result = await App.Context.InsertItemAsync(_Item);
            if (result == 1)
                return _Item;
            else
                CancelTask(lstTimers);

            return null;

        }

        public async Task<ToDoItem> EditScheduleAsync()
        {
            var lstDeserializeListOfWeek = JsonConvert.DeserializeObject<List<DayOfWeekModel>>(_Item.ObjDaysToRepeat);
            List<DayOfWeek> targetDays = await GetDaysFromModel(lstDeserializeListOfWeek);

            // Define the target days of the week
            
            List<System.Timers.Timer> lstTimers = new List<System.Timers.Timer>();

            CancelTask(JsonConvert.DeserializeObject<List<System.Timers.Timer>>(_Item.ObjTimer));

            await Task.Run(() =>
            {

                // Schedule timer for each target day of the week
                foreach (DayOfWeek targetDay in targetDays)
                {
                    // Get the next target day
                    DateTime today = DateTime.Today;
                    int daysUntilNextTargetDay = ((int)targetDay - (int)today.DayOfWeek + 7) % 7;
                    DateTime nextTargetDay = today.AddDays(daysUntilNextTargetDay);

                    // Set the start time for the timer
                    TimeSpan startTime = new TimeSpan(_Item.Hour.Hours, _Item.Hour.Minutes, _Item.Hour.Seconds);
                    DateTime startDateTime = nextTargetDay + startTime;

                    // Calculate the initial delay until the first timer elapses
                    TimeSpan initialDelay = startDateTime - DateTime.Now;

                    // Set timer properties

                    var timer = new System.Timers.Timer(initialDelay.TotalMilliseconds > 0 ? initialDelay.TotalMilliseconds : initialDelay.TotalMilliseconds * -1);
                    //var timer = new System.Timers.Timer();

                    if (_Item.WithInterval != 0)
                    {
                        timer.Interval = TimeSpan.FromHours(_Item.WithInterval).TotalMilliseconds;
                    }

                    //timer.AutoReset = true;

                    // Start the timer
                    timer.Elapsed += OnTimerElapsed;
                    timer.Start();

                    lstTimers.Add(timer);

                }

            });

            _Item.ObjTimer = JsonConvert.SerializeObject(lstTimers.ToList());
            _Item.CreateTime = DateTime.Now;
            int result = await App.Context.EditItemAsync(_Item);
            if (result == 1)
                return _Item;
            else
                CancelTask(lstTimers);

            return null;

        }

        private async void OnTimerElapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                #region TaskHere

                // Implement your task here
                var locales = await TextToSpeech.GetLocalesAsync();

                // Grab the first locale
                var locale = locales.FirstOrDefault();

                var settings = new SpeechOptions()
                {
                    Volume = 1.0f,
                    Pitch = 0.70f,
                    Locale = locale
                };


                await TextToSpeech.SpeakAsync(_Item.Description, settings);
                await Task.Delay(2000);

                await TextToSpeech.SpeakAsync(_Item.Description, settings);
                await Task.Delay(2000);

                await TextToSpeech.SpeakAsync(_Item.Description, settings);

                #endregion
            }
            catch (Exception ex)
            {
                Console.Write(ex.ToString());
                throw;
            }
            // Task to run every 5 hours on each target day of the week

            

        }

        public async void CancelTask(List<System.Timers.Timer> InLstTimers)
        {
            await Task.Run(() =>
            {
                foreach (var item in InLstTimers)
                {
                    item.Enabled = false;
                    item.Dispose();
                    item.Stop();
                   
                }
            });
        }

        private async Task<List<DayOfWeek>> GetDaysFromModel(List<DayOfWeekModel> ModelWeek)
        {
            List<System.DayOfWeek> lstselectedDays = new List<System.DayOfWeek>();
            #region LllenadoListaDayOfWeek

            await Task.Run(() =>
            {
                //listViewDaysOfWeek.ItemsSource
                foreach (DayOfWeekModel item in ModelWeek)
                {
                    if (item.IsChecked)
                    {
                        switch (item.DayOfWeek)
                        {
                            case "Monday":
                                lstselectedDays.Add(System.DayOfWeek.Monday);
                                break;
                            case "Tuesday":
                                lstselectedDays.Add(System.DayOfWeek.Tuesday);
                                break;
                            case "Wednesday":
                                lstselectedDays.Add(System.DayOfWeek.Wednesday);
                                break;
                            case "Thursday":
                                lstselectedDays.Add(System.DayOfWeek.Thursday);
                                break;
                            case "Friday":
                                lstselectedDays.Add(System.DayOfWeek.Friday);
                                break;
                            case "Saturday":
                                lstselectedDays.Add(System.DayOfWeek.Saturday);
                                break;
                            case "Sunday":
                                lstselectedDays.Add(System.DayOfWeek.Sunday);
                                break;

                            default:
                                break;
                        }

                    }
                }

            });

            #endregion
            return lstselectedDays;

        }

        

        
    }
}

I call some CRUD function in the c# part of a view:

using Android.App;
using Android.App.Job;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ToDo.App.Models;
using ToDo.App.Services;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat;
using Android.App.AppSearch;
using Android;
using Java.Sql;
using System.Threading;
using Android.OS;
using Android.Content;
using Xamarin.Essentials;
using static Android.Content.ClipData;
using static Android.OS.Build;
using Newtonsoft.Json;
using Android.Widget;
using ToDo.App.Strategy;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
using Android.Text.Format;
using System.ComponentModel;
//using static Android.InputMethodServices.Keyboard;
//using static Android.Util.EventLogTags;

//using Java.Lang;

namespace ToDo.App.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class AddPage : ContentPage
    {
        //JobScheduler jobScheduler;
        //ServiceDownloadJob service;
        private CancellationTokenSource cancelSource;
        ToDoItem result = null;
        int EditItemId = 0;
        bool isEdit = false;
        bool isAddInterval = false;

        List<DayOfWeekModel> daysOfWeekList = null;

        public AddPage()
        {
            InitializeComponent();
            FillcboDayOfWeek();
        }

        public AddPage(ToDoItem item)
        {
            InitializeComponent();
            FillFields(item);
        }

        private async void BtnGuardar_Clicked(object sender, EventArgs e)
        {
            try
            {

                var selectedDays = daysOfWeekList.Where(d => d.IsChecked).ToList();

                if (String.IsNullOrEmpty(nombre.Text) || String.IsNullOrEmpty(descripcion.Text) || selectedDays.Count() <= 0)
                {
                    await DisplayAlert("Error", "No puedes dejar campos vacíos.\n\n", "Aceptar");
                    return;
                }

                var existName = await App.Context.FindItemByNameAsync(nombre.Text);
                if (existName != null && !isEdit)
                {
                    await DisplayAlert("Error", "No puedes tener más de una tarea con el mismo nombre.\n\n" + " Porfavor elige otro nombre para tu tarea", "Aceptar");
                    return;
                }


                //ValidationForm(selectedDays);

                var context = Android.App.Application.Context;
                ToDoItem inItem = new ToDoItem();

                if(isEdit)
                    inItem = await App.Context.FindItemAsync(EditItemId);

                await Task.Run(() =>
                {
                    inItem.WithInterval = 0;
                    inItem.Hour = TimePicker.Time;
                    inItem.Name = nombre.Text;
                    inItem.Description = descripcion.Text;

                    // iterar sobre la lista de modelos de días de la semana
                    // get list of selected days
                    inItem.ObjDaysToRepeat = JsonConvert.SerializeObject(selectedDays);

                    if (isAddInterval && !String.IsNullOrEmpty(txtInterval.Text))
                    {
                        inItem.WithInterval = Convert.ToInt32(txtInterval.Text);
                        inItem.Interval = TimeSpan.FromHours(Convert.ToInt32(txtInterval.Text));
                    }
                });

                SchedulerTaskService schedulTaskAsync = new SchedulerTaskService(inItem, context);

                if (isEdit)
                {
                    result = await schedulTaskAsync.EditScheduleAsync();
                    isEdit = false;
                    EditItemId = 0;
                }
                else
                    result = await schedulTaskAsync.ScheduleTask();
                

                if (result != null)
                {
                    //await Task.Delay((int)SleepProccess);
                    Toast.MakeText(context, "Se programó correctamente la alarma", ToastLength.Short).Show();
                    await Navigation.PopAsync();

                }
                else
                    await DisplayAlert("Error", "No se pudo guardar la tarea", "Aceptar");
                

            }
            catch(Exception ex)
            {
                if (result != null)
                {
                    var findItem = await App.Context.FindItemAsync(result.Id);
                    var Deleteresult = await App.Context.DeleteItemAsync(result);
                    List<System.Timers.Timer> CancelDaysAlarm= JsonConvert.DeserializeObject<List<System.Timers.Timer>>(findItem.ObjTimer);

                    if (Deleteresult == 1)
                    {
                        SchedulerTaskService taskScheduler = new SchedulerTaskService();
                        taskScheduler.CancelTask(CancelDaysAlarm);
                    }
                }

                await DisplayAlert("Error", ex.Message, "Aceptar");
            }
        }

        private void CheckBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
        {
            // handle checkbox checked event
            var checkbox = sender as Xamarin.Forms.CheckBox;
            var checkedState = checkbox.IsChecked;
            var dayOfWeek = checkbox.BindingContext as DayOfWeekModel;

            // update the IsChecked property in the DayOfWeekModel object
            dayOfWeek.IsChecked = checkedState;
        }

        private void chkIntervalo_CheckedChanged(object sender, CheckedChangedEventArgs e)
        {
            lbIntervalo.IsVisible = e.Value;
            txtInterval.IsVisible = e.Value;

            if (txtInterval.IsVisible)
                isAddInterval = true;
            else
                isAddInterval = false;

        }

        private void FillcboDayOfWeek()
        {

            daysOfWeekList = new List<DayOfWeekModel>
            {
                new DayOfWeekModel { DayOfWeek = "Monday" },
                new DayOfWeekModel { DayOfWeek = "Tuesday" },
                new DayOfWeekModel { DayOfWeek = "Wednesday" },
                new DayOfWeekModel { DayOfWeek = "Thursday" },
                new DayOfWeekModel { DayOfWeek = "Friday" },
                new DayOfWeekModel { DayOfWeek = "Saturday" },
                new DayOfWeekModel { DayOfWeek = "Sunday" }
            };

            // set the listview's itemssource to the list of days of the week
            listViewDaysOfWeek.ItemsSource = daysOfWeekList;
        }

        private List<DayOfWeekModel> GetDaysLisDayOfWeekModel()
        {
            daysOfWeekList = new List<DayOfWeekModel>
            {
                new DayOfWeekModel { DayOfWeek = "Monday", IsChecked = false },
                new DayOfWeekModel { DayOfWeek = "Tuesday", IsChecked = false },
                new DayOfWeekModel { DayOfWeek = "Wednesday", IsChecked = false },
                new DayOfWeekModel { DayOfWeek = "Thursday", IsChecked = false },
                new DayOfWeekModel { DayOfWeek = "Friday", IsChecked = false },
                new DayOfWeekModel { DayOfWeek = "Saturday", IsChecked = false },
                new DayOfWeekModel { DayOfWeek = "Sunday", IsChecked = false }
            };

            return daysOfWeekList;
        }

        private async void FillFields(ToDoItem item)
        {
            try
            {
                await Task.Run(() =>
                {
                    #region DataEdit
                    TimePicker.Time = item.Hour;
                    nombre.Text = item.Name;
                    descripcion.Text = item.Description;
                    isEdit = true;
                    EditItemId = item.Id;

                    if (item.WithInterval != 0)
                    {
                        txtInterval.Text = item.WithInterval.ToString();
                        chkIntervalo.IsChecked = true;
                    }

                    List<DayOfWeekModel> LstCheckedDaysOfWeek = JsonConvert.DeserializeObject<List<DayOfWeekModel>>(item.ObjDaysToRepeat).ToList();
                    List<DayOfWeekModel> DaysOfWeek = GetDaysLisDayOfWeekModel().ToList();

                    var lstNotIn = (from c in DaysOfWeek
                                    where !(from o in LstCheckedDaysOfWeek
                                            select o.DayOfWeek)
                                          .Contains(c.DayOfWeek)
                                    select c).ToList();

                    daysOfWeekList = LstCheckedDaysOfWeek.Union(lstNotIn).ToList();

                    listViewDaysOfWeek.ItemsSource = daysOfWeekList;

                    #endregion
                });
            }
            catch (Exception ex)
            {

                await DisplayAlert("Error", ex.Message, "Aceptar");
            }

        }

        private async void ValidationForm(List<DayOfWeekModel> CheckedDays)
        {
            //List<DayOfWeekModel>
            //var CheckedDays = daysOfWeekList.Where(d => d.IsChecked).ToList();
           
            if (String.IsNullOrEmpty(nombre.Text) || String.IsNullOrEmpty(descripcion.Text) || CheckedDays.Count() <= 0)
            {
                await DisplayAlert("Error", "No puedes dejar campos vacíos.\n\n", "Aceptar");
                return;
            }

            var existName = await App.Context.FindItemByNameAsync(nombre.Text);
            if (existName != null)
            {
                await DisplayAlert("Error", "No puedes tener más de una tarea con el mismo nombre.\n\n" + " Porfavor elige otro nombre para tu tarea", "Aceptar");
                return;
            }
        }


    }
}

Note: My classes are in the shared project, and I'd like to find a solution to follow to work of this way.

I was trying with Shiny.Core, but I had some problems, sorry I'm a noob in Xamarin

Upvotes: 0

Views: 97

Answers (1)

Jianwei Sun - MSFT
Jianwei Sun - MSFT

Reputation: 4302

As ToolmakerSteve said that you can refer to the answer of this case: How to create a never ending background service in Xamarin.Forms?.

It achieves the effect that app do something in background by ForegroundService of Android.

Upvotes: 0

Related Questions