Search code examples
c#androidmaui.net-7.0

AlarmManager Scheduled Task Android Maui


I'm trying to make alarm program, that will start a page when it goes of. Main problem here is that it is not trowing an error but method OnRecieve() of AlarmReciever class is not starting in time. Also i dunno how to check whether pendingIntent is cancelled or not. And how to get this pendingIntent to cancel if program is restarted.

There some code i tried:

MainPage:

using Android.App;
using Android.Content;

namespace TAlarm;

public partial class MainPage : ContentPage
{
    public PendingIntent pendingIntent;
    Android.Content.Intent intent;
    public AlarmManager alarmManager;

    public MainPage()
    {
        InitializeComponent();
    }

    private void OnTimerClicked(object sender, EventArgs e)
    {
        TimeSpan time = timePicker.Time;

        intent = new Android.Content.Intent(Android.App.Application.Context, typeof(AlarmReceiver));

        pendingIntent = PendingIntent.GetBroadcast(Android.App.Application.Context, 0, intent, PendingIntentFlags.Immutable);

        intent.PutExtra("pendingIntent", pendingIntent);

        alarmManager = (AlarmManager)Android.App.Application.Context.GetSystemService(Context.AlarmService);

        DateTime startTime = DateTime.Today.AddHours(time.Hours).AddMinutes(time.Minutes);
        long interval = 60 * 1000;//AlarmManager.IntervalDay;  60 * 

        alarmManager.SetRepeating(AlarmType.RtcWakeup, startTime.Ticks, interval, pendingIntent);

        text.Text = "Lets start!";

    }

    private void Button_Clicked(object sender, EventArgs e)
    {
        var isPendingIntentValid = (pendingIntent != null);

        if (isPendingIntentValid)
        {
            textMain.Text = "The PendingIntent is still valid";
        }
        else
        {
            textMain.Text = "The PendingIntent is no longer valid";
        }
    }

    private void OnCancelButtonClicked(object sender, EventArgs e)
    {
        // Cancel the PendingIntent
        alarmManager.Cancel(pendingIntent);

    }

}

AlarmReciever:

using Android.Content;

namespace TAlarm
{
    class AlarmReceiver : BroadcastReceiver
    {
        public override async void OnReceive(Context context, Intent intent)
        {
            var m = (MainPage)Microsoft.Maui.Controls.Application.Current.MainPage;

            var nextPage = new NewPage();

            var navigationPage = new NavigationPage();

            await navigationPage.PushAsync(nextPage);

            m.alarmManager.Cancel(m.pendingIntent);
        }

    }
}

I've tried to change:

var navigationPage = new NavigationPage();

into:

var navigationPage = Microsoft.Maui.Controls.Application.Current.MainPage as NavigationPage;

But nothing changed.
So this code should start nextPage when alarm goes off.

All kind of help will be appreciated!

Update: here is added receiver in AndoidManifest.xml:

    <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" >
        <receiver android:name=".AlarmReceiver" />
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
    <!--<uses-permission android:name="android.permission.REQUEST_SCHEDULE_EXACT_ALARM"
                     android:maxSdkVersion="29" />-->
</manifest>

Solution

  • I have create a sample and make the broadcast recevier work.

    The BroadcastReceiver:

     [BroadcastReceiver(Exported =true,Enabled =true)]
       public class AlarmReceiver : BroadcastReceiver
        {
            public override async void OnReceive(Context context, Intent intent)
            { //I added a breakpoint here
    
            }
    
        }
    

    And the Alarmanager:

    private void Button_Clicked(object sender, EventArgs e)
        {
            var intent = new Android.Content.Intent(Android.App.Application.Context, typeof(AlarmReceiver));
            var pendingIntent = PendingIntent.GetBroadcast(Android.App.Application.Context, 0, intent, PendingIntentFlags.Immutable);
            intent.PutExtra("pendingIntent", pendingIntent);
             var alarmManager = (AlarmManager)Android.App.Application.Context.GetSystemService(Context.AlarmService);
            long interval = 60 * 1000;//AlarmManager.IntervalDay;  60 *
            alarmManager.Set(AlarmType.ElapsedRealtimeWakeup, SystemClock.ElapsedRealtime() + interval, pendingIntent);
           // alarmManager.SetRepeating(AlarmType.RtcWakeup, startTime.Ticks, interval, pendingIntent);
        }
    

    The alarmManager.SetRepeating can't work but the alarmManager.Set work. The OnReceive method will trigger after about one minute. I also tested the code in the native android with the android studio and got the same result.

    In addition, you can use the special custom intent filter to make the broadcast receiver work. Such as:

    [BroadcastReceiver(Exported =true,Enabled =true)]
    [IntentFilter(new[] { "com.com.test" })]
       public class AlarmReceiver : BroadcastReceiver
        {
            public override async void OnReceive(Context context, Intent intent)
            { 
    
            }
        }
    

    And send broadcast:

    private void Button_Clicked(object sender, EventArgs e)
        {
         Intent intent1 = new Intent("com.com.test");
         Android.App.Application.Context.SendBroadcast(intent1);
        }
    

    Note: you need to use [BroadcastReceiver(Exported =true,Enabled =true)] instead of registering the broadcast receiver in the AndroidManifest.xml. I don't know why.