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.
New Proxy Component in UcompOS SDK: UcompOSAlertProxy
This morning, I have just added a new Proxy Component to the UcompOS SDK: UcompOSAlertProxy that is available in version 0.2.2 and later of the UcompOS Developers Package.
UcompOSAlertProxy provides a way to create a modal dialogue on the UcompOS Portal via an implementation of the Flex Alert control.
Previously, I had made this possible with the createAlert(); method of the SDK class UcompOSGlobalManagerProxy.
The inefficiency with this is that UcompOSGlobalManagerProxy is a Singleton class (along with UcompOSDockProxy, UcompOSMenuBarProxy, and UcompOSStyleProxy). So one instance of UcompOSGlobalManagerProxy would handle the event dispatching for all Alert dialogues created with its createAlert(); method.
So handling events dispatched by Alerts created with UcompOSGlobalManagerProxy’s createAlert(); method required extra handling and actually provided risks of gaps in data integrity and continuity.
I have removed the createAlert(); from UcompOSGlobalManagerProxy and replaced the functionality with the simpleAlert(); and confirmAlert(); methods of the UcompOSAlertProxy class.
The simpleAlert(); method launches a simple Alert with a single control button that will default with a label of “OK”.
The signature of simpleAlert(); is as follows:
public function simpleAlert(title:String, message:String, okLabel:String="OK"):void
You have the ability to customize the label on the single button presented on the Alert control.
The signature of confirm(); is as follows:
public function confirm(title:String, message:String, okLabel:String = null, cancelLabel:String = null, defaultButton:String="OK"):void
You can specify the labels to be used on both the OK and Cancal buttons and you can articulate which should be the default button.
When the user takes action and clicks a button on the Alert dialogue, an instance of SDKEvent is dispatched by the UcompOSAlertProxy instance of type UcompOSAlertProxy.ALERT_SUBMIT.
The SDKEvent instance’s data property will have a detail property of type String which will have a value of UcompOSAlertProxy.OK or UcompOSAlertProxy.CANCEL.
Let’s look at a very simple example with the aid of some code:
private function fileExists(file:String):void { var a:UcompOSAlertProxy = new UcompOSAlertProxy(); a.data = file; a.addEventListener(UcompOSAlertProxy.ALERT_SUBMIT,fileExists_handler); a.confirm("A file named "+file+" exists in the target location. Do you wish to overwrite it?","Yes","No",UcompOSAlertProxy.CANCEL); } private function fileExists_handler(event:SDKEvent):void { var a:UcompOSAlertProxy = event.proxyComponent as UcompOSAlertProxy; if(event.data.detail==UcompOSAlertProxy.OK) { trace("Overwrite file "+a.data); } }
Building Full Screen Mode UcompOS Implementations
Since the release of Adobe Flash Player 9 in 2006, the Flash Player is capable of entering into what is known as “Full Screen Mode“.
In Full Screen Mode, the content playing in the Flash Player takes over the entire viewport of the display device such that the only content in view and capable of being interacted with is the content currently being executed in the Flash Player.
Depending on the nature of your application, this can in some circumstances create a much more focused and positive experience for your users.
The UcompOS Portal is configured to operate in Full Screen Mode and the UcompOS SDK provides a means for sending the UcompOS Portal into Full Screen Mode from one of your UcompOS applications or sub-applications.
Of course there are some rules and principles to observe when leveraging the power of Full Screen Mode in your UcompOS Applications.
In my blog posting on the Mechanics of UcompOS Desktop (AIR 2.0) Applications, we learned that the launching of an AIR application by the Flash Player must be preceded by a user-triggered mouse event.
The same rule applies to entering into Full Screen Mode.
The UcompOSGlobalManagerProxy class has two pertinent methods: enterFullScreenMode(); and exitFullScreenMode();.
Both of these methods accept an optional single input parameter, message, of type String.
When you call either of these methods from one of your UcompOS applications or sub-applications, in order to generate the compulsory user mouse click, the UcompOS Portal throws up an Alert dialogue asking the user if they want to allow the request to go into or out of full screen mode.
Of course having the dialogue appear for exitFullScreenMode(); calls is somewhat dubious as all the user needs to do is hit the Escape key on their computer to break out of Full Screen Mode but for continuity, I have decided to keep the mechanics of throwing the Alert in calls to both enterFullScreenMode(); and exitFullScreenMode();.
The optional message parameter lets you customize the text that is presented on the Alert dialogue.
When the UcompOS Portal breaks into or out of Full Screen Mode, a Continuum Event (an instance of the SDKEvent class) of type UcompOSGlobalManagerProxy.SCREEN_MODE_CHANGE is broadcast.
The data Object of the event will have a state property with a literal string value of “normal” or “fullScreen” so that your applications and sub-applications can behave accordingly when the user enters/exits Full Screen Mode.
In the scenario we discussed in my AIR 2.0 UcompOS Applications posting mentioned above, we saw we can leverage the UcompOSArtifactProxy class to create a clickable UcompOS Artifact that immediately launches a particular AIR application (with the aid of the UcompOSAIRProxy class).
This same concept is also available with Full Screen Mode implementations. You can create a UcompOS Artifact and assign its data Object special properties that tell the UcompOS Portal to launch or exit Full Screen Mode when the artifact is clicked on.
The property names will be enterFullScreenMode and exitFullScreenMode and both should be of type Boolean with a value of true.
They should be mutually exclusive and you should not have the enterFullScreenMode and exitFullScreenMode properties set on the artifact’s data Object simultaneously. If you do, the exitFullScreenMode property takes precedent.
Let’s look at a simple code-driven example:
private var artifact:UcompOSArtifactProxy; private function start():void { SDKClient.getInstance(this); UcompOSGlobalManagerProxy.getInstance().addEventListener (UcompOSGlobalManagerProxy.SCREEN_MODE_CHANGE,screenModeChangeHandler); artifact = new UcompOSArtifactProxy(); artifact.add(null,null,"http://www.server.com/enterFullScreenModeImage.png"); artifact.data = {enterFullScreenMode:true}; } private function screenModeChangeHandler(event:SDKEvent):void { switch(event.data.state) { case "fullScreen": artifact.data = {exitFullScreenMode:true}; artifact.setImage(null,"http://www.server.com/exitFullScreenModeImage.png"); break; case "normal": artifact.data = {enterFullScreenMode:true}; artifact.setImage(null,"http://www.server.com/enterFullScreenModeImage.png"); } }
An important point to make about Full Screen Mode implementations, at this time, UcompOS HTML content cannot be displayed while in full-screen mode. UcompOS HTML applications and sub-applications rely on an iframe implementation which is outside the visual scope of the Flash Player.
UcompOS HTML entities can still be interacted with via Proxy Components while in Full Screen Mode however.
The same applies to UcompOS AIR 2.0 content. It cannot be in view while the in Full Screen Mode but it can be interacted with by Proxy Component-driven interactions.
A Simple UcompOS Shared Object Impementation Example
The new Shared Object implementation in the UcompOS Rich Experience Framework provides an additional model for achieving inter-application and sub-application communication and data exchange in a manner that is grounded in the MVC (Model-View-Controller) Design Pattern.
The term Shared Object in the context of the UcompOS RXF is similar in principle to the concept of the Local Shared Object infrastructure that has been in the Flash Player since the days of Flash 4 but in our case, the UcompOS RXF Shared Object is a run-time Object that does not get saved locally (unless you of course configure your own implementation to do so).
In the UcompOS Portal is an Object that gets instantiated into the application’s run-time memory upon authentication.
This Object is then freely available for all UcompOS entities to access using the GlobalManager.setSharedObject, GlobalManager.getSharedObject, and GlobalManager.getSharedObjects public API methods sponsored by the UcompOS Portal.
The UcompOSGlobalManagerProxy class in the UcompOS SDK has an API to these methods in the public methods of the class as indicated below:
public function setSharedObject(key:String, value:Object):void public function getSharedObject(key:String):void public function getSharedObjects(keys:Array):void
One of the more important aspects of the Shared Object implementation is that the UcompOS Portal dispatches a Continuum Event of type SDKEvent.SHARED_OBJECT whenever a property of the Shared Object changes.
A Continuum Event is an event dispatched to every entity in the UcompOS Continuum.
An entity can attach a listener to the UcompOSGlobalManagerProxy Singleton listening for SDKEvent.SHARED_OBJECT events.
The event Object will have a key and a value property representing the property on the Shared Object that was just modified and its associated value.
As a very simple example of what we can do with this, I set up a basic UcompOS implementation.
The implementation involves a UcompOS background application that launches a UcompOS Window with a text area. Upon initialization, this application launches another UcompOS Window with a sub-application in it next to the initial window This sub-application also has a text area.
When text is entered in the primary application window’s text area, the text is instantly displayed in the sub-application window’s text area.
The mechanics for this employ the UcompOS Shared Object.
I’ll share the code below so you can get an understanding of how it was configured:
The Dock Manifest
<applications> <application background="true"> http://desktop.ucompass.com/Shared_Object/manifest.xml </application> </applications>
The Application Manifest
<application selfLoading="true" width="500" height="500" x="0" y="100"> <source> <base>http://desktop.ucompass.com/Shared_Object/windowA.html</base> </source> <titles> <title locale="en_US">Shared Object Example</title> </titles> </application>
The Application Code
<html> <head> <title>Window A</title> <script type="text/javascript" src="/UcompOSSDK.js"></script> <script type="text/javascript"> var g; var w; function start() { g = new UcompOSGlobalManagerProxy(); w = new UcompOSWindowProxy(); w.add("http://desktop.ucompass.com/Shared_Object/windowB.html","Echo",500,500,510,100); } function echo() { // pause for 1/100th of a second as the onkeypress event is dispatched before the textarea is updated setTimeout(function() { g.setSharedObject("echo",document.getElementById('text').value); },10); } </script> </head> <body> <u>Enter some text</u><br/> <textarea id="text" style="width: 400px; height: 400px;" onkeypress="echo();"></textarea> </body> </html>
The Sub-Application Code
<html> <head> <title>Window B</title> <script type="text/javascript" src="/UcompOSSDK.js"></script> <script type="text/javascript"> var g; var w; function start() { g = new UcompOSGlobalManagerProxy(); g.addEventListener(g.SHARED_OBJECT,echo); } function echo(data) { switch(data.key) { case "echo": document.getElementById('text').value = data.value; break; } } </script> </head> <body> <u>What you're saying in the other window</u><br/> <textarea id="text" style="width: 400px; height: 400px;"></textarea> </body> </html>
Shared Object Model Implemented into UcompOS RXF
As of the UcompOS RXF Public Alpha Release 0.1.13 that I posted last night, there is a new Shared Object implementation model.
On the UcompOS Portal, there is a run-time Object that can be used to share information across multiple entities in a UcompOS implementation.
Note that at this time this is just a run-time Object. This is NOT an implementation of the Local Shared Object that gets permanently housed in the Flash Player’s local storage infrastructure.
The UcompOSGlobalManagerProxy has three new methods: setSharedObject();, getSharedObject();, and getSharedObjects();.
I’ll describe each of them below:
public function setSharedObject(key:String, value:Object):void
This sets a property named key on the UcompOS Portal shared object and assigns it a value of value.
A very important point is that when this method is called, the UcompOS Portal dispatches a UcompOS Continuum event of type UcompOSGlobalManagerProxy.SHARED_OBJECT with properties of key and value such that each entity in the Continuum is notified when a Shared Object value is updated. This could easily enable two disparate UcompOS Applications to update their interfaces in real-time according to model changes implemented by the other.
public function getSharedObject(key:String):void
This retrieves the value on the UcompOS Portal shared object in the property key.
The information is received asynchronously by listening for an SDKEvent of type UcompOSGlobalManagerProxy.SHARED_OBJECT and testing the value of the event object’s key property.
public function getSharedObjects(keys:Array):void
This retrieves multiple shared objects simultaneously. Each member of the keys array is the name of a property on the UcompOS Portal shared object.
The data is retrieved asynchronously by listening for an SDKEvent of type UcompOSGlobalManagerProxy.SHARED_OBJECTS whose data Object will have a sharedObjects property that is an Object. Each property name requested in the keys array is a property name in the sharedObjects Object with its value the value of the corresponding item on the UcompOS Portal shared object.
A Simple HTML Digital Camera Browser
In this tutorial, we will look at a variety of core UcompOS Rich Experience Framework concepts, and in particular we will explore the mechanics of adding a desktop component to your UcompOS Rich Portal Application.
It is recommended you download the source of the application we’ll build in this tutorial at the link below so you can follow along and there is also a video demonstration of the application we are going to build in this tutorial.
Download the Simple HTML Digital Camera Browser Source Code
This tutorial assumes you have at least a basic working knowledge of:
- Adobe AIR 2.0
- Adobe Flash Builder
- ActionScript 3.0
- HTML
- JavaScript
- You should have read my blog posts or watched my video tutorials about UcompOS Proxy Components and Services Dictionaries
The goals for our application are as follows:
We want to build a simple digital camera browser that lets the user browse through and view images from their digital camera in a Rich Portal Application implementation.
We are going to keep the application as deliberately simple as possible and we are not going to address its cosmetics or aesthetics so that we can focus on providing instruction on specific UcompOS concepts and principles.
To further define the specifications for our application, we want to build a UcompOS Application that prompts the user to connect their digital camera to their computer. We want our application to be able to know when their digital camera has been connected. Then when the digital camera has been connected, we want to display the contents of their camera to them. The user should be able to easily browse through their camera’s contents and they should be able to click on a file on the camera to view it. Also, the file should be opened in the native photo-viewing application on their computer versus simply displayed in the browser.
Again, we are going to focus on a very simple implementation and will not focus on aesthetics or presentation so that we can focus more on the core UcompOS mechanics we are leveraging to build our application.
Our UcompOS application is comprised of the following components:
- An AIR 2.0 UcompOS Application built with Adobe Flash Builder 4
- An HTML UcompOS Sub Application
We’ll walk through the process of setting up and building the different pieces of the application fairly linearly and then tie it all together at the end with a screenshot of our application.
Implementation Details
The way I want to design our program, I want a UcompOS Application to load on the UcompOS Application Dock entitled “My Camera”. When this application is opened, we want it to launch our UcompOS AIR 2.0 application. I want that application to prompt the user to connect their digital camera.
When the user connects their digital camera, I want to instantly launch a UcompOS Window instance in the UcompOS Portal that displays the contents of the camera to the end user and allows them to browse through any folder structures housed on the camera and then I want to allow them to select a file to be viewed in their default photo viewing application on their computer.
When the camera is disconnected, I want to shut down the application.
Setting up the AIR 2.0 Application
While I could use a number of different technologies to build our AIR application, I am going to use Adobe Flash Builder 4. The minimum required version for a UcompOS AIR application is AIR 2.0. You can learn more about AIR 2.0 and access its run-time and SDK at http://labs.adobe.com/technologies/air2/.
The first step is to set up a Flash Builder project for my AIR application.
My project is called Camera_Example. Pictured at left is the fully expanded project in Flash Builder with all its files. 
Our main class in our AIR application is Camera_Example.mxml.
Notice in my libs folder is the file UcompOSAIRSDK.swc. This is the UcompOS SDK file for AIR applications. This file is found in the UcompOS Developers Package in the sdk/air folder that is created when you unzip the UcompOSSDK.zip file contained in the package.
Simply drag and drop that file into the libs folder of any Flash Builder (or Flex) based UcompOS AIR application.
You can also incorporate the UcompOS AIR SDK into Flash-based and HTML-based AIR applications (the SDK has no Flex dependencies) but the techniques for doing so are outside the scope of this tutorial.
Ideally, my goal is for the end user to not even have any knowledge that an AIR application is involved other than the initial install process. I want the user to operate entirely within the web browser here and my rationale for this in this tutorial is with the goal in mind of showing how multiple technologies are fusing together to create a seamless rich experience.
Of course, AIR needs to be involved because AIR is what we use to do most of the heavy lifting in our application including detecting the camera attachment/detachment, browsing through the camera’s contents, and opening individual pictures on the desktop.
From an implementation point of view, an AIR application can only be launched from the web browser following a user-initiated event such as a mouse click.
When an AIR application is configured as the base source code for a UcompOS Application, and this application appears on the UcompOS Application Dock, when the user clicks the icon in the application dock, that user event is what triggers the launching of the AIR application.
I’ll add that it is possible to implement UcompOS AIR sub-applications and the best practice for doing this is to leverage the UcompOSArtifactProxy class. This topic will be covered in a future tutorial in the near future.
Our AIR Application’s Descriptor File
Our Camera_Example-app.xml file needs a very crucial adjustment.
By default, you’ll see this XML element commented out:
<!-- <allowBrowserInvocation></allowBrowserInvocation> -->This needs to be uncommented and issued a true value:
<allowBrowserInvocation>true</allowBrowserInvocation>
This tells the AIR runtime that your application is allowed to be launched from the web browser.
If you try to instantiate the UcompOS AIR SDK in an AIR application that does not have its descriptor set up in this manner, you’ll get a compile-time error and you won’t be able to package your application.
Even though we do want our AIR application to be as innocuous as possible, if a user does stumble upon it on their main OS’ dock or in the folder on their computer where it’s been installed, I want them to see the custom icon at left and this icon and other varieties in different sizes are in the assets_embed/png folder.
Therefore in my app-descriptor file, I have implemented the following:
<icon> <image16x16>assets_embed/png/image16x16.png</image16x16> <image32x32>assets_embed/png/image32x32.png</image32x32> <image48x48>assets_embed/png/image48x48.png</image48x48> <image128x128>assets_embed/png/image128x128.png</image128x128> </icon>
AIR Application Code
Since I want the user to know as little as possible, if anything, about the presence of the AIR application, I want it to be invisible. Therefore, I’ll give the visible property in the root WindowedApplication tag a value of false. This will suppress any windows from being displayed.
Next, I want to instantiate the UcompOS SDK.
This should happen once the main application dispatches its applicationComplete event.
My root MXML tag looks like this:
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" applicationComplete="start();" visible="false">
Now let’s take a look at the private variables I am declaring in my main class. We’ll cover the purpose of each of these variables further in the tutorial:
private static var _cameraRoot:String; private var _h:UcompOSHTMLProxy; private var _d:UcompOSDockProxy;
Now let’s take a look at my start(); method:
private function start():void { AIRSDKClient.getInstance(this,new ServicesDictionary()); AIRSDKClient.getInstance().addEventListener(SDKClient.SDK_READY,ucompos_init); implementStorageVolumeListeners(); }
The instantiation of the UcompOS AIR SDK is very similar to the instantiation of the UcompOS Flex/Flash SDK that targets browser-based content with a few key exceptions:
- The Singleton class AIRSDKClient is leveraged versus the SDKClient class
- We pass this as the first parameter to the getInstance(); method and an optional Services Dictionary as the second parameter. We’ll look at the Services Dictionary for this application below.
- Internal to the AIRSDKClient class, the SDKClient class is instantiated. Once it is instantiated, it dispatches an Event of type SDKClient.SDK_READY and only then can you safely instantiate and use any of the Proxy Components built into the SDK so you must listen for this event and implement any Proxy Component-related startup code in the event handler for this event
In our start(); method, we have a call to implementStorageVolumeListeners();
Let’s take a look at the implementStorageVolumeListeners(); method:
private function implementStorageVolumeListeners():void { StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT,mountHandler); StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT,unmountHandler); }
This method leverages AIR 2.0 capabilities. StorageVolumeInfo is a Singleton class in AIR 2.0 that can have an event listener attached to it to handle StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT and StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT events. These events are dispatched whenever a new mount point is introduced to the base Operating System or whenever a mount point is removed. Our handlers for these events are mountHandler(); and unmountHandler(); respectively.
Before we take a look at mountHandler(); and unmountHandler();, let’s take a look at the ucompos_init(); method that is invoked once our UcompOS AIR SDK has been fully initialized and we are ready to interact with it fully:
private function ucompos_init(event:Event):void { _h = new UcompOSHTMLProxy(); _h.alert("Please connect your digital camera to your computer"); _d = UcompOSDockProxy.getInstance(); }
In this method, we are creating an instance of UcompOSHTMLProxy. This class has a number of methods that let us execute common JavaScript methods such as alert();, prompt();, and confirm(); in the UcompOS Portal’s HTML wrapper file.
I am choosing to use a regular JavaScript alert to prompt the user to connect their digital camera to their computer.
I am also going to create a reference to the UcompOSDockProxy Singleton and my reasons for doing this will become clear below.
Now, let’s take a look at the mountHandler(); method.
private function mountHandler(event:StorageVolumeChangeEvent):void { _d.setAlert(true); _cameraRoot = event.rootDirectory.nativePath; w = new UcompOSWindowProxy(); w.add("http://desktop.ucompass.com/Camera_Example/Camera_Browser.html",event.rootDirectory.name,400,400); var object:Object = API.getFiles({}); }
This method is invoked when the user attaches a new storage volume to their computer. It is worth mentioning at this time that the simple example being developed here could be used to browse any type of removable storage. We just happen to be focusing on a scenario that would involve a digital camera.
The implementation details of mountHandler(); are straightforward. First, we want to call the setAlert(); method of the UcompOSDockProxy instance and pass a value of true to it. This makes the icon associated with this application on the UcompOS Portal Application Dock “Chirp” and glow drawing the user’s attention to it.
Then, we want to set the _cameraRoot property to event.rootDirectory.nativePath. The StorageVolumeChangeEvent contains a rootDirectory property which is of type File and represents the file location on the file system where the base of the mount point is located.
Next we create an instance of UcompOSWindowProxy of 400 x 400 and load our HTML sub-application into it. Our HTML sub-application will be the actual camera browser that the end user interacts with and we’ll review that later.
Our unmountHandler(); method is extremely simple:
private function unmountHandler(event:StorageVolumeChangeEvent):void { w.close(); UcompOSGlobalManagerProxy.getInstance().quitApplication(); }
This calls the close(); method on our UcompOSWindowProxy instance and then quits out of the application once the camera is removed.
Way back when we instantiated the UcompOS AIR SDK, we passed a new instance of ServicesDictionary to the instantiation method.
There are two public API methods we need our AIR application to sponsor and we are calling them Camera.getFiles and Camera.openFile.
Camera.getFiles will take the path to a given folder on the file system and return a list of the contents of that folder.
Camera.openFile will take the path to a given file on the file system and open it up with the application on the computer that the file is associated with.
First, let’s take a look at our ServicesDictionary:
package cameraexample { import com.ucompass.ucompos.sdk.server.AbstractServicesDictionary; public class ServicesDictionary extends AbstractServicesDictionary { public function ServicesDictionary() { _map = { 'Camera.getFiles': { static:true, classRef:API, method:'getFiles', description:'Lists files in a folder' }, 'Camera.openFile': { static:true, classRef:API, method:'openFile', description:'Opens a file in its native application' } } } } }
As you can see, both of our public API methods are housed as static methods in an API class.
Here is the method that corresponds to the Camera.getFiles public API method:
public static function getFiles(data:Object):Object { var folder:String = data.folder; if(!folder) { folder = Camera_Example.cameraRoot; } var file:File = new File(folder); var files:Array = []; for(var i:uint = 0;i<file.getDirectoryListing().length;i++) { var _file:File = file.getDirectoryListing()[i] as File; files.push({name:_file.name,isDirectory:_file.isDirectory}); } return {eventType:"files",files:files,folder:folder}; }
The Camera.getFiles public API method expects a folder property to be on the Object parameter passed to the method. If it’s not, it retrieves the contents at the base of the camera.
Back in our base application, we have a static getter function that retrieves the value of cameraRoot (which is why we established the value of _cameraRoot in the mountHandler(); method).
Our method simply builds an Array of Objects each having a name property and a Boolean to indicate if the item is a directory.
In our return Object, we return the eventType property set to files as well as our Array of files and a reference to the folder whose contents were retrieved. We’ll learn more about the purpose of this eventType property when we look at our HTML sub-application.
Our public API method Camera.openFile is extremely simple:
public static function openFile(data:Object):Object { var file:File = new File(data.file); file.openWithDefaultApplication(); return {}; }
That’s it for our AIR application. We are ready to package it with adt or the compiler built into Flash Builder.
I am packaging it into a file named Camera_Example.air and it will be reachable at a network URL of http://desktop.ucompass.com/Camera_Example/Camera_Example.air
Our HTML Sub-Application
Now we are ready to build our HTML sub-application which will be the interface the end-user actually interacts with.
The URL of our application will be at http://desktop.ucompass.com/Camera_Example/Camera_Browser.html. This is the URL passed to the add(); method of our UcompOSWindowProxy instance of our mountHandler(); method in our AIR application.
We want this application to be extremely simple.
We just want it to list out the contents of our digital camera and present them as files or folders.
When the user clicks on a folder resource, we want to display the items in that folder. When they click on a file resource, we want to open that file in the application that the file is associated with on their computer.
From an implementation point of view, when we click a folder, we are going to call our AIR application’s public API method Camera.getFiles and when we click a file we are going to call Camera.openFile.
The first thing we are going to do in our HTML sub-application is implement the UcompOS JavaScript SDK:
<script type="text/javascript" src="/UcompOSSDK.js"></script>
While it is not a requirement, best practice recommends you place the UcompOS JavaScript SDK and SWF files in the root directory of your webserver.
Here are two variables we initialize:
var camera; var d;
When the UcompOS JavaScript SDK has initialized, it looks for a start(); method in the application its implemented into.
Our start(); method is as follows:
function start() { d = new UcompOSDockProxy(); camera = new Camera(); camera.addEventListener("files",filesHandler); camera.getFiles(); }
We are creating an instance of UcompOSDockProxy which we’ll use to suspend the Dock alert we set in our AIR application.
More importantly, we are creating an instance of Camera, and adding an event listener to it and calling its getFiles(); method.
Camera is a Proxy Component we have built in our HTML sub-application. A Proxy Component is an interface to the public API methods located in other entities.
In our case, the Proxy Component Camera in our sub-application is the interface to the Camera.getFiles and Camera.openFile public API methods sponsored by our AIR application.
Let’s take a look at our Proxy Component Camera and walk through it as the mechanics of Proxy Components are very important to understand:
function Camera() { this.setDestination(parentConnectionId); this.getFiles = function(folder) { this.call("Camera.getFiles",{folder:folder}); } this.openFile = function(file) { this.call("Camera.openFile",{file:file}); } } Camera.prototype = new AbstractProxyComponent(); Camera.prototype.constructor = Camera;
The last two lines of the class would be analogous to saying Camera extends AbstractProxyComponent in ActionScript 3.0. Any Proxy Component must extend AbstractProxyComponent (in ActionScript as well as JavaScript).
In our class implementation, we pass the parentConnectionId property to the setDestination method of our class (which is a method inherited from AbstractProxyComponent).
Since our sub-application was launched by our AIR application, in the context of the UcompOS Continuum, we know that our AIR application is the parent of the sub-application in scope and we can safely use the UcompOS JavaScript SDK global variable parentConnectionId (this is analogous to the public property SDKModel.getInstance().parent in the UcompOS AIR/Flash/Flex SDK).
Our Camera class also implements two methods: getFiles(); and openFile();. As you can see by referring to the class code, both of these call the public API methods in our AIR application Camera.getFiles and Camera.openFile by using the call(); method in our class that is inherited from AbstractProxyComponent.
Another very important point, in our start(); method, refer again to this command:
camera.addEventListener("files",filesHandler);
This tells our instance of our Camera class to pass any SDKEvent’s of type “files” to the method filesHandler.
If you refer to our AIR application public API method Camera.getFiles, you’ll recall its return Object sets an eventType property to “files“.
The return Object of the public API method Camera.getFiles is passed to our filesHandler(); method.
Here is the code of our filesHandler(); method:
function filesHandler(data) { var e = document.getElementById('files'); e.innerHTML = '<p><a href="javascript:void(0);" onclick="getFiles();">Camera Root</a><p/><hr/><p/><u>Current folder: '+data.folder+'</u>'; for(var i = 0;i<data.files.length;i++) { if(data.files[i].isDirectory) { e.innerHTML+='<p/><img src="icons/folder.gif"/> <a href="javascript:void(0);" onclick="getFiles(\''+data.folder+'/'+data.files[i].name+'\');">'+data.files[i].name+'</a>'; } else { e.innerHTML+='<p/><img src="icons/file.gif"/> <a href="javascript:void(0);" onclick="openFile(\''+data.folder+'/'+data.files[i].name+'\');">'+data.files[i].name+'</a>'; } } }
Notice we are referencing the files and folder properties of the Object passed to filesHandler();. We iterate on the files property which we know from our inspection of our AIR application’s public API method Camera.getFiles is an Array and we further know that each Object in this Array has a name:String and isDirectory:Boolean property.
We create simple HTML that displays the name of the files and folders with the appropriate icons and calls the methods getFiles(); for folders and openFile(); for files.
These methods appear below:
function getFiles(folder) { d.setAlert(false); camera.getFiles(folder); } function openFile(file) { camera.openFile(file); }
In getFiles(); as well as openFile();, notice we are calling the setAlert(); method of the UcompOSDockProxy and passing it a value of false. This is to cancel the Dock alert we set on the UcompOS Portal’s Application Dock that we set in the AIR application the first time the user clicks on a resource.
That’s all there is to our HTML sub-application.
Configuring Everything as a UcompOS Application
Now we need to set up our application manifest for our simple Digital Camera browser application.
This should be very straightforward if you’ve reviewed some of my other UcompOS tutorials but for AIR applications, there are some special configurations you need to make:
<application> <source> <base>http://desktop.ucompass.com/Camera_Example/Camera_Example.air</base> <params> <param> <name>appId</name> <value>Camera-Example</value> </param> <param> <name>publisherId</name> <value>0E5CA255707A7E3F70F12D38B16B8D2A4C17413C.1</value> </param> </params> </source> <titles> <title locale="en_US" default="true">My Camera</title> </titles> <icons> <icon locale="en_US" default="true">http://desktop.ucompass.com/Camera_Example/icons/camera.png</icon> </icons> </application>
Notice the appId and publisherId parameters you must include in the <params/> element of the manifest.
IMPORTANT: At the time I am authoring this tutorial, the evening of December 27, 2009, the publisherId field faces an uncertain future in AIR 2.0 and may be deprecated. At present, you can find your publisherId by looking in the $APP/Contents/Resources/META-INF/AIR/publisherid file in the application installation directory for your installed application. The appId and publisherId parameters must be included otherwise, the UcompOS Portal will not be able to successfully launch your UcompOS AIR application. Any changes to the AIR 2.0 implementation specifics for publisherId will be blogged about here and updates will immediately be made to the UcompOS RXF accordingly.
Next we’ll take a very quick peak at my Dock Manifest:
<applications> <application> http://desktop.ucompass.com/Camera_Example/manifest.xml </application> </applications>
In this case, I obviously just have a single application I am loading into my UcompOS Portal implementation that is our simple Digital Camera browser example.
Screenshot of the Application
Conclusion
In this tutorial, we created a deliberately simple application to demonstrate a number of core UcompOS RXF concepts and principles – particularly integrating the desktop into a UcompOS Rich Portal Application implementation.


