26
Jun

CSS Tip: align FormItem label property to the left

Here’s a quick little tip. In case you are trying to align a form label to the left and can’t get it to align, you will find that textAlign left which should work according to the LiveDoc doesn’t work on all Flex SDK versions. Apparently this is a known bug in Adobe’s JIRA system:
http://bugs.adobe.com/jira/browse/SDK-12776.

Adobe SDK team added a new property to the FormItem component called: labelStyleName.

Here’s the solution. Create a style sheet and use text-align: left.


	<fx:Style>
        .formItemLabelLeft {
            text-align: left;
        }
	</fx:Style>

Next, attach the CSS to the labelStyleName property.


	<mx:Form>
		<mx:FormItem label="Label Name:" width="148" labelStyleName="formItemLabelLeft">
		<mx:ComboBox editable="true" width="79"></mx:ComboBox>
	</mx:FormItem>

This was tested in Flex 4 beta SDK.

20
Jun

It’s official: this October FP10 beta will be available on browsers of Android, Symbian, Window Mobile and Pre!

It’s official: Adobe’s CEO, Shantanu Narayen, announced in the Q2 audio press release that a version of Flash Player 10 beta will be available for Smartphone’s browsers starting this October (during Adobe MAX) . The FP10 will be available on Google Android, Nokia Symbian OS, Window Mobile and Palm Web OS Pre browsers. No official word regarding AIR support for Smartphones.

Flash player 10 on the android

I am happy to hear that Adobe are meeting their deadlines and we can start developing mobile applications for Smartphones using Flash Builder 4 this year!

20
Jun

Tutorials for skinning Spark ButtonBar component with an icon image

Adobe Catalyst Beta is out and although it allows manipulating and creating some great skins, programmers are not off the hook just yet. Sometimes you need that extra knowledge on how to skin Spark component to get your component to look and behave exactly as you need. In this tutorial I will show you how to skin the ButtonBar component so it has a label text and an icon.

The final results will look and behave as shown in the figure below:


Spark ButtonBar component

Create VerticalButtonBarSkin skin

First create a skin for the ButtonBar and call it VerticalButtonBarSkin.mxml. VerticalButtonBarSkin includes the states:


    <s:states>
        <s:State name="normal" />
        <s:State name="disabled" />
    </s:states>

Each Flex 4 Beta holds an instance to the HostComponent metadata so the component can identify the skin you are creating.


[HostComponent("spark.components.ButtonBar")]

The skin also includes a declaration tag and you can include the button skin. In our case the button skin will hold the label and icon image as well as code to handle the different states.


    <fx:Declarations>
        <fx:Component id="middleButton" >
            <s:ButtonBarButton skinClass="components.IconButtonSkin"
            	left="0" right="0">
            </s:ButtonBarButton>
        </fx:Component>
    </fx:Declarations>

Lastly, we would need to draw a rectangle around the component so I decided to that here. Notice that I am getting a reference to the hostComponent component, so we can set the color of the button bar component from the implementation. Also notice that we have the data group to hold the data we will be passing to the button skin.


    <!-- layer: fill -->
	<s:Rect width="100%" height="100%" x="0" y="0">
		<s:fill>
			<mx:SolidColor color="{hostComponent.getStyle('color')}" />
		</s:fill>
	</s:Rect> 

    <!---
        @copy spark.components.SkinnableDataContainer#dataGroup
    -->
    <s:DataGroup id="dataGroup" width="100%" height="100%">
        <s:layout>
            <s:ButtonBarHorizontalLayout gap="-1"/>
        </s:layout>
    </s:DataGroup>	

Create IconButtonSkin skin

Next step is to create the following skin: components/IconButtonSkin.mxml. The IconButtonSkin skin will hold the label a rectangle to be placed on the selected item and an icon. I am overridden the updateDisplayList method and picking up the data that will be set in the implementation in order to set the icon image and the label text name. Additionally, I am setting the width of the button to fit the 100% size of the host component so once the user re-size the component the buttons will get re-sized automatically.


    <fx:Script>
    	<![CDATA[

	        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
	        {
	        	super.updateDisplayList((this.hostComponent as Object).outerDocument.hostComponent.width, unscaledHeight);

	        	var image:Class = Class((this as Object).hostComponent.data.imageIcon);
	        	var label:String = (this as Object).hostComponent.data.label;

	        	labelElement.text = label;
	        	icon.source = image;

	        	this.width = this.hostComponent.width;
	        	this.minWidth = this.hostComponent.width;
	        }    		

    	]]>
    </fx:Script>

We also need to set the states we will be using. In our case we will be using some more advanced states other than the traditional: up, down and over states, since we need to customize the component to fit our exact need.


    <!-- states -->
    <s:states>
        <s:State name="up" />
        <s:State name="over" stateGroups="overStates" />
        <s:State name="down" stateGroups="downStates" />
        <s:State name="disabled" stateGroups="disabledStates" />
        <s:State name="upAndSelected" stateGroups="selectedStates, selectedUpStates" />
        <s:State name="overAndSelected" stateGroups="overStates, selectedStates" />
        <s:State name="downAndSelected" stateGroups="downStates, selectedStates" />
        <s:State name="disabledAndSelected" stateGroups="selectedUpStates, disabledStates, selectedStates" />
    </s:states>

The code below create the square we will be drawing using the FP10 drawing API utilizing the FXG code. GradientEntry sets the color to match our button bar component and include the color gradient once we select and item.


    <!-- layer: fill -->
	<s:Rect left="1" right="1" top="1" bottom="1" width="100%">
		<s:fill>
			<s:LinearGradient x="114" y="18.5" scaleX="15.5724" rotation="-90">

				<s:GradientEntry color="{hostComponent.getStyle('color')}" ratio="0"
		                         color.selectedUpStates="#2a2a2a"
		                         color.overAndSelected="#2a2a2a"/>

				<s:GradientEntry color="{hostComponent.getStyle('color')}" ratio="1"
		                         color.selectedUpStates = "#444444"
		                         color.overAndSelected = "#444444"/>

			</s:LinearGradient>
		</s:fill>
	</s:Rect> 

Lastly, we define the SimpleText and BitmapImage components:


    <s:SimpleText id="labelElement"
		color.selectedUpStates="{hostComponent.getStyle('color')}"
		color.overAndSelected="{hostComponent.getStyle('color')}"
		textAlign="left"
		verticalAlign="left"
		color="0x000000"
		truncation="1"
		horizontalCenter="0" verticalCenter="1"
		left="50" right="10">
    </s:SimpleText>

    <s:BitmapImage left="5" id="icon" width="20" height="20" resizeMode="scale" />

Implementation

Now that we created all the skins all we have to do is to implement. Take a look at the implementation below which includes the ButtonBar tag pointing to our VerticalButtonBarSkin skin and a click event once the user selected an item. Notice that we define the data we will be passing to the skin, which includes the label text and the icon we will be using to create the button bar image icon.


	<s:Panel title="Panel" width="400">

	    <s:ButtonBar requiresSelection="true"
	    	width="100%" color="#ece7e7"
			skinClass="components.VerticalButtonBarSkin"
			click="buttonClickHandler(event)">

	        <s:layout>
	             <s:VerticalLayout />
	        </s:layout>

	        <s:dataProvider>
	            <s:ArrayCollection>
	            	<s:source>
	            		<fx:Object label="Download files" imageIcon="{icon1}"  />
	            		<fx:Object label="Home" imageIcon="{icon2}"  />
	            		<fx:Object label="My Computer" imageIcon="{icon3}"  />
	            		<fx:Object label="Documents" imageIcon="{icon4}"  />
	            		<fx:Object label="Download images" imageIcon="{icon1}"  />
	            	</s:source>
	            </s:ArrayCollection>
	        </s:dataProvider>

	    </s:ButtonBar>

    </s:Panel>	

The event handler for the user click event is listed below. It take the selectedIndex property from the component and uses the data provider to find the item.


protected function buttonClickHandler(event:MouseEvent):void
{
	var index:int = event.currentTarget.selectedIndex;
	var item:Object = (event.currentTarget.dataProvider as ArrayCollection).getItemAt(index);

	trace(item.label+" label was clicked");
}

Where to go from here?

To read more about skinning Flex Spark components follow this link:
http://livedocs.adobe.com/flex/gumbo/html/WSA95C9644-B650-4783-B5C0-D2C7F95A23E3.html

02
Jun

Capture / Detect Flash Player version with simple utility class

Flash Player 10 is out and although it’s around 75% penetration wise, which is amazing considering the amount of time it’s out, there are many cases where we would like to check the version of the FP and deploy different applications based on the results. For instance, you may create an application using FP 10 using Catalyst but you still support FP 9 users. Or you can create the main application as FP 9 and create two modules based on the FP player or sub version.

The idea is loading an application with lower FP, for instance FP 9 and than detect the FP version and load the correct application based on the version. Here are a simple utility class and VO to detect the FP version and sub version.


package utils
{
	import vo.FlashPlayerVersionVO;
	import flash.system.Capabilities;

	public class DetectHelper
	{
		public static function detectFlashPlayerVersion():FlashPlayerVersionVO
		{
			var flashPlayerVersion:FlashPlayerVersionVO;
			var playerVersion:String = Capabilities.version;
			var pattern:RegExp = /^(\w*) (\d*),(\d*),(\d*),(\d*)$/;
			var result:Object = pattern.exec(playerVersion);

		    var input:String = "";
		    var platform:String = "";
		    var majorVersion:Number = 0;
		    var minorVersion:Number = 0;
		    var buildNumber:Number = 0;
		    var internalBuildNumber:Number = 0;			

			if (result != null)
			{
			    input = String(result.input);
			    platform = String(result[1]);
			    majorVersion = Number(result[2]);
			    minorVersion = Number(result[3]);
			    buildNumber = Number(result[4]);
			    internalBuildNumber = Number(result[5]);
			}
			else
			{
			    trace("could'nt match RegExp, detect flash version didn't work, using default values");
			}

			flashPlayerVersion = new FlashPlayerVersionVO(input, platform, majorVersion, buildNumber, internalBuildNumber);
			return flashPlayerVersion;
		}
	}
}

package vo
{
	public class FlashPlayerVersionVO
	{
		public function FlashPlayerVersionVO(input:String = "", platform:String = "", majorVersion:Number = 0, buildNumber:Number = 0, internalBuildNumber:Number = 0)
		{
			this.input = input;
			this.platform = platform;
			this.majorVersion = majorVersion;
			this.buildNumber = buildNumber;
			this.internalBuildNumber = internalBuildNumber;
		}

		public var input:String;
		public var platform:String;
		public var majorVersion:Number;
		public var buildNumber:Number;
		public var internalBuildNumber:Number;

	}
}
01
Jun

Flash Catalyst and Flash Builder 4 beta are out and will impact our workflow

I am just back from FlashCamp in San Francisco from one of the most exciting and talked about events of the year. Adobe’s release of the beta version of Flash Catalyst and Flash Builder 4 (http://www.labs.adobe.com) will help push Adobe’s products to the boundaries and will pave the road for creating mobile applications with the Flash Platform.
Let’s talk about some of the changes that these new products will bring and their impact on our workflow.

FlashCamp SanFran

FlashCamp SanFran

New development cycle with Flash Builder 4 and Catalyst

The new development cycle allows us to shift to a design centric cycle, where designers and developers can work in parallel. Here’s the breakdown of the new responsibilities

  • Developer responsibilities: application logic, processing, data, services and testing.
  • Designer responsibilities: frames, states and visual appearance (pixel) of the application.

With that said, as a developer I wouldn’t wait until a designer will learn Flash Catalyst since developers are feeling the pain every day when they have to chop images and convert them into MXML component, so although eventually these responsibilities will likely shift to designers, you can start enjoying Catalyst to easily convert your PSD, AI files into MXML component.

Shift responsibilities

By utilizing Flash Catalyst, the experience, such as animation between different states, becomes the responsibility of the designer. It allows the designer to control the Flash interaction, choreography of the application and the appearance without even visiting FB. Once the work is completed, the FXG file format can be provided to a developer, which can integrate it with business logic, data and services. As the project continues, designers can modify the appearance and interaction without disrupting the workflow.

FXG and FXP

Flash XML Graphic (FXG) is a declarative format based on MXML and similar to SVG. FXG is supported by Adobe Creative Suite CS4: Photoshop, Illustrator and Firework.
The round-trip workflow between Adobe Creative Suite and Flash Catalyst is meant to be seamless. Flash Catalyst interface was built primarily for the designer and the IDE is similar in look and feel to Flash Professional and Photoshop. It allows designers to quickly jump in and start using the tool in a familiar environment, all without writing any code. As a developer you can use Flash Catalyst and toggle between code view to design view.

Why do we even need a new development cycle?

Users expectations increase since businesses demand more out of our Flash applications. These expectations cause applications to be larger, more complex and include custom components and many services. Also, as Flex and AIR get deployed on more and more devices, we need to be able to easily create different presentation for the same logic so we can easily create an application that gets deployed on different devices.
The challenge is that the current development cycle is developer centric and as a designer you are only responsible for creating the pixel discipline, and you are not involved in any of the Flash experience. It becomes challenging to create Flash applications, since as a developer you need to be able to juggle between all the application disciplines such as converting Photoshop .psd files into MXML, handling data, testing, services, coding and many others responsibilities.

If you ever tried to extend or skin a Flex component you know that is not an easy task and it takes a tremendous LOE to control every aspect of the component. These challenges led developers to create their own custom components and the creation of tools and APIs such as Degrafa (www.degrafa.org/).

The process with Degrafa is not seamless and requires tweaking. Flash Builder 4 addresses these issues and allows full control over graphic components. To achieve that, Adobe had to re-work the component architecture. Under the new Flex 4 architecture, components are loosely coupled and split into three parts: model, visual design and behavior.

The programmer is responsible for the model and the designer can create the visual design as well as the behavior of the component. Using CS4 tools and Flash Catalyst, designers can generate a new file format called FXG, which includes the components skins as well as the behavior of the different components and the whole application.

Adobe re-worked the component architecture

A major effort was put to re-work the component architecture, separate the view, model and logic to allow a better manipulation and the ability to move to design centric development.

GraphicElement is a new core class, which was added in SDK 4 and supports text, images and shapes, as well as advanced layout. It supports UIComponent style layout and invalidation capability, however, it is not a DisplayObject and GraphicElements are able to share the same DisplayObject.

fx:Component is another Flex 4 core class and it is used as the base class for all the skinnable components. The FxComponent class defines the base class for skinnable components.

Skin class - The skins used by a FxComponent class are children classes of the Skin class. You can set the skin class using the skinClass attribute.

The Skin class defines the base class for all skins used by skinnable components. Skin class extends Group (which is the base container and allows nesting UI childrens which can point to other skins). The SkinnableComponent class defines the base class for skinnable components. In the Skin class, you define the component you are skinning using HostComponent metadata.

Adobe Catalyst generates a declarative xml code called FXG and uses the new design model to help support the workflow and features of Catalyst by extending the UIComponent with a new namespace of Fx-prefixed base components (namespace is likely to be changed by the time Flex 4 is released).

The idea is that these base components will allow developers the ability to create and extend core components and focus on the functionality, with as little influence and dependency on the layout and display of the components as possible. The work of re-skining the Flex components and re-designing the layout, behavior and new state architecture are all the responsibility of the designer. Ideally we want to be able to make changes and round trip with Flex without affecting the underlying code that controls the functionality of the component.

Flash catalyst project was saved as FXP file format and Flash Builder 4 is capable of opening a FXP project.

Flash Catalyst saves the projects as FXP file format, which stands for Flash XML Project (FXP). Flash Builder 4 allows you to import and export .FXP file format.

Creating multi-screen applications

Mobile devices are going under an exciting revolution today due to an increase in network subscribers’ growths. Over a billion people are connected to the Internet. Out of those people, 600 million worldwide are connected to a 3G network with fast Internet connection of 144Kbps., about 1.4 billion phones supporting FL.

There is also an increase in UI and hardware innovations such as touch screen experience, as well as consumer expectations of their mobile devices. All of these changes and the dream of an Adobe Open Project materialized into a reality with the announcement of Flash 10 and AIR available on many mobile devices that opens up new possibilities to mobile developers.

Mobile devices consist of many devices such as: Smartphones, Mobile Internet Device (MID), Ultra-Mobile PC (UMPC), ARM based devices, game consoles and others.
The MID is a multi-media mobile device capable of accessing wireless Internet. MID is bigger than smartphones but smaller than UMPC. MID is capable of running Linux OS and with dual-core processors. It can even run Windows XP or Vista. Adobe released Flash 10 on Linux so many MID devices are capable of running Flex and AIR applications.

First let’s take a look at the different mobile devices:

  • Mobile Phone is the most used electronic device worldwide. A mobile phone started as mobile device capable of making phone calls, however, in recent years mobile phones have more and more features than just making calls and they turned into what is called smartphone, which includes more capable CPU, larger memory to support multimedia and other programs that we usually use in our desktop or on the Web. Up until now, mobile phones only supported Flash Lite (FL) and the development was limited to create Flash application with AS 1.0 or 2.0, which was a major draw back to many Flex/AIR/AS developers that didn’t want to create applications using legacy code. However, today there are already Smartphones that support Flash 9 on their web browser and this year we are going to see more and more Smartphones support Flash 10/AIR.
  • Ultra-Mobile PC is a small sized computer with wireless access capabilities. It can run Linux, Windows XP or Windows Vista Operation system (OS). Mobile Internet Device is a multimedia handheld computer capable of connecting to the Internet wirelessly. It represents the middle between UMPC and Mobile phones. OS can be Linux, or Windows XP/Vista depending on CPU. Although these devices are not very popular, they are a good platform to test Flash 10/AIR applications on a mobile device.
  • ARM based devices are devices that have the ARM architecture and can be found in many devices such as mobile phones, set-top boxes, MIDs, TVs, automotive platforms, MP3 players and many other mobile and computing devices. Many of these devices will support Flash 10 and AIR.

As technology progresses and our mobile devices have better CPU and more memory, one challenge is unsolved: fragmentation. Fragmentation means that each screen device such as mobile phone, desktop, Web or TV are based on a different platform or OS and may have a different browser specification and/or a different network. It has become a real challenge and costly to create a multi-screen application that can work on many platforms.


Passive Multi-View Design Pattern

To achieve multi screen using FP 10, I recommend using a design pattern that is a mix of the Passive View design pattern as well as the Factory design pattern.
Using the passive multi-view design pattern allows you to create different views. All views are using the same base high-level classes, and you can combine them easily with other tools such as: Degrafa, Flex FXG, Flex 3 or AS3.0 with CSS.

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

11
May

I am humbled, proud and thankful for being featured in this month’s Developer Spotlight

What is the Developer Spotlight anyway?

The Developer Spotlight program is where Adobe periodically feature one developer out of the entire community, who is pushing the limits of Adobe’s technologies, or contributing to the larger Adobe developer community in a significant way.

I was picked for my usage of Flex and Flash Catalyst to create applications for the web, desktop, and mobile devices. See the post here.

In our field we hardly get recognized, it’s nice to be in the limelight and get noticed. I am humbled, proud and thankful for being featured in this month’s Developer Spotlight on Adobe Developer Connection. Thank you Adobe!

09
May

Skinning FxVideoDisplay Flash 10 component

In Flash 10 Adobe have added a component for Flex Gumbo called FxVideoDisplay class, which is an addition to the VideoDisplay component in Flex and the main features is that the new video player component supports skinning, progressive download, multi-bitrate, and streaming of video right out of the box.

The MXML code to create the FxVideoDisplay component looks as follow:


<FxVideoDisplay id="videoDisplay" />

To create a video player you create the component and set the video file like so:


<FxApplication xmlns="http://ns.adobe.com/mxml/2009" minWidth="1024" minHeight="768">

    <FxVideoDisplay id="videoDisplay" source="videofile_H264.mp4" />

</FxApplication>

Once you compile and run the application, you can see the result, below;

FxVideoDisplay deployed in the browser screen shot

As you can see the component includes toolbar for common operations such as pause, stop, mute, fullscreen, volume and seek. The component can be skinned easily using VideoElement. Let’s take a look how you can skin the component:

Create a new Flex project and call it VideoPlayerGumboExample. In the entry point, VideoPlayerGumboExample.mxml paste the following code:


<FxApplication xmlns="http://ns.adobe.com/mxml/2009" minWidth="1024" minHeight="768">
     <Script>
          <![CDATA[
               import components.VideoSkin;
          ]]>
     </Script>

     <FxVideoDisplay skinClass="{components.VideoSkin}"/>

</FxApplication>

Notice that we included the FxVideoDisplay component and we set the skinClass property to point to a class we will create. The VideoSkin class we will be creating includes the VideoElement and the toolbar.


<Skin xmlns="http://ns.adobe.com/mxml/2009" width="400" height="520">

    <Metadata>
         [HostComponent("mx.components.FxVideoDisplay")]
    </Metadata> 

     <VideoElement  id="videoElement" autoPlay="true"
          source="assets/videofile_H264.mp4">
     </VideoElement>

     <HBox x="5" y="485">
          <FxButton id="playButton" skinClass="{PlayButton}" />
          <FxButton id="stopButton" skinClass="{StopButton}" />
     </HBox>

</Skin>

Let’s examine the code. The component we are using is a Skin component, which is common when we create skins for Flex 4 components.


<Skin xmlns="http://ns.adobe.com/mxml/2009" width="400" height="520">

The HostComponent tag point to the component we are skining.

    <Metadata>
         [HostComponent("mx.components.FxVideoDisplay")]
    </Metadata> 

The VideoElement is necessary to skin the FxVideoDisplay component and we point to the video file we are using where the location of the folder is relative to the FxVideoDisplay component that defines the skin, not to the skin.


     <VideoElement  id="videoElement" autoPlay="true"
          source="assets/videofile_H264.mp4">
     </VideoElement>

Next we can define all the sub classes that are being used by the FxVideoDisplay component such as the pause, play, seek and volume as well as the different video states. The way the sub components are mapped is by the id name, each sub component needs to correspond to the expected id. Additionally, notice that each button point to another skin class such as the playButton point to PlayButton skin.


     <HBox x="5" y="485">
          <FxButton id="playButton" skinClass="{PlayButton}" />
          <FxButton id="stopButton" skinClass="{StopButton}" />
     </HBox>

</Skin>

Lastly, take a look at the playButton skin for the FxButton. We can define all the different states and the pixel (graphic). For graphic we used a simple box with border and a text field that holds the text play.


<Skin xmlns="http://ns.adobe.com/mxml/2009"
     xmlns:d="http://ns.adobe.com/fxg/2008/dt"
     xmlns:ai="http://ns.adobe.com/ai/2008">

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

     <Metadata>[HostComponent("mx.components.FxButton")]</Metadata>
     <Rect y="1.5" height="27" width="75" x="1.5" d:userLabel="playButton">
          <fill>
               <SolidColor color="0x3d3d3d"/>
          </fill>
          <stroke>
               <SolidColorStroke color="0xa5a7aa"
                    caps="none" joints="miter" miterLimit="4" weight="3"/>
          </stroke>
     </Rect>
     <TextGraphic width="44" height="14"
          fontFamily="Myriad Pro" lineHeight="120%"
          color="0xffffff" whiteSpaceCollapse="preserve"
          kerning="on" x="20" y="10" ai:knockout="0"
          d:userLabel="Play" id="labelElement" text="Play">
          </TextGraphic>
</Skin>

Create the same skin for the stopButton and name the class StopButton. The only difference is going to be the text field text property will be set to Stop.
Once complete you can compile and run and see the result, below:

FxVideoDisplay skinned

To view and download the complete example, click here.

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.

28
Mar

Finally… Flex Builder shortcut cheat sheet for Mac OS

I was looking for a quick reference or cheat sheet card to print that includes all the shortcuts that are available for Mac OS and although I found many for PC there is nothing for Mac, so I created one myself.

All the available shortcuts are split into different categories and include the most useful shortcuts that exist. The complete list of the entire shortcut list is available using the key assist (Command+Shift+L).

You can download a one page PDF version from here and print to place it next to your desk or keep this page on your bookmark for future reference. In case I forget any important ones please feel free to drop a line.

Category

Type

Shortcut

Comments

Add Comments

Shift+Command+D

 

Comment Selected Code

Command+/

History

Backward History

Command+[

 

Forward History

Command+]

File

Build All

Command+B

 

Save

Command + S

 

Close

Command+W

 

Close All

Command+Shift+W

 

New Menu

Option + Command + N

 

New File

Command + N

 

Compile and Run

Shift + Command + F11

Edit text

Copy

Command+C

 

Copy Selected Lines Above

Option+Command+ Up Arrow

 

Copy Selected Lines Below

Option+Command+ Down Arrow

 

Paste

Command + V

 

Cut

Command+X

 

Delete

Delete

 

Delete Line

Command+Delete

 

Delete Next Word

Option+Small Delete

 

Delete Previous Word

Option+Large Delete

 

Delete to the end of the line

Shift+Command+Small delete

 

Redo

Command + Y

 

Insert line above

Command + Shift + Enter

 

Move selected lines Up

Option + Up Arrow

 

Move selected lines down

Option + Down Arrow

 

Duplicate lines

Command + Option + Arrow down

 

Optimize imports

Command + Shift + O

 

Select all

Command + A

 

Select Line end

Shift + Command + right arrow

 

Select Next word

Shift + Command + right arrow

 

Select Previous word

Shift + Command + left arrow

 

Select Text End

Shift + Page down

 

Select Text Start

Shift + Page up

 

Select everything to the left

Command + Shift + left arrow

 

Select everything to the right

Command + Shift + right arrow

 

Last edit location

Control + Q

 

To Lower case

Shift + Command + Y

 

To Upper case

Shift + Command + X

 

Toggle block comment

Shift + Command + C

 

Toggle breakpoint

Shift + Command + B

 

Word completion

Control + Space

 

Undo

Control + Z

Search/Find

Find all declaration – select expression first.

Command + G

 

Find all References in Workspace – select expression first.

Shift + Command + G

 

Find next – select expression first

Command + K

 

Find Previous

Shift + Command + K

 

Find Text in Workspace

Option + Command + G

 

Find and Replace

Command + F

 

File Search

Command + Shift + F

 

Goto definition – Step into

F3 or Command + Enter

 

Go to Line number

Command + L

 

Go to matching bracket

Command + Shift + P

 

Line end

Command + Right arrow

 

Line start

Command + Left arrow

 

Open resource

Command + Shift + R

 

Open class types window

Command + Shift + T

 

Previous/Next word

Option + right arrow/left arrow

Views/Editors

Maximize view

Command + M

 

Next Editor

Command + F6

 

Previous Editor

Shift + Command + F6

 

Next Prospective

Command + F8

 

Previous Prospective

Shift + Command + F8

 

Previous View

Shift + Command + F7

 

Next View

Command + F7

 

Dynamic Help

Command + Shift + ?

 

Properties

Option + Enter

 

Quick access

Command + 3

 

Quick Switch Editor

Command + E

 

Refresh

F5

 

Show Key assist

Command + Shift + L

 

Ruler Context Menu

Command + F10

 

Show system menu

Shift + Command + F10

 

Switch to Editor

Shift + Command + E