×
Namespaces

Variants
Actions

Detecting user inactivity in Symbian OS

From Nokia Developer Wiki
Jump to: navigation, search

This article shows how a Symbian C++ app can arrange to be notified after a specified number of seconds of user inactivity. The approach described uses active object callback on the completion of RTimer::Inactivity; this is more efficient than polling on User::ResetInactivityTime().

Article Metadata
Article
Created: giridharn (23 May 2007)
Last edited: hamishwillee (16 Dec 2011)

Contents

Introduction

This article describes how to detect user activity and inactivity in Symbian OS. An active object is described, that can be used to detect user activity events. By user activity I mean any key presses or other activity the user performs with the phone. User activity can also be simulated by applications with the system call:

User::ResetInactivityTime();

Several system applications simulate user activity. For example the phone application simulates user activity when there is an incoming call. This is because user activity causes the system screen saver to disappear and the back light to come on for example. System information notes might also call ResetInactivityTime().

You too can control system screen saver and system back light in your applications by calling ResetInactivityTime(). In addition, you can detect the number of user inactivity seconds at any time by calling:

User::InactivityTime();

However, to be notified when there has been a certain amount of seconds without user activity you need to use the call:

RTimer::Inactivity(TRequestStatus& aStatus, TTimeIntervalSeconds aSeconds);

This call can be used to implement an active object that allows you to be notified when the has been a certain period of user inactivity. This is the basis for implementing applications such as screen savers or other applications that need to do some processing when there is no user activity.

In this article such an active object is described. This active object uses the observer paradigm as an interface towards the rest of the application. You can therefore implement the logic of your application in the observer callbacks. Detecting user inactivity is relatively straightforward.

However not so detecting when user activity is resumed after an inactivity period. This is because there is no API to do that. Whilst one could be tempted to periodically call User::InactivityTime() this would cause a drain of battery life.

This article therefore also describes a trick to detect user activity by abusing a little bit the semantics of the RTimer::Inactivity() call[1].

Implementation

The active object monitoring user activity or inactivity uses an observer to communicate to the rest of the application. The declaration of the observer is as follows:

class MactivityManagerObserver
{
public :
virtual void ActivityDetected() = 0;
virtual void InactivityDetected() = 0;
};

In InactivityDetected() you implement what your application needs to do when there has been inactivity, e.g. if you are writing a screen saver your application will come to the foreground.

In ActivityDetected() you implement what your application needs to do when activity is resumed after the inactivity period, e.g. if you are writing a screen saver your application will go to the background.

This declaration of the active object is as follows:

class CActivityManager : public CActive
{
public:
 
IMPORT_C static CActivityManager* NewL(MActivityManagerObserver* aObserver, TInt aTimeout = 60);
 
IMPORT_C ~CActivityManager();
 
IMPORT_C void SetTimeout(TInt aTimeout);
 
IMPORT_C TBool Start();
 
IMPORT_C void Reset();
 
 
protected: // from CActive
 
void DoCancel();
 
void RunL();
 
 
protected:
 
CActivityManager(MActivityManagerObserver* aObserver, TInt aTimeout);
 
void ConstructL();
 
 
protected:
 
enum TWatch { ENone = 0, EWaitingForInactivity, EWaitingForActivity };
 
protected:
 
RTimer iTimer;
TWatch iWatch;
MActivityManagerObserver * iObserver; ///The observer of activity status
TInt iTimeout; ///Current inactivity period
 
};

You create an instance of this active object by calling NewL. You can change the timeout (in seconds) by calling SetTimeout(). This is the timeout after which, if there has been no user activity, InactivityDetected() is called in your observer. You start monitoring user activity by calling Start() and stop by calling Reset(). alling SetTimeout() always causes a reset of inactivity monitoring.

As for the implementation, here are some trivial methods:


EXPORT_C CActivityManager* CActivityManager::NewL(MActivityManagerObserver* aObserver, TInt aTimeout)
{
CActivityManager* self = new (ELeave) CActivityManager(aObserver, aTimeout);
 
CleanupStack::PushL(self);
 
self->ConstructL();
 
CleanupStack::Pop(self);
 
return self;
}
CActivityManager::CActivityManager(MActivityManagerObserver* aObserver, TInt aTimeout)
: CActive(CActive::EPriorityHigh), iObserver(aObserver), iTimeout(aTimeout)
{
CActiveScheduler::Add(this);
}
 
EXPORT_C CActivityManager::~CActivityManager()
{
Cancel();
 
iTimer.Close();
}
 
 
void CActivityManager::ConstructL()
{
iTimer.CreateLocal();
}
 
 
EXPORT_C void CActivityManager::SetTimeout(TInt aTimeout)
{
iTimeout = aTimeout;
 
Reset();
}
 
 
EXPORT_C void CActivityManager::Reset()
{
Cancel();
 
Start();
}
 
 
void CActivityManager::DoCancel()
{
iTimer.Cancel();
 
iWatch = ENone;
}

The iWatch variable implements the state machine of the active object: we are either doing nothing, or monitoring inactivity or monitoring activity. The core methods are Start() and RunL().

This is the implementation of Start():

EXPORT_C TBool CActivityManager::Start() 
{
TInt inactivity = User::InactivityTime().Int();
 
TBool userIsActive = ( inactivity < iTimeout );
 
if (!IsActive())
{
if ( userIsActive )
{
iWatch = EWaitingForInactivity;
iTimer.Inactivity(iStatus, iTimeout);
SetActive();
}
else
{
iWatch = EWaitingForActivity;
iTimer.Inactivity(iStatus, 0); // See explanation for the RunL method
SetActive();
}
}
 
return userIsActive;
}

Essentially we set the state variable to monitoring inactivity and launch the inactivity notification offered by RTimer by specifying the desired timeout in seconds. RunL() will be executed after iSeconds seconds of user inactivity.

Note that any previous user inactivity period is taken into consideration. This means that if there have already been, for example, 5 seconds of user inactivity when we issue the call, and we request to be notified after 10 seconds of user inactivity, then the call will complete in 5 seconds and not in 10 seconds.

Another thing which is very important and is at the core of the mechanism to detect user activity after an inactivity period is that we are not notified for an interval less than the current inactivity time. To explain it better, if we request to be notified after 3 seconds of user inactivity but the system has already been inactive for 5 seconds then we are not notified until activity is resumed and a following 3 second inactivity period occurs.

This is the implementation of RunL():

void CActivityManager::RunL()
{
if (iStatus == KErrNone)
{
if (iWatch == EWaitingForInactivity)
{
TInt inactivity = User::InactivityTime().Int();
 
if (inactivity >= iTimeout)
{
if (iObserver)
{
iObserver->InactivityDetected();
}
 
if (!IsActive()) //observer might have called a Reset()
{
iTimer.Inactivity(iStatus,0);
iWatch = EWaitingForActivity;
}
}
else
{
iTimer.Inactivity(iStatus,iTimeout);
}
}
else if (iWatch == EWaitingForActivity)
{
if (iObserver)
{
iObserver->ActivityDetected();
}
 
if (!IsActive()) //observer might have called a Reset()
{
iTimer.Inactivity(iStatus,iTimeout);
iWatch = EWaitingForInactivity;
}
}
 
if (!IsActive()) //observer might have called a Reset()
{
SetActive();
}
}
else
{
iWatch = ENone;
}
}

As you can see it is a simple binary state machine going from monitoring inactivity to monitoring activity and so forth and calling the appropriate observer callbacks. After an inactivity period we use a call to RTimer::Inactivity(iStatus, 0) to be notified of activity again.

This is the trick to detect activity without polling User::InactivityTime(). If there has already been any inactivity period longer than 1 second, RTimer::Inactivity(iStatus,0) will in fact complete exactly when activity is resumed-

This page was last modified on 16 December 2011, at 04:39.
67 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×