UcompOS Rich Experience Framework is Now in Beta
Over the last 6 weeks, we have been diligently working to move the UcompOS RXF project from Alpha to Beta and as of this morning, that effort has come to completion.
There are numerous enhancements and bug fixes that are introduced into the Beta release. Also, we are compiling the project against the Adobe Flex 4.1 SDK nightly builds.
An MXML Image Component That Loads Images With No Associated Policy Files
In a major UcompOS project I am working on, I am finding the need more and more to load images from various different servers where the destination servers often have no crossdomain.xml policy file.
I don’t want to use a back-end proxy to deal with this as that would be chewing up my back-end resources and bandwidth which goes against my grain especially when targeting a product for wide deployment.
I will post the code of the component below which should be very self-explanatory:
<?xml version="1.0" encoding="utf-8"?> <mx:Image xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" securityError="securityErrorHandler(event)" trustContent="true"> <fx:Script> <![CDATA[ private var _legal:Boolean = true; private var _url:String; public function get legal():Boolean { return _legal; } private function securityErrorHandler(event:SecurityErrorEvent):void { this.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,securityErrorHandler); _legal = false; var sprite:Sprite = new Sprite(); var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE,load_handler); sprite.addChild(loader); var loaderContext:LoaderContext = new LoaderContext(); loaderContext.checkPolicyFile = false; loader.load(new URLRequest(url),loaderContext); } private function load_handler(event:Event):void { var loaderInfo:LoaderInfo = event.target as LoaderInfo; loaderInfo.removeEventListener(Event.COMPLETE,load_handler); var bitmapData:BitmapData = new BitmapData(loaderInfo.width,loaderInfo.height); var matrix:Matrix = new Matrix(); bitmapData.draw(loaderInfo.content,matrix); var bitmap:Bitmap = new Bitmap(bitmapData); source = bitmap; } public function get url():String { return _url; } public function set url(value:String):void { loaderContext = new LoaderContext(false); _url = value; source = value; } ]]> </fx:Script> </mx:Image>
Some things to point out about the component in no particular order:
-
You set a value of a URL to the image to the url property instead of the source property
A Boolean named legal has a false value if the content of the image was loaded using an assisting Sprite and Loader instance
The source property on an Image control is of a wildcard (*) type – if the value of legal is true, then source is a String and will have the same value as url. If the value of legal is false, then source will be an Object of type Bitmap
Garbage Collection in the UcompOS RXF
In large and sophisticated Flash-based applications, such as the Rich Portal Applications you develop with the UcompOS Rich Experience Framework, Garbage Collection becomes an increasingly important consideration.
Garbage Collection is a computer science concept that deals with memory management and re-allocating memory occupied by objects that are no longer needed or in use by the computer program.
Garbage Collection in a large-scale event-driven Flash architecture is a crucial consideration and unfortunately, can also be a tedious proposition to properly implement.
One of the more important considerations in aiding with Garbage Collection in Flash-based application is proper management of event listeners.
In a best-practice implementation, any call to addEventListener() would be followed up with a call to removeEventListener() once the event listener was no longer needed. Event listeners perpetuate the objects they are associated with so even once a particular object is destroyed, that doesn’t necessarily mean any event listeners associated with it will be destroyed too.
Failure to remove event listeners once they are no longer needed in a large-scale implementation can cause memory leaks and performance can break down over time.
In all your Flash applications, you should employ this strategy when and where possible.
In a typical large-scale UcompOS RXF implementation, Flash-based applications and sub-applications are continuously being loaded and unloaded into the UcompOS Portal.
The concept of Proxy Components in the UcompOS RXF involves event listeners being attached to a Proxy Component in one entity that represents an object in another UcompOS entity and then listening for dispatched events.
Therefore, the concept of garbage collection takes on especially critical importance in the UcompOS RXF because if UcompOS applications are being loaded and unloaded constantly and the event listeners that are attached to them are not being properly removed, performance would become an issue in a large-scale implementation.
The UcompOS Portal has been designed such that when SWF content that has been loaded into the UcompOS Portal is unloaded, a garbage collection routine is implemented to try to optimize system performance. (The three ways SWF content is loaded into the UcompOS Portal is in UcompOS Window instances, UcompOS Artifact instances, or into the UcompOS run-time.)
As of the UcompOS RXF Public Alpha release 0.4.6, some additional enhancements to the garbage collection process have been implemented that I will discuss here.
Understanding the UcompOS RXF garbage collection architecture can help you build more scalable Rich Portal Applications.
The recent enhancements come in the realm of Proxy Components.
By strict convention, all Proxy Components must extend the SDK class AbstractProxyComponent. As of UcompOS RXF 0.4.6, AbstractProxyComponent now extends a new SDK class called SDKEventDispatcher which extends EventDispatcher.
(Also, as a side-note, the AbstractProxyComponent class now implements IEventDispatcher in addition to IProxyComponent so you can refer to your Proxy Components as IEventDispatcher or IProxyComponent instances interchangably.)
SDKEventDispatcher simply overrides addEventListener() and offers a protected property _listeners of type Array.
I chose to create this separate class versus simply overriding addEventListener() in AbstractProxyComponent so that in the event the new clean-up process employed can be useful outside the scope of a Proxy Component, one could simply extend SDKEventDispatcher versus EventDispatcher.
Here is the over-ridden addEventListener():
override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void { _listeners.push({type:type,listener:listener}); super.addEventListener(type,listener,useCapture,priority,true); }
Notice that I am always assigning a true value to the useWeakReference parameter in the super call to addEventListener(). This is because in the context of attaching event listeners to Proxy Components, you always would want them to be weak references to aid in garbage collection.
In AbstractProxyComponent, there is a new protected method, cleanup() which is as follows:
protected function cleanup(event:SDKEvent=null):void { while(_listeners.length) { var object:Object = _listeners.pop(); removeEventListener(object.type,object.listener); } _sdkModel.removeProxyComponent(this.uuid); var commandObject:CommandObject = new CommandObject("SDK.cleanup",{uuid:this.uuid}); var dataPackage:DataPackage = new DataPackage(Utilities.createUID(),_sdkModel.connectionId,this.destination,commandObject); _sdkClient.send(dataPackage); }
What the cleanup() method does is it automatically removes any event listeners that have been attached to the Proxy Component.
It also sends a message to the end-point of the Proxy Component targeting its SDK.cleanup public API method that is built into the UcompOS SDK and therefore exposed by any UcompOS entity. This aids in UcompOS RXF garbage collection-related activities.
Calls to cleanup() do not occur automatically.
When you would call cleanup() in a Proxy Component would be an implementation specific consideration.
For instance, with these new capabilities, I have updated some of the UcompOS Portal Proxy Components that are built into the UcompOS SDK.
Consider the updated constructor of the UcompOSWindowProxy class:
public function UcompOSWindowProxy() { super(); // set the destination property to the UcompOS Portal super._destination = _sdkModel.root; <strong>addEventListener(CLOSE,cleanup);</strong> }
The call to addEventListener(CLOSE,cleanup) is the new addition to the constructor.
What it does is it causes the inherited cleanup() method to always be called when the UcompOS Window instance on the UcompOS Portal that is represented by the UcompOSWindowProxy instance is closed.
Therefore, any event listeners you have attached to the UcompOSWindowProxy instance are automatically removed whenever the window is closed.
In 100% of the possible use cases for the UcompOSWindowProxy, this is the desired behavior.
The net result of this is you don’t have to worry about making your own calls to removeEventListener() for your particular UcompOSWindowProxy instances.
The same sort of behavior has been engineered into some of the other Proxy Components built into the UcompOS SDK including UcompOSAlertProxy, UcompOSDialogueProxy, UcompOSArtifactProxy, and UcompOSBrowserWindowProxy.
Consider this strategy when you are building your own Proxy Components and remember to always use removeEventListener() in all your other Flash applications whether you are deploying them in the UcompOS Portal or not.
Auto-Updating the UcompOS Portal
As you know, at the Downloads portion of the UcompOS Project site, you can download the UcompOS Developers Package which contains the UcompOS Portal run-time, as well as the UcompOS SDK for JavaScript, Flash/Flex, and AIR 2.0.
This enables you to host a full UcompOS implementation on your local workstation for testing and development, or to host a public UcompOS implementation from your own webservers.
With the UcompOS project being updated frequently, the physical process of updating the UcompOS RXF and its associated components can be tedious.
There are ways you can automate the process, but it requires some simple scripting that can be accomplished in a variety of languages.
There are some static URLs to know about first of all.
The latest release of the UcompOS Developers package is always found at a URL of http://ucompos.ucompass.com/UcompOS.zip. That zip package expands into a folder named bin-release and within that folder are the contents of the UcompOS Portal. Also inside that folder is a file UcompOSSDK.zip which expands into an sdk folder, inside of which are an html, air, and flashOrFlex folder with each of the three respective SDKs for those different platforms.
In my current large scale UcompOS implementation, what I am doing is setting it up so that I have one and only one copy of the different SDK files – UcompOSSDK.swc (Flash/Flex), UcompOSSDKAIR.swc (AIR), and UcompOSSDK.js and UcompOSSDK.swf (JavaScript).
I then establish relative symlinks to the various locations on my local file system where those files need to be placed – libs folders in Flex projects and root webserver paths for JavaScript applications. For Flash applications I simply embed the UcompOSSDK.swc file from the singular location where the file exists on my workstation.
As far as updating the UcompOS Portal, the developers package bin-release folder contains a simple XML file named release.xml.
This is what the current one looks like as of this morning:
<release> <portal> <version>0.4.5</version> <packaged>Tue Mar 23 08:47:44 2010</packaged> </portal> <sdk> <version>0.4.5</version> <packaged>Tue Mar 23 02:02:53 2010</packaged> </sdk> </release>
Also, a file exists at the URL http://ucompos.ucompass.com/release.xml of the same format which you can test against to determine if you are running the same version of the UcompOS Portal or if there is an update available.
Note there IS an SVN Repository for the UcompOS Project but at this time it is just meant as a means to distribute the UcompOS SDK source code. Eventually, I plan to make all of the UcompOS RXF project open source and there will be a model for accessing the full source code of all the associated UcompOS technologies and you’d be able to structure your updates according to check-outs from SVN. (My timeline for this by the way is to coincide the open-sourcing of the UcompOS Portal with the production 1.0 release of the UcompOS RXF which I am trying to coincide with Adobe MAX 2010 in October).
In the meantime, there are still a number of simple ways you can go about coordinating an auto-update.
Below is a very simple PERL script I wrote that auto-updates the UcompOS Portal in my Educator 2 project:
#!/usr/local/bin/perl use strict; use LWP::Simple; use XML::LibXML; my $myVersion; if(!-e "../../frontend/release.xml") { $myVersion = 0; } else { open(FILE,"../../frontend/release.xml"); my @line = <FILE>; close(FILE); my $myReleaseInfo = join("",@line); $myVersion = getVersion($myReleaseInfo); } my $latestVersion = getVersion(get("http://ucompos.ucompass.com/release.xml")); if($myVersion ne $latestVersion) { update(); } sub getTextNode { my $thisNode = $_[0]; my @thisChild = $thisNode->getChildNodes; my $textNode; eval { $textNode = $thisChild[0]->getNodeValue; }; if(!$textNode) { eval { $textNode = $thisChild[0]->nodeValue; }; } return $textNode; } sub getVersion { my $releaseInfo = $_[0]; my $xml = XML::LibXML->new; my $string = $xml->parse_string($releaseInfo); my $xp = XML::LibXML::XPathContext->new($string); my @nodeSet = $xp->findnodes('//portal/version'); my $version = getTextNode($nodeSet[0]); return $version; } sub update { chdir('cache'); my $content = get("http://ucompos.ucompass.com/UcompOS.zip"); open(FILE,">UcompOS.zip"); print FILE $content; close(FILE); system("unzip -qq 'UcompOS.zip'"); unlink("UcompOS.zip"); system("rsync -a --delete --exclude=\"LocalLib.js\" --exclude=\"styles.css\" bin-release ../../../frontend"); system("\\rm -rf bin-release"); }
In my example above, I basically have my UcompOS Portal code housed in a bin-release folder. I check the latest release version and see if it matches my local version, and if it doesn’t, I retrieve the latest package, unzip it, and overwrite my existing copy with rsync excluding the LocalLib.js and styles.css files – which are the only files in the UcompOS Developers package you would typically need to update with your own implementation specific information.
Embedding Application Manifests Inside the Dock Manifest
As of UcompOS RXF build 0.4.3, you can now embed Application Manifests inside of your Dock Manifest.
The
<applications> <application>http://applications.ucompass.com/myApplication.xml</applications> </applications>
Now, you can embed a
<applications> <manifestURL>http://applications.ucompass.com/myApplication.xml</manifestURL> <manifest> <source> <base>http://applications.ucompass.com/mySource.swf</base> </source> <manifest> </applications>
Notice you also need to articulate a unique URL for the manifest file in a
Also, the UcompOSGlobalManagerProxy’s launchApplication() method now accepts an optional third manifest parameter of type XML that can be a dynamically generated application manifest.
Therefore, the possibility to generate UcompOS applications on the fly exists.
Instantiating UcompOS Global (Continuum) Variables at Startup
In my post about UcompOS Shared Objects, I described how the UcompOS Portal furnishes a Shared Object Implementation that can be written to and read by any UcompOS entity in the UcompOS Continuum.
In a large-scale UcompOS Rich Portal Application, you very likely may find a need to share complex data across multiple UcompOS entities.
One effective approach to this challenge is to use the UcompOSGlobalManagerProxy class’s setSharedObject(), getSharedObject(), and getSharedObjects() methods.
These methods get/set shared objects on the UcompOS Portal at run-time.
But what if you want to populate the UcompOS Portal’s shared Object at start-up even before any UcompOS applications are launched?
As of the version 0.4.1 Milestone release of UcompOS RXF, there is a way to do this.
The key is a new <globalVariables/> element that can be added as a child element to the root <applications/> element in a dock manifest and as a child element to the root <application/> element in a UcompOS application manifest.
Below is a simple example of this in action in the dock manifest of my Educator 2 software application being built entirely on top of the UcompOS RXF:
<?xml version="1.0" encoding="utf-8"?> <applications> <globalVariables> <globalVariable> <key>_DOMAIN_</key> <value>applications.ucompass.com</value> </globalVariable> <globalVariable> <key>_PROTOCOL_</key> <value>http</value> </globalVariable> <globalVariable> <key>_GATEWAY_</key> <value>http://pilotfish2.ucompass.com/apps/gateway</value> </globalVariable> </globalVariables> <application background="true" default="true"> _PROTOCOL_://_DOMAIN_/Educator2PortalFoundation/manifest.xml </application> </applications>
Each global variable is wrapped in a <globalVariable/> element with child <key/> and <value/> elements.
All global variables in the dock manifest will automatically be added to the UcompOS Portal’s Shared Object implementation and will immediately be available to all UcompOS entities.
String Substitutions in Manifest Files
In the above example, notice I am instantiating global variable named _PROTOCOL and _DOMAIN_. And then notice that those strings appear in my <application/> element.
A string substitution will automatically take place here such that the <application/> value will be http://applications.ucompass.com/Educator2PortalFoundation/manifest.xml after the substitution.
As mentioned earlier, the <globalVariables> element is supported in both application manifests and dock manifests so you can also establish global variables in this fashion when launching a UcompOS application.
Typing Shared Objects
The <value/> element supports an optional type attribute such that you can articulate the data type of a Shared Object.
The best way to show how this is accomplished is with examples:
Array
<globalVariable> <key>groceryList</key> <value type="Array"> <item>Bananas</item> <item>Cereal</item> <item>Diapers</item> </value> </globalVariable>
Object
<globalVariable> <key>contacts</key> <value type="Object"> <item> <key>Joe</key> <value>555-1212</value> </item> <item> <key>Bob</key> <value>555-2121</value> </item> </value> </globalVariable>
The type attribute can also be of type uint, int, Boolean, Number and String. The default data type is String. Also, the <item/> element supports the type attribute for Arrays and Objects.
Also, nesting of complex data structures is supported, so you can implement an Array of Arrays, or an Array of Objects, etc.
Introducing 3 New UcompOS Portal Proxy Components
The UcompOS SDK in the latest milestone release of the UcompOS Rich Experience Framework, Public Alpha 0.3.1, contains 3 new UcompOS Portal Proxy Components that open up a host of new capabilities.
In this article, I’ll briefly introduce each of these 3 new components and show some examples of how they can be used and I plan to build some new tutorial content in the near future to cover these 3 new components.
UcompOSKeyboardProxy
Within the scope of an individual UcompOS application or sub-application, you can certainly implement your own logic to handle keyboard events.
However, suppose you want to capture keyboard events that occur in the scope of the UcompOS Portal and pass them to other UcompOS entities such as those that may be running as AIR applications on the desktop, in separate UcompOSBrowserWindowProxy instances, or in sandboxed SWF implementations.
You can now use the new UcompOSKeyboardProxy class to do this.
The class is a Singleton with a single property named listen.
When you set the listen property to true, any keyboard events that occur in the scope of the UcompOS Portal will be dispatched by the UcompOSKeyboardProxy Singleton.
Let’s look at a simple code-based example of this:
package { import flash.display.Sprite; import com.ucompass.ucompos.sdk.SDKClient; import com.ucompass.ucompos.sdk.proxycomponents.UcompOSKeyboardProxy; import com.ucompass.ucompos.sdk.events.SDKEvent; public class KeyboardExample extends Sprite { public function KeyboardExample() { SDKClient.getInstance(this); UcompOSKeyboardProxy.getInstance().listen = true; UcompOSKeyboardProxy.getInstance().addEventListener(UcompOSKeyboardProxy.KEY_DOWN,eventHandler); UcompOSKeyboardProxy.getInstance().addEventListener(UcompOSKeyboardProxy.KEY_UP,eventHandler); } private function eventHandler(event:SDKEvent):void { trace("Type of event: "+event.type); trace("Character code: "+event.data.charCode); trace("Alt key pressed: "+event.data.altKey); trace("Shift key pressed: "+event.data.shiftKey); trace("Control key pressed: "+event.data.ctrlKey); trace("Key code: "+event.data.keyCode); } } }
As you can see, the properties in the event dispatched by the UcompOSKeyboardProxy Singleton are very similar to those of the Flash KeyboardEvent class.
UcompOSAlertProxy
The Flex Alert class is a modal dialogue. It prevents the user from doing anything until they click a button control on the Alert dialogue.
The UcompOSAlertProxy class lets you throw a Flex Alert onto the UcompOS Portal.
When the user clicks a button on the Alert class, instances of UcompOSAlertProxy dispatch an SDKEvent instance.
The UcompOSAlertProxy class has two methods: confirm() and simpleAlert().
confirm() throws a confirmation style dialogue onto the UcompOS Portal.
Below is the signature of the confirm() method:
public function confirm(title:String,message:String,okLabel:String=null,cancelLabel:String=null,defaultButton:String=OK):void
As you can see, you can customize the alert title, the message of the Alert body, and the labels used on the “OK” and “Cancel” buttons. You can also indicate which button should be the default button.
Let’s look at a code-based example:
package { import flash.display.Sprite; import com.ucompass.ucompos.sdk.SDKClient; import com.ucompass.ucompos.sdk.proxycomponents.UcompOSAlertProxy; import com.ucompass.ucompos.sdk.events.SDKEvent; public class AlertExample extends Sprite { public function AlertExample() { SDKClient.getInstance(this); var a:UcompOSAlertProxy = new UcompOSAlertProxy(); a.addEventListener(UcompOSAlertProxy.ALERT_SUBMIT,eventHandler); a.confirm("Confirm","Are you sure you want to delete this file?","Yes!","Hell No!",UcompOSAlertProxy.CANCEL); } private function eventHandler(event:SDKEvent):void { switch(event.data.detail) { case UcompOSAlertProxy.OK: trace("The file will be deleted"); break; case UcompOSAlertProxy.CANCEL: trace("The file will NOT be deleted"); break; } } } }
Notice in the example above, the SDKEvent’s data Object will have a detail property that will be a value of UcompOSAlertProxy.OK or UcompOSAlertProxy.SUBMIT. This is parallel to the implementation of the Alert in Flex.
The simpleAlert() method simply throws an Alert with a single button of type UcompOSAlertProxy.OK.
UcompOSDialogueProxy
In some instances, you’ll want to set up a modal user interaction dialogue when you want to capture some sort of input from the user.
To address this need, I created the UcompOSDialogueProxy class.
The UcompOSDialogueProxy is used to throw up a few different types of modal user interaction dialogues.
For instance, suppose you have a File Management application and you want to give the user the ability to choose a “Save As” option.
You don’t want the user to be able to do anything else until they either input a file name and press submit, or, cancel the operation.
This would be a perfect place to use the new UcompOSDialogueProxy class.
There are initially 4 different types of dialogues I have set up: TextInput for single line text input; TextArea for multiple line text input; RadioButtons for survey-type interaction; and SWFLoader which enables you to throw your own content into the dialogue and this content can be a UcompOS entity.
Below is the signature of the add() method of UcompOSDialogueProxy:
public function add(controlType:String=TEXT_INPUT,title:String="UcompOS Dialogue",label:String="",width:uint=400,height:uint=0,showCloseButton:Boolean=false,cancelLabel:String=CANCEL,submitLabel:String=SUBMIT,cancelEnabled:Boolean=true,submitEnabled:Boolean=true,initData:Object=null):void
Notice the default height is 0. What happens is when the width or height is 0, the size of the Dialogue automatically conforms to the content that is placed inside of it. Otherwise, the width/height is explicitly set.
The initData Object parameter has special meaning.
It is passed to the RadioButtons and SWFLoader types of Dialogues and examples of how initData is constructed in those two scenarios is depicted in the examples below.
Let’s look at a code-based example that shows how to use each of the 4 different types of dialogues:
TextInput Dialogue Example
package { import flash.display.Sprite; import com.ucompass.ucompos.sdk.SDKClient; import com.ucompass.ucompos.sdk.proxycomponents.UcompOSDialogueProxy; import com.ucompass.ucompos.sdk.events.SDKEvent; public class TextInputDialogue extends Sprite { public function TextInputDialogue() { SDKClient.getInstance(this); var d:UcompOSDialogueProxy = new UcompOSDialogueProxy(); d.add(UcompOSDialogueProxy.TEXT_INPUT,"Sample Dialogue","What is your name?"); d.addEventListener(UcompOSDialogueProxy.DIALOGUE_SUBMIT,eventHandler); } private function eventHandler(event:SDKEvent):void { var d:UcompOSDialogueProxy = event.target as UcompOSDialogueProxy; trace("Your name is "+event.data.data); d.close(); } } }
Notice in the above example, in the eventHandler() method, the UcompOSDialogueProxy’s close() method is called. The dialogue does not disappear by itself upon a submission. You have to explicitly remove it with the close() method such that you have much better control over when the dialogue is closed based on the specifics of the user’s input.
TextArea Dialogue Example
package { import flash.display.Sprite; import com.ucompass.ucompos.sdk.SDKClient; import com.ucompass.ucompos.sdk.proxycomponents.UcompOSDialogueProxy; import com.ucompass.ucompos.sdk.events.SDKEvent; public class TextAreaDialogue extends Sprite { public function TextAreaDialogue() { SDKClient.getInstance(this); var d:UcompOSDialogueProxy = new UcompOSDialogueProxy(); d.add(UcompOSDialogueProxy.TEXT_AREA,"Sample Dialogue","Please tell us why you like the UcompOS in 1,000 words or less:"); d.addEventListener(UcompOSDialogueProxy.DIALOGUE_SUBMIT,eventHandler); } private function eventHandler(event:SDKEvent):void { var d:UcompOSDialogueProxy = event.target as UcompOSDialogueProxy; trace(event.data.data); d.close(); } } }
The TextArea example is almost identical to the TextInput example.
RadioButtons Dialogue Example
package { import flash.display.Sprite; import com.ucompass.ucompos.sdk.SDKClient; import com.ucompass.ucompos.sdk.proxycomponents.UcompOSDialogueProxy; import com.ucompass.ucompos.sdk.events.SDKEvent; public class RadioButtonsDialogue extends Sprite { public function RadioButtonsDialogue() { SDKClient.getInstance(this); var d:UcompOSDialogueProxy = new UcompOSDialogueProxy(); var choices:Array = ["ActionScript 3","JavaScript","PERL","C++"]; d.add(UcompOSDialogueProxy.RADIO_BUTTONS,"Sample Dialogue","What is your favorite programming language?",0,0,false,UcompOSDialogueProxy.CANCEL,UcompOSDialogueProxy.SUBMIT,true,true,{choices:choices}); d.addEventListener(UcompOSDialogueProxy.DIALOGUE_SUBMIT,eventHandler); } private function eventHandler(event:SDKEvent):void { var d:UcompOSDialogueProxy = event.target as UcompOSDialogueProxy; trace("Your favorite programming language is: "+event.data.data); d.close(); } } }
Notice in the example above, we are building an Array of choices to give to the user where a Radio Button will be associated with each choice. The Array is passed as a property named choices in the initData parameter passed to the UcompOSDialogueProxy’s add() method.
SWFLoader Dialogue Example
package { import flash.display.Sprite; import com.ucompass.ucompos.sdk.SDKClient; import com.ucompass.ucompos.sdk.proxycomponents.UcompOSDialogueProxy; import com.ucompass.ucompos.sdk.events.SDKEvent; public class SWFLoaderDialogue extends Sprite { public function SWFLoaderDialogue() { SDKClient.getInstance(this); var d:UcompOSDialogueProxy = new UcompOSDialogueProxy(); var source:String = "http://applications.ucompass.com/MyCustomDialogue.swf": d.add(UcompOSDialogueProxy.SWF_LOADER,"Sample Dialogue","",0,0,false,UcompOSDialogueProxy.CANCEL,UcompOSDialogueProxy.SUBMIT,true,true,{source:source}); d.addEventListener(UcompOSDialogueProxy.DIALOGUE_SUBMIT,eventHandler); } private function eventHandler(event:SDKEvent):void { var d:UcompOSDialogueProxy = event.target as UcompOSDialogueProxy; trace("The Connection Id of your SWF content is: "+event.data.data); d.close(); } } }
In this case, we are passing the URL of a SWF to the dialogue. When the user presses the Submit button for the dialogue, the connection id of the SWF content is returned in the SDKEvent dispatched by the UcompOSDialogueProxy instance.
From here, UcompOS SDK transactions could be used to communicate directly with the SWF content and retrieve special information from it.
Major UcompOS Updates Coming
It has been a while since I’ve posted, but that doesn’t mean I don’t have some great things in store.
I am in the process of finishing up a number of enhancements, including a number of bug fixes as well as some new UcompOS Portal Proxy Components.
I plan to get caught up on documentation this weekend, and to put forward some new tutorials and roll out Milestone Public Alpha Release 0.4 this Monday.
There will probably be one more Milestone Public Alpha Release after this next one before we officially move the project to its Public Beta sometime in May.
Uploading Files to a UcompOSArtifactProxy Instance
In a major UcompOS Portal Application I am building, I place UcompOS Artifact instances that represent file volumes on the UcompOS Portal. Over this past weekend, I found myself with the need to enable the user to right-click on the file volume artifacts (revealing their context menus) and choose an “Upload Files” option with the goal of providing the user a way to upload files directly to the root directory of the particular file volume.
My initial approach to this was to attach a Context Menu to the artifact instances with an “Upload Files” option. I then created an event listener to listen for the UcompOSArtifactProxy.MENU_ITEM_SELECT event dispatched by the UcompOSArtifactProxy instance.
Once this event is dispatched, I reasoned I could then create a FileReferenceList instance and then call its browse() method and go from there.
However, when I attempted this, I was reminded of an important detail when an error was thrown once I called the FileReference.browse() method.
Just like the launching of AIR applications as well as the entry into Full Screen Mode, the opening of the file browse dialogue by the Flash Player must succeed a user-triggered mouse event.
In the context of the UcompOS Continuum, the entity where the mouse event occurs is the UcompOS Portal. This is a completely separate entity from the code that is calling the FileReference.browse() method.
In my implementation, I have a Flash application that is running as a background application that sets up the aforementioned UcompOS Artifact instances that represent file volumes.
So when the user right clicks on the artifact and selects the Context Menu item, they are actually operating on the UcompOS Portal itself.
An SDKEvent is then dispatched to the background application and then what follows is the call to FileReferenceList.browse().
However, as far as the Flash Player is concerned, in that scenario, the call to FileReferenceList.browse() does NOT proceed a user-triggered mouse event because the mouse event occurs in a totally different scope.
So I have engineered a new process into the UcompOS Portal to enable the goals I described above.
I think the use case I am speaking to here is one that has good merit to be repeated by many developers – i.e. a widget that you can upload files to. I can envision many scenarios where this could be useful such as file conversion utilities for one.
The model I have developed involves creating an uploadFiles property on the UcompOSArtifactProxy’s data Object property.
The value of this uploadFiles property must be the same String value as the label placed on a Context Menu item that is attached to the artifact.
What will happen in this case, when the user selects the context menu option on the artifact on the UcompOS Portal, the file browse dialogue will open, and the user will be able to select multiple files.
After the user selects the files, what happens next is very important.
The UcompOS Portal will dispatch to the UcompOSArtifactProxy instance an SDKEvent for each attached file.
The SDKEvent’s data property will have two properties: fileName:String and fileData:ByteArray, i.e. the name of the file and the contents of the file as a ByteArray.
From here you can do whatever you need to do with this information including upload the file to a remote server, do some sort of conversion, or anything else your specific use case requires.
Below, is a very simple code-based example of how you can create an Artifact and the user can change the artifact’s icon representation by uploading a new image to it.
The application is a UcompOS Flash application loaded into the UcompOS Portal as a background application.
package { import flash.display.Sprite; import com.ucompass.ucompos.sdk.proxycomponents.UcompOSArtifactProxy; import com.ucompass.ucompos.sdk.SDKClient; import com.ucompass.ucompos.sdk.events.SDKEvent; import flash.events.Event; public class Main extends Sprite { private static const UPLOAD_LABEL:String = "Upload a new artifact image"; public function Main() { // Instantiate the UcompOS SDK SDKClient.getInstance(this); // Create a UcompOS Artifact Proxy instance var artifact:UcompOSArtifactProxy = new UcompOSArtifactProxy(); // Listen for uploaded files artifact.addEventListener(UcompOSArtifactProxy.UPLOAD_FILE,uploadHandler); // Initialize the artifact using a pre-defined image artifact.add("RIGHT CLICK ON ME!",null,"http://desktop.ucompass.com/UploadToArtifact/cloud-48x48.png",100,100,true,true,{uploadFiles:UPLOAD_LABEL}); // Establish the context menu on the artifact with an Upload option artifact.setContextMenu(new XML("")); } private function uploadHandler(event:SDKEvent):void { var artifact:UcompOSArtifactProxy = event.target as UcompOSArtifactProxy; // Replace the artifact image with the content of the uploaded file artifact.setImage(event.data.fileData); // Show the name of the uploaded file as the label on the artifact artifact.setLabel(event.data.fileName); } } }
Also, here is a link to a movie that shows the simple application in action.



