Sometimes in your application you need to know if the user has been idle for some time. Mostly this is used for improving security. For example, a banking application may want to logging out the current user once it detects that the user has been idle for at least 5 minutes. This prevents other people from using the application with the logged in user privilege in case the real user is away from the computer forgetting to logout first.
There are two kinds of this "user idle" detection. The first one is to detect idly user only for the application, and the other one is to detect if the user is idle system wide. For the first one, the user is considered idle if our application does not detect user activity performed on it. So if the user is busy with other application, our application will still consider that the user is idle. Let's call this kind of detection as "local user idle detection".
For the second one, the user is considered idle if he/she really does not interact with the computer no matter which focused application is. Let's call this detection as "System-wide user idle detection".
Local User Idle Detection
For this we can use TApplicationEvents component usually found in Additional tab of the component pallette. It has OnIdle event, which will be triggered when user stop doing any activity with the application. Combined with TTimer (found in System tab), we can do some action after user has been idle for specified time.
It's quite simple to do that. The basic steps would be:
- Set the interval of the TTimer to the amount of time we want to wait before executing the action.
- Set the Enabled property of the TTimer to False.
- Enable the TTimer in the TApplicationEvents's OnIdle event.
- Execute the action in the TTimer's OnTimer event.
So we need a way to detect that the user is active. After dig a bit deeper to Windows inner working, we found that user activity can be detected by the reception of windows messages. For our case, we can just detect the reception of the following messages.
For the demo project, create a new application, drop a TApplicationEvents, a TTimer, a TLabel, and a TButton. Leave their properties to default values, and arrange them to get something like shown below.
- Click the TApplicationEvents, open the Object Inspector and open its Events tab. Double click in the area labeled OnIdle to create skeleton code of OnIdle event handler. Then use the following code for the event handler.
procedure TForm1.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean); begin // if the timer has been enabled means the idle duration is already counting. // Do not continue to avoid resetting the start idle time if Timer1.Enabled then Exit; // store the time when the user become idle FStartIdle := GetTickCount; // enable the timer Timer1.Enabled := True; end;
Double click on our TTimer to generate skeleton code of its OnTimer event handler. And use the following code for the event handler.
procedure TForm1.Timer1Timer(Sender: TObject); var vIdleTime : Cardinal; vHours : Cardinal; vMinutes : Cardinal; vSeconds : Cardinal; begin // calculate the idle time. It's in milliseconds vIdleTime := GetTickCount - FStartIdle; // get how many hour has the user been idle vHours := vIdleTime div (60 * 60 * 1000); if vHours > 0 then vIdleTime := vIdleTime - (vHours * 60 * 60 * 1000); // get the minutes part of the time the user been idle vMinutes := vIdleTime div (60 * 1000); if vMinutes > 0 then vIdleTime := vIdleTime - (vMinutes * 60 * 1000); // get the seconds part of the time the user been idle vSeconds := vIdleTime div 1000; // Show the duration of time user has been idle Label1.Caption := Format('%.2d:%.2d:%.2d', [vHours, vMinutes, vSeconds]); end;Click the TApplicationEvents, open the Object Inspector and open its Events tab. Double click in the area labeled OnMessage to create skeleton code of OnMessage event handler. Then use the following code for the event handler.
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean); begin Handled := False; case Msg.message of WM_KEYDOWN , WM_MOUSEMOVE , WM_MOUSEWHEEL , WM_LBUTTONDOWN , WM_RBUTTONDOWN , WM_MBUTTONDOWN : Timer1.Enabled := False; end; end
Note that if you activate other application and do some activity with it (typing some text, clicking mouse, things like that), the idle counter of our demo application will still counting. The counting however will reset if you move the mouse cursor over our demo application's form, or click on the form or the button.
That's it! Now you can see how easy it is to detect how long has your application user been idle and take action accordingly. You just need to adjust your TTimer's Interval property and what you want to do in its OnTimer event.
Full source code of the demo project is attached. Feel free to use or improve it.
Visit the second part of this tutorial in this page. It discusses how to do "System-wide user idle detection".
Edited by LuthfiHakim, 12 February 2013 - 12:51 AM.