Search code examples
c#androidtimermaui.net-maui

Navigate back to home page if there is no user interaction for x duration (user is idle)


I am working on a self management app and I need to implement an inactivity timer or something similar to know if the user is idling (without interacting with a view for x duration) so when this happens it returns to the home page.

Is this possible? Already check app lifecycle docs and doesn't found any relevant info.

I was thinking of making a timer per view that when the time comes x redirects but it seems less than optimal. Is there a way to integrate it directly into the application or do I really have to set a timer per view?

Sorry if my question is not clear. Please ask and I can update if it doesn't make sense.


Solution

  • Cross platform code (common)

    using Timer = System.Timers.Timer;
    

    App.xaml.cs

    Timer IdleTimer = new Timer( 60 * 1000);  //each 1 minute
    
    public App()
    {
        InitializeComponent();
        MainPage = new AppShell();
    
        IdleTimer.Elapsed += Idleimer_Elapsed;
        IdleTimer.Start();
    }
    
    async void Idleimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        Console.WriteLine(":::Elapsed");
        if (MainThread.IsMainThread)
            await Shell.Current.Navigation.PopToRootAsync();
        else
            MainThread.BeginInvokeOnMainThread(async () => await Shell.Current.Navigation.PopToRootAsync());}
    
    public void ResetIdleTimer()
    {
        IdleTimer.Stop();
        IdleTimer.Start();
    }
    

    Android

    To detect user interaction on Android you can relay on OnUserInteraction().

    Android docs

    MainActivity.cs

    public override void OnUserInteraction()
    {
        base.OnUserInteraction();
        (App.Current as App).ResetIdleTimer();
    }
    

    iOS

    on ios you can listen to touche events at application level source.

    Program.cs

    static void Main(string[] args)
    {
       UIApplication.Main(args, typeof(CustomApplication), typeof(AppDelegate));
    }
    

    CustomApplication.cs

    public class CustomApplication : UIApplication
    {
        public CustomApplication() : base()
        {
        }
    
        public CustomApplication(IntPtr handle) : base(handle)
        {
        }
    
        public CustomApplication(Foundation.NSObjectFlag t) : base(t)
        {
        }
        public override void SendEvent(UIEvent uievent)
        {
            if (uievent.Type == UIEventType.Touches)
            {
                if (uievent.AllTouches.Cast<UITouch>().Any(t => t.Phase == UITouchPhase.Began))
                {
                    (App.Current as App).ResetIdleTimer();
                }
            }
    
            base.SendEvent(uievent);
        }
    

    Windows

    For windows you can listen to some native window events like WM_NCACTIVATE: 0x0086 or WM_SETCURSOR 0x0020 or WM_MOUSEACTIVATE.

    I am sure there is a more efficient way which listens to mouse cursor moving event, for some reasons is not reported at this level (for example WM_MOUSEMOVE).

    MauiProgram.cs

                .ConfigureLifecycleEvents(events =>
                {
    #if WINDOWS
                    events
                        .AddWindows(windows => windows
                            .OnPlatformMessage((window, args) =>
                            {
                                if (args.MessageId == Convert.ToUInt32("0x0086", 16) ||
                                    args.MessageId == Convert.ToUInt32("0x0020", 16) ||
                                    args.MessageId == Convert.ToUInt32("0x0021", 16) )
                                {
                                    (App.Current as App).ResetIdleTimer();
                                }
                            }));
    #endif