Flex

Issue with Flash Media Server 4 and Client.id value

In Flash Media Server 4, the way the Server-Side Actionscript Client.id property is represented has changed...but if you're using the Admin API, it is still expecting the old version.

Previously, Flash clients connected to Flash Media Server would be given a server-generated id which looked something like "oAADAAAA" (this is the value you would see for the Client ID in the admin console). In FMS 4, the Client.id was changed to a 64-bit int in the form of "4702111234525315439". It's the same value represented two different ways. However, the Admin API was not updated to reflect the change and still expects the values in their old form.

To convert the new Client.id value back to the old one, I used the LongInt class found here:

Actionscript:
  1. var clientIDString:String = "4702111234525315439"; //should equal oAADAAAA
  2.  
  3. var longInt:LongInt = new LongInt(clientIDString);
  4. var bytes:ByteArray = new ByteArray();
  5. longInt.writeToByteArray(bytes);
  6. bytes.position = 0;
  7. trace(bytes.toString())// prints AAAADAAo, so just need to reverse it

I'm definitely not in my comfort zone here, so if anyone knows a simpler way to to do this please let me know.

Append html text to a spark TextArea component

Ran into an issue today trying to figure out how to append additional html formatted text to a Spark TextArea component. As stated here the insertText() and appendText() methods always add the new text as a literal string, meaning the html content doesn't get parsed.
In Spark, when you're dealing with the text components and doing any sort of formatting, you're going to be dealing with TLF, the Text Layout Framework. It's a complete pain in the ass and makes me sad to see how horrific flash/flex development has become. Anyway, with TLF you're generally going to be dealing with TextFlow objects when getting/setting text on a text component. On top of that, you'll be importing and exporting the text by running things through helper classes such as TextFlowUtil and TextConverter.

Here's how I managed to "append" html text to a TextArea, in quotes because it actually exports the old text, merges the new text, and reassigns the whole things as the new content:

Actionscript:
  1. var oldText:String = TextConverter.export(messageOutput.textFlow, TextConverter.TEXT_FIELD_HTML_FORMAT, ConversionType.STRING_TYPE) as String;
  2. messageOutput.textFlow = TextConverter.importToFlow(oldText + message, TextConverter.TEXT_FIELD_HTML_FORMAT);

Definitely not as cool as textArea.htmlText.

Sending an ArrayCollection – Flex to amfphp

Here are my tweaks to amfphp for sending an ArrayCollection from Flex to amfphp. May not be the cleanest or most proper but it works. From what I understand, there is an ArrayCollection class included in Zend_AMF, but I haven't looked into that.

I've been sending ArrayCollections from php to Flex for a while, it's pretty simple and info for doing that can be found at Wade Arnold's blog here. The issue going the other direction is that Flex only sends the AC's underlying source array when serializing it for amf. So when amfphp unpacks the data, you're dealing with a normal array.

Obviously, the first thing you'll need is your ArrayCollection class to use (I use the version that extends ArrayObject at the link above), and make sure it is in your include path.

The first tweak to amfphp is to the method AMFBaseDeserializer->mapClass(). In that method, right after the checks looking for CommandMessage and RemotingMessage types, I added a similar check for ArrayCollection:

PHP:
  1. if($typeIdentifier == "flex.messaging.io.ArrayCollection")
  2. {
  3.         return new ArrayCollection();
  4. }

Next change is to the AMFDeserializer->readAmf3Object() method. Near the middle of the method, around line 570, there is a check for ArrayCollection, and if found it will call the readAmf3Data() method. I changed it to pass in a value of "true" to the method, you'll see why in a sec:

PHP:
  1. if( $classDefinition['externalizable'] )
  2. {
  3.     if($type == 'flex.messaging.io.ArrayCollection')
  4.     {
  5.         $obj = $this->readAmf3Data(true);
  6.     }

Now just two small tweaks to the readAmf3Data() method. I changed the method signature to add an $isArrayCollection parameter, which just gets forwarded on when calling readAmf3Array. Here's the whole method:

PHP:
  1. function readAmf3Data($isArrayCollection = false)
  2. {
  3.     $type = $this->readByte();
  4.    
  5.     switch($type)
  6.     {
  7.         case 0x00 : return null; //undefined
  8.         case 0x01 : return null; //null
  9.         case 0x02 : return false; //boolean false
  10.         case 0x03 : return true//boolean true
  11.         case 0x04 : return $this->readAmf3Int();
  12.         case 0x05 : return $this->readDouble();
  13.         case 0x06 : return $this->readAmf3String();
  14.         case 0x07 : return $this->readAmf3XmlString();
  15.         case 0x08 : return $this->readAmf3Date();
  16.         case 0x09 : return $this->readAmf3Array($isArrayCollection);
  17.         case 0x0A : return $this->readAmf3Object();
  18.         case 0x0B : return $this->readAmf3XmlString();
  19.         case 0x0C : return $this->readAmf3ByteArray();
  20.         default: trigger_error("undefined Amf3 type encountered: " . $type, E_USER_ERROR);
  21.     }
  22. }

Now just another two small tweaks to the readAmf3Array method. As with the readAmf3Data() method, we add the $isArrayCollection parameter. Within the body of the method, I just check if that param is true when creating the $hashtable variable, and if it is, use an ArrayCollection instead of a regular array:

PHP:
  1. function readAmf3Array($isArrayCollection = false)
  2. {
  3.     $handle = $this->readAmf3Int();
  4.     $inline = (($handle & 1)  != 0 ); $handle = $handle>> 1;
  5.  
  6.     if( $inline )
  7.     {
  8.         $hashtable = $isArrayCollection ? new ArrayCollection() : array();
  9.        
  10.         $this->storedObjects[] = & $hashtable;
  11.         $key = $this->readAmf3String();
  12.         while( $key != "" )
  13.         {
  14.             $value = $this->readAmf3Data();
  15.             $hashtable[$key] = $value;
  16.             $key = $this->readAmf3String();
  17.         }
  18.  
  19.         for($i = 0; $i <$handle; $i++)
  20.         {
  21.             //Grab the type for each element.
  22.             $value = $this->readAmf3Data();
  23.             $hashtable[$i] = $value;
  24.         }
  25.         return $hashtable;
  26.     }
  27.     else
  28.     {
  29.         return $this->storedObjects[$handle];
  30.     }
  31. }

That's it. Now I can send the an AC to the server, serialize it, unserialize it and send it back to Flex with no issues. The one thing that may an issue is if one of the items within an AC is also another AC...haven't checked out if that works yet.

Frameworks 2.0

Subititled: "The Rise of 'Burger King' frameworks....'have it your way'".

I am loving the new trend I'm seeing in a lot of the frameworks (both client- and server-side) I've been checking out lately. What I'm seeing is a move away from the monolithic, "do it my way or suffer trying to work around it" approach, towards more architectures that provide a more generalized, "less is more" approach, even to the point that some of the functionality and/or features of the "do-it-all" frameworks are being broken out into smaller, specialized bits that allow the developer to pick and choose between whichever approach best suits their style.

To me, the main benefit of the new trend is readily apparent: just as different individuals would write the same story different ways, the same goes for writing code. For one framework to dictate too much influence over too many aspects of an application, or even worse to make it difficult to extend or adapt to suit your needs, provides too much lock-in and and can hinder more than it's trying to help.

On the flash/flex side, frameworks like RobotLegs , Cairngorm 3, Gaia, and even libraries like CasaLib provide example of this (ok, Gaia probably goes a little further as far as initial structure, but once you start developing it stays out of your way). On the backend, for php you have things such as Konstrukt, a "URI-to-controller-mapping" (i.e., REST) framework which handles "routing based on logic rather than rules", and the Outlet and phpDataMapper ORM frameworks.

One thing about the above mentioned frameworks is that most, if not all, can be used in conjunction with each other (like so). If you don't like the way one part does it's job, you can more than likey swap out that part with a similar one without affecting the other parts of an application, provided you we good with encapsulation and keeping things pretty cleanly separated. This is not an insignificant side effect of the "less-is-more" approach, it's the central theme. It's having it your way.

Senior Flash/Flex Developer available

I've recently completed a long-term project, so I'm currently looking for some remote (off-site) freelancing projects. I am an advanced Flex/Actionscript 3 developer with about 6 years of heavy actionscript development.

Key Skills:
Flash – Actionscript 3.0, Flex, AMFPHP, Flash Media Server, AIR, Flex Builder/Eclipse, ElectroServer
Frameworks – PureMVC , Mate, Cairngorm, Gaia
Server Side – PHP, MySQL, Propel ORM, ImageMagick, Amazon EC2/S3/SQS
Other – SVN, CVS, XML, Javascript
Software design – OO design, Design Patterns

My resume can be found at http://www.f1fd.com/resume, and you can email me at f1info@f1fd.com.

Finally, AS 3 Autocompletion in TextMate

Found this at http://blog.simongregory.com/09/as3-autocompletion-in-textmate/ . I'll admit it's not to the level of Flex Builder, but it's great to see actual autocomplete working in TextMate for AS 3. Once installed, hit option-esc to bring up the autocomplete suggestion list. If you choose a function it will give you the the expected parameters:

Actionscript:
  1. stage.addEventListener(type:String,listener:Function,useCapture:Boolean=false,priority:int=0,useWeakReference:Boolean=false);

This is the flaky part, because the idea is that you can tab through the parameters to enter your values. But say if you wanted to listen for MouseEvent.CLICK, and don't have the MouseEvent class imported alread. You can easily import it by starting to type M.. and hitting apple-shift-I to import it (then again using autocomplete to choose 'CLICK'), but then you can't tab over to the rest of the parameters. I'm going to dig around to see if there's a workaround.

Other than that, seems to work fine with custom classes, i can autocomplete methods and function just fine. Mix this the other tab triggers in the bundle and you really start to feel a big speed increase when busting out the code.

By the way, apparently there is some confusion because there is an older AS3 bundle. I wound up removing the one i already had installed, and using the GetBundles bundle (a bundle that acts like a package manager) to install the newer version, then the autocomplete worked properly.

How I (sort of) modified Flex’s SystemManager.initialize() method

I'm not sure if this method is way off base or not, but it worked. In this case, I wanted to specify a different class to be returned when calling ResourceManager.getInstance(). Currently, flex apps are pretty much hardcoded to return an instance of the mx.resources.ResourceManagerImpl class.

How does it that method know to return an instance of that class? Well, the Flex framework uses a class called Singleton, which keeps a registry that maps interface names to classes that are supposed to be singletons in the app. So, ResourceManager.getInstance() calls Singleton.getInstance() to ask if for the IResourceManager class to use in the app:

Actionscript:
  1. instance = IResourceManager(Singleton.getInstance("mx.resources::IResourceManager"));

The problem I ran into is that for IResourceManager, and a few other interfaces, the class to return is registered in the SystemManager.initialize() method, which runs way before any of the actual flex application code is available.

However, there is one custom class which can be accessed during the SystemManager.intialize() method: the custom preloader class. The preloader class is accessed a few lines above where Singleton.registerClass() is called to register the IResourceManager singleton.

So, I made a subclass of DownloadProgressBar and set that to be the preloader class. That class uses a static initializer to register my own class to use for ResourceManager.getInstance():

Actionscript:
  1. private static var classConstructed:Boolean = classConstruct();
  2.    
  3.         private static function classConstruct():Boolean {
  4.             var rm:ResourceManager2;
  5.             Singleton.registerClass("mx.resources::IResourceManager",
  6.             Class(getDefinitionByName("com.venarc.designer.managers.ResourceManager2")));
  7.             return true;
  8.         }

Now my custom ResourceManager2 class is used whenever you call ResourceManager.getInstance(). I wish there were some compiler options for specifying which classes to use for those Singletons, would've made everything a lot easier.

Flex localization with Resource Bundles

Working with resource bundles in Flex is (surprise, surprise) relatively easy and pretty cool. They're a great way to separate content from code and localize your flex apps. You have the choice to either compile the different locales statically into the app, or load them dynamically using the ResourceManager.


Adding support for different locales


First thing you need to know is that, Flex techinically doesn't support most locales right "out of the box". In order to support more, it needs to create a bunch of framework resource SWCs for each locale you want to use. This is really easy though. There is a command-line utillity called copylocale that will handle creating those for you...all you need to do is tell it which locale you want to create. Pop open a terminal and navigate to your flex SDK directory, and run this command, substituting "fr_FR" for the locale you wish to create:

./bin/copylocale en_US fr_FR

This will generate all the asset files the framework needs. You're done with this part.


Adding resource bundles


To add resource bundles, there are a few steps you need to follow. Each step will be discussed further below:

  • Create a source directory in your project for each locale you wish to use. Normally, it would go something allong the lines of src/locale/en_US, src/locale/en_GB, etc.
  • Create a properties file that will contain the resources to be localized. This property file is a simple text file containing key/value pairs. Mostly it's going to contain strings to be localized, but can also contain ClassReferences and Embedded assets, just like a style swf.
  • If you want to compile all the locales into the app, update the compiler settings to include the locales and source paths into the app.
  • If you would like to load the additional locales on demand, create a resource swf to be loaded in by calling ResourceMananger.loadResourceModule().


Creating the properties file


The properties file is what the compiler uses to make the resource bundle. In fact, the compiler parses that file and creates a subclass of ResourceBundle for use in the app. It is made up of simple key/value pairs:

mytitle=Title
myothervalue=This is some other text we want to localize

You don't enclose the strings in quotes, and any whitespace before the value is trimmed (which is great for keeping your properties file nice and neat). Whatever you name the file will be the name of the resource bundle: a file named "strings.properties" becomes the "strings" resource bundle in the app. One important note, the text file must be encoded in UTF-8.

Telling the flex compiler which bundles to include in the app
In order for flex to know which resource bundles you plan on using in the app, you need to explicitly tell it using metadata tags. In mxml it looks like:

XML:
  1. <mx:Metadata>
  2.      [ResourceBundle("strings")]
  3. </mx:Metadata>

and in actionscript it looks like this:

Actionscript:
  1. [ResourceBundle("myResources")]


Specifying which locales to compile into the app


If you are going to statically compile all the locales you want to use in the app, you simple add a couple of compiler options:

-locale=en_US,fr_FR -source-path=locale/{locale}

if you get a warning about the source path overlapping after changing those options, you can also add the compiler option -allow-source-path-overlap=true.

Now when you compile your app, flex will create resource bundles for each of the locales.


Creating resource modules to dynamically load in


If you would like to load different locale bundles on the fly, you need to comile swfs for each locale. Unfortunately, you cannot do this within Flex Builder, you must use the command line. Luckily, this is also easy, though it is a two step process.

First, we need to find out exactly which bundles to include into the module swf. The way we do this is by compiling our app with no locale specified, and including the compiler option -resource-bundle-list:

mxmlc -locale= -resource-bundle-list=myresources.txt MyApp.mxml

Note that the above example using the command line, but you can do this part in Flex Builder by adding additional compiler options under the project preferences (Flex Compiler > additional compiler arguments). Now when you compile the app, a text file will be output that contains a list of bundles to include in the module:

bundles = strings collections containers controls core effects skins styles

Note the "strings" bundle. Flex knew to include this because we specified it in the metadata.

What do we do with that list? That's the second step. We go back to the command line, and use that list as part of a the -include-resource-bundles compiler argument when we compile the module swf. Open a terminal window, navigate to your projects directory, and use the following command:

mxmlc -locale=en_US -source-path=locale/{locale} -include-resource-bundles=strings,collections,containers,controls,core,effects,skins,styles -output en_US_resources.swf

You can customize the -output file name to your liking, and be sure the -source-path arguments points to the directory which contains the properties file. Repeat this step for each locale you want to compile, changing the -locale and -output options accordingly.


Using the resource bundles


Using the resource bundles is pretty easy. Wherever you want a string localized, you can do two things:

If you plan on only localizing once when the app starts, you can use the @Resource() compiler directive wherever you want to use the text. You simply pass the name of the resource bundle, and the key which you wish to use:

XML:
  1. <mx:Label text="@Resource(bundle='strings', key='mytitle')" />

If you plan on dynamically updating the content during the life of the app (for example, allowing the suer to set the locale), you can use bindings and the ResourceManager singleton to specify the strings:

XML:
  1. <mx:Label text="{resourceManager.getString('strings', 'mytitle')}" />

When using bindings, the value of the string will be updated once you set the ResourceManager.localeChain property, which is an array containing the locales to use, in cascading order. For example, if you specify ['fr_FR', 'en_US'], the ResourceManager will first look for the key in the fr_FR bundle. If that key is not found, it will use the key from the en_US bundle instead. You can use just a single locale in the array if you wish.


Loading additional resource bundles


Again, pretty simple. you can use ResourceManager.loadResourceBundles() to load the resource module swf containing the new locale. One that locale is loaded, you can set the ResourceManager.localeChain property to use the new locale.

For some additional info, the Runtime Localization article on Labs is a great resource. Also see:

http://www.herrodius.com/blog/123
http://soenkerohde.com/2008/07/flex-localization/
http://hillelcoren.com/2008/09/12/resource-bundles-in-flex-wo-lots-of-extra-code/ ( for a nice ResourceManager utility class)

TransformManager fix for Mac Firefox

This post is specifically about Jack Doyle's (aka greensock) awesome TransformManager tool, but the overall idea solves the issue of mouse up events when releasing the mouse button outside the stage in Mac Firefox.

In case you didn't know already, in Firefox on the Mac there is a problem with releasing the mouse outside the browser window. If you click within the swf, then while holding the mouse button down, drag outside the browser window, then release the mouse button, Mac FF doesn't hear the MOUSE_UP event. Obviously, this is a problem for the TM because if you do that, when you bring the mouse back over the swf, the MOUSE_MOVE handlers are still firing, and the selection is still following the mouse, event though you've released the mouse button.

The key to the whole fix is that in Mac FF, the MOUSE_LEAVE event will not fire until you release the mouse button, if it was being held down when you drag out of the swf.

So to solve this I tweaked the TM class as follows:

add a helper method to determine if it is mac ff, and one to dispatch fake mouse up events (you'll see why in a bit)

Actionscript:
  1. // on Mac Firefox, if you click the mouse button,
  2. // then drag out of the browser window and release the mouse button,
  3. // the MOUSE_LEAVE event doesn't fire doesn't fire until the mouse button is released.
  4. // Knowing that we can properly handle MOUSE_UP events for moving, resizing, etc.
  5. private function get isFirefoxMac():Boolean{
  6.             // this is adapted from some flex code
  7.             var browserUserAgent:String = ExternalInterface.call("navigator.userAgent.toString");
  8.             var browserPlatform :String = ExternalInterface.call("navigator.platform.toString");
  9.  
  10.             var isFirefoxMac:Boolean = (browserUserAgent && browserPlatform &&
  11.                 browserUserAgent.indexOf("Firefox")> -1 && browserPlatform.indexOf("Mac")> -1);
  12.             return isFirefoxMac;
  13. }
  14.  
  15.  
  16. private function dispatchFakeMouseUpEvent():void{
  17.             var mevent:MouseEvent = new MouseEvent(MouseEvent.MOUSE_UP, true, false, _stage.mouseX, _stage.mouseY);
  18.             _stage.dispatchEvent(mevent);
  19. }

Then, I made a slight modification to the onPress* methods, to check if it isFirefoxMac, if so also listen for the MOUSE_LEAVE event.

Actionscript:
  1. private function onPressRotate($e:MouseEvent):void {
  2.                        
  3.                         //... whatever pre-existing code ...
  4.                        
  5.                         _stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveRotate, false, 0, true);
  6.                         _stage.addEventListener(MouseEvent.MOUSE_UP, onReleaseRotate, false, 0, true);
  7.                        
  8.                         if ( isFirefoxMac ){
  9.                                     _stage.addEventListener(Event.MOUSE_LEAVE, onMouseLeaveRotate, false, 0, true);
  10.                         }
  11.  
  12.                         // other stuff...
  13.             }
  14. }

Each type of press gets an associated onMouseLeave* event (onMouseLeaveScale, onMouseLeaveRotate, etc).

Actionscript:
  1. private function onMouseLeaveRotate(event:Event):void{
  2.             _stage.removeEventListener(Event.MOUSE_LEAVE, onMouseLeaveRotate);
  3.             dispatchFakeMouseUpEvent();
  4. }

Actually, the onMouseLeaveMove handler is slightly different since onReleaseMove() doesnt need a mouse event:

Actionscript:
  1. private function onMouseLeaveMove(event:Event):void{
  2.             _stage.removeEventListener(Event.MOUSE_LEAVE, onMouseLeaveMove);
  3.             onReleaseMove(event);
  4. }

I also added a line to the removeParentListeners() method to remove the onMouseLeaveMove handler:

Actionscript:
  1. _stage.removeEventListener(Event.MOUSE_LEAVE, onMouseLeaveMove);

That's just about it, just one more small tweak to the TransformItem class. You need to do the same routine for the TransformItem.onMouseDown() method: Check for Mac FF, if so add a MOUSE_LEAVE handler, and in the onMouseLeave() method just call the onMouseUp() method.

Flex 4 stuff up on labs

New releases of Flash Builder and the Flex SDK are up on Adobe Labs. I'm very excited about the new version of Flex, it seems to work out a lot of the issues I had with Flex 3. States actually look easy to use now, and skinning and styling looks much, much improved. There's a good read here at the Flex Developer Center with an overview about the main differences between Flex 3 & 4.