EladElrom.com

Deep Dive Into Technology

Tips – Debugging Flex applications with flashlog.txt on a Mac

Debugging Flex applications with mm.cfg and flashlog.txt let us track trace statements and error messages for projects running in the browser using the debug mode. Since I haven’t done it since 2001 I completely forget what to do and came across couple of issues, so I figured I’ll throw a post for other people that may have the same issue.

You first need to create the txt file and directories here:
/Users/YourUserName/Library/Preferences/Macromedia/Flash Player/Logs/flashlog.txt

Then you need to create the mm.cfg file in both location:
/Library/Application Support/Macromedia/mm.cfg
/Users/YourUserName/mm.cfg

Then paste the follwing code in the mm.cfg:
ErrorReportingEnable=1
TraceOutputFileEnable=1
MaxWarnings=0

Initially, I pasted the code only in the first directory and couldn’t get it to work, not sure why, so place the file in both directories to play it safe.
I am running the Flash Debug Player 10, on Mac OS X.

To see the messages as they come on Terminal, navigate to the flashlog directory and type in:
tail -f flashlog.txt

I Hope you find this tip handy.

Flex Camp Chicago 2009 Adobe AIR presentation uploaded

Just uploaded the Flex Camp Chicago 2009 presentation. The topic covered getting started with Adobe AIR as well as AIR 1.5 / Flash 10 capabilities. It was a great event and I enjoyed presenting as well as listening to other speakers.
http://www.slideshare.net/eladnyc/getting-started-with-adobe-air-15-presentation

Looking for a strong AS3/Flex Engineer to work for a high profile entertainment company in LA

We are looking for a strong AS3/Flex Engineer to work as a contractor for a high profile entertainment company in LA. The job description is listed below:

* General qualities
o Good communication skill
o Reliable
o Must be passionate about programming
o Must be able to work under directions and independently
* Programming languages
o Actionscript 3.0
o Flex 3.0 MXML
o Javascript
+ AJAX (DOM manipulation, XMLHTTPRequest, CSS, DHTML)
o Jquery javascript framework
o Good understanding of XML, XSL, XUL, Xpath, etc.
* Software
o Eclipse
o Flex Builder
o Flash CS3
o ANT
o Subversion

Drop me an email if you are interested.

Adobe AIR Mobile Touch and Multi-Touch screen applications

Today, there are already few systems that are using Adobe AIR to build a multi-touch GUI. There is an open project called Touchlib that allows listening to user gestures and building your own touch-screen. Intuilab has presented in Adobe MAX in Milan an application that takes full advantage of surface computers. The system integrates many contents such as: image, video, web content, Adobe pdf, Flash, Illustrator, Photoshop, Microsoft Office etc.

The idea behind Touch and Multi-touch computer screens is to follow the instructions of finger/s or hand/s and be able to track gestures. The idea is simple:

1. Register to receive gestures events.
2. Handle gesture events.
3. Interpret the gesture events.

Creating a touch screen application on UMPC can be done today, and we can rely on the UMPC device to handle the touch screen; however you may find that relying entirely on the device may be buggy.

For instance, the device may register a user gesture when your fingertip is on the device and didn’t even move your finger since a slight move also get registered, however you may not need your application to be that sensitive.

I recommend implementing your own instructions to register user gestures. Microsoft released the beta version of Windows 7 which track user gestures via WM_GESTURECOMMAND, WM_GESTURE and WM_TOUCH so I think it’s possible to communicate with these method using a proxy such as Merapi or you can create your Java proxy.

I have created a simple POC that I havn’t test much but it gives you an idea. The API is for touch screen application for mobile devices such as UMPC that register user gestures and can be expended to register multi-touch screen user gestures. The way it works is that the API translate the mouse events into user gestures based on time and movement.

Here’s a screen shot of the application on a UMPC:
AIR Touch Screen

And you can download it from here:
http://eladelrom.com/blog/Flex/TouchScreen/TouchScreen/Touchscreen.zip

Take a look at the class below that gives its own instructions on when to register user gestures.

package com.elad.framework.touchscreen
{
	import com.elad.framework.touchscreen.events.TouchEvent;
	import com.elad.framework.touchscreen.vo.TouchVO;

	import flash.events.EventDispatcher;
	import flash.events.MouseEvent;
	import flash.utils.Timer;

	import mx.core.UIComponent;

	public class TouchManager extends EventDispatcher
	{
		private var moveTimer:Timer;
		private var previousX:int = 0;
		private var previousY:int = 0;
		private var component:UIComponent;

		public function TouchManager(component:UIComponent)
		{
			this.component = component;
		}

		public function start():void
		{
			initialize();
			this
		}

		public function stop():void
		{
			component.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
			component.removeEventListener(MouseEvent.MOUSE_UP, onMouseUpHandler);

			moveTimer.stop();
			moveTimer = null;
		}

		protected function initialize():void
		{
			component.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
			component.addEventListener(MouseEvent.MOUSE_UP, onMouseUpHandler);
		}

		private function startTimer():void
		{
			moveTimer = new Timer(100,1000);
			moveTimer.start();
		}

		private function onMouseDownHandler(event:MouseEvent):void
		{
			startTimer();
			component.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveHandler);

			var touch:TouchVO = new TouchVO(this.previousX, this.previousY, event.localX, event.localY, this.moveTimer.currentCount);
			this.dispatchEvent( new TouchEvent(	TouchEvent.TOUCH_DOWN, touch ) );
		}

		private function onMouseUpHandler(event:MouseEvent):void
		{
			moveTimer.stop();
			component.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveHandler);

			var touch:TouchVO = new TouchVO(this.previousX, this.previousY, event.localX, event.localY, this.moveTimer.currentCount);
			this.dispatchEvent( new TouchEvent(	TouchEvent.TOUCH_UP, touch ) );
		}

		private function onMouseMoveHandler(event:MouseEvent):void
		{
			var isMove:Boolean = isTouchMove(event.localX, event.localY);
			var touch:TouchVO = new TouchVO(this.previousX, this.previousY, event.localX, event.localY, this.moveTimer.currentCount);

			if (isMove)
			{
				this.dispatchEvent( new TouchEvent(TouchEvent.TOUCH_DRAG, touch) );
			}
		}

		private function isTouchMove(x:int, y:int):Boolean
		{
			var retVal:Boolean = false;
			var ignore:int = 3;
			var isXmoved:Boolean;
			var isYmoved:Boolean;

			if (previousX != 0 && previousY != 0)
			{
				isXmoved = (x > previousX+ignore || x < previousX-ignore) ? true : false;
				isYmoved = (y > previousY+ignore || y < previousY-ignore) ? true : false;

				if ( isXmoved || isYmoved )
				{
					retVal=true;
				}
			}

			previousX = x;
			previousY = y;

			return retVal;
		}

	}
}

And than we can implement the class with a simple interface:

<windowedApplication xmlns="http://ns.adobe.com/mxml/2009" layout="absolute"
	width="800" height="600"
	initialize="initializeHandler()">

	<script>
		<![CDATA[
			import com.elad.framework.touchscreen.vo.TouchVO;
			import com.elad.framework.touchscreen.events.TouchEvent;
			import com.elad.framework.touchscreen.TouchManager;
			import mx.collections.ArrayCollection;

			[Bindable]
			private var arrayCollection:ArrayCollection = new ArrayCollection;

			private var touch:TouchManager;

			protected function initializeHandler():void
			{
				touch = new TouchManager(this);
				touch.addEventListener(TouchEvent.TOUCH_DOWN, onMouseDownHandler);
				touch.addEventListener(TouchEvent.TOUCH_UP, onMouseUpHandler);
				touch.addEventListener(TouchEvent.TOUCH_DRAG, onMouseDragHandler);

				touch.start();
			}

			private function onMouseDownHandler(event:TouchEvent):void
			{
				ellipse.visible = true;
				moveEllipse(event.touchVO.currentX, event.touchVO.currentY);
				registerLocation(ellipse.x, ellipse.y, "MouseDown", 0);
			}

			private function onMouseUpHandler(event:TouchEvent):void
			{
				ellipse.visible = false;
			}

			private function onMouseDragHandler(event:TouchEvent):void
			{
					moveEllipse(event.touchVO.currentX, event.touchVO.currentY);
					registerLocation(ellipse.x, ellipse.y, "MouseMove", event.touchVO.moveTimer);
			}

			private function registerLocation(x:int, y:int, type:String, time:int):void
			{
				arrayCollection.addItem({locationX: ellipse.x, locationY: ellipse.y, type: type, time: time});
				dg.dataProvider = arrayCollection;
			}

			private function moveEllipse(x:int, y:int):void
			{
				ellipse.x = x - ellipse.width/2;
				ellipse.y = y - ellipse.height/2;
			}

		]]>
	</script>

	<group>
		<ellipse height="80" width="80" id="ellipse" visible="false">
			<stroke>
				<solidColorStroke color="0x000000" weight="2"/>
			</stroke>
		</ellipse>
	</group>

	<dataGrid x="341" y="4" id="dg" dataProvider="{arrayCollection}" height="410">
		<columns>
			<dataGridColumn headerText="X" dataField="locationX"/>
			<dataGridColumn headerText="Y" dataField="locationY"/>
			<dataGridColumn headerText="Type" dataField="type"/>
			<dataGridColumn headerText="Time" dataField="time"/>
		</columns>
	</dataGrid>

I am looking for a .NET developer to spare time to create a simple class that gets Windows 7 events and compile them into a DLL, so we can pick them up in a Java proxy, which will make a request every few millisecond and look for user gesture changes, so if you are interested, please reply in this blog post.

Adobe AIR SQLite Manager help you handle your database easily.

Working with Adobe AIR you often find yourself writing the same code over and over again to do common tasks such as:

1. Connecting to a database.
2. Execute common SQL commands such as “SELECT * FROM table”.
3. Execute custom SQL commands on tables.
4. Closing connection.

Additionally, user may erase the database or the database isn’t created yet, which requires you to check that the database exists and the table exists and if not create the table.

AIR APIs create the sql database automatically, if it doesn’t exists, but you need to handle the create table SQL command and execute it or copy a sql dabase with the table.

SQLite Manager easily manage these tasks automatically. Let’s take a look;

Our contract is pretty straight forward, we just need to ensure the connection starts and ends:

/*

     Copyright (c) 2009 Elad Elrom.  Elrom LLC. All rights reserved.

    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:

    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

     @author  Elad Elrom
     @contact elad.ny at gmail.com

 */
package com.elad.framework.sqlite
{
	/**
	 * Describes the contract for Objects that serve as a central point to access SQLite database.
	 *
	 * @author Elad Elrom
	 *
	 */
	public interface ISQLiteManager
	{
		function start(dbFullFileName:String, tableName:String, createTableStatement:String):void
		function close():void
	}
}

The SQLite manager handles all these common tasks. It’s a singleton to ensure we don’t open more than one connection at a time as well as avoiding placing the setting information over and over again. In case the database doesn’t exists it will be created automatically and in case the table doesn’t exists it will be generated by a SQL command.

Take a look at the SQLite manager:

/*

     Copyright (c) 2009 Elad Elrom.  Elrom LLC. All rights reserved.

    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:

    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

     @author  Elad Elrom
     @contact elad.ny at gmail.com

 */
package com.elad.framework.sqlite
{
	import com.elad.framework.sqlite.events.StatementSuccessEvent;

	import flash.data.SQLConnection;
	import flash.data.SQLStatement;
	import flash.errors.SQLError;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.SQLErrorEvent;
	import flash.events.SQLEvent;
	import flash.filesystem.File;

	/**
	 * <code>SQLiteManager</code> help handling the database connections and calls. You pass a start properties to the singleton and you
	 * can execute common commands or custom commands.
	 *
	 * @author elad
	 *
	 * @example The following code sets the volume level for your sound:
	 * <listing version="3.0">
	 * 		var database:SQLiteManager = SQLiteManager.getInstance();
	 * 		database.start("Users.sql3", "Users", "CREATE TABLE Users(userId VARCHAR(150) PRIMARY KEY, UserName VARCHAR(150))");
	 *
	 * 		database.addEventListener(SQLiteManager.COMMAND_EXEC_SUCCESSFULLY, onSelectResult);
	 * 		database.addEventListener(SQLiteManager.COMMAND_EXEC_FAILED, onFail);
	 *
	 * 		database.executeSelectAllCommand();
	 *
	 *		private function onSelectResult(event:StatementSuccessEvent):void
	 *		{
	 *		     var result:Array = event.results.data;
	 *		}
	 *
	 *		private function onFail(event:Event):void
	 *		{
	 *		     // handle fail
	 *		}
	 * </listing>
	 *
	 */
	public class SQLiteManager extends EventDispatcher implements ISQLiteManager
	{
		/**
		 * Database file name and extension
		 */
		public var dbFullFileName:String;

		/**
		 * Database Name
		 */
		public var tableName:String;

		/**
		 * SQL command to create the database
		 */
		public var createDbStatement:String;

		// datsbase apis instances
		protected var connection:SQLConnection;
		protected var statement:SQLStatement;
		protected var sqlFile:File;

		// repeated sql command
		protected var repeateFailCallBack:Function;
		protected var repeateCallBack:Function;
		protected var repeateSqlCommand:String = "";

		// events strings
		public static var COMMAND_EXEC_SUCCESSFULLY:String = "commandExecSuccesfully";
		public static var DATABASE_CONNECTED_SUCCESSFULLY:String = "databaseConnectedSuccessfully";
		public static var COMMAND_EXEC_FAILED:String = "commandExecFailed";
		public static var DATABASE_READY:String = "databaseReady";

		// Singleton instance.
		protected static var instance:SQLiteManager;

		/**
		 * Enforce singleton design pattern.
		 *
		 * @param enforcer
		 *
		 */
		public function SQLiteManager(enforcer:AccessRestriction)
		{
			if (enforcer == null)
				throw new Error("Error enforcer input param is undefined" );
		}

		/**
		 * Opens a database connection.
		 *
		 * @param dbFullFileName the database file name for instance: Users.sql
		 * @param tableName holds the database name, for instance: Users
		 * @param createTableStatement holds the create database statment for instance: CREATE TABLE Users(userId VARCHAR(150) PRIMARY KEY, UserName VARCHAR(150))
		 *
		 */
		public function start(dbFullFileName:String, tableName:String, createTableStatement:String):void
		{
			this.dbFullFileName = dbFullFileName;
			this.tableName = tableName;
			this.createDbStatement = createTableStatement;

			connection = new SQLConnection();
			sqlFile = File.applicationStorageDirectory.resolvePath(dbFullFileName);

			try
			{
			    connection.open(sqlFile);
			    this.dispatchEvent(new Event(DATABASE_CONNECTED_SUCCESSFULLY));
			}
			catch (error:SQLError)
			{
			    trace("Error message:", error.message);
			    trace("Details:", error.details);
			    fail();
			}
		}


		/**
		 * Close connection
		 *
		 */
		public function close():void
		{
			connection.close();
		}

		/**
		 * Test the table to ensure it exists. Sends a fail call back function to create the table if
		 * it doesn't exists.
		 *
		 */
		public function testTableExists():void
		{
			var sql:String = "SELECT * FROM "+tableName+" LIMIT 1;";
			executeCustomCommand(sql, this.onDatabaseReady, this.createTable );
		}

		/**
		 * Method to create the database table.
		 *
		 */
		private function createTable():void
		{
		    statement = new SQLStatement();
		    statement.sqlConnection = connection;
		    statement.text = createDbStatement;
		    statement.execute();

		    statement.addEventListener(SQLEvent.RESULT, onDatabaseReady);
		}

		/**
		 * Common sql command: select all entries in database
		 *
		 * @param callback
		 * @param failCallback
		 *
		 */
		public function executeSelectAllCommand(callback:Function=null, failCallback:Function=null):void
		{
			var sql:String = "SELECT * FROM "+tableName+";";
			executeCustomCommand(sql, callback, failCallback);
		}

		/**
		 * Common sql command: delete all entries in database
		 *
		 * @param callback
		 *
		 */
		public function executeDeleteAllCommand(callback:Function=null):void
		{
			var sql:String = "DELETE * FROM "+tableName+";";
			executeCustomCommand(sql, callback);
		}

		/**
		 * Method to execute a SQL command
		 *
		 * @param sql SQL command string
		 * @param callback success call back function to impliment if necessery
		 * @param failCallBack fail call back function to impliment if necessery
		 *
		 */
		public function executeCustomCommand(sql:String, callBack:Function=null, failCallBack:Function=null):void
		{
		    statement = new SQLStatement();
		    statement.sqlConnection = connection;

		    statement.text = sql;

		    if (callBack!=null)
		    {
		    	statement.addEventListener(SQLEvent.RESULT, callBack);
		    }
		    else
		    {
		    	statement.addEventListener(SQLEvent.RESULT, onStatementSuccess);
		    }

		    statement.addEventListener(SQLErrorEvent.ERROR, function():void {
		    		fail();
		    	});

			try
			{
			    statement.execute();
			}
			catch (error:SQLError)
			{
				this.handleErrors(error, sql, callBack, failCallBack);
			}
		}

		/**
		 * Utility method to clean bad characters that can break SQL commands
		 *
		 * @param str
		 * @return
		 *
		 */
		public static function removeBadCharacters(str:String):String
		{
			var retVal:String = str.split("'").join("&#8217;&rsquo;");
			return retVal;
		}

		// ------------------------------HANDLERS----------------------------

		/**
		 * Method to handle SQL command that create the dataabase.
		 * If the method was created due to a fail SQL command method checks if need to repeate any SQL command.
		 *
		 * @param event
		 *
		 */
		private function onDatabaseReady(event:Event=null):void
		{
			var evt:Event = new Event(DATABASE_READY);
			this.dispatchEvent(evt);

			if (repeateSqlCommand != "")
			{
				this.executeCustomCommand(repeateSqlCommand, repeateCallBack, repeateFailCallBack);

				repeateSqlCommand = "";
				repeateFailCallBack = null;
				repeateCallBack = null;
			}
		}

		/**
		 * Handle successful calls
		 * @param event
		 *
		 */
		private function onStatementSuccess(event:SQLEvent):void
		{
			var results:Object = statement.getResult();
			var evt:StatementSuccessEvent = new StatementSuccessEvent(COMMAND_EXEC_SUCCESSFULLY, results);
			this.dispatchEvent(evt);
		}

		/**
		 * Error handler
		 *
		 * @param error
		 * @param sql
		 * @param callBack
		 * @param failCallBack
		 *
		 */
		private function handleErrors(error:SQLError, sql:String, callBack:Function, failCallBack:Function):void
		{
			trace("Error message:", error.message);
			trace("Details:", error.details);

		    if (error.details == "no such table: '"+tableName+"'")
		    {
		    	repeateSqlCommand = sql;
		    	repeateFailCallBack = failCallBack;
		    	repeateCallBack = callBack;
		    	createTable();
		    }
		    else
		    {
			    if (failCallBack != null)
			    {
			    	failCallBack();
			    }
			    else
			    {
			    	fail();
			    }
		    }
		}

		/**
		 * Handler for fail calls
		 *
		 * @param event
		 *
		 */
		private function fail(event:Event=null):void
		{
	        var evt:Event = new Event(COMMAND_EXEC_FAILED);
	        this.dispatchEvent(evt);

	        close();
		}

		/**
		 * Method function to retrieve instance of the class
		 *
		 * @return The same instance of the class
		 *
		 */
		public static function getInstance():SQLiteManager
		{
			if( instance == null )
				instance = new  SQLiteManager(new AccessRestriction());

			return instance;
		}

	}
}

class AccessRestriction {}

Here’s an example of implementing the SQL manager;

var database:SQLiteManager = SQLiteManager.getInstance();
database.start("Users.sql3", "Users", "CREATE TABLE Users(userId VARCHAR(150) PRIMARY KEY, UserName VARCHAR(150))");

database.addEventListener(SQLiteManager.COMMAND_EXEC_SUCCESSFULLY, onSelectResult);
database.addEventListener(SQLiteManager.COMMAND_EXEC_FAILED, onFail);

database.executeSelectAllCommand();

private function onSelectResult(event:StatementSuccessEvent):void
{
     var result:Array = event.results.data;
}

private function onFail(event:Event):void
{
     // handle fail
}

And here’s a custom SQL command using custom SQL command:

var sql:String =  "INSERT INTO Users VALUES('"+userVO.userId+"','"+userVO.userName+"');";

database.addEventListener(SQLiteManager.COMMAND_EXEC_SUCCESSFULLY, onInsertSuccess);
database.executeCustomCommand(sql);

Take a look at this example. Very simple and easy to insert and read from the SQLite database.

<windowedApplication xmlns="http://ns.adobe.com/mxml/2009" layout="absolute"
	initialize="initializeHandler(event)">

	<script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import mx.events.FlexEvent;
			import com.elad.framework.sqlite.events.StatementSuccessEvent;
			import com.elad.framework.sqlite.SQLiteManager;

			private var database:SQLiteManager = SQLiteManager.getInstance();

			protected function initializeHandler(event:FlexEvent):void
			{
				database.start("Users.sql3", "Users", "CREATE TABLE Users(UserId VARCHAR(150) PRIMARY KEY, UserName VARCHAR(150))");

				database.addEventListener(SQLiteManager.COMMAND_EXEC_SUCCESSFULLY, onSelectResult);
				database.addEventListener(SQLiteManager.COMMAND_EXEC_FAILED, function():void {
					trace("fail!");
				});

				readEntries();
			}

			private function insertEntry():void
			{
				var sql:String =  "INSERT INTO Users VALUES('"+String(userId.text)+"','"+userName.text+"');";
				database.executeCustomCommand(sql);
			}

			private function readEntries():void
			{
				database.executeSelectAllCommand();
			}

			private function onSelectResult(event:StatementSuccessEvent):void
			{
				var result:Array = event.results.data;
				var rowsAffected:int = event.results.rowsAffected;

				if (rowsAffected == 1)
					readEntries();

				if (result == null)
					return;

				var len:int = result.length;
				var dp:ArrayCollection = new ArrayCollection();

				for (var i:int; i<len; i++)
				{
					dp.addItem( {UserId: result[i].UserId, UserName: result[i].UserName} );
				}

				dataGrid.dataProvider = dp;
			}

		]]>
	</script>
	<panel x="5" y="5" layout="absolute" height="356">

		<vbox horizontalScrollPolicy="off" verticalScrollPolicy="off">
			<!-- Form -->
			<form width="414">
				<formItem label="User ID:">
					<fxTextInput id="userId"/>
				</formItem>
				<formItem label="User Name:">
					<fxTextInput id="userName"/>
				</formItem>
				<formItem>
					<fxButton label="Insert Entry" click="insertEntry();"/>
				</formItem>
			</form>
		</vbox>

		<!-- Results -->
		<dataGrid x="19" y="123" id="dataGrid">
			<columns>
				<dataGridColumn headerText="User Id" dataField="UserId"/>
				<dataGridColumn headerText="User Name" dataField="UserName"/>
			</columns>
		</dataGrid>

	</panel>

</windowedApplication>

SQLite Manager example

Click here to download the sample application

Just released – Adobe AIR YouTube Video Widget works Online and Offline. Watch a presentation video

Alpha version of YouTube Video Widget, allows you to search videos from YouTube and download them into your local drive. Once you go offline the application automatically aware of the change and switches to offline mode, which allow you to view the downloaded videos.

Application can be deployed on desktops or on a touch screen UMPC as well as future ARM devices that will support AIR 1.5.

Download the application from Adobe AIR Marketplace or from here.

Developed with Flex 4 (Gumbo), Flash Catalyst and AIR 1.5. The framework is Caringorm with the Presentation Model, allows us to easily create a template so the application changes based on platform.

YouTube AIR Widget Preview

Once you select a video you can download the video. Which will be saved on your local drive and information captured in SQLite database. In fact the application recognize automatically that you went offline.

YouTube AIR Widget Preview, download video.

I made a quick presentation that shows the application capabilities. Take a look at the video below:

The full tutorials and explanation on how to build applications like this one will be available in our new book which will be out in May by APress and can be purchased now for a limited discount price from Amazon.

Adobe AIR 1.5 DownloadManager API – download files from web / server to your local drive

I wrote couple of handy classes to handle retrieving / downloading of any type of files to a local directory in AIR. These classes are useful for building an AIR application that works online and offline. You can download multimedia, HTML and other file types. Once files are downloaded you can store the information in SQLite database and serve the information, once your system is not connected to the internet.

The core class “DownloadManager.as” allows you to download files to any location in your local drive using: “File.desktopDirectory.resolvePath(fileLocalLocation)” or you can also point to where the application directory sits: “File.applicationStorageDirectory.resolvePath(fileLocalLocation);”

		public function downloadFileFromServer(fileURL:String, fileLocalLocation:String):void
		{
			//var file:File = File.desktopDirectory.resolvePath(fileLocalLocation);
			var file:File = File.applicationStorageDirectory.resolvePath(fileLocalLocation);
			request = new URLRequest(fileURL);
	        fileStream.openAsync(file, FileMode.WRITE);

	        stream.addEventListener(ProgressEvent.PROGRESS, onDownloadProgress);
	        stream.addEventListener(Event.COMPLETE, onDownloadComplete);

	        stream.load(request);
		}

Notice we defined the steam event handlers and load the file. We now need to define event handler, to handle the progess during the download and once completed;

        private function onDownloadProgress(event:ProgressEvent):void
        {
            var byteArray:ByteArray = new ByteArray();
            var precent:Number = Math.round(bytesLoaded*100/bytesTotal);

            bytesLoaded = event.bytesLoaded;
            bytesTotal = event.bytesTotal;

            stream.readBytes(byteArray, 0, stream.bytesAvailable);
            fileStream.writeBytes(byteArray, 0, byteArray.length);

			var progressEvent:ProgressEvent = new ProgressEvent(ProgressEvent.PROGRESS);
			progressEvent.bytesLoaded = bytesLoaded;
			progressEvent.bytesTotal = bytesTotal;

			this.dispatchEvent(progressEvent);
    	}

    	private function onDownloadComplete(event:Event):void
    	{
            fileStream.close();
            stream.close();

            stream.removeEventListener(ProgressEvent.PROGRESS, onDownloadProgress);
            stream.removeEventListener(Event.COMPLETE, onDownloadComplete);

			var completeEvent:Event = new Event(Event.COMPLETE);
			this.dispatchEvent(completeEvent);
    	}

Here’s a little MXML component that implements the download manager and the ReadDirectoryHelper.
Once we init the application, we check if the file exists and download the file if it doesn’t exists. Additionally, we are using the download manager with a ProgressBar to show the download progress.

<windowedApplication xmlns="http://ns.adobe.com/mxml/2009"
	layout="absolute"
	initialize="init()">

	<script>
		<![CDATA[
			import com.elad.framework.utils.ReadDirectoryHelper;
			import com.elad.framework.utils.DownloadManager;
			import mx.controls.Alert;

			private function init():void
			{
				var array:Array = ReadDirectoryHelper.getDirectoryFiles("/Users/elad/Desktop/tmp/");
				var isExists:Boolean = ReadDirectoryHelper.isFileExists(array, "file.flv");

				if (isExists == false)
					downloadFile();
				else
					Alert.show("File Already exists");
			}

			private function downloadFile():void
			{
		        var downloadHelper:DownloadManager = new DownloadManager();

		        downloadHelper.addEventListener(ProgressEvent.PROGRESS, onDownloadProgress);
		        downloadHelper.addEventListener(Event.COMPLETE, onDownloadComplete);
		        downloadHelper.downloadFileFromServer("http://samples.mplayerhq.hu/FLV/asian-commercials-are-weird.flv",
		        "/Users/elad/Desktop/tmp/file.flv");
			}

	        private function onDownloadProgress(event:ProgressEvent):void
	        {
                var value:Number = event.bytesLoaded;
                var total:Number = event.bytesTotal;
                var precent:Number = Math.round(value*100/total);

				if (progressBar.minimum==0)
				{
					progressBar.minimum = value;
					progressBar.maximum = total;
				}

				progressBar.label = "Progress "+precent+"%";
                progressBar.setProgress(value, total);
        	}

        	private function onDownloadComplete(event:Event):void
        	{
 				Alert.show("Download Completed!");
        	}

		]]>
	</script>

	<progressBar id="progressBar"
		x="4" y="63"
		minimum="0"
		label="Progress 0%"
		mode="manual" />

</windowedApplication>

Download Files from Web to local drive Adobe AIR

To download the source code of the AIR application click here.

Flex 4 Gumbo FXG – tips for creating custom FxVScrollBar and FxHScrollBar components

Flex Gumbo FXG offers new APIs to work with vertical and horizontal scrollers. There are two components: FxVScrollBar and FxHScrollBar, which extends the FxScrollBar class. You can use Flash Catalyst to generate a custom scrollers easily or create the skin on your own. Once the code is completed it’s not clear what you need to do with the code or how to attach it to FXG components, since there are no examples in the live docs available yet, I decided to put a post on my blog.

Four properties are available to set the scroller to a FXG component;

1. Minimum – Min value for the scroller.
2. Maximum – Max value for the scroller.
3. Value – Current position of the scroller, which must be within the min and max values and needs to be attached to some component.
4. Viewport size – number of items in the range that can displayed at a time.

Use the FxVScrollBar.value property to attach to a group component as shown below;


	<group id="group" width="320" height="1500">
		..
		..
	</group>

	<fxVScrollBar id="pageScrollbar"
		includeIn="SomeState"
skinClass="com.elad.mobilevideo.view.components.PageScrollbar"
		height="100%"
		value="@{group.verticalScrollPosition}"
		maximum="{group.height}"
		/>

Notice that I used the “includeIn” property to include the scroller component in “SomeState” state. Also note that the skin points to a custom component PageScrollBar.mxml, which contains the skin that holds both the scroll bar and scroll thumb components, take a look;

<skin xmlns="http://ns.adobe.com/mxml/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt"
	 height="100%" xmlns:th="http://ns.adobe.com/thermo/2009">

	<transitions>
		<transition fromState="normal" toState="disabled"/>
		<transition fromState="disabled" toState="normal"/>
	</transitions>

	<states>
		<state name="normal" th:color="0xcc0000"/>
		<state name="disabled" th:color="0x0081cc"/>
	</states>

	<metadata>[HostComponent("mx.components.FxVScrollBar")]</metadata>
	<fxButton left="0" top="0" skinClass="com.elad.mobilevideo.view.components.ScrollTrack" id="track"/>
	<fxButton left="1" top="1" skinClass="com.elad.mobilevideo.view.components.ScrollThumb" id="thumb"/>
</skin>

ScrollThumb.mxml component:

<skin xmlns="http://ns.adobe.com/mxml/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt" resizeMode="scale">

	<transitions>
		<transition fromState="up" toState="over"/>
		<transition fromState="up" toState="down"/>
		<transition fromState="up" toState="disabled"/>
		<transition fromState="over" toState="up"/>
		<transition fromState="over" toState="down"/>
		<transition fromState="over" toState="disabled"/>
		<transition fromState="down" toState="up"/>
		<transition fromState="down" toState="over"/>
		<transition fromState="down" toState="disabled"/>
		<transition fromState="disabled" toState="up"/>
		<transition fromState="disabled" toState="over"/>
		<transition fromState="disabled" toState="down"/>
	</transitions>

	<states>
		<state name="up"/>
		<state name="over"/>
		<state name="down"/>
		<state name="disabled"/>
	</states>

	<metadata>[HostComponent("mx.components.FxButton")]</metadata>
	<bitmapGraphic source="@Embed('assets/SearchVideo/thumb.png')"
		d:userLabel="thumb"
		left="0" top="0"/>

</skin>

ScrollTrack.mxml component:

<skin xmlns="http://ns.adobe.com/mxml/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt" resizeMode="scale">

	<transitions>
		<transition fromState="up" toState="over"/>
		<transition fromState="up" toState="down"/>
		<transition fromState="up" toState="disabled"/>
		<transition fromState="over" toState="up"/>
		<transition fromState="over" toState="down"/>
		<transition fromState="over" toState="disabled"/>
		<transition fromState="down" toState="up"/>
		<transition fromState="down" toState="over"/>
		<transition fromState="down" toState="disabled"/>
		<transition fromState="disabled" toState="up"/>
		<transition fromState="disabled" toState="over"/>
		<transition fromState="disabled" toState="down"/>
	</transitions>

	<states>
		<state name="up"/>
		<state name="over"/>
		<state name="down"/>
		<state name="disabled"/>
	</states>

	<metadata>[HostComponent("mx.components.FxButton")]</metadata>
	<bitmapGraphic source="@Embed('assets/SearchVideo/slider.png')"
		resizeMode="scale" d:userLabel="slider"
		left="0" top="0"/>

</skin>

I didn’t implement the “Transition” components but feel free to play around with these components to create interesting custom scrollers.

Flex Gumbo FXG use DataGroup instead of Repeater

Flex Gumbo SDK Iteration 10 FXG has an issue with the Repeater object the content property is not binding, I submited a ticket to Adobe here.

The code that repoduce the error is below:

   <mx:Repeater id="r" dataProvider="{ar}">
<fxButton left="20" top="335" content="{r.currentItem}" skinClass="components.Button" />
    </mx:Repeater>

I couldn’t find any workaround and end up using the ‘DataGroup’ FXG object instead. The DataGroup allow you to do what the repeater does so I am not going to be surprised if Adobe decide to deprecate the component.

    <dataGroup>
        <layout><verticalLayout gap="1" /></layout>
        <dataProvider>
            <arrayCollection>
				<fxButton id="btn4" left="20" top="335" content="{arrayCollection.getItemAt(0)}"
					skinClass="components.Button" includeIn="SearchPage" />
            </arrayCollection>
        </dataProvider>
    </dataGroup>

Or you can set the array collection as any component and attach it;

<dataGroup dataProvider="{arrayCollection}" />

Cross Platform AIR Application on Flex Gumbo – Context Awareness Manager to gather, detect and translate system info

The cure in adopting AIR as a cross platform application as well as creating mobile applications is to be able to know the user’s system capabilities as well as track changes in its environment.

The AIR Context Awareness Manager can help you develop a cross-platform application which is operation system (OS) independent.
AIR Context Awareness Manager API can be utilized to gather and track information available in our application. We can split the information gathered into three categories:

1. System capability and support.
2. User activity.
3. Track changes in system.

As new capabilities will be available we can add them to the manager.

Download SWC, example and source code from Google code:
http://code.google.com/p/contextawarenessmanager/

Adobe AIR is available on PC, MAC and Linux, and as Adobe Open Screen Project materializes into a reality with the announcement of Flash 10 and AIR available on mobile devices, the challenge will increase.

Platforms are usually capable of adjusting to many changes in the device on their own. For instance, system track network errors and displaying an error message, however, our application cannot rely entirely on the platform to adjust correctly and we may want to make changes in the application once changes in the device occur. For instance, our AIR application can go into offline mode once we are not connected to the internet.

In essence our AIR application should have the ability to react and adjust during the entire lifetime of the application, additionally our application is responsible for hiding issues and constraints of the mobile device from the user. For instance, user doesn’t care that our system has low network download speed and will blame our application on choppy video.

In order to achieve that kind of awareness in our application, we should be able to be aware of the changes as well as translate the context. That’s where Context Awareness comes into place. Context Awareness means that once we get information regarding the application platform and as changes are made in the platform environment we need to translate the information and understand what it means as well as react accordingly.

Most of the system capability information is available through the flash.system.Capabilities API. The Capabilities that API provides are properties that describe the system and flash player that are hosting the application. ContextVO will hold the capability properties as well as other properties.

Detecting System Capabilities

          public function ContextVO()
          {
               avHardwareDisable = Capabilities.avHardwareDisable;
               hasAccessibility = Capabilities.hasAccessibility;
               hasAudio = Capabilities.hasAudio;
               hasAudioEncoder  = Capabilities.hasAudioEncoder;
               hasEmbeddedVideo = Capabilities.hasEmbeddedVideo;
               hasMP3 = Capabilities.hasMP3;
               hasPrinting = Capabilities.hasPrinting;
               hasScreenBroadcast = Capabilities.hasScreenBroadcast;
               hasScreenPlayback = Capabilities.hasScreenPlayback;
               hasStreamingAudio = Capabilities.hasStreamingAudio;
               hasVideoEncoder = Capabilities.hasVideoEncoder;
               isDebugger = Capabilities.isDebugger;
               language = Capabilities.language;
               localFileReadDisable = Capabilities.localFileReadDisable;
               manufacturer = Capabilities.manufacturer;
               os = Capabilities.os;
               osName = Capabilities.os.substr(0, 3).toLowerCase();
               pixelAspectRatio = Capabilities.pixelAspectRatio;
               playerType = Capabilities.playerType;
               screenColor = Capabilities.screenColor;
               screenDPI = Capabilities.screenDPI;
               screenResolutionX = Capabilities.screenResolutionX;
               screenResolutionY = Capabilities.screenResolutionY;
               serverString = Capabilities.serverString;
               version = Capabilities.version;
          }

Detecting System Support

private function setSystemSupportCapability():void
{
     contextVO.supportsDockIcon = NativeApplication.supportsDockIcon;
     contextVO.supportsMenu = NativeApplication.supportsMenu;
     contextVO.supportsSystemTrayIcon = NativeApplication.supportsSystemTrayIcon;
     contextVO.supportsMenu = NativeWindow.supportsMenu;
     contextVO.supportsNotification = NativeWindow.supportsNotification;
     contextVO.supportsTransparency = NativeWindow.supportsTransparency;
     contextVO.systemMaxSize = NativeWindow.systemMaxSize;
     contextVO.systemMinSize = NativeWindow.systemMinSize;
}

Detecting User Presence

private function detectUserPresence():void
{
     nativeApp.idleThreshold = idleThresholdTime;
     nativeApp.addEventListener(Event.USER_IDLE, onUserIdleHandler);
     nativeApp.addEventListener(Event.USER_PRESENT, onUserPresentHandler);
}
private function onUserIdleHandler(evt:Event):void
{
     var lastUserInput:Number = NativeApplication.nativeApplication.timeSinceLastUserInput;
     var event:Event = new Event(USER_IDLE, true);
     contextVO.isUserPresent = false;
     contextVO.lastUserInput = lastUserInput;
     this.dispatchEvent(event);
}
private function onUserPresentHandler(evt:Event):void
{
     var event:Event = new Event(USER_PRESENT, true);
     contextVO.isUserPresent = true;
     this.dispatchEvent(event);
}

Detecting network connectivity changes

private function detectNetworkChanges():void
{
     nativeApp.addEventListener(Event.NETWORK_CHANGE, onNetworkStatusChange);
}
private function onNetworkStatusChange(evt:Event):void
{
     var event:Event = new Event(NETWORK_CHANGE, true);
     contextVO.isNetworkChanged = true;
     this.dispatchEvent(event);
}

Detecting HTTP connectivity

private function detectHTTPConnectivity():void
{
     monitor = new URLMonitor(new URLRequest(siteToTrack));
     monitor.addEventListener(StatusEvent.STATUS, onHTTPConnectivityChange);
     monitor.start();
}
private function onHTTPConnectivityChange(evt:StatusEvent):void
{
    var event:Event;
    contextVO.isHTTPAvaliable = monitor.available;
    event = (monitor.available) ? new Event(HTTP_CONNECTIVITY_TRUE, true) :
         new Event(HTTP_CONNECTIVITY_FALSE, true);</p>
<p>     this.dispatchEvent(event);
}

Detecting socket connectivity

private function detectSocketConnectivity():void
{
     socketMonitor = new SocketMonitor(siteSocketMonitor,portToCheck);
     socketMonitor.addEventListener(StatusEvent.STATUS, onSocketStatusChange);
     socketMonitor.start();
}
private function onSocketStatusChange(evt:StatusEvent):void
{
     var event:Event;
     contextVO.isSocketMonitorAvailable = socketMonitor.available;
     event = (socketMonitor.available) ? new Event(SOCKET_CONNECTIVITY_TRUE, true) :
         new Event(SOCKET_CONNECTIVITY_FALSE, true);
     this.dispatchEvent(event);
}

Detecting Local Drives

private function detectLocalDrivers():void
{
     contextVO.currentAvaliableDrives = (contextVO.osName=="mac") ?
          new File('/Volumes/').getDirectoryListing() : File.getRootDirectories() ;
}
public function refreshLocalDrives():void
{
     detectLocalDrivers();
}

Detecting WindowedApplication movement

private function detectWindowedApplicationMovment():void
{
     Application.application.addEventListener(NativeWindowBoundsEvent.MOVING, onWindowedApplicationMovment);
}
private function onWindowedApplicationMovment(evt:NativeWindowBoundsEvent):void
{
     var event:Event = new Event(NATIVE_WINDOW_MOVED, true);
     contextVO.windowPositionAfterBounds = evt.afterBounds;
     contextVO.windowPositionBeforeBounds = evt.beforeBounds;
     this.dispatchEvent(event);
}

Getting the runtime version and patch level

private function setRuntimeInformation():void
{
     contextVO.getRuntimeVersion = nativeApp.runtimeVersion;
     contextVO.getRuntimePatchLevel = nativeApp.runtimePatchLevel;
}

I have created an example of a monitor application that will display the changes across the application. Take a look at the code below:

Context Awareness Manager

<windowedApplication xmlns="http://ns.adobe.com/mxml/2009"
	layout="absolute"
	creationComplete="init()">

	<script>
		<![CDATA[
			import com.elad.framework.utils.ContextAwarenessManager;

			private var contextAwareness:ContextAwarenessManager = ContextAwarenessManager.getInstance();

			private function init():void
			{
				contextAwareness.addEventListener(ContextAwarenessManager.USER_IDLE, onUserHandler);
				contextAwareness.addEventListener(ContextAwarenessManager.USER_PRESENT, onUserHandler);
				contextAwareness.addEventListener(ContextAwarenessManager.HTTP_CONNECTIVITY_TRUE, onHTTPConnectivityChange);
				contextAwareness.addEventListener(ContextAwarenessManager.HTTP_CONNECTIVITY_FALSE, onHTTPConnectivityChange);
				contextAwareness.addEventListener(ContextAwarenessManager.NATIVE_WINDOW_MOVED, onWindowedApplicationMovment);
				contextAwareness.addEventListener(ContextAwarenessManager.NETWORK_CHANGE, onNetworkStatusChange);
				contextAwareness.addEventListener(ContextAwarenessManager.SOCKET_CONNECTIVITY_FALSE, onSocketStatusChange);
				contextAwareness.addEventListener(ContextAwarenessManager.SOCKET_CONNECTIVITY_TRUE, onSocketStatusChange);

				contextAwareness.start();
				textArea.text = "Start Tracking";
			}

			private function onUserHandler(evt:Event):void
			{
				textArea.text += "nisUserPresent: "+contextAwareness.contextVO.isUserPresent.toString();
				textArea.text += "nlastUserInput: "+contextAwareness.contextVO.lastUserInput.toString();
			}

			private function onNetworkStatusChange(evt:Event):void
			{
				textArea.text += "nisNetworkChanged: "+contextAwareness.contextVO.isNetworkChanged.toString();
			}

			private function onHTTPConnectivityChange(evt:Event):void
			{
			    textArea.text += "nisHTTPAvaliable: "+contextAwareness.contextVO.isHTTPAvaliable.toString();
			}

			private function onSocketStatusChange(evt:Event):void
			{
				textArea.text += "nisSocketMonitorAvailable: "+contextAwareness.contextVO.isSocketMonitorAvailable.toString();
			}

			private function onWindowedApplicationMovment(evt:Event):void
			{
				textArea.text += "nwindowPositionAfterBounds: "+contextAwareness.contextVO.windowPositionAfterBounds.toString();
				textArea.text += "nwindowPositionBeforeBounds: "+contextAwareness.contextVO.windowPositionBeforeBounds.toString();
			}

			private function showCapabilitiesClickHandler(event:MouseEvent):void
			{
				textArea.text += "navHardwareDisable: "+contextAwareness.contextVO.avHardwareDisable.toString();
				textArea.text += "nhasAccessibility: "+contextAwareness.contextVO.hasAccessibility.toString();
				textArea.text += "nhasAudio: "+contextAwareness.contextVO.hasAudio.toString();
				textArea.text += "nhasAudioEncoder : "+contextAwareness.contextVO.hasAudioEncoder.toString();
				textArea.text += "nhasEmbeddedVideo: "+contextAwareness.contextVO.hasEmbeddedVideo.toString();
				textArea.text += "nhasMP3: "+contextAwareness.contextVO.hasMP3.toString();
				textArea.text += "nhasPrinting: "+contextAwareness.contextVO.hasPrinting.toString();
				textArea.text += "nhasScreenBroadcast: "+contextAwareness.contextVO.hasScreenBroadcast.toString();
				textArea.text += "nhasScreenPlayback: "+contextAwareness.contextVO.hasScreenPlayback.toString();
				textArea.text += "nhasStreamingAudio: "+contextAwareness.contextVO.hasStreamingAudio.toString();
				textArea.text += "nhasVideoEncoder: "+contextAwareness.contextVO.hasVideoEncoder.toString();
				textArea.text += "nisDebugger: "+contextAwareness.contextVO.isDebugger.toString();
				textArea.text += "nlanguage: "+contextAwareness.contextVO.language.toString();
				textArea.text += "nlocalFileReadDisable: "+contextAwareness.contextVO.localFileReadDisable.toString();
				textArea.text += "nmanufacturer: "+contextAwareness.contextVO.manufacturer.toString();
				textArea.text += "nos: "+contextAwareness.contextVO.os.toString();
				textArea.text += "nosName: "+contextAwareness.contextVO.osName.toString();
				textArea.text += "npixelAspectRatio: "+contextAwareness.contextVO.pixelAspectRatio.toString();
				textArea.text += "nplayerType: "+contextAwareness.contextVO.playerType.toString();
				textArea.text += "nscreenColor: "+contextAwareness.contextVO.screenColor.toString();
				textArea.text += "nscreenDPI: "+contextAwareness.contextVO.screenDPI.toString();
				textArea.text += "nscreenResolutionX: "+contextAwareness.contextVO.screenResolutionX.toString();
				textArea.text += "nscreenResolutionY: "+contextAwareness.contextVO.screenResolutionY.toString();
				textArea.text += "nserverString: "+contextAwareness.contextVO.serverString.toString();
				textArea.text += "nversion: "+contextAwareness.contextVO.version.toString();
			}

			private function showSystemSupportClickHandler(event:MouseEvent):void
			{
				textArea.text += "nsupportsDockIcon: "+contextAwareness.contextVO.supportsDockIcon.toString();
				textArea.text += "nsupportsMenu: "+contextAwareness.contextVO.supportsMenu.toString();
				textArea.text += "nsupportsSystemTrayIcon: "+contextAwareness.contextVO.supportsSystemTrayIcon.toString();
				textArea.text += "nsupportsNotification: "+contextAwareness.contextVO.supportsNotification.toString();
				textArea.text += "nsupportsTransparency: "+contextAwareness.contextVO.supportsTransparency.toString();
				textArea.text += "nsystemMaxSize: "+contextAwareness.contextVO.systemMaxSize.toString();
				textArea.text += "nsystemMinSize: "+contextAwareness.contextVO.systemMinSize.toString();
			}

		]]>
	</script>

	<vbox width="100%" height="100%" >
		<textArea id="textArea" width="100%" height="95%" />

		<hbox>
			<button label="Show System Capabilities" click="showCapabilitiesClickHandler(event)" />
			<button label="Show System Support" click="showSystemSupportClickHandler(event)" />
		</hbox>

		<hbox>
			<button label="Start Tracking" click="contextAwareness.start(); textArea.text+='nStart Tracking';" />
			<button label="Stop Tracking" click="contextAwareness.stop(); textArea.text+='nStop Tracking';" />
			<button label="Clear TextArea" click="textArea.text=''" />
		</hbox>

	</vbox>

</windowedApplication>

Download SWC, example and source code from Google code:
http://code.google.com/p/contextawarenessmanager/