Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if user is idle on UWP? [duplicate]

Tags:

c#

timer

uwp

I wanted to make a function that would timeout and navigate to the main page if the user is idle for a certain period of time. After a little research, I found that the ThreadPoolTimer should suit my needs. Testing it I decided to use a 10 sec interval.

    timer =ThreadPoolTimer.CreatePeriodicTimer(Timer_Tick,TimeSpan.FromSeconds(10));

And this is where I'm at a loss. I couldn't figure out a way to check user input on a UWP without having to individually check PointerPressed, PointerExited, etc. So I did some more digging and I found a block of code that's supposed to give you a boolean value if the user is idle or not.

    public static uint GetIdleTime()
    {
        LASTINPUTINFO lastInPut = new LASTINPUTINFO();
        lastInPut.cbSize = (uint)Marshal.SizeOf(lastInPut);
        GetLastInputInfo(ref lastInPut);

        return ((uint)Environment.TickCount - lastInPut.dwTime);
    }

    public static bool IsUserIdle()
    {
        uint idleTime = (uint)Environment.TickCount - GetLastInputEventTickCount();
       if (idleTime > 0)
       {
           idleTime = (idleTime / 1000);
       }
       else
       {
           idleTime = 0;
       }
       //user is idle for 10 sec
       bool b = (idleTime >= 10);
       return b;
    }

    private static uint GetLastInputEventTickCount()
    {
        LASTINPUTINFO lii = new LASTINPUTINFO();
        lii.cbSize = (uint)Marshal.SizeOf(lii);
        lii.dwTime = 0;
        uint p = GetLastInputInfo(ref lii) ? lii.dwTime : 0;
        return p;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct LASTINPUTINFO
    {
        public static readonly int SizeOf = Marshal.SizeOf<LASTINPUTINFO>();
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 cbSize;
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dwTime;
    }

    [DllImport("user32.dll")]
    private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

I then call the function in the tick function and use the conditional statement if IsUserIdle() is equal to true then navigate to the main page.

    public static void Timer_Tick(object sender)
    {
       if (IsUserIdle() == true)
       {
           Frame.Navigate(typeof(MainPage));
       }                                        
    }

But when I start it nothing happens, and after I set a couple breakpoints I found that IsUserIdle() never returns a true value even after 10 sec of inactivity. I am completely stuck so any help would be appreciated.

like image 479
Calvin Carter Avatar asked Dec 24 '22 22:12

Calvin Carter


1 Answers

GetLastInputInfo isn't supported for Windows store apps:

Minimum supported client: Windows 2000 Professional [desktop apps only]

I'm not aware of any intrinsic UWP API to detect if the user is idle, but it's definitely possible to whip up your own mechanism for doing so.

I couldn't figure out a way to check user input on a UWP without having to individually check PointerPressed, PointerExited, etc.

What's so bad about that approach? Here's my attempt:

App.xaml.cs

public sealed partial class App : Application
{
    public static new App Current => (App)Application.Current;

    public event EventHandler IsIdleChanged;

    private DispatcherTimer idleTimer;

    private bool isIdle;
    public bool IsIdle
    {
        get
        {
            return isIdle;
        }

        private set
        {
            if (isIdle != value)
            {
                isIdle = value;
                IsIdleChanged?.Invoke(this, EventArgs.Empty);
            }
        }
    }

    protected override void OnLaunched(LaunchActivatedEventArgs e)
    {
        idleTimer = new DispatcherTimer();
        idleTimer.Interval = TimeSpan.FromSeconds(10);  // 10s idle delay
        idleTimer.Tick += onIdleTimerTick;
        Window.Current.CoreWindow.PointerMoved += onCoreWindowPointerMoved;
    }

    private void onIdleTimerTick(object sender, object e)
    {
        idleTimer.Stop();
        IsIdle = true;
    }

    private void onCoreWindowPointerMoved(CoreWindow sender, PointerEventArgs args)
    {
        idleTimer.Stop();
        idleTimer.Start();
        IsIdle = false;
    }
}

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        App.Current.IsIdleChanged += onIsIdleChanged;
    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        App.Current.IsIdleChanged -= onIsIdleChanged;
    }

    private void onIsIdleChanged(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine($"IsIdle: {App.Current.IsIdle}");
    }
}

Idle is detected when the pointer hasn't moved for 10s within the app window. This will work also for touch-only apps (like mobile apps) because PointerMoved will fire when the window is tapped, too.

like image 85
Decade Moon Avatar answered Dec 28 '22 09:12

Decade Moon