Idle Events in 3.10.0

In a few recent posts I’ve mentioned plans to add Idle events. The day has arrived!

There have been some discussions about screensavers & automated closing of pages. None of that was possible without having a way to detect user inactivity. Now you can monitor this by handling Idle events & configuring the IdleTimeout in MainPage.xaml

IdleTimeout is the period of inactivity before the Idle event is raised. The value is in seconds & defaults to 60.

The status of application idle is available through the boolean system variable App.IsIdle. This variable will equal True when in idle status. Any touch or click gesture on the screen will exit idle status.

There are 2 different ways to handle the events:

  1. Globally with Scenes
    You can create a custom scene that includes a Trigger for App.IsIdle equals True. This is demonstrated in the example project with the IdleCounter virtual device. That counter is incremented each time the system variable is set.

  2. Individually with Pages
    Each ContentPage has an Idle event. If you need to run a custom set of Actions only when a particular page is open it might make more sense to handle it here. This is demonstrated with the ShowToast message in the example project.

IdleEvents.hrp (4.3 KB)

6 Likes

Hi Bill this is great, thank you. I am trying to follow your examples and apply an Action when Idle on a page.
What I am trying to do is, if the page is Idle, then go to another page. I would also reset the counter. That other page in turn would have a similar Idle even, but would then go to another page.

I have the same Scene as in your example as well as the Virtual IdleCounter device.

I am getting an error when the Idle is triggered: Calling thread can not access object … because another thread owns it"

Any idea what I could be doing wrong?

Send me your project so I can test it. You should be able to execute any action. Also, you do not need that virtual IdleCounter. That was just part of the example. You don’t need any virtual devices to handle Idle events.

IdleEvents.hrp (4.5 KB)
I have modified your example to what I am trying to acheive.

Added Page2.xaml
Modified Page1.xaml event to GoToPage -> Page2.xaml on Idle instead of ShowToast message.

I am getting the same error: “Calling thread can not access object … because another thread owns it”

Any help appreciated.

Ok, I’m seeing the same thing. This will be fixed in the next release.

Any reason you can’t just do that in the global Scene event?

For now, I’d say use that. You won’t have that issue with the Scene.

Can i create the scene to cycle though multiple pages? I got it to GoToPage at Idle but how do I make it the go to another page after the next x sec?

Maybe with a global Variable? I could set that variable to the name of “next page” and use it in the scene?

I don’t recommend doing that. Are you designing “screensaver” functionality?

If so, create a single ScreenSaver.xaml page. Then add a PageBrowser to it. Have the PageBrowser cycle through your pages. It’ll perform much better.

My idea is to have individual control screens e.g. start.xaml, camera.xaml, lights.xaml, windows.xaml.

When the application start it starts on start.xaml. From there the user can chose the control screen. after 5 min of inactivity, all screens cycle through except the start.xaml

So its not really a screensaver, more of a constant monitor of various devices.

OK. For that logic it would make sense to do it in the XAML page. It’ll be simpler if you do it there. I’ll try to get the fix posted in a day or 2.

Amazing. Thank you. Looking forward to testing it.

Thanks Bill - I now have the Idle Event working with my screensaver and have gotten rid of some clunky javascript that was doing much less in a more complicated fashion. Basically before the screensaver would come on after a set timeout whether it was idle or not. This new feature is great.

Is there a way I’m not seeing to set the IdleTimeout parameter from an incoming (mqtt) variable? Previously, I had it set up with javascript to allow the timeout to be adjustable from the UI. If it’s not possible, definitely not a big deal as this is already a huge improvement to what I had.

Sorry, there is not. That can’t be changed during runtime.

1 Like

No problem and thanks for the quick response.

Just wondering if there has been any progress on these idle events. I must say I was a bit absent from the forum as of late.

Not sure I follow. These were completed last month. There’s an example in the very top post.

There originally was that 1 bug you found but the fix was posted only a few days after you reported it. Everything should all be good now.

My bad. I missed your previous posts on the fix.

Hi Bill,

Been looking for screensaver functionality for a while now; this is a huge improvement. There is one thing I’m trying to do though that I haven’t yet been able to figure out. It boils down to using the value of a binding as a Parameter in a GoToPage method action.

Basically I have a simple Screensaver.xaml page, which my EnterScreensaver scene is successfully directing to at idle. At App.Idle=False, the ExitScreensaver scene is triggered, whose action is another GoToPage; however I want it to return to the page that it originally came from. Currently I’m using Loaded triggers on every page to set a variable (using a plugin similar to your IdleCount), which appears to be correctly loading the page’s name into my “LastPage.Value” binding; I can see the correct text using a label. However I don’t know how to get this binding to be a parameter in GoToPage (LastPage.Value contains the text PageToGoBackTo.xaml, which changes depending on which page was last loaded). I just keep getting “Could not load file or assembly ‘{LastPage.Value}’ or one of its dependencies”. It’s clearly not parsing the variable as I’d hoped. I’ve also tried {Binding LastPage.Value}. What is the correct syntax to allow this, if it’s allowed?

Is there a different syntax I have to use for the Parameter field to allow it to parse the binding? Is there a simpler way to accomplish what I’m looking for?

Hold on with building the screensaver. Parameter bindings are not supported but there is a simpler way to accomplish this. I was just going to share an example with you but I see that it’s not currently working. So hold on for the time being & I’ll let you know when the fix is ready.

There was a minor change to Idle behavior in the latest release that resets App.IsIdle when a new page is opened. This helped fix 1 issue but it broke screensaver behavior. Now, what is happening is your “EnterScreensaver” will run but it’ll immediately reset App.IsIdle when it changes the page causing “ExitScreensaver” to also run.

Here’s what I was just going to share with you until I tested it myself & realized it wasn’t working:

Use the parameterless ClosePage method that was recently added. The app has its own internal tracking of the current “OpenPage” so you don’t need to track that yourself. For this to work, make sure “EnterScreensaver” calls OpenPage. Don’t use GoToPage for your screensaver.
Screensaver.hrp (4.5 KB)

For what it’s worth, this is working perfectly fine for me using the Designer on Windows (3.16) and version 3.18 of the Android app - the OpenPage and ClosePage seem to play nicely with App.IsIdle now.

Here is another screensaver example that uses a Plugin to cycle through images indefinitely.

Screensaver_Plugin.hrp (15.1 KB)

plugin.Name = "Screensaver";
plugin.OnChangeRequest = onChangeRequest;
plugin.OnConnect = onConnect;
plugin.OnDisconnect = onDisconnect;
plugin.OnPoll = onPoll;
plugin.OnSynchronizeDevices = onSynchronizeDevices;
plugin.PollingInterval = 10000;
plugin.DefaultSettings = {};

var counter = 0;
var images = ['image_0.png', 'image_1.png', 'image_2.png', 'image_3.png']

function onChangeRequest(device, attribute, value) {
}

function onConnect() {
}

function onDisconnect() {
}

function onPoll() {
    var device = plugin.Devices["1"];
    device.Image = images[counter];
    counter = counter + 1;
    if (counter >= images.length) {
        counter = 0;
    }
}

function onSynchronizeDevices() {
    var screensaverDevice = new Device();
    screensaverDevice.Id = "1";
    screensaverDevice.DisplayName = "Screensaver";
    screensaverDevice.Capabilities = [];
    screensaverDevice.Attributes = ["Image"];
    plugin.Devices[screensaverDevice.Id] = screensaverDevice;
}