Archive for the 'Flex APIs' Category

14
May

Utility class to help reading/writing files in Flash 10

Before the release of Flash 10 we needed to use some sort of a server side proxy or Javascript in order to read or write file in the user’s system. We would send the request to a proxy, which will handle the request and send it back to Flash once completed.

Flash Player 10 has exposed two new methods in FileReference: load and save.

The new methods allow you to read and write data right into the user’s local system. You get information about the files such as modify date, creator, size and other properties, however unlike AIR’s FileStream API the location of the files will not be visible to us and we can only do asynchronous calls.

Asynchronous operations perform operations in the background without waiting for the operation to complete, long processes such as uploading or downloading a large file will not impact the application and the user can keep using the application. Synchronous operations on the other hand, the operation is waiting for the original operation to complete before allowing the user to do any interactions with the application.

Recently, Adobe have closed a gap and forced the load and save methods to be used follow user interaction, such as clicking a button, so the user will be aware of being asked to save or load and not see a browse window comes out of nowhere.

The process of reading and writing files is easy, however I created a helper to make the process of reading and writing files even easier. The class called: LocalFileHelper.as and it uses the FileReference API.

Let’s take a look at the code that shows a simple implementation of loading and saving a file:


<FxApplication xmlns="http://ns.adobe.com/mxml/2009"
	minWidth="1024"
	minHeight="768" initialize="initializeHandler(event)">

	     <Script>
          <![CDATA[
	     		import com.elad.framework.utils.events.LocalFileErrorEvent;
	     		import com.elad.framework.utils.events.LocalFileLoadedEvent;
	     		import mx.controls.Alert;
	     		import com.elad.framework.utils.events.LocalFileEvent;
	     		import com.elad.framework.utils.enum.FileTypeFormat;
	     		import com.elad.framework.utils.LocalFileHelper;
	     		import mx.events.FlexEvent;

	     		private var localFileHelper:LocalFileHelper;

	     		protected function initializeHandler(event:FlexEvent):void
	     		{
	     			localFileHelper = new LocalFileHelper( FileTypeFormat.FILE_FILTER_TEXT_TYPE );

	     			localFileHelper.addEventListener(LocalFileEvent.FILE_LOAD_BROWSE, onFileSelect);
	     			localFileHelper.addEventListener(LocalFileEvent.FILE_SAVE_BROWSE, function():void { trace("Save browse complete"); } );
	     			localFileHelper.addEventListener(LocalFileEvent.FILE_SAVE_SUCCESSFULLY, function():void { trace("Save complete!"); } );
	     			localFileHelper.addEventListener(LocalFileEvent.FILE_CANCEL, function():void { Alert.show("Cancel"); } );
	     			localFileHelper.addEventListener(LocalFileErrorEvent.FILE_ERROR, function():void { trace("file error") } );
	     		}

	     		private function loadFile():void
	     		{
	     			localFileHelper.browse();
	     		}

				private function onFileSelect(event:LocalFileEvent):void
				{
					localFileHelper.addEventListener(LocalFileLoadedEvent.DATA_LOADED, onDataLoaded );
					localFileHelper.load();
				}

				private function onDataLoaded(event:LocalFileLoadedEvent):void
				{
					output.text = LocalFileHelper.convertByteArrayToText( event.byteLoaded );
				}

				private function saveFile():void
				{
					localFileHelper.save( output.text, "test.txt" );
				} 		

          ]]>
     </Script>

     <FxButton label="Load" click="loadFile()" />
     <FxButton label="Save" click="saveFile()"  x="84"/>
     <TextArea id="output" y="37" width="397" height="327"/>

</FxApplication>

Take a look at the utility class:


package com.elad.framework.utils
{
	import com.elad.framework.utils.enum.FileTypeFormat;
	import com.elad.framework.utils.enum.InteractionStates;
	import com.elad.framework.utils.events.LocalFileErrorEvent;
	import com.elad.framework.utils.events.LocalFileEvent;
	import com.elad.framework.utils.events.LocalFileLoadedEvent;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.net.FileReference;
	import flash.utils.ByteArray;

	public class LocalFileHelper extends EventDispatcher
	{

	    //--------------------------------------------------------------------------
	    //
	    //  Variables
	    //
	    //--------------------------------------------------------------------------

		/**
		 * @private
		 *
		 */
		private var fileType:Array;

		/**
		 * @private
		 *
		 */
		private var fileReference:FileReference;

		/**
		 * @private
		 *
		 */
		private var interactionState:String;

	    //--------------------------------------------------------------------------
	    //
	    //  Constructor
	    //
	    //--------------------------------------------------------------------------

		/**
		 * Default constructor, which call the <code>reset</code> method to set the file reference and type.
		 *
		 * @param fileType	you can set the type of files you want to use.  The file types are listed in <code>FileTypeFormat</code>
		 * @see	com.elad.framework.utils.enum.FileTypeFormat
		 *
		 */
		public function LocalFileHelper(fileType:Array=null)
		{
			reset(fileType);
		}

		/**
		 * Reset method will allow the implementation to reset the file reference and set again the file type filter.
		 * The default file type is <code>FileTypeFormat.FILE_FILTER_ALL_FILES_TYPE</code> which will allow selecting
		 * any file type.
		 *
		 * @param fileType	you can set the type of files you want to use.  The file types are listed in <code>FileTypeFormat</code>
		 * @see	com.elad.framework.utils.enum.FileTypeFormat
		 *
		 */
		public function reset(fileType:Array=null):void
		{
			if (fileType == null)
			{
				fileType = FileTypeFormat.FILE_FILTER_ALL_FILES_TYPE;
			}

			this.fileType = fileType;
			fileReference = new FileReference();
		}

		/**
		 * The browse method is useful when you need to load a file, you first browse for the file and than you load the file.
		 * When you browse for the file the file type that was selected is used.
		 *
		 */
		public function browse():void
		{
			this.interactionState = InteractionStates.BROWSE;

			fileReference.addEventListener(Event.SELECT, onFileSelectEventHandler);
            fileReference.addEventListener(Event.CANCEL, onFileCancelEventHandler);

			fileReference.browse(fileType);
		}

		/**
		 * Static method to convert the <code>ByteArray</code> data into a string.
		 *
		 * @example
		 * <listing version="3.0">
		 * 	var text:String = LocalFileHelper.convertByteArrayToText( event.byteLoaded );
		 * </listing>
		 *
		 * @param data
		 * @return
		 *
		 */
		public static function convertByteArrayToText(data:ByteArray):String
		{
			var retVal:String;
			retVal = data.readUTFBytes(data.bytesAvailable);

			return retVal;
		}

		/**
		 * Method to load a file.  The method add event listeners and use the <code>fileReference.load</code>
		 * method to do the loading.  To load a file you must first call the <code>browse</code> method.
		 *
		 */
		public function load():void
		{
			if (this.interactionState != InteractionStates.BROWSE)
			{
				this.dispatchEvent( new LocalFileErrorEvent( "You must browse before trying to load a file!" ) );
			}

			this.interactionState = InteractionStates.LOAD_FILE;

            fileReference.addEventListener(Event.COMPLETE, onCompleteEventHandler);
            fileReference.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorEventHandler);

            fileReference.load();
		}

		/**
		 * Save method can be used to save data into a file.  The method must follow a user interaction.
		 *
		 * @param data	Any type of data
		 * @param fileName	You can set the file name, ie: "test.txt"
		 *
		 */
		public function save(data:*, fileName:String):void
		{
			this.interactionState = InteractionStates.SAVE_FILE;

			fileReference.addEventListener(Event.COMPLETE, onCompleteEventHandler);
			fileReference.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorEventHandler);

			fileReference.addEventListener(Event.SELECT, onFileSelectEventHandler);
			fileReference.addEventListener(Event.CANCEL, onFileCancelEventHandler);

			fileReference.save(data, fileName);
		}

	    //--------------------------------------------------------------------------
	    //
	    //  Event handlers
	    //
	    //--------------------------------------------------------------------------

		/**
		 * Method to handle the two cases where the browse gets calls:
		 *
		 * <ul>
		 * 	<li>Once you browse to load a file</li>
		 * 	<li>Once browse is used when saving a file</li>
		 * </ul>
		 *
		 * <p>The internal <code>InteractionStates</code> is used to know which case we are dealing with.
		 *
		 * @param event
		 *
		 */
		private function onFileSelectEventHandler(event:Event):void
		{
			fileReference.removeEventListener(Event.SELECT, onFileSelectEventHandler);
			fileReference.removeEventListener(Event.CANCEL, onFileCancelEventHandler);

			if (this.interactionState == InteractionStates.BROWSE)
				this.dispatchEvent( new LocalFileEvent( LocalFileEvent.FILE_LOAD_BROWSE ) );
			else
				this.dispatchEvent( new LocalFileEvent( LocalFileEvent.FILE_SAVE_BROWSE ) );
		}

		/**
		 * Method to be called in case the used decide to cancel the option to save or load a file.
		 * The method is related to the browse window.
		 *
		 * @param event
		 *
		 */
		private function onFileCancelEventHandler(event:Event):void
		{
			fileReference.removeEventListener(Event.SELECT, onFileSelectEventHandler);
			fileReference.removeEventListener(Event.CANCEL, onFileCancelEventHandler);

			this.dispatchEvent( new LocalFileEvent( LocalFileEvent.FILE_CANCEL ) );
		}

		/**
		 * In case an IOError gets called when trying to load or save a file this method will
		 * be dispatched.
		 *
		 * @param event
		 *
		 */
		private function onIOErrorEventHandler(event:Event):void
		{
			fileReference.removeEventListener(Event.COMPLETE, onCompleteEventHandler);
			fileReference.removeEventListener(IOErrorEvent.IO_ERROR, onIOErrorEventHandler);

			this.dispatchEvent( new LocalFileErrorEvent( "FileReference: IOErrorEvent.IO_ERROR received: "+event.toString() ) );
		}

		/**
		 * Method to handle the two cases where the complete gets calls:
		 *
		 * <ul>
		 * 	<li>Once complete loading a file</li>
		 * 	<li>Once coomplete saving a file</li>
		 * </ul>
		 *
		 * <p>The internal <code>InteractionStates</code> is used to know which case we are dealing with.
		 *
		 * @param event
		 *
		 */
		private function onCompleteEventHandler(event:Event):void
		{
			fileReference.removeEventListener(Event.COMPLETE, onCompleteEventHandler);
			fileReference.removeEventListener(IOErrorEvent.IO_ERROR, onIOErrorEventHandler);

			if (this.interactionState == InteractionStates.LOAD_FILE)
				this.dispatchEvent( new LocalFileLoadedEvent( fileReference.data ) );
			else
				this.dispatchEvent( new LocalFileEvent( LocalFileEvent.FILE_SAVE_SUCCESSFULLY ) );
		}
	}
}

Example:

  • To load a file: select the load button, which will prompt you to select a text file.
  • To save a file: type in the box and select the save button. You will be prompt to browse and select a place to save the file under.

5

16
Apr

Adobe AIR SQLite manager API for Adobe AIR 1.5 with password encryption

Back in January I posted a blog entry that show how to create a SQLite manger API that allow you to work with an application that has many SQL commands easily. These commands may be initialized from different classes and used in cases where we may want to keep the database connection open and avoid duplicating code. The orginal blog post can be found here.

SQLite Adobe AIR database

Adobe AIR 1.5 closed a security gap. The application is using the exact same database. Essentially any AIR application can read other applications database.

Adobe has updated the framework and now includes the ability to add additional properties to encrypt your SQLite database. Under the new security you can create an encrypted database and when attempting to open the database, your code must provide the database’s encryption key.

In the past few months I received many requests to add encryption to the API. I finally got a chance to update the code and have added the option to add a password to the database. The key is created using the Adobe’s EncryptionKeyGenerator class and is bond to Adobe’s license.

Here’s the method signature:


public function start(dbFullFileName:String, tableName:String, createTableStatement:String, password:String=null):void

In case you don’t need to include encryption to your database, just keep the password as “null”. Other than the start method and a method to generate a key I have updated. Additionally, I added custom events as well meta data for the event constants.


		public function start(dbFullFileName:String, tableName:String, createTableStatement:String, password:String=null):void
		{
			this.dbFullFileName = dbFullFileName;
			this.tableName = tableName;
			this.createDbStatement = createTableStatement;
			var encryptionKey:ByteArray = null;

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

			try
			{
				if (password != null)
				{
					encryptionKey = getEncryptionKey(password, sqlFile);
				}

				connection.open(sqlFile, SQLMode.CREATE, false, 1024, encryptionKey);

			    this.dispatchEvent(new DatabaseSuccessEvent(DatabaseSuccessEvent.DATABASE_CONNECTED_SUCCESSFULLY));
			}
			catch (error:SQLError)
			{
			    var errorMessage:String = "Error message:" + error.message;

			    if (error.details != "")
			    	errorMessage += ", Details:" + error.details;

			    fail(null, errorMessage);
			}
		}

The getEncryptionKey method takes the password and the File class validate the password that is 8-32 char with one letter lowercase letter as well as one upper case and one number. It return a ByteArray which being pass to the connection.open method.


		private function getEncryptionKey(password:String, sqlFile:File):ByteArray
		{
			var keyGen:EncryptionKeyGenerator = new EncryptionKeyGenerator();
			var encryptionKey:ByteArray;

			if (!keyGen.validateStrongPassword(password))
			{
				var errorMessage:String = "The password must be 8-32 char, " + "with one letter lowercase letter, " +
						"one upper case and one number";

				fail(null, errorMessage);

				return null;
			}

			encryptionKey = keyGen.getEncryptionKey(sqlFile, password);	

			return encryptionKey;
		}

To see an example of implementation, take a look at the code below:


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

	<Script>
		<![CDATA[
			import com.elad.framework.sqlite.events.DatabaseSuccessEvent;
			import com.elad.framework.sqlite.events.StatementCompleteEvent;
			import com.elad.framework.sqlite.events.DatabaseFailEvent;
			import mx.collections.ArrayCollection;
			import mx.events.FlexEvent;
			import com.elad.framework.sqlite.SQLiteManager;

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

			protected function initializeHandler(event:FlexEvent):void
			{
				database.addEventListener(DatabaseSuccessEvent.COMMAND_EXEC_SUCCESSFULLY, onSelectResult);
				database.addEventListener(DatabaseFailEvent.COMMAND_EXEC_FAILED, function(event:DatabaseFailEvent):void {
					trace("execution fail: "+event.errorMessage);
				});
				database.addEventListener(DatabaseFailEvent.DATABASE_FAIL, function(event:DatabaseFailEvent):void {
					trace("database fail: "+event.errorMessage);
				});
				database.addEventListener(DatabaseSuccessEvent.CREATING_DATABASE, function(event:DatabaseSuccessEvent):void {
					trace(event.message);
				});
				database.addEventListener(DatabaseSuccessEvent.DATABASE_CONNECTED_SUCCESSFULLY, onConnectedHandler);
				database.addEventListener(DatabaseSuccessEvent.DATABASE_READY, function():void { trace("database ready!"); } );

				// start database
				var password:String = "Pa55word";
				var createTableStatement:String = "CREATE TABLE Users(UserId VARCHAR(150) PRIMARY KEY, UserName VARCHAR(150))";
				database.start("Users.sql3", "Users", createTableStatement, password);
			}

			private function onConnectedHandler(event:DatabaseSuccessEvent):void
			{
				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:StatementCompleteEvent):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>

To download the complete code click here.

21
Feb

mp3tunes-as3-api open source API to use MP3Tunes in Flex/AIR/AS3 projects

mp3tunes is a service to load your audio files and share them between different devices such as: desktop, mobile, game consoles and the Web. I just completed creating a foundation API for AS3.0 so you can start using the API in your Flex/AIR/AS3.0 projects.
You can see the project in google code: http://code.google.com/p/mp3tunes-as3-api/
The SWC and ASDOC can be downloaded from here: http://mp3tunes-as3-api.googlecode.com/files/Archive.zip
And here’s a simple implementation of the API in Flex 4 (Gumbo). The code login into the MP3tunes Music API, retrieve album result, artist data, track data and plays the first audio file in your account.


<FxApplication xmlns="http://ns.adobe.com/mxml/2009"
	creationComplete="creationCompleteHandler(event)">
	<Script>
		<![CDATA[
			import flash.events.Event;
			import flash.media.Sound;
			import flash.net.URLRequest;
			import com.elad.MP3tunes.vo.TrackItemVO;
			import com.elad.MP3tunes.vo.TrackListVO;
			import com.elad.MP3tunes.events.TrackDataEvent;
			import com.elad.MP3tunes.vo.AlbumListVO;
			import com.elad.MP3tunes.events.AlbumDataEvent;
			import com.elad.MP3tunes.vo.ArtistItemVO;
			import com.elad.MP3tunes.vo.ArtistListVO;
			import mx.controls.Alert;
			import com.elad.MP3tunes.events.ArtistsResultEvent;
			import com.elad.MP3tunes.events.MusicEvent;
			import mx.events.FlexEvent;
			import com.elad.MP3tunes.Music;
			private var music:Music;
			protected function creationCompleteHandler(event:FlexEvent):void
			{
				music = new Music("YOUR_DEVELOPER_KEY");
				music.addEventListener(MusicEvent.LOGIN_SUCCESSFULL, onLogin);
				music.addEventListener(MusicEvent.LOGIN_ERROR, function():void { Alert.show("Error login"); } );
				music.login("YOUR_USERNAME", "YOUR_PASSWORD");
			}
			private function onLogin(event:MusicEvent):void
			{
				music.removeEventListener(MusicEvent.LOGIN_SUCCESSFULL, onLogin);
				music.addEventListener(ArtistsResultEvent.ARTIST_RESULT_COMPLETED, onArtistsResult);
				music.addEventListener(ArtistsResultEvent.ARTIST_RESULT_ERROR, function():void { Alert.show("Error getting artist list"); });
				music.getMusicByArtists();
			}
			private function onArtistsResult(event:ArtistsResultEvent):void
			{
				music.removeEventListener(ArtistsResultEvent.ARTIST_RESULT_COMPLETED, onArtistsResult);
				var artistList:ArtistListVO = new ArtistListVO(event.artistList.list.source);
				var item:ArtistItemVO = artistList.getItem(0);
				music.addEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
				music.addEventListener(AlbumDataEvent.ALBUM_DATA_ERROR, function():void { Alert.show("Error getting album list"); });
				music.getAlbumData(item.artistId);
			}
			private function onAlbumDataComplete(event:AlbumDataEvent):void
			{
				music.removeEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
				music.removeEventListener(AlbumDataEvent.ALBUM_DATA_ERROR, onAlbumDataComplete);
				var albumList:AlbumListVO = new AlbumListVO(event.collection.list.source);
				var albumId:String = albumList.getItem(0).albumId;
				music.addEventListener(TrackDataEvent.TRACK_DATA_COMPLETED, onTrackDataComplete);
				music.addEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
				music.getTrackData(albumId);
			}
			private function onTrackDataComplete(event:TrackDataEvent):void
			{
				music.removeEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
				music.removeEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
				var trackList:TrackListVO = new TrackListVO(event.collection.list.source);
				var trackItem:TrackItemVO = trackList.getItem(0);
				playSong(trackItem.downloadURL);
			}
			private function playSong(url:String):void
			{
				var sound:Sound = new Sound();
				var req:URLRequest = new URLRequest(url);
				sound.load(req);
				sound.play();
			}
		]]>
	<Script>
<FxApplication>
</p>
09
Feb

Using Pixel Bender to do heavy lifting calculations, makes Flash Player multi-thread.

In Flash 10 Adobe added a compiler to handle filters, which is possible through Pixel Bender kernels. Pixel Bender kernels is a program that calculate a single pixel at run time.

This is how it works. The Pixel Bender graph is an XML language for combining individual pixel-processing operations called kernels, which are in PBK file format.
We can create the PBK XML file using Pixel Bender toolkit. After our kernel is ready we can then export it as a byte-code called PBJ. PBJ can then be used in the Flash 10 player.
Flex Gumbo Bender shader is the Pixel Bender kernels, which uses a separate thread than Flash Player to calculate a single pixel.

You can use the following math functions for in pixel bender for calculations:

1. sin(x) - Trigonometric sine function.
2. cos(x) - Cosine function.
3. tan(x) – Tangent function.
4. asin(x) - Arcsine (inverse sine) function.
5. acos(x) - inverse cosine (arccosine) function.
6. atan(x) - Arctangent (inverse tangent) function.
7. atan(x, y) - Arctangent (inverse tangent) function.
8. exp(x) - exponential function.
9. log(x) – Logarithm function.
10. pow(x, y) - power of function.
11. reciprocal(x) - multiplicative inverse function.
12. sqrt(x) - square root function.

The challenge with Flash related to single thread processing always caused issues, and while the Flash Player is processing information we cannot run another thread to do other things. We often find the player get “stuck” when doing heavy lifting. Pixel Bender can help in certain cases. I managed to create an example and an API that uses Pixel Bender to calculate information, while a video is playing. In the example here you can run calculations while the video is playing and compare performance with Flash Player doing the same calculation.

First play the video and hit the Pixel Bender calculation and while the video is playing calculations are being made in the background. The results are astonishing, using Flash Player to calculate 5M sine took about 10 seconds (on iMac 2.8GHz and 4GB memory) and the video paused. Using Pixel Bender the video had a light glitch and we received the results back for the 5M calculation after about 10 secounds, without the user noticing.


Pixel Bender for calculation

Create the pbj file in Pixel Bender Toolkit:


<languageVersion : 1.0;>
kernel SinCalculator
<
    namespace : "pixelBender";
    vendor : "Elad Elrom";
    version : 1;
    description : "Sin Calculator";
>
{
    input image1 src;
    output pixel3 result;

    void evaluatePixel()
    {
    	pixel1 value = pixel1(sin(sample(src, outCoord())));
    	result = pixel3(value, 0.0, 0.0);
    }
}

The API handle calculating with Pixel bender by splitting the process into chucks of 5,000 per kernal, since Pixel Bender produce an error message on large calculations.


package com.elad.framework.pixelBender
{
	import com.elad.framework.pixelBender.events.PixelBenderCalcEvent;

	import flash.display.Shader;
	import flash.display.ShaderJob;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.ByteArray;
	import flash.utils.Endian;

	[Event(name="completed", type="com.elad.pixelBender.events.PixelBenderCalcEvent")]

	/**
	 *  The PixelBenderCalculator class is the base class for math calculation with pixel bender
	 */
	public class PixelBenderCalculator extends EventDispatcher
	{
		public var kernalClass:Class;
		public var numberCollection:Array;

		private var shader:Shader;
		private var shaderJob:ShaderJob;
		private var input:ByteArray;
		private var output:ByteArray;
		private var retCollection:Array;
		private var requestsCounter:Number;
		private var numberOfRequest:Number;

		/**
		 * Number of calculations per kernal
		 */
		private const COLLECTION_SIZE:int = 5000;

		/**
		 * Default constructor for the pixel bender calculator
		 *
		 * @param numberCollection	collection contain numbers
		 * @param kernalClass	the kernal class of type pbj to be used to run the calculation
		 *
		 */
		public function PixelBenderCalculator(numberCollection:Array, kernalClass:Class)
		{
			reset();

			this.kernalClass = kernalClass;
			this.numberCollection = numberCollection;

			requestsCounter = numberCollection.length/COLLECTION_SIZE;
		}

		/**
		 * Method to start the calculation
		 *
		 */
		public function start():void
		{
		    output = new ByteArray();
		    output.endian = Endian.LITTLE_ENDIAN;

		    var start:int = numberOfRequest*COLLECTION_SIZE;
		    var end:int = ( (numberOfRequest+1)*COLLECTION_SIZE > numberCollection.length) ? numberCollection.length : ((numberOfRequest+1)*COLLECTION_SIZE);

		    input = convertArrayToByteArray(numberCollection, start, end);
		    createShaderJob();
		    numberOfRequest++;
		}

		/**
		 * Creates a shader class based on the kernal to pass the numbers and start the calculations.
		 *
		 * @see flash.display.ShaderJob
		 * @see flash.display.Shader
		 *
		 */
		private function createShaderJob():void
		{
			var width:int = input.length >> 2;
		    var height:int = 1;

		    shader = new Shader(new kernalClass());
		    shader.data.src.width = width;
		    shader.data.src.height = height;
		    shader.data.src.input = input;			    

		    shaderJob = new ShaderJob(shader, output, width, height);
		    shaderJob.addEventListener(Event.COMPLETE, shaderJobCompleteHandler);
		    shaderJob.start();
		}

		/**
		 * Static method to convert the array given into byte array.
		 *
		 * @param array
		 * @return
		 *
		 */
		private static function convertArrayToByteArray(array:Array, start:int, end:int):ByteArray
		{
			var retVal:ByteArray = new ByteArray();
		    var number:Number;

		    retVal.endian = Endian.LITTLE_ENDIAN;

		    for (var i:int=start; i<end; i++)
		    {
		    	number = Number(array[i]);
		    	retVal.writeFloat(number);
		    }

		    retVal.position = 0;
		    return retVal;
		}

		/**
		 * Convert the a <code>ByteArray</code> into an <code>Array</code>
		 *
		 * @param byteArray
		 * @return an array collection
		 *
		 */
		private function addByteArrayToCollection(byteArray:ByteArray):void
		{
		    var length:int = byteArray.length;
		    var number:Number;

		    for(var i:int=0; i<length; i+=4)
		    {
				number = byteArray.readFloat();
				if(i % 3 == 0)
				{
		    		retCollection.push(number);
		  		}
		    }
		}

		/**
		 * Handler for the shader once job is completed.
		 *
		 * @param event
		 *
		 */
		private function shaderJobCompleteHandler(event:Event):void
		{
		    output.position = 0;
			addByteArrayToCollection(output);

			input = null;
			output = null;

			if (requestsCounter>numberOfRequest)
			{
				start();
			}
			else
			{
				calculationCompleted();
			}

		}

		/**
		 * Method to dispatch an event once calculation is completed and reset this class.
		 *
		 */
		private function calculationCompleted():void
		{
			this.dispatchEvent( new PixelBenderCalcEvent(retCollection) );

			reset();
		}

		/**
		 * Method to clean up this class so we are not using un-needed memory.
		 *
		 */
		public function reset():void
		{
			retCollection = new Array();
			numberCollection = new Array();
			requestsCounter = 0;
			numberOfRequest = 0;
			numberCollection = null;
		}
	}
}

The implimentation is straight forward:


<FxApplication xmlns="http://ns.adobe.com/mxml/2009" xmlns:local="*" viewSourceURL="srcview/index.html">

	<!-- 

	////////////////////////////////////////////////////////////////////////////////
	//
	//  Elad Elrom (elad@elromdesign.com)
	//  Copyright 2009 Elorm LLC,
	//  All Rights Reserved.
	//
	//  NOTICE: Elad Elrom permits you to use, modify, and distribute this file
	//  in accordance with the terms of the license agreement accompanying it.
	//
	////////////////////////////////////////////////////////////////////////////////

 	@author  Elad Elrom

	-->

	<Script>
		<![CDATA[

			import com.elad.framework.pixelBender.PixelBenderCalculator;
			import com.elad.framework.pixelBender.events.PixelBenderCalcEvent;

			import mx.collections.ArrayCollection;
			import mx.collections.IList;
			import mx.events.FlexEvent;

			[Embed(source="SinCalculator.pbj", mimeType="application/octet-stream")]
			private var kernalClass:Class;
			private var pixelBenderCalc:PixelBenderCalculator;

			protected function startCalculatorPixelBender():void
			{
				// create a number collection
				var numberCollection:Array = new Array();

				for (var i:int=0; i<5000000; i++)
				{
					numberCollection.push( i );
				}

				// calculate
				pixelBenderCalc = new PixelBenderCalculator(numberCollection, kernalClass);
				pixelBenderCalc.addEventListener(PixelBenderCalcEvent.COMPLETED, onComplete );

				pixelBenderCalc.start();
			}

			private function startCalculatorFlashPlayer():void
			{
				// create a number collection
				var numberCollection:Array = new Array();

				for (var i:int=0; i<5000000; i++)
				{
					numberCollection.push( Math.sin(i) );
				}

				list.dataProvider = new ArrayCollection(numberCollection);
			}			

			private function onComplete(event:PixelBenderCalcEvent):void
			{
				list.dataProvider = new ArrayCollection(event.numberCollection);
				pixelBenderCalc.removeEventListener(PixelBenderCalcEvent.COMPLETED, onComplete);
			}

		]]>
	</Script>

	<List id="list" width="200" height="531.5"/>
	<Button label="Calculate with Pixel Bender" width="200" height="20" y="545" click="startCalculatorPixelBender()"/>
	<Button label="Calculate with Flash Player" width="200" height="20" y="574" click="startCalculatorFlashPlayer()"/>

	<VideoDisplay id="vid" width="355" height="290"
		source="http://thehq.tv/wp-content/uploads/flv/super-street-fighter-2-hd-round-1-trailer.flv"
		autoPlay="false" x="209" y="-1"/>
	<Button label="Play" click="vid.play();" x="213" y="303"/>

	<local:MemoryDashBoard  x="211" y="343"/>

</FxApplication>
23
Jan

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://elromdesign.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.

13
Jan

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.

12
Jan

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}" />
27
Mar

Increase memory performance on data structures using HashMapCollection API

Hash map is a data structure that can associates keys with values. It provides constant time performance for the basic operations of addItem, removeItemAt, getItemValue and other methods. The main reason for the increase in memory performance is iteration. Unlike ArrayCollection, the HashMapCollection doesn’t need to loop through in order to find a key or a value. The HashMapCollection associates a value with a key.HashMap Collection is a great choice when you need to:

  1. When you need in-memory data structures.
  2. When you need a pair of key-value data structure.
  3. When you need to Increase performance.

Flex and Action Script provides the “IList” interface that is implemented by the Array and ArrayCollection. Using array is a great choice when you need to associate an object with an ID, but if you have scenarios where you want to copy a large database structure to the memory or have a key-values pairs you will have to iteration through the array in order to do a lookup.
The API I developed is called “HashMapCollection” and it is using naming conventions that are very similar to the “ListCollectionView”.

The API is also implementing the change events classes, so you will be able to start using the API right away with much similarity to the “IList” methods.

Let’s take an example. Say we have list of contacts and phone numbers and we need to store them in the memory, the list was given to us through the SQL database and it has about 10,000 contacts.

Action Scriot HashMap Collection example

Using Array or ArrayCollection will force us to loop through the list until we find the name we are looking for and then get the phone number, that process will put a large expense on Flash player and will basically look as if the program is stuck, since action script is a single thread programming language, which means that the program cannot split itself into more than one simultaneously tasks.

The HashMapCollection doesn’t need to loop it’s basically using the following code to find the value:

map[key] = value;

I am giving a list of contacts, as an example, but let me make it clear that the HashMapCollection is flexible to use any type of key-value such as;

• Name-UIComponents
• Name-Object
• Name-Array

Below you can find a use-case example of creating a data structure and modifying the data;


private function map():void {

var map:IMap = new HashMapCollection();
map.addEventListener(CollectionEvent.COLLECTION_CHANGE, handler);

map.addItem(”John”, “212-452-8086″);
map.addItem(”James”, “718-345-3455″);
map.addItem(”Micheal”, “917-782-8822″);
map.addItem(”Ron”, “212-426-8855″);
map.addItem(”Mike”, “212-255-2436″);
map.addItem(”Jenny”, “718-344-2433″);
map.addItem(”Jack”, “917-222-4352″);
map.addItem(”Riki”, “981-222-1122″);
trace(”\nAll items: “+map.toString()+”\n”);

trace(”containsKey Jack? “+map.containsKey(”Jack”));
trace(”containsValue 718-344-2433? “+map.containsValue(”718-344-2433″));
trace(”getItemKey 718-344-2433: “+map.getItemKey(”718-344-2433″));
trace(”getItemValue Jenny: “+map.getItemValue(”Jenny”));

map.removeItemAt(”Riki”);
trace(”Remove Riki.”);
trace(”getItemValue Riki: “+map.getItemValue(”Riki”));
trace(”Comapre: “+map.compare(”Ron”, “212-426-8855″));

map.removeAll();
trace(”\nAll items: “+map.toString()+”\n”);

}

private function handler(event:CollectionEvent):void
{
trace(”Event: “+event.kind);
}

All of this is great, but let me actually prove to you that you gain performance. I created a small Flex application that creates two data structures; ArrayCollection and HashMapCollection. Watch the memory monitor and see how Flash Player actaully stopped while it loops and looks for the key in the ArrayCollection. On the other hand the HashMap doesn’t add any expense on the Flash player.

All source code contained in this API has been published under the MIT licence and is protected as stated therein.

To see the example and download source code (via right click):
http://elromdesign.com/blog/Flex/API/HashMapCollection/

To view the ASDOC of all the API:
http://elromdesign.com/blog/Flex/API/asdoc/

Have fun!

15
Mar

Boost Flex performance with Object Pooling Manager API

Object Pooling Manager API is an open source API that allows you to create a collection of objects and use them at any given time. These usable objects can be anything from a UI component to an XML list. The Object Pooling manager ensures the management of the collections of objects and allows the client to keep using them without costing any additional resources.

Flex is a single thread programming language which means that the program cannot split itself into more than one simultaneously tasks and it causes flex to slow down, especially when the cost of initializing a class is high. Object pooling can be used to manage object caching and boost performance.

A real life example is a book case. You have a case full of books and you decide to read one of the books. While you use the book you may stained a page by a coffee and changed the look of the book. After you are done using the book you return the book to the book case for later usage.

Object pooling works the same way, the object pooling class is singleton to insure you are not pulling more than one reusable object at a time. At any given time you pick a reusable object, the object is being removed from the collection so the client can change it and place it back for future usage.

The Flex API I developed is based on creating a usable collection of objects, the objects can be any component such as Flex Sprite or Canvas, array, class or anything you want it to be.

The object pooling can boost performance; especially in cases where the resources used to initializing a class instance are high, the number of times the class will be usage is often, and the number of instantiations in use at any single time is low.

All source code contained in this API has been published under the MIT licence and is protected as stated therein.

To see an example and download source code:
http://elromdesign.com/blog/Flex/API/ObjectPoolManager/

To view the ASDOC of all the API:
http://elromdesign.com/blog/Flex/API/asdoc/

Object Pooling UML

03
Mar

Custom cursor API is a great solution for creating sets of custom cursors

Custom cursor API is a great solution for creating sets of custom cursors. Each set is a concrete class that you can set. In addition, you can implement custom events such as: mouse down, mouse up and custom busy cursor.

CursorManager class is useful for replacing the cursor with a custom cursor or for creating a busy cursor, but what do you do when you want to create a set of custom cursors? You may also be interested in building few sets of custom cursors to be used in different part of the application and keep track of them in one centralized location.
For example you want to create couple of sets of cursors. One of them will be a custom image, the cursor will change on mouse down and mouse up events and the cursor will have a custom busy image. That set will be used in one component and another set will be used in a different component.

Custom cursor API is based on creating custom sets of cursors for usage in an application. You create a concrete class and set all the images, swf or sprite objects you want to assign to that set and the cursor will change according to how you set it in the concrete class.

All source code contained in this API has been published under the MIT licence and is protected as stated therein.

To see an example and download source code:
http://elromdesign.com/blog/Flex/API/Cursor/

To view the ASDOC of the API:
http://elromdesign.com/blog/Flex/API/asdoc/

Cursor UML Class diagram