The Mechanics of UcompOS Desktop (AIR 2.0) Applications

One of the more important and high-impact features of the UcompOS Rich Productivity Framework is the ability for UcompOS Rich Portal Applications to extend to the desktop.

The technology leveraged to accomplish this is Adobe AIR 2.0.

When you consider the presence of AIR 2.0′s new NativeProcess class, which enables an AIR application to target the native Operating System’s base functionality, the prospect of building industrial-strength applications that place very few limits on the developer and allow their imaginations to extend to new heights becomes very attainable.

In this post I want to talk a bit about the mechanics of AIR applications in the UcompOS RPF.  Understanding these mechanics can help you conceptualize and then deploy the most compelling rich experiences for your UcompOS Rich Portal Application.

The first thing to understand is that in the context of the UcompOS RPF, any AIR application will simply be another UcompOS entity.

A UcompOS entity represents any application or sub-application running in a UcompOS implementation.  The UcompOS Portal itself is a UcompOS entity.  All the UcompOS entities running at one time in the UcompOS RPF comprise what I refer to as the UcompOS Continuum as you know if you’ve been reading my blog postings.

There are a few different ways to launch a UcompOS AIR entity – as an application from the UcompOS Application Dock, using the UcompOSAIRProxy class, or by creating a UcompOS Artifact the user can click on to launch the AIR application.

First, let’s understand the mechanics at work here.  The UcompOS Portal is obviously a Flash-based browser entity.  In order for a browser-based Flash application to launch an AIR application, a couple things need to happen.

First, the author of the AIR application has to articulate that the application is allowed to be launched by the browser.

This is very easy to accomplish.

You simply need to have the following element as a direct child of the root element in the application’s manifest descriptor file.

<allowBrowserInvocation>true</allowBrowserInvocation>

The manifest descriptor file is NOT the UcompOS application manifest for the application.  It is the XML file that articulates the base-level behaviors and properties of an AIR application.

If you try to compile an AIR application that instantiates the UcompOS AIR SDK that does not have the above provision made, you’ll get a compilation error.

Does this mean that the only way your UcompOS AIR applications should be launched is by the browser via the UcompOS Portal?

Not necessarily.  There are ways you can set it up so that when your UcompOS AIR application is launched directly by the end-user, it opens the UcompOS Portal to a precise configuration.

It can even be configured so that your AIR application can save custom file types on the user’s desktop that when double-clicked upon can launch the AIR application which in turn launches the UcompOS Portal and then feeds one or more applications with the data contained in the desktop file.

These are a bit more advanced topics that I’ll definitely devote coverage to these capabilities sometime in the next month or so.

The next thing we need to understand is that the browser can only launch an AIR application following a user-triggered mouse click event.

When you configure an AIR application as the base source for a UcompOS Application in that application’s UcompOS Application Manifest and have the application show up on the UcompOS Application Dock, the UcompOS Portal is configured such that the clicking of the application icon on the dock is the click that is used to immediately launch the AIR application.

When you configure the Application manifest that will use an AIR application as its base source code, there are some parameters you need to incorporate into the manifest.

Below is a sample application manifest from a UcompOS AIR application that provides an example of this:

 
<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>

Note that you must at least include the appId and publisherId parameters.

There are a couple other ways to launch a UcompOS AIR application as a sub-application.

The first is with the UcompOSAIRProxy class which is built into the UcompOS SDK.

The UcompOSAIRProxy Class

The UcompOSAIRProxy class deserves some attention in this posting.  It is a Proxy Component that is used to create a virtual representation of a UcompOS AIR application in a UcompOS entity.  Inspecting the class is a good way to learn more about the mechanics of UcompOS AIR Applications.

Let’s look at the signature of the UcompOSAIRProxy‘s constructor:

public function UcompOSAIRProxy(appSource:String, appId:String,
      publisherId:String, airVersion:String, host:String="127.0.0.1", 
      operationPort:uint=0, validationPort:uint=0)

Let me touch on each of the parameters of the UcompOSAIRProxy‘s constructor:

  • appSource: This is the URL of the .air package file associated with the AIR application
  • appId: This is the application id of the AIR file as it exists in the AIR application’s descriptor file
  • publisherId: This is the value of the publisherid that is found in the file at $APP_INSTALL_DIR/Contents/Resources/META-INF/AIR/publisherid - WARNING: the publisherid will be deprecated in AIR 2.0 soon and I will then remove it as a required parameter in the UcompOSAIRProxy’s constructor at that time
  • airVersion: This is the minimum version of AIR to run the application.  AIR 2.0 is the UcompOS minimum requirement.
  • host: UcompOS AIR Applications rely on the ServerSocket class to communicate with other UcompOS entities.  By default, the UcompOS AIR application binds to 127.0.0.1 on the local machine and other UcompOS entities target that destination in socket transmissions.  However, you can override this and bind to some public facing IP address.  (NOTE: The implications here are remotely deployed UcompOS AIR Applications)
  • operationPort and validationPort: UcompOS AIR Applications bind to two ports – one for validation purposes and the other for actual UcompOS data transmissions.  By default, the UcompOS SDK will start with port number 1024, and climb upward using the first two openly available ports for operation and validation.  These port numbers can be explicitly articulated.

A UcompOSAIRProxy class has three specific types of SDKEvent instances associated with it:

  • UcompOSAIRProxy.AIR_APPLICATION_LAUNCHED: Dispatched when the AIR application has launched
  • UcompOSAIRProxy.AIR_APPLICATION_QUIT: Dispatched when the AIR application has quit
  • UcompOSAIRProxy.AIR_APPLICATION_STATUS: Dispatched following a call to the UcompOSAIRProxy instance’s getStatus(); method

An instance of UcompOSAIRProxy also has a getter/setter on the Boolean property status which returns true when the AIR application is running.

The UcompOSAIRProxy class has two methods key to our discussion: launchApplication(); and quitApplication();

You could use launchApplication(); to launch a UcompOS AIR Application as a sub-application.

When you do, however, the UcompOS Portal must find a way to meet the compulsory user-triggered mouse-click requirement mentioned above.

It does this by throwing an Alert onto the interface of the UcompOS Portal asking the user if they agree to allow the AIR application to launch and then by clicking “OK”, the user is providing the necessary mouse-click.

The full signature of the launchApplication(); method is as follows:

public function launchApplication(message:String=null):void

The message parameter allows you to display a custom message on the Alert used in this scenario.

There is an alternative way to launch a UcompOS AIR sub-application that I’ll cover next.

Using a UcompOS Artifact to launch an AIR Application

By now, you know that the UcompOSArtifactProxy class in the UcompOS SDK provides a way to implement the equivalent of “desktop widgets” on the UcompOS Portal interface.  I prefer the term “Artifact” over “Widget” however as I feel widget has functionality implied, whereas a UcompOS Artifact can be functional and can be a full-fledged UcompOS sub-application or it can be strictly presentational.

As is the case with all Proxy Components (i.e. any class that extends AbstractProxyComponent), UcompOSArtifactProxy instances have a data property.

When you set the data property of a UcompOSArtifactProxy instance, this data property is housed on the actual artifact itself on the UcompOS Portal.

There are two special properties that can be added to this data Object: launchAIRApplication and quitAIRApplication

The values of these properties would be the uuid property of the UcompOSAIRProxy instance that was to be launched upon a mouse click of the artifact involved.

So the following code may be something you’d see in practical operation:

var myAIRAppProxy:UcompOSAIRProxy = new UcompOSAIRProxy("http://apps.mysite.com/myApp.air","My-App","My-Publisher-Id","2.0");
var myArtifactProxy:UcompOSArtifactProxy = new UcompOSArtifactProxy();
myArtifactProxy.add("Label for my Artifact",imageContent);
myArtifactProxy.data = {launchAIRApplication:myAIRAppProxy.uuid};

In the above example, as soon as the artifact is clicked on, the AIR application will launch.

You could also do something like the following too:

private function launchHandler(event:SDKEvent):void
{
 switch(event.type)
 {
 case UcompOSAIRProxy.AIR_APPLICATION_LAUNCHED:
  myArtifactProxy.setImage(imageContentAppOn);
  myArtifactProxy.data = {quitAIRApplication:myAIRAppProxy.uuid};
 break;
 case UcompOSAIRProxy.AIR_APPLICATION_QUIT:
  myArtifactProxy.setImage(imageContentAppOff);
  myArtifactProxy.data = {quitAIRApplication:myAIRAppProxy.uuid};
 break;
 }
}

In the above example, we are listening to see when the AIR application is launched as well as when it quits.  We then replace the artifact image with a status indicator using the UcompOSArtifactProxy‘s setImage(); method so it easily shows the user if the associated AIR application is running or not.

As an example of how I am using this concept myself, in the Educator 2 Rich Portal Application I am building entirely on top of the UcompOS RPF, on the taskbar (an artifact container with a number of artifacts housed in it), there is an icon to launch the “Educator 2 Desktop”, which is obviously an AIR application.

When they click this icon, the Educator 2 Desktop immediately launches and the icon turns green indicating the feature is connected.  When they click it again, the Educator 2 Desktop quits and the icon turns red indicating the feature is disconnected.

Some Tips and Tricks for Working with the UCompOS Framework

As I’ve experimented with some various aspects of the UCompOS framework, I have picked up on a couple of subtle nuances that I thought I’d share.

First, if you haven’t yet switched to the Flash Builder 4 Beta from Flex Builder 3, doing so is probably a good idea.  One key reason (beyond enhanced functionality) is that I have noticed a significant reduction in the sizes of exported Flash Builder 4 swf files.  For example, one artifact I had created in Flex Builder 3 was about 300 k when I exported it, but this was reduced to about 70 k once I ported it to Flash Builder.  I was surprised at the huge difference.

Speaking of Artifacts, after talking with Edward Mansouri at some length about some of the file size issues I mentioned above, and his recommendation is to use Flash CS 4 or 5 (if you’re lucky enough to be in the private beta program) for artifact development wherever possible.  Flash can get your file sizes down to roughly a tenth of what I can get them down to in Flash Builder, which is a big deal if you have a lot of artifacts in your implementation.

I want to move on a little bit and talk about developing applications using the AIR 2.0 SDK, which is the only way to take advantage of some of the AIR functionality in the UcompOS framework.  The AIR 2.0 SDK and runtime can both be downloaded from http://labs.adobe.com/downloads/air2.html (you will need both).  Once you have downloaded it, go ahead and install the runtime (which installs automatically).  However, you will need to need to do some tweaking to Flash Builder in order to install the SDK properly (I will only cover Flash Builder in this blog post).  What you need to do is find the “sdks” folder in your Flash Builder folder and open it.  It should look something like this:

pic1

What you are going to want to do next is make a copy of the 4.0.0 folder.  In my case I copied it and named it “4.0.0 AIR 2.0″

pic2

This will be the folder where we copy the files from the AIR 2.0 SDK into.  Simply open the ZIP file containing the AIR 2.0 SDK and copy and paste the files into the 4.0.0 AIR 2.0 folder.  If you are prompted, be sure to overlay any old files with the new ones from the ZIP file, or you will run into problems.

pic3

Now, you will need to configure Flash Builder to use the new SDK for applications.  To do this, open up Flash Builder, and create a new Flex project.  On the dialog that comes up, select the “Configure Flex SDKs” on the middle right of the dialog:

pic4

On the dialog that comes up, click the “Add” button, and browse to the 4.0.0 AIR 2.0 folder you created earlier:

pic5

pic6

Then, select the “Ok” button, and your dialog should look like this:

pic7

Once you’re done, click the “Apply” button, and then click “Ok”.  Then, under the “Flex SDK version” part of the dialog, select the “Use a Specific SDK” radio button, and select your new SDK from the select box.  From here on out, setting up your project is normal.  Note that unless you made your new SDK the default, you will have to manually select your SDK every time you create a new application that leverages the AIR 2.0 SDK (including UcompOS applications).

pic8

The final topic I wanted to touch on was getting your publisherID that you need to add to your application descriptor file in order to make it work with UCompOS.  The easiest way to do this is to export a release build of your AIR application and install it.  Once that’s done, you will need to browse to the location where you installed the AIR application, and browse to the “META-INF/AIR” folder.  In that folder, you will see a file called “publisherid” that contains the value that you need to set for publisherID in your AIR application descriptor file (don’t forget to set allowBrowserInvocation to true!):

pic9

I hope some of these tips help you out as you work with UcompOS.

A Simple HTML Digital Camera Browser

In this tutorial, we will look at a variety of core UcompOS Rich Productivity 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. flash_builder_project

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.

cameraEven 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 RPF 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

demo

Conclusion

In this tutorial, we created a deliberately simple application to demonstrate a number of core UcompOS RPF concepts and principles – particularly integrating the desktop into a UcompOS Rich Portal Application implementation.

The UcompOS RPF and Adobe AIR 2.0

One of the more exciting features of the UcompOS Rich Productivity Framework is the ability for UcompOS Applications to exist as Adobe AIR 2.0 applications.

Therefore, the browser-based UcompOS Portal can launch and control these applications which are running on the desktop.  The implications of this are very exciting to me as we can implement a Rich Portal Application that gives us the best of a web application with basically full range of the Desktop to create whatever experience we want or need to.

Further, using the UcompOS SDK, a UcompOS Application can launch a UcompOS AIR application as a sub-application.

Over the next week or so, I’ll publish some tutorials that highlight some of the really cool things you can do with this capability, but clearly the key value-added proposition here is a portal continuum implementation that transcends the browser and extends to the Desktop.

I thought I’d start my AIR-related blogs with a technical discussion of why I have chosen to require any UcompOS AIR Application to be based on AIR 2.0 or greater versus the AIR 1.x versions that are now in production.

The UcompOS AIR SDK in fact will only work when embedded in an AIR Application created with AIR 2.0 or greater.  If you try to instantiate it, for instance, in an AIR 1.5.3 application you’ll get a compile error.

AIR 2.0 is in a state of public beta right now. You can download the runtime or the SDK at the following link:

http://labs.adobe.com/technologies/air2/

There are many advancements and new features that have been made in AIR 2.0 but I only really wish to focus on one of them here and that is the one that led me to make the decision I made which is to require at least AIR 2.0.

The capability I am speaking of is the new ServerSocket class.

Perhaps one of the most powerful advancements in Flash technology (in my opinion anyway) came way back in Flash 5 near the turn of the century with the ability for Flash content to make socket connections to a socket endpoint thus ushering in an era of bi-directional socket applications.  This has paved the way for Flash applications to extend to the realm of chat and IM clients, telephony-based applications, etc.

With AIR 2.0, in addition to the application being able to connect to a socket endpoint, it can itself serve as a socket endpoint.

The UcompOS SDK is what makes communication between entities in the UcompOS Continuum possible – specifically the SDKServer and SDKClient classes in the SDK.

Each entity that has the UcompOS SDK implemented – Flash, Flex, AIR, or JavaScript – is equipped to operate both as a server and a client in terms of its ability to receive as well as send commands from / to other entities in a particular UcompOS implementation.

The transmission framework in the SDK is based on the LocalConnection infrastructure of the Flash Player.  I chose to go this route because I was looking for something Lowest Common Denominator and LocalConnection has been around in the Flash Player for a very long time.

This would simplify the ability to use the same SDK in content ranging from ActionScript 1.0 through Flex 4.

When I went to implement my SDK in AIR Applications, I was confronted with some harsh limitations of the LocalConnection.

And I am not even talking about the limitation of being able to send no more than a 40 K chunks of data at a time – that limitation was easily side-stepped.

What I would find is that if you tried to send more than one SDK transmission at a time to an AIR application via the original LocalConnection strategy, the AIR application would cease being able to participate in any further LocalConnection activity until that application would quit and be restarted.  The ability for an AIR application to “queue” inbound LocalConnection requests seems broken and it seems that if a synchronous operation is processing in an AIR application and another LocalConnection request arrives during that time period, there is a total breakdown in the ability for the AIR application to participate in any further LocalConnection activity.

This apparently is a known issue and is said to be the case with any and all Flash-based content – running in the AIR runtime or in the Flash Player in the browser – however I have primarily only been able to observe this for myself with AIR-based content.

Obviously, this is unacceptable.  In an ambitious UcompOS implementation, you may have one AIR application that is being bombarded with requests from dozens of UcompOS Applications simultaneously.

With the limitations of LocalConnection, this would break down the Continuum and the ability for AIR applications to be a part of the Continuum very quickly.

I have engineered an alternative to LocalConnection to use in AIR applications at least for in-bound traffic.

When the UcompOS SDK is instantiated in an AIR 2.0 application, the application binds and listens on two ports – the first two available ports on the computer above port 1024 (though you can explicitly set these port numbers but more on that in another blog entry).

One port is used for validation purposes (as Flash Player socket activity is governed by very strict protocols these days) and the other is used for transmission of actual SDK messages.  Whether it is intentional or not I don’t know, but I never was able to get validation and SDK data transmission activity working on the same port so by strict convention I implemented the need for two ports.

In the SDK, if the destination of SDK activity is an AIR application, it automatically sends the message via a class in the SDK that extends Socket such that any in-bound SDK activity arriving to a UcompOS AIR Application arrives via the new ServerSocket capabilities in AIR 2.0.

Any other activity in the UcompOS Continuum leverages the legacy LocalConnection architecture of the Flash Player.

I have been very impressed by the durability of the ServerSocket architecture I have implemented here.  I have successfully transmitted very large files as ByteArrays from a browser-based UcompOS Flex application to a UcompOS AIR 2.0 application without any issues whatsoever.

LocalConnection is not without its issues however, but the UcompOS SDK has managed to sidestep most all of these issues.

So in summary, the reason I have chosen to require AIR 2.0 for UcompOS AIR Applications exclusively has to do with the introduction of the great new ServerSocket class.  However, make no mistake about it.  The great other new features of AIR 2.0 are definitely game-changers and I encourage you to check them out.

In the near future, I’ll post about UcompOS JavaScript applications and how I have implemented them into the UcompOS RPF.