Prerequisite knowledge
Basic knowledge of Flex, ActionScript 3.0, and software architecture. Familiarity with at least one touchscreen application, for example an ATM, DVD kiosk, ticket kiosk, or smart phone.
 
User level
Intermediate
Required products
Flex Builder (Download trial)
Sample files
Adobe Flex makes building an incredibly rich and attractive kiosk application relatively simple. Chances are you have never had the opportunity to work on a kiosk or build one, in which case a kiosk can seem like a mystery. Flex technology, however, is behind an increasing number of kiosks and will most likely continue to grow in acceptance for this type of application.
 
In this article I'm going to cover some basic kiosk concepts, such as working with both user-driven and timed events. I will also describe the general architecture of a kiosk application, and build a simple kiosk application to help explain the concepts.
 

 
Kiosk concepts

Before diving into the nitty-gritty details of building a kiosk, there are some concepts to learn that will help change your mindset from traditional application development to working with kiosks. The following list is by no means comprehensive, but should help you wrap your head around how a kiosk is put together.
 
The machine itself
Chances are, one of the reasons you became a Flex developer was because it saved you time moving the application from one platform to another. Almost gone are the days of using JavaScript to detect what version of Opera, Firefox, or Internet Explorer the user is on, and if that version supports CSS 2.x or 1.x, and so forth. Flex gives you the ability to standardize the user environment, so you no longer worry about those exceptions and instead can focus on the rich interface.
 
This is my favorite part of kiosk development – your company usually owns the hardware, the machine itself. You get to control the processor, RAM, video card, touchscreen, printer, swiper, or anything else you can imagine. This is by far the greatest advantage of kiosk development. Of course the tighter you integrate with a Windows machine, the harder it will be to switch to a Linux or Mac machine, so I encourage you to use as many cross-platform technologies as possible, for example Java, basic JavaScript, and printer/swiper/other hardware that will work across the platforms with little change, instead of browser and platform specific ActiveX.
 
Interacting with hardware
So here's the big question: How does Flex interact with hardware? Do you need to interact with hardware at all? Some kiosk applications require a printer, a card swiper, and maybe another piece of hardware (for example a scanner on an ATM, a barcode reader on a DVD Kiosk, and so on). Interacting with the hardware isn't as complicated as it might seem though; after all you usually own it. There are a few methods for interacting with hardware, but as of right now, the only way is to tunnel your communication from Flex to another application on the kiosk's hard drive to do the dirty work for you.
 
You can, for example, create and ActiveX or Java application that has native support for hardware interaction. The application might have to be signed (and trusted) in order to run with heightened permissioning to talk to the hardware; but as long as you have a valid Certificate Authority (CA) and signing certificate, it should be no problem. To bridge the ActiveX or Java application to Flex, I use ExternalInterface and create a hardware class in my Flex app that will take care of everything for me.
 
Touchscreens
Touchscreen development shouldn't be difficult, but if you have never worked with a touchscreen it can be a bit overwhelming. Touchscreens are just another Human Interface Device (HID) that your computer can use to determine what your users are trying to do—it's basically an expensive mouse. At the time of this writing, you can pick one up for development purposes, or even production, off of eBay for between $100 and $4000.
 
When you set up your touchscreen device, the software drivers that come with it will include various options to configure it for your application. These may include the ability to disable right-click from a touchscreen (yes, right-click is possible), decide when a user's click action is actually performed (for example on lift or on press), and even at which processor priority level the touchscreen application should run. In my experience, I would recommend disabling right-click, configuring the click to occur on press (mouseDown), and setting the processor priority level to High to simplify the development with kiosks and touchscreens..
 
Parallax
Parallax refers to an apparent shift in position of an object when the viewer's position changes. You have likely experienced this on older ATMs; there are physical buttons on either side of the screen, but the labels just don't seem to line up with the buttons. If you bend over or stand on tiptoes then you can line up the labels with the buttons–that's parallax in action.
 
If you don't want your users to have to bend over to make sure they're selecting what they really want, a full understanding of this phenomenon is important when you build your kiosk. Make sure designers are aware of this as well; placing vertical buttons too close together can cause users to accidentally select the wrong button.
 

 
User-driven and timedscreens–keeping track of the application's state

The first thing to consider when building the application itself is also one of the easiest to overlook: allowing Flex to keep track of the application's state. The typical case for Flex development is to allow the user almost complete control of the entire process and flow of the application. While kiosk development is similar, user's sessions must be kept separate from one user to the next. And, unlike a game or other web application, kiosks are always running. It's very important to add some sort of timer to every page that is displayed in the application to make sure your kiosk's display is dynamic and rich. The timing can vary depending with the business needs of the application, or with your professional judgment as a developer.
 
The sample application in this article uses two timers, one to wait for user input, another to kick unresponsive users out of their session on an input screen.
 
It seems that everybody has their own way of tracking application state; here I'll use a basic singleton class. For a real-world application, you will want to embed this into your MVC architecture and other related classes.
 
Create ApplicationState.as
To get the project started, create a new Flex project and name it "My First Kiosk". Next, within the main package create a class called ApplicationState that extends EventDispatcher. The contents will look like this:
 
package { import flash.events.Event; import flash.events.EventDispatcher; public class ApplicationState extends EventDispatcher { // events in the state public static var EVENT_STATE_CHANGE:String = "stateChange"; // "states" in the application public static var SCREEN_SAVER:Number = 0; public static var WELCOME_SCREEN:Number = 1; public static var USER_INPUT:Number = 2; public static var COUNTDOWN:Number = 99; // stores the current state of the application private var _currentState:Number; public function set currentState(val:Number):void { previousState = _currentState; _currentState = val; dispatchEvent(new Event(EVENT_STATE_CHANGE)); } [Bindable(EVENT_STATE_CHANGE)] public function get currentState():Number { return _currentState; } public var previousState:Number; // singleton work private static var _instance:ApplicationState; public static function getInstance():ApplicationState { if ( !_instance ) { _instance = new ApplicationState() } return _instance; } } }
At the top of the class you will see the three states I have defined, SCREEN_SAVER, WELCOME_SCREEN, and USER_SELECT.I have them defined as Number for right now, but any data type will work.
 
The variable currentState will store the current state of the application; no surprises there. You can get or set this value in any module of your program. Here it is set on a UI element's show event. This class uses a setter method to set the value so it can dispatch an event whenever the user changes the state. I made the event string bindable in case any view is binding to that value and wants to update the UI. You will also notice that I'm storing the previousState at the top of the setter. This is used mostly for the ApplicationTimer class that I'll discuss near the end of this article.
 
Finally you will see the details for the singleton class for the ApplicationState class. There are a few tricks to enforce a singleton class in Flex, but you will most likely be putting this into a manager class somewhere else in your MVC architecture, so I will not go into the details.
 

 
Creating the initial Welcome screen

Now that you have created the class that will keep track of your application's state, you need to start adding views and visual elements to the display.
 
There are a couple different types of screens you can show at the beginning of your kiosk while it loads the modules or other pieces of the application, and it all really depends on what your business needs are. You may want to start the kiosk and let the user have immediate access to it while you download the extra modules they might not need for a few minutes. Or, you might prefer to load all of your modules before allowing the user to get started. In this case, you might also want to default your initial view to a "loading kiosk" state. In this example application, I'm going to allow the user to jump right in and start moving around.
 
Create Welcome.mxml
The first visual item I need to create is the Welcome.mxml (See Figure 1). This view will be the screen displayed while waiting for the user to click a button. I create a new Component, name it "Welcome", select VBox as the Based On component, and set the height and width to 100%.
 
Next, I set the component's horizontalAlign and verticalAlign to center and middle, respectively. I add a label, a button, and some code to dispatch an event whenever the user clicks on the button.
 
<?xml version="1.0" encoding="utf-8"?> <mx:VBox height="100%" width="100%" horizontalAlign="center" verticalAlign="middle" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Metadata> [Event(name="startClicked")] </mx:Metadata> <mx:Script> <![CDATA[ private function onButtonClick():void { dispatchEvent(new Event("startClicked")); } ]]> </mx:Script> <mx:Label text="Welcome to my Kiosk, press the button below to begin!" /> <mx:Button label="Click me to start" height="200" width="250" fontSize="18" click="onButtonClick()" /> </mx:VBox>



Figure 1. The Welcome.mxml.
Figure 1. The Welcome.mxml.
Note: Using Metadata tags in the components allows you to add the events in MXML mode.
 
In the click handler for my button, you can see I created a new method, onButtonClick() that will dispatch a custom event type called startClicked. While this is sufficient to dispatch an event, I added the event to the Metadata tag to ensure the event is visible when adding this component via MXML.
 
Keep track of the application state
Now that the first visual component is done, I need to let the rest of the application know that the currently visible state is now the Welcome screen. To accomplish this, I need to notify the ApplicationState class about what state change just happened. Since I'm not using any MVC for this example, I'm going to plug it into the show property of the component itself. It should be a relatively safe place to put it, since if it's showing, then it's in that state.
 
In the script block, I'll get the instance of the ApplicationState singleton class, and set its currentState equal to WELCOME_SCREEN, the static variable I defined in the ApplicationState class for the Welcome screen, whenever the show event is fired for the component. The updated Welcome.mxml code now looks like this:
 
<?xml version="1.0" encoding="utf-8"?> <mx:VBox height="100%" width="100%" horizontalAlign="center" verticalAlign="middle" show="onShow()" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Metadata> [Event(name="startClicked")] </mx:Metadata> <mx:Script> <![CDATA[ private var _applicationState:ApplicationState = ApplicationState.getInstance(); private function onShow():void { _applicationState.currentState = ApplicationState.WELCOME_SCREEN; } private function onButtonClick():void { dispatchEvent(new Event("startClicked")); } ]]> </mx:Script> <mx:Label text="Welcome to my Kiosk, press the button below to begin!" /> <mx:Button label="Click me to start" height="200" width="250" fontSize="18" click="onButtonClick()" /> </mx:VBox>
Note: The button on this component is quite large; this is doneto account for parallax and user experience considerations.
 

 
Creating the user input screen

I have now created the ApplicationState class to keep track of the application's state and Welcome.mxml to add a visual component to the application. Now I am going to create one more visual component to prompt for a user's selection and then tie those three items together, so a very basic application can appear on the screen and function.
 
The new component basically does everything that Welcome.mxml does, but instead of just one button, it has four. Again, you will want to integrate this into your MVC architecture and the framework you use to manage the content and your UI views.
 
Create UserInputSelection.mxml
Following the same steps for creating the Welcome.mxml file, I add the four buttons (See Figure 2). I also pass the MouseEvent with the click events in order to capture just what was selected and display a simple Alert message with the label's text in it. Since this form has multiple buttons that won't change the state of the application, I'm going to dispatch a new event called extendTimeout in the onButtonClick() method. The main.mxml will listen for this event, which I will explain in the Time for timers section. The UserInputSelection.mxml file now looks like:
 
<?xml version="1.0" encoding="utf-8"?> <mx:VBox height="100%" width="100%" horizontalAlign="center" verticalAlign="middle" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Metadata> [Event(name="itemSelected")] </mx:Metadata> <mx:Script> <![CDATA[ import mx.controls.Alert; private function onButtonClick(e:MouseEvent):void { Alert.show(e.currentTarget.label); dispatchEvent(new Event("itemSelected")); dispatchEvent(new Event("extendTimeout", true)); } ]]> </mx:Script> <mx:Label text="Please select an item below to continue" /> <mx:Button label="Item 1" height="100" width="150" fontSize="18" click="onButtonClick(event)" /> <mx:Button label="Item 2" height="100" width="150" fontSize="18" click="onButtonClick(event)" /> <mx:Button label="Item 3" height="100" width="150" fontSize="18" click="onButtonClick(event)" /> <mx:Button label="Item 4" height="100" width="150" fontSize="18" click="onButtonClick(event)" /> </mx:VBox>


 

Figure 2. Create UserInputSelection.mxml
Figure 2. Create UserInputSelection.mxml
Note: Using Metadata tags in the components allows you to add the events in MXML mode.
 
Just as I did in the Welcome.mxml file, I add click handlers to the button and dispatch a custom event, itemSelected. More than likely, you will want to build these buttons dynamically based off a dataset of some sort.
 
Keep track of application state
Now that the functionality of the UserInputSelection screen is created, I need to tell the ApplicationState class that the state of the application is now in the UserInputSelection screen. I use the same mechanism as in the Welcome.mxml file, except I change WELCOME_SCREEN to USER_INPUT. The UserInputSelection.mxml file now looks like:
 
<?xml version="1.0" encoding="utf-8"?> <mx:VBox height="100%" width="100%" horizontalAlign="center" verticalAlign="middle" show="onShow()" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Metadata> [Event(name="itemSelected")] </mx:Metadata> <mx:Script> <![CDATA[ import mx.controls.Alert; private var _applicationState:ApplicationState = ApplicationState.getInstance(); private function onShow():void { _applicationState.currentState = ApplicationState.USER_INPUT; } private function onButtonClick(e:MouseEvent):void { Alert.show(e.currentTarget.label); dispatchEvent(new Event("itemSelected")); dispatchEvent(new Event("extendTimeout", true)); } ]]> </mx:Script> <mx:Label text="Please select an item below to continue" /> <mx:Button label="Item 1" height="100" width="150" fontSize="18" click="onButtonClick(event)" /> <mx:Button label="Item 2" height="100" width="150" fontSize="18" click="onButtonClick(event)" /> <mx:Button label="Item 3" height="100" width="150" fontSize="18" click="onButtonClick(event)" /> <mx:Button label="Item 4" height="100" width="150" fontSize="18" click="onButtonClick(event)" /> </mx:VBox>

 
Tying the new views into main.mxml

I now have the ApplicationState and created two visual components, I want to tie them all together so I can see what shape my application is in as of right now. I need to integrate those components into the main.mxml application file, and a viewstack provides the easiest way to integrate the new components into this view.
 
Modify main.mxml
Open up main.mxml, create the new ViewStack and add the children. Set the height and width of the ViewStack to 100% for now. I like to be able to reference the children by id, so I add an id to each new child. The main.mxml code now looks like this:
 
<?xml version="1.0" encoding="utf-8"?> <mx:Application layout="absolute" xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*"> <mx:ViewStack id="myViewStack" height="100%" width="100%"> <local:Welcome id="welcomeScreen" /> <local:UserInput id="userInputScreen" /> </mx:ViewStack> </mx:Application>
Running the application in its current form will only display the Welcome.mxml component. I still need to manage what gets displayed and when.
 
Changing what is visible in the ViewStack
When I first created the Welcome.mxml and UserInputSelection.mxml screens, I added event dispatching into the mix. That is now going to help me control the application as the user moves around in it. I just need to add in the listeners and code to handle that change. The component declarations will now look like:
 
<local:Welcome id="welcomeScreen" startClicked="onStartClicked()" /> <local:UserInput id="userInputScreen" itemSelected="onItemSelected()" />
And the script looks like:
 
private function onStartClicked():void { myViewStack.selectedChild = userInputScreen; } private function onItemSelected():void { // do nothing yet }
The entire main.mxml file should now look like:
 
<?xml version="1.0" encoding="utf-8"?> <mx:Application layout="absolute" xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*"> <mx:Script> <![CDATA[ private function onStartClicked():void { myViewStack.selectedChild = userInputScreen; } private function onItemSelected():void { // do nothing yet } ]]> </mx:Script> <mx:ViewStack id="myViewStack" height="100%" width="100%"> <local:Welcome id="welcomeScreen" startClicked="onStartClicked()" /> <local:UserInput id="userInputScreen" itemSelected="onItemSelected()" /> </mx:ViewStack> </mx:Application>
By adding the events in the Metadata tags in the custom component, I now get my custom events to appear in the mxml tags of my components, startClicked and itemSelected; these new events now invoke the onStartClicked() and onItemSelected() event handlers. When startClicked is fired from the component, onStartClicked() will run and will update the ViewStack's selected child to display the UserInput component. The onItemSelected() method does nothing for now.
 
Running this code should now give you the Welcome screen, and clicking the button labeled "Click me to start" will now change the ViewStack to the UserInput component and display the four item buttons. There is no way to move to the previous screen in the application. When you build a kiosk for production, you will most likely want to include this capability and others, such as pop-up help. I will leave that to you to add at your discretion.
 

 
Making the screen saver

I have now created the framework for a kiosk application. However, I don't want my users to get stuck at the user input screen. Moreover, if they walk away, I don't want the next user to start on that screen. So the next piece to work on is the ScreenSaver component, which will help reset application state and allow for application clean-up.
 
While I call this next component a screen saver, it is really a multipurpose component that you can display at the end of the entire user entry process, when a user times out on a state, or if the user walks away, to give your application time to reset itself. You can display an advertisement, logo, or graphic. It provides time for the application to reset, letting animations complete, clearing data, maybe sending a few calls to the server to pass data about user interactions, or anything that's part of the clean-up or resetting phase in your application.
 
Create ScreenSaver.mxml
I'm going to make a very simple screen saver, with some minor effects to achieve the traditional screen saver effect (See Figure 3). In order to manipulate text with effects (grow, fade, and so on) I need to embed the font. I have included and embedded a royalty-free font in the downloadable sample code.
 
To get started, I create a new component called ScreenSaver and base the component on an HBox object, settting both the height and width properties to 100%. Next, I modify the component's horizontalAlign and verticalAlign to center and middle, respectively. I add a label and a few effects to run on Show. The resulting code looks like:
 
<?xml version="1.0" encoding="utf-8"?> <mx:HBox showEffect="{ fadeIn }" show="onShow()" hideEffect="{ fadeOut }" backgroundColor="0xFFFFFF" horizontalAlign="center" verticalAlign="middle" width="100%" height="100%" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ private var _applicationState:ApplicationState = ApplicationState.getInstance(); private function onShow():void { _applicationState.currentState = ApplicationState.SCREEN_SAVER; } ]]> </mx:Script> <mx:Sequence id="fadeIn"> <mx:SetPropertyAction target="{ screenText }" name="alpha" value="0" /> <mx:SetPropertyAction target="{ screenText }" name="visible" value="true" /> <mx:Fade target="{ this }" alphaFrom="0" alphaTo="1" duration="600" /> <mx:Parallel target="{ screenText }"> <mx:Rotate angleTo="360" /> <mx:Fade alphaFrom="0" alphaTo="1" duration="800" /> </mx:Parallel> </mx:Sequence> <mx:Sequence id="fadeOut"> <mx:Parallel target="{ screenText }" duration="800"> <mx:Rotate angleTo="-360" /> <mx:Fade alphaFrom="1" alphaTo="0" duration="800" /> </mx:Parallel> <mx:SetPropertyAction target="{ screenText }" name="visible" value="false" /> <mx:Fade target="{ this }" alphaFrom="1" alphaTo="0" duration="600" /> </mx:Sequence> <mx:Label id="screenText" text="Screen Saver" fontSize="30" visible="false" /> </mx:HBox>
Figure 3. A very simple screen saver
Figure 3. A very simple screen saver
I added an event handler for the HBox show event, which calls onShow() to set the current application state. I am also binding the showEffect and hideEffect to the fadeIn and fadeOut sequence effects. In order for the effects to look right, I'm setting the text to animate its alpha to zero, then its visible property to true. Since this is in a sequence effect, it will ensure the alpha is set to 0 before showing the label, so that it doesn't flash on the screen before fading in. The rest of the code is straightforward.
 
Modify Main.mxml to include ScreenSaver.mxml
Now that I've created the screen saver, I'm going to add it at the very bottom of main.mxml. I'm doing this because Flex lays out UI components from the top down. By adding this new component to the bottom of the main.mxml file and setting its visible property to true, I make it appear on top of everything else, allowing me to move UI components around and reset the application's UI under the cover of the ScreenSaver component.
 
So, under the ViewStack, I'm going to add:
 
<local:ScreenSaver id="screenSaver" visible="false" />
When you run the application now, it should appear the same as before. I'll integrate the screen saver in the next section.
 

 
The countdown

With the visual components mostly complete, the last visual component I need is a countdown that will appear on the user input screen when the user waits too long to make a selection.
 
The countdown screen will be shown whenever a user input screen is displayed and a user doesn't select an item within a specified timeframe (see Figure 4). You will be able to configure the times in the Time for timers section.
 
Create Countdown.mxml
Just like the screen saver, I am going to make a very basic countdown screen. The countdown numbers are going to come from the ApplicationTimer class, which I'll be creating in the next section. For this visual component, I want the user to click on the screen (touch the screen) in order to return to the input screen.
 
To get started, I create a new component called Countdown and base it on a VBox object. I set both the height and width properties to 100%. Next, I modify the component's horizontalAlign and verticalAlign to center and middle, respectively. I add two Label tags and a custom event. The resulting code looks like:
 
<?xml version="1.0" encoding="utf-8"?> <mx:VBox backgroundAlpha=".7" backgroundColor="0xFFFFFF" horizontalAlign="center" verticalAlign="middle" click="formClick()" show="onShow()" width="100%" height="100%" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Metadata> [Event(name="userClickedScreen")] [Event(name="extendTimeout")] </mx:Metadata> <mx:Script> <![CDATA[ [Bindable] public var countdownValue:Number; private var _applicationState:ApplicationState = ApplicationState.getInstance(); private function formClick():void { dispatchEvent(new Event("userClickedScreen")); // let the listener know user touched/clicked screen dispatchEvent(new Event("extendTimeout", true)); // notify timers to extend timeout _applicationState.currentState = _applicationState.previousState; } private function onShow():void { _applicationState.currentState = ApplicationState.COUNTDOWN; } ]]> </mx:Script> <mx:Label text="Touch the screen to continue" fontSize="18" /> <mx:Label text="{countdownValue}" fontSize="22" /> </mx:VBox>


 

Figure 4. The countdown screen
Figure 4. The countdown screen
On this screen I detect a click event on the VBox and that will fire the formClick() method. This method dispatches two events, userClickedScreen and extendTimeout. The userClickedScreen event notifies any listeners that the user is trying to keep their session alive. The extendTimeout event is a piece of the final component of this example and I will go into that in more detail in the next section. The last task that this method performs is to set the current application state to the previous application state. Since the countdown is simply an overlay for the UI and I did not want to try to integrate all the pieces together, I decided to allow this component to know the previous application state and handle what it is already familiar with.
 
Modify Main.mxml to include Countdown.mxml
Now that Countdown.mxml is finished, I need to integrate it into main.mxml. I do this in the same way I added ScreenSaver.mxml, by adding it near the bottom. I'm going to place it under the ViewStack, but above the ScreenSaver component. This will allow the Countdown to appear on top of the ViewStack, while still allowing the ScreenSaver to appear on top of everything. The code to add the Countdown component looks like:
 
<local:Countdown id="countdown" userClickedScreen="countdown.visible = false" visible="false" />
I don't have a value for the countdownValue property yet, but I will get that once I create the ApplicationTimer class.
 

 
Time for timers

With all the UI components mostly complete, I want to control how long each user should be allowed to view a screen before making a selection and when to display the screen saver. I'll need to integrate a new ApplicationTimer class to tell the application when to show the countdown and screen saver, and when to reset the views and "session" data.
 
In order to manage that, I'm going to create a new class, but base it off a UIComponent so I can plug it onto my main.mxml as a component. You don't have to use a UIComponent for this, but I like to so I can look just at the MXML and get a good idea of what it is doing without having to scroll up to read the <script> tags and find the addListener methods. Both are acceptable practices.
 
Create ApplicationTimer.as
My new class is going to be listening to the ApplicationState singleton for the state change event EVENT_STATE_CHANGE. It will then decide what screen is showing and how long it should be visible before throwing an event to show/hide the screen saver or show/hide
 
the countdown display. In this example, I have hardcoded the values, but these values will most likely be dynamic depending on your application and MVC design.
 
The ApplicationTimer.as class looks like:
 
package { import flash.events.Event; import flash.events.TimerEvent; import flash.utils.Timer; import mx.core.UIComponent; [Event(name="showScreenSaver")] [Event(name="hideScreenSaver")] [Event(name="showCountdown")] [Event(name="hideCountdown")] public class ApplicationTimer extends UIComponent { // events this class dispatches public static var EVENT_SHOW_SCREEN_SAVER:String = "showScreenSaver"; public static var EVENT_HIDE_SCREEN_SAVER:String = "hideScreenSaver"; public static var EVENT_SHOW_COUNTDOWN:String = "showCountdown"; public static var EVENT_HIDE_COUNTDOWN:String = "hideCountdown"; [Bindable("currentTimerCount")] public function get countdownNumber():Number { return _countdownNumber } public function set countdownNumber(val:Number):void { _countdownNumber = val; dispatchEvent(new Event("currentTimerCount")); } // internal values private var _applicationState:ApplicationState = ApplicationState.getInstance(); private var _stateTimer:Timer = new Timer(5000); // default the timer to 5 seconds private var _countdownTimer:Timer = new Timer(1000); // set to 1sec to update UI private var _countdownNumber:Number; public function userInteractionReceived():void { if ( _stateTimer ) { _stateTimer.stop(); _stateTimer.reset(); _stateTimer.start(); } } public function onStateChange(e:Event):void { _stateTimer.stop(); _stateTimer.reset(); switch ( _applicationState.currentState ){ case ApplicationState.WELCOME_SCREEN: _stateTimer.delay = 5000; // 5 second delay break; case ApplicationState.USER_INPUT: _stateTimer.delay = 5000; // 5 second delay break; case ApplicationState.SCREEN_SAVER: _stateTimer.delay = 2500; // 2.5 second delay break; } // don't restart the state timer if the current state is the countdown if ( _applicationState.currentState != ApplicationState.COUNTDOWN ){ _countdownTimer.stop(); _stateTimer.start(); } } private function onTimer(e:TimerEvent):void { _stateTimer.stop(); switch ( _applicationState.currentState ){ case ApplicationState.WELCOME_SCREEN: dispatchEvent(new Event(EVENT_SHOW_SCREEN_SAVER)); break; case ApplicationState.USER_INPUT: startCountdown(6); break; case ApplicationState.SCREEN_SAVER: dispatchEvent(new Event(EVENT_HIDE_SCREEN_SAVER)); break; } } private function startCountdown(val:Number):void { countdownNumber = val; dispatchEvent(new Event(EVENT_SHOW_COUNTDOWN)); _countdownTimer.reset(); _countdownTimer.repeatCount = countdownNumber; _countdownTimer.start(); } private function onCountdownTimer(e:TimerEvent):void { // if user "times out", hide the countdown, show screen saver if ( _countdownTimer.currentCount == _countdownTimer.repeatCount ){ _countdownTimer.stop(); dispatchEvent(new Event(EVENT_HIDE_COUNTDOWN)); dispatchEvent(new Event(EVENT_SHOW_SCREEN_SAVER)); } else // otherwise update the countdownNumber { countdownNumber = _countdownTimer.repeatCount - _countdownTimer.currentCount; } } // constructor public function ApplicationTimer(){ super(); _applicationState.addEventListener(ApplicationState.EVENT_STATE_CHANGE, onStateChange); _stateTimer.addEventListener(TimerEvent.TIMER, onTimer); _countdownTimer.addEventListener(TimerEvent.TIMER, onCountdownTimer); } } }
Near the top of the class you will see four events: showScreenSaver, hideScreenSaver, showCountdown, hideCountdown. The various timer return calls dispatch these events, which can then control the UI. Static variables are used for the event names to make referencing them easier throughout the class.
 
The variable countdownNumber has a public getter and setter and dispatches a currentTimerCount event whenever that value changes. The Countdown.mxml component uses this value to display how many seconds the user has left to touch the screen.
 
Moving down a few lines, you will see I declare two timers, _stateTimer and _countdownTimer. The _stateTimer value defaults to 5 seconds, but could default to any number. The _countdownTimer value needs to stay at 1 second, since it will need to update the countdownNumber every second for display purposes. Under the timer declarations, notice the public function userInteractionReceived(). I‘ll cover this in the Detecting user presence section.
 
When the ApplicationTimer class initializes, it sets an event listener for the stateChange event from ApplicationState class and calls onStateChange(). Once the method fires, it stops the _stateTimer, resets it and determines what the new currentState is. Depending on the new state, it will update the _stateTimer.delay to how long that screen should be visible, and then if the currentState is not the COUNTDOWN state, it will ensure the _countdownTimer is stopped and then restart the _stateTimer.
 
Once _stateTimer hits the limit, onTimer() is called and it determines what the current state is and if it should show the screen saver, hide the screen saver, or start a countdown process of some sort. In this example, if the currentState is WELCOME_SCREEN, show the screen saver. If the currentState is USER_INPUT, display a 6-second countdown, and if the currenState is SCREEN_SAVER, hide the screen saver.
 
Whenever the _countdownTimer throws a TimerEvent, onCountdownTimer() is called. This method will compare what the currentCount of the timers is and see if it is equal to the repeatCount property. If they are equal, then it has reached the end of the countdown period; it stops the timer, dispatches an event to hide the countdown, and dispatches another event to show the screen saver, which will in turn, fire a stateChange event and start the timer for how long the screen saver should be displayed. If, however, the currentCount is less than the repeatCount, then it takes the difference in the values and updates the countdownNumber variable, which, in turn, updates the displayed value on the countdown screen.
 
The constructor for the class is at the very bottom of the class; this is where I add event listeners to the ApplicationState singleton and both the _stateTimer and _countdownTimer objects.
 
Integrate ApplicationTimer into Main.mxml
It doesn't matter where you add ApplicationTimer in main.mxml, but I usually put it at the very bottom or very top. In this example, I'm going to place it all the way at the bottom. The events that ApplicationTimer creates to control the UI can get quite complicated, so I'm going to add two new methods to main.mxml to accommodate the new events, handleScreenSaver and handleCountdown. Both methods will handle both the show/hide events being thrown. The added component code will look like:
 
<local:ApplicationTimer id="applicationTimer" showScreenSaver="handleScreenSaver(event)" hideScreenSaver="handleScreenSaver(event)" showCountdown="handleCountdown(event)" hideCountdown="handleCountdown(event)" />
And the resulting methods will be:
 
private function handleScreenSaver(e:Event):void { // if the timer says to hide the screen saver, always // show the welcomeScreen child first if ( e.type == ApplicationTimer.EVENT_HIDE_SCREEN_SAVER ){ screenSaver.visible = false; myViewStack.selectedChild = welcomeScreen; } elseif ( e.type == ApplicationTimer.EVENT_SHOW_SCREEN_SAVER ){ screenSaver.visible = true; myViewStack.selectedChild = emptyCanvas; } } private function handleCountdown(e:Event):void { if ( e.type == ApplicationTimer.EVENT_SHOW_COUNTDOWN ){ countdown.visible = true; } elseif ( e.type == ApplicationTimer.EVENT_HIDE_COUNTDOWN ) { countdown.visible = false; } }
The handleScreenSaver() method takes a generic Flex Event and if the type is EVENT_HIDE_SCREEN_SAVER, the method will set the visible property of the screen saver to false, and set selectedChild of the ViewStack to welcomeScreen (the default screen). If the Flex Event type is EVENT_SHOW_SCREEN_SAVER, the method will set the visible property of the screen saver to false and hide all the items in the ViewStack by switching to an empty canvas. This is the best place to fire off any changes or resets to the application that you need to do, including moving assets on the stage, clearing session variables, and so on. You can also set a listener for the SCREEN_SAVER application state in your various classes to achieve the same effect.
 
The other method I add is handleCountdown(), which also takes a generic Flex Event. If the event type is EVENT_SHOW_COUNTDOWN, it will set the visible property of the Countdown.mxml component to true. If the event type is EVENT_HIDE_COUNTDOWN, it will set the visible property of the Countdown.mxml component to false.
 
Now that I added the ApplicationTimer class, I can update my Countdown component to get the value for countdownValue from it. By binding applicationTimer.countdownNumber to the countdownValue property on the component, then the countdown component will know whenever the timer updates its countdownNumber value.
 

 
Detecting User Presence

The final piece to the application is adding functionality to determine when a user clicks a UI component or is active on a screen. In the ApplicationTimer class, I added a public method called userInteractionReceived() which will reset the state timer for the currentState. Whenever the application determines that a user is active on a screen, it will dispatch an event called extendTimeout with its bubble property set to true.
 
Here, I manually dispatch the extendTimeout event from the UserInputSelection.mxml component. For larger applications, I would recommend extending the Button class (or any other UIComponent), and have all your applications' interaction objects extend that new object. Then you can dispatch the event in your new class; just make sure the event's bubbles property is set to true so the main.mxml application can handle the event accordingly.
 
To setup this functionality correctly, I need to edit main.mxml to add this change. In main.mxml I have already created a creationComplete event handler, so I'm going to add a new listener to handle the extendTimeout event:
 
this.addEventListener("extendTimeout", userInteractionReceived);
The new method, userInteractionReceived(), is relatively simple:
 
private function userInteractionReceived(e:Event):void { applicationTimer.userInteractionReceived(); }
My final main.mxml now looks like:
 
<?xml version="1.0" encoding="utf-8"?> <mx:Application layout="absolute" creationComplete="onCreationComplete()" xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*"> <mx:Style source="assets/main.css" /> <mx:Script> <![CDATA[ [Bindable] private var _applicationState:ApplicationState = ApplicationState.getInstance(); private function onStartClicked():void { myViewStack.selectedChild = userInputScreen; } private function onItemSelected():void { // do nothing yet } private function onCreationComplete():void { // start off with welcome screen first myViewStack.selectedChild = welcomeScreen; this.addEventListener("extendTimeout", userInteractionReceived); } private function userInteractionReceived(e:Event):void { applicationTimer.userInteractionReceived(); } private function handleScreenSaver(e:Event):void { // if the timer says to hide the screen saver, always // show the welcomeScreen child first if ( e.type == ApplicationTimer.EVENT_HIDE_SCREEN_SAVER ){ screenSaver.visible = false; myViewStack.selectedChild = welcomeScreen; } elseif ( e.type == ApplicationTimer.EVENT_SHOW_SCREEN_SAVER ){ screenSaver.visible = true; myViewStack.selectedChild = emptyCanvas; } } private function handleCountdown(e:Event):void { if ( e.type == ApplicationTimer.EVENT_SHOW_COUNTDOWN ){ countdown.visible = true; } elseif ( e.type == ApplicationTimer.EVENT_HIDE_COUNTDOWN ){ countdown.visible = false; } } ]]> </mx:Script> <!-- adding an empty canvas allows the 'show' property to fire on the children --> <mx:ViewStack id="myViewStack" height="100%" width="100%"> <mx:Canvas id="emptyCanvas" /> <local:Welcome id="welcomeScreen" startClicked="onStartClicked()" /> <local:UserInput id="userInputScreen" itemSelected="onItemSelected()" /> </mx:ViewStack> <local:Countdown id="countdown" countdownValue="{ applicationTimer.countdownNumber }" userClickedScreen="countdown.visible = false" visible="false" /> <local:ScreenSaver id="screenSaver" visible="false" /> <local:ApplicationTimer id="applicationTimer" showScreenSaver="handleScreenSaver(event)" hideScreenSaver="handleScreenSaver(event)" showCountdown="handleCountdown(event)" hideCountdown="handleCountdown(event)" /> </mx:Application>

 
Final pieces of the puzzle

There are only a couple steps remaining to complete.
 
Hide the mouse cursor and disable right-click
At this point, most of the UI work is done for this application. To give that final touch of a kiosk, you need to hide the mouse cursor. Just call Mouse.hide() and there goes your mouse cursor. The application is ready for a touchscreen.
 
I would recommend either waiting until the very end of testing to do this, or create a custom right-click menu to hide/show cursor. Hopefully you will have disabled the right-click in the drivers for the touchscreen; this allows you to hide a lot of functionality into your right-click menu, including logs, cursor control options, and other maintenance functions.
 
Run the application
You can run the application in AIR or even in a browser, such as Internet Explorer or Firefox. Both browsers offer a kiosk mode of some sort. The one in Internet Explorer is better documented. Remember, it's your hardware and software, so you get to decide how it should run.
 

Where to go from here

The sample application in this article should help you understand what creating a kiosk application entails. It's not that different from a regular application. The tricky parts are the timing of screens and effects, but with a good architecture, that shouldn't be a problem.
 
I recommend using this article only as a general guide of creating a kiosk application. For a full-fledged kiosk, it doesn't make sense to jam everything together as in this example application. It will get very messy very quickly. You should find a good MVC architecture to use, such as Cairngorm, PureMVC, or Mate. You can then move the timer and state management functionality into the appropriate classes. I would also recommend the use of custom events, because you can pass more data via custom events and avoid having to use such strong references to other components.
 
The final thing to think about is hardware integration, which I have not covered in this article. There are some creative ways of using hardware with a Flex application that I touched upon at the beginning of this article, such as using ExternalInterface, and there is forward progress in third-party support, such as Merapi (formerly Artemis). At this time, however, there is no official Flex/Flash support for hardware (aside from graphics hardware acceleration). Sometimes, building a kiosk using Flex requires thinking outside of the box. It's definitely possible and not as hard as you might think.