I wanted a class that could handle all my logins and with global access to it. It should also handle automatic logout and warn the user.
I used the singleton pattern. This solution is easy to make as a screen saver too. Send me a copy if you do :)
The Application:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:nsViews="views.*" >
<mx:Script>
<![CDATA[
/* herby granted all usage of this code By UmbrellaCorp.no
* athor Cato Paus */
import no.umbrellaCorp.util.userIdle.UserIdleWatcher;
import no.umbrellaCorp.util.userIdle.events.UserIdleEvent;
import views.events.LoginEvent;
protected function loginPanelHandler(event:LoginEvent):void{
//trace('loginPanelHandler: ' + event);
if(event.type == LoginEvent.USERLOGINSUCCSESS){
viewStack.selectedChild = userLogedIn;
viewStack.percentHeight = 100;
viewStack.percentWidth = 100;
UserIdleWatcher.getInstance().startWatch(event.timeToBee,event.timeToBee/10,userLogedIn);
UserIdleWatcher.getInstance().addEventListener(UserIdleEvent.USERIDLE, logOutHandler);
trace(event.timeToBee/10);
}
}
protected function logOutHandler(event:UserIdleEvent):void{
//trace('logOutHandler: ' + event);
UserIdleWatcher.getInstance().stopWatch();
viewStack.selectedChild = loginPanel;
viewStack.height = 320;
viewStack.width = 329;
// here you can do some cleaning.
// or store som data's to sharedObject.
}
]]>
</mx:Script>
<mx:ViewStack id="viewStack" width="329" height="320" horizontalCenter="0" verticalCenter="0">
<nsViews:LoginPanel id="loginPanel" width="329" height="100%" userLogInSuccess="loginPanelHandler(event);" userLogInFault="loginPanelHandler(event);" />
<nsViews:UserLogedIn id="userLogedIn" width="329" height="100%" logOutUser="logOutHandler(event);" />
</mx:ViewStack>
</mx:Application>
The AlertPopUp:
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" title="Warning...." layout="vertical" width="240" height="102" verticalScrollPolicy="off" horizontalScrollPolicy="off">
<mx:Script>
<![CDATA[
import mx.effects.easing.*;
]]>
</mx:Script>
<mx:Sequence id="elastic" target="{this}">
<mx:Move duration="300" yBy="-100"/>
<mx:Move duration="700" yBy="100" easingFunction="{Elastic.easeOut}"/>
</mx:Sequence>
<mx:Text id="UserIdleText" text="WARNING" width="100%" height="100%" textAlign="center" />
</mx:TitleWindow>
The SingleTon:
package no.umbrellaCorp.util.userIdle{
[Event(name="userIdleHandler", type="no.umbrellaCorp.util.userIdle.events.UserIdleEvent")]
[Event(name="warnUserIdleHandler", type="no.umbrellaCorp.util.userIdle.events.UserIdleEvent")]
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.EventDispatcher; // the class I have choosed as my super class.
import no.umbrellaCorp.util.userIdle.events.UserIdleEvent; // The custom events created for this class.
import no.umbrellaCorp.util.userIdle.UserIdlePopUp; // the popUp (TitleWindw) that warn the user. Created in mxml.
import mx.managers.PopUpManager;
import flash.display.DisplayObject; // used for receiveing the object UIobject to watch.
public final class UserIdleWatcher extends EventDispatcher {
// the public final ..
// means that this is the last subclass of EventDispatcher.
// If you want to subclass this class you will have to remove the "final" keyword.
[Bindable]
public var showTime:String;
// the showTime is for this demo.
// I use [Bindable] to create a binding to this showTime var, becouse I want to show the changes in another part of the app.
private const SECOND:uint = 1000; // this is milisecounds.
private var _userIdlePopUp:UserIdlePopUp; // Here I store my PopUp for later use.
private var _objectToWatch:DisplayObject; // this is where I store the reference to the DisplayObjet I want to watch.
private var _wait:uint; // these variables are in the outer scope so I can accesse them when I want and I can connect
private var _warning:uint // public getters and setters on them, if I will alter them after the instance.
private var _timer:Timer // The Haert of this class :)
public function startWatch(wait:uint, warning:uint, objectToWatch:DisplayObject):void{
/* after calling the UserIdleWatcher.getInstance() you call this method like this
UserIdleWatcher.getInstance().startWatch(event.timeToBee,event.timeToBee/10,userLogedIn);
as you can see I have divided the timeToBee by 10, then the warning starts to tick after a 10'th
of the time the user enters as a limit to stay if she or her went to the WC.
*/
this._objectToWatch = objectToWatch;
this._wait = wait;
this._warning = warning;
this._objectToWatch.addEventListener(MouseEvent.MOUSE_MOVE, this.mouseEventHandler);
this._objectToWatch.addEventListener(MouseEvent.CLICK, this.mouseEventHandler);
this._objectToWatch.addEventListener(KeyboardEvent.KEY_DOWN, this.keyboardEventHandler);
this._timer = new Timer(SECOND,wait);
this._timer.addEventListener(TimerEvent.TIMER, this.timerTicEventHandler);
this._timer.start();
trace('Watching MouseEvent.MOUSE_MOVE\ton comp:' + this._objectToWatch.name + ' = ' + this._objectToWatch.hasEventListener(MouseEvent.MOUSE_MOVE));
trace('Watching MouseEvent.CLICK\ton comp:' + this._objectToWatch.name + ' = ' + this._objectToWatch.hasEventListener(MouseEvent.CLICK));
trace('Watching KeyboardEvent.KEY_DOWN\ton comp:' + this._objectToWatch.name + ' = ' + this._objectToWatch.hasEventListener(KeyboardEvent.KEY_DOWN));
trace('\nTimer has EventListener: = ' + _timer.hasEventListener(TimerEvent.TIMER));
}
public function stopWatch():void{
this._timer.stop();
this._objectToWatch.removeEventListener(MouseEvent.MOUSE_MOVE, this.mouseEventHandler);
this._objectToWatch.removeEventListener(MouseEvent.CLICK, this.mouseEventHandler);
this._objectToWatch.removeEventListener(KeyboardEvent.KEY_DOWN, this.keyboardEventHandler);
this._timer.removeEventListener(TimerEvent.TIMER, this.timerTicEventHandler);
trace('Watching MouseEvent.MOUSE_MOVE\ton comp:' + this._objectToWatch.name + ' = ' + this._objectToWatch.hasEventListener(MouseEvent.MOUSE_MOVE));
trace('Watching MouseEvent.CLICK\ton comp:' + this._objectToWatch.name + ' = ' + this._objectToWatch.hasEventListener(MouseEvent.CLICK));
trace('Watching KeyboardEvent.KEY_DOWN\ton comp:' + this._objectToWatch.name + ' = ' + this._objectToWatch.hasEventListener(KeyboardEvent.KEY_DOWN));
trace('\nTimer has EventListener: = ' + _timer.hasEventListener(TimerEvent.TIMER));
this._objectToWatch = null;
}
private function timerTicEventHandler(event:TimerEvent):void{
var target:Timer = Timer(event.target);
/* var's for This Demo */
var mWait:int = this.wait /60;
var sWait:int = this.wait %60;
var mWarning:int = this.warning /60;
var sWarning:int = this.warning %60;
var sm:int = target.currentCount /60;
var ss:int = target.currentCount %60;
showTime = 'Settings: ' + mWait + " minuts and " + ( sWait <= 9 ? "0" + sWait : sWait) + " seconds to logout"
+ "\nwaring after: " + mWarning + " minuts and " + ( sWarning <= 9 ? "0" + sWarning : sWarning) + " seconds"
+ "\n\nCurrent Time: " + sm + " minuts and " + ( ss <= 9 ? "0" + ss : ss) + " seconds";
/* var's for This Demo - end */
if(target.currentCount == wait){
var userIdleEvent:UserIdleEvent = new UserIdleEvent(UserIdleEvent.USERIDLE);
userIdleEvent.message = 'User is Idle';
userIdleEvent.timeLeft = this.timeLeft;
PopUpManager.removePopUp(_userIdlePopUp);
_userIdlePopUp = null; // freeing up memory for popUp;
dispatchEvent(userIdleEvent);
return;
}else if(target.currentCount >= warning){
var warningEvent:UserIdleEvent = new UserIdleEvent(UserIdleEvent.WARNING);
warningEvent.message = '!Warning User is starting Idle Time!';
warningEvent.timeLeft = this.timeLeft;
dispatchEvent(warningEvent);
if(_userIdlePopUp){
var m:int = timeLeft / 60;
var s:int = timeLeft % 60;
_userIdlePopUp.UserIdleText.text ='You will automatic be loged out after \n' + m + " minuts and " + ( s <= 9 ? "0" + s : s) + " seconds";
//_userIdlePopUp.elastic.play(); // uncomment this for fun effect.
}else{
_userIdlePopUp = UserIdlePopUp(PopUpManager.createPopUp(_objectToWatch, UserIdlePopUp, false)); // Createding a non-modal popup so the user can trigger the handlers.
PopUpManager.centerPopUp(_userIdlePopUp);
}
return;
}
}
/** event handlers for the object retreeved in to this Class **/
private function mouseEventHandler(event:MouseEvent):void{
if(_userIdlePopUp){
PopUpManager.removePopUp(_userIdlePopUp);
_userIdlePopUp = null;
}
this._timer.reset();
this._timer.start();
}
private function keyboardEventHandler(event:KeyboardEvent):void{
if(_userIdlePopUp){
PopUpManager.removePopUp(_userIdlePopUp);
_userIdlePopUp = null;
}
this._timer.reset();
this._timer.start();
}
/** Public interface to this Class done with getters and settes **/
public function get wait():uint{
return this._wait;
}
public function set wait(wait:uint):void{
this._wait = wait;
}
public function get warning():uint{
return this._warning;
}
public function set warning(warning:uint):void{
this._warning = warning;
}
/** private getter for class use **/
private function get timeLeft():uint{
return wait - _timer.currentCount;
}
/* Singleton stuff..
Single Instance and give us a Global Access trow the Application
*/
private static var _instance:UserIdleWatcher;
/* The private modifier makes the variable accessible only within this class
the static keyword together with the private modifier, means that it can not be modified outside this class.
Because it is static, it can be invoked before an instance of the class exists.
and I use it in the method "public static function getInstance()"
NOTE: If you realy want to edit this static var you can use public getters and setters.
*/
public function UserIdleWatcher(enforcer:SingletonEnforcer){}
/* The Public function UserIdleWatcher. (same as the class file name) is way we have to do the Singleton Patter.
AS3 do not have true private constructors and you can see that I have a var named enforcer declered
as SingletonEnforcer this SingletonEnforcer Class is at the bottom of this Class, and it is outside
this package, and only accessible to primary class. So here is the geniale idea.
You Can't Create a Instance of this class by calling new UserIdleWatcher()
like: var myUserIdleWatcher:UserIdleWatcher = new UserIdleWatcher();
NOTE: This is not an absolute restriction since you can get around this by putting null into the
constructor. However, it is the best we can do without a true private constructor.
*/
public static function getInstance():UserIdleWatcher{
/* Because it is static, it can be invoked before an instance of the class exists.
and it give us a global accsess.
This method return UserIdleWatcher. You can see at it like a copy of UserIdleWatcher and then use the copy.
*/
if(UserIdleWatcher._instance == null){
UserIdleWatcher._instance = new UserIdleWatcher(new SingletonEnforcer());
//trace('instance created!')
return UserIdleWatcher._instance;
/* Here I check to see if the getInstance have bee invoked before.
if not create and return the "copy of myself <-- UserIdleWatcher class talking"
*/
}
//trace('first born instacne returned!')
return UserIdleWatcher._instance;
/* The check did find out that the _instance was not empty
so return that instance of UserIdleWatcher.
that's all there is to it to make a fake private method in AS3.
*/
}
}//Class
}// package
class SingletonEnforcer {} /* This is a new feature of ActionScript 3.0 the ability to add multiple class definitions to one
file. You can access only one of the classes from outside the ActionScrip file, and that
is the class inside the package definition with the same name as the file.
I use this feature to create a "private" class and make it our constructor parameter's type.
I realy recomend you to read: Advanced ActionSrcipt 3 with Design Patterns
By Joey Lott and Danny Patterson. Adobe Press - ISBN 0-321-42656-8
And: Programing Flex 2 By Chafic Kazoun & Joey Lott. at www.oreilly.com - ISBN-10: 0-596-52689-x
*/
And the Custom Event's:
package no.umbrellaCorp.util.userIdle.events {
import flash.events.Event;
/**
* This custom Event class adds a message, and time left property to a basic Event.
*/
public class UserIdleEvent extends Event {
/**
* The name's of the new UserIdleEvent's type's.
*/
public static const USERIDLE:String = "userIdle";
public static const WARNING:String = "warning";
public static const LOGOUTUSER:String = "logOutUser";
/**
* A message that can be passed to an event handler
* with this event object.
* Used for debuging.
*/
public var message:String;
public var timeLeft:uint;
/**
* Constructor.
* @param message The text to display when the alarm goes off.
* @param timeLeft The text to display when the how long to the alarm goes off.
*/
public function UserIdleEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false, message:String=null, timeLeft:uint=0){
super(type, bubbles, cancelable);
this.message = message;
this.timeLeft = timeLeft;
}
/**
* Creates and returns a copy of the current instance.
* @return A copy of the current instance.
*/
public override function clone():Event{
return new UserIdleEvent(type, bubbles, cancelable, message, timeLeft);
}
/**
* Returns a String containing all the properties of the current
* instance.
* @return A string representation of the current instance.
*/
//--------------------------------------------------------------------------
//
// Overridden methods: Event
//
//--------------------------------------------------------------------------
public override function toString():String{
return formatToString("UserIdleEvent", "type", "bubbles", "cancelable", "eventPhase", "message","timeLeft");
}
}