Thanks, Mister! Arcosanti by Jackie Cortez

Secure login using the EncryptedLocalStore in Adobe AIR

August 18th, 2008  |  Published in AIR, Flex

Adobe AIR has a quick and safe way to store encrypted user data when building applications. Data such as login and password can be persisted in the application using the EncryptedLocalStore available for AIR applications.

To demonstrate this, I wrote a quick demo application that will store username and password after users login. When the application is launched again, the same username and password will be retrieved from the EncryptedLocalStore and propagate the login and password text boxes. User's also have the option to reset the data and store a new username and password, removing the stored data from the EcryptedLocalStore.

This can be handy when you want your application to do auto-login, using the existing stored information from the user's previous session, and of course, the stored data is encrypted.

Example:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    width="410" height="260" creationComplete="initComponent()">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
           
            private function initComponent():void
            {
                var password:ByteArray = EncryptedLocalStore.getItem("password");
                var username:ByteArray = EncryptedLocalStore.getItem("username");
               
                try{
                    if(password.length && username.length){
                        passwordInput.textpassword.readUTFBytes(password.length);
                        nameInput.text = username.readUTFBytes(username.length);
                    }
                } catch (e:Error){Alert.show(e.message);}
            }
           
            private function onLogin(event:Event):void
            {
                if(nameInput.text == "" || passwordInput.text == ""){
                Alert.show("Please enter username and password");
                    return;
                }
               
                try {
                    EncryptedLocalStore.reset();
                   
                    var bytes:ByteArray = new ByteArray();
                        bytes.writeUTFBytes(nameInput.text);
                       
                    EncryptedLocalStore.setItem("username", bytes);
                   
                        bytes = new ByteArray();
                        bytes.writeUTFBytes(passwordInput.text);
                       
                    EncryptedLocalStore.setItem("password", bytes);
                   
                    Alert.show("Writing username and password to local store.");
                   
                } catch(e:Error){Alert.show("Error writing store: " + e.message)}
            }
           
            private function onReset(event:Event):void
            {
                passwordInput.text = "";
                nameInput.text = "";
                nameInput.setFocus();
                EncryptedLocalStore.reset();
            }
           
            private function onKeyDown(event:KeyboardEvent):void
            {
                if ( event.charCode == Keyboard.ENTER) {
                    onLogin(null);
                }   
            }
        ]]>
    </mx:Script>
    <mx:Canvas width="380" height="240" horizontalCenter="0" verticalCenter="0">
        <mx:VBox verticalAlign="middle" horizontalAlign="left" verticalGap="0" horizontalCenter="0" bottom="142">
            <mx:Label id="userText" text="username"  creationComplete="nameInput.setFocus()"/>
            <mx:TextInput id="nameInput" creationComplete="{nameInput.setFocus()}" keyDown="onKeyDown(event)"/>
        </mx:VBox>
       
        <mx:VBox verticalAlign="middle" horizontalAlign="left" verticalGap="0" horizontalCenter="0" bottom="95">
            <mx:Label text="password"  />
            <mx:TextInput id="passwordInput" displayAsPassword="true" keyDown="onKeyDown(event)"/>
        </mx:VBox>
       
        <mx:ControlBar  bottom="16" width="161" height="35" horizontalCenter="0">
            <mx:Button id="submitButton" label="submit" click="onLogin(event)"  textAlign="center" />
            <mx:Button id="resetButton" label="reset" click="onReset(event)" textAlign="center"/>   
        </mx:ControlBar>
    </mx:Canvas>
</mx:WindowedApplication>

To use the example code, just create a new AIR application in Flex Builder and paste the code into your main MXML file.

-Mister

Tags: , ,

Detecting Local Drives with Adobe AIR

August 6th, 2008  |  Published in AIR, actionscript3

Detecting a listing for local drives using Adobe AIR is fairly straight forward using the Adobe AIR File function getDirectoryListing for Mac or getRootDirectories for PC. The following code will get you a listing of the local drives for Mac or PC:

import flash.filesystem.File;
import flash.system.Capabilities;
 
private function getDrives():void
{
     var os:String = Capabilities.os.substr(0, 3).toLowerCase();
     var currentDrives:Array = (os=="mac") ? new File('/Volumes/').getDirectoryListing() : File.getRootDirectories() ;
     trace("\ncurrent drives: " + currentDrives.toString());
}

However, what I wanted to do was automatically refresh the the drive listing on a set time interval so I could detect newly added or removed drives. Here is a sample for that:

import flash.events.TimerEvent;
import flash.filesystem.File;
import flash.system.Capabilities;
import flash.utils.Timer;

private function setDriveWatcher():void
{
     var timer:Timer = new Timer( 2000 );
     timer.addEventListener( TimerEvent.TIMER, timerHandler );
     timer.start();
}

private function timerHandler(event : TimerEvent):void
{
     var os:String = Capabilities.os.substr(0, 3).toLowerCase();
     var currentDrives:Array = (os=="mac") ? new File('/Volumes/').getDirectoryListing() : File.getRootDirectories() ;
     trace("\ncurrent drives: " + currentDrives.toString());
}

Everything seems to work great on my Mac, but when I tested the application on my PC I heard a strange constant grinding noise coming from the A: drive, you know, that dinosaur floppy drive you probably forgot your PC had. It seems that when AIR gets a listing of all the local drives on the user machine, it pings the A: drive which causes it to make the noise. Remember when you try to access a PC floppy drive when it was empty it makes a lovely grinding noise that sounds like the drive is eating itself.

I suspect this issue arises because I am doing something with the directory detection feature of AIR that was not intended by Adobe, that is to repeatedly get listings of all local drives. However, I am trying to make a hack for a feature that Adobe probably should have included with AIR, automatically detecting drives being added and removed from the users machine. This is the issue that probably needs to be addressed by Adobe. I could not find any way to just skip certain drives, like the A: drive to avoid this issue either.

This issue does not happen on all PC's with floppy drives, just a handful that I have tested. It seems to be less of a problem when users have an actual floppy in the drive, but its not a total solution. It's slightly annoying to auto-detect the adding or removing of drives, so I recommend making a manual drive refresh option for users, something they can click one whenever they add or remove an external drive. Now, if I can only get AIR to recognize the type of drives automatically, like the difference between a hard drive, usb drive, and my camera, that would be nice.

-Mr

Google crawls Flash, mostly…

July 1st, 2008  |  Published in Flex News

Today Google reported that they now have a new algorithm for indexing the textual content of Flash files. This has always been something businesses have been concerned with, how to do SEO (Search Engine Optimization) with Flash. Flash developers (and Flex) would have to place the content of the Flash into a page so that search engines could index it, now it looks like Google has stepped up and created a way to index the content of the Flash file itself, right down to the menu items. Take a look at the article on Google's blog.

Google came out with another post today to answer questions around indexing Flash content. The question answer section of the post was great until you get to the very last question:

Q: What are the current technical limitations of Google's ability to index Flash?
There are three main limitations at present, and we are already working on resolving them:

1. Googlebot does not execute some types of JavaScript. So if your web page loads a Flash file via JavaScript, Google may not be aware of that Flash file, in which case it will not be indexed.
2. We currently do not attach content from external resources that are loaded by your Flash files. If your Flash file loads an HTML file, an XML file, another SWF file, etc., Google will separately index that resource, but it will not yet be considered to be part of the content in your Flash file.
3. While we are able to index Flash in almost all of the languages found on the web, currently there are difficulties with Flash content written in bidirectional languages. Until this is fixed, we will be unable to index Hebrew language or Arabic language content from Flash files.

We're already making progress on these issues, so stay tuned!

Hmm, if Google doesn't index JavaScript and you use something like SWFObject to embed your Flash then you are pretty much back to where you were before Google made the improvements. For issue 1, this might not be a problem for smaller gadgets on 3rd party sites, but you don't want your main application site to have that annoying click through problem caused by the patent law suit by EOLAS against Microsoft. Remember those times, before the issue was solved with JavaScript. For issue 2, your content will get indexed, but it won't be considered part of your Flash content, this might effect some metrics or Omniture reporting (which means money).

Well, I guess its partial good news, at least they say they are working on those isues.

- Mr

Xdrive AS3 Library

June 17th, 2008  |  Published in AIR, API, ActionScript, Flash, Flex, Flex Development, Google Code, Xdrive, as3xdrivelib

I have not posted in a long long while it seems. That would be nice if I were just hanging out on a beach some place. However, I have been busy doing lots of things for work and working on a new AS3 library.

I have been developing for the past few months an AS3 library (as3xdrivelib) wrapper for the Open Xdrive JSON API. Xdrive is an online storage platform and they offer a free JSON API for developers to create storage and other types of applications upon their service. With Xdrive, each user gets a 5GB account for free, with additional costs for more storage.

I finally got around to making an AS3 library that wraps much of the Xdrive API and abstracts the inner workings from those developing Flex, AIR, or Flash applications. The Xdrive API is extremely rich, it offers ways for users to share folders, collaborate on shared collections (look for a later post and application on this feature), and publish assets. However, main part of the platform is file management.

I have placed the library on Google Coders and included a Flex sample application for doing some basic operations with Xdrive. You just need an Xdrive account to start using the library. I am hoping this helps developers starts to build some cool applications using the Xdrive plaform. Along with the library, I included an example application called Xdrive SlimDrive, which demonstrates the basic functionality for doing file management and shows how to work with the library.

===============
as3xdrivelib
===============

The as3xdrivelib project is a wrapper for the Open Xdrive JSON API. It provides much of the client-side functionality for applications; for example, authentication, uploading files, sharing files, and basic file management.

Project Page: http://code.google.com/p/as3xdrivelib
Project Group: http://groups.google.com/group/as3xdrivelib

Authors: Michael Ritchie, Maria Vazquez, Lucas McGregor, Joe Provost
Dependencies: corelib (http://code.google.com/p/as3corelib)

How to use the as3xdrivelib with Cairngorm.

I didn't get a chance to post this to the Google Code, but I wanted to show a quick login example that uses the library with Cairngorm. The following example uses a very simplistic login screen with a Cairngorm Event/Command/Delegate to login to a users Xdrive account. The example is pretty straight forward if you know Cairngorm already. There are some things to notice in the Delegate and Command. In the Delegate, notice how the I setup the delegate to use the as3xdrivelib as a service and how I mapped the events back to the command.

public function login(user:User) : void  {     
     var token : XdriveAPIToken = service.login(user);     
     token.addEventListener(XdriveAPIEvent.API_RESULT, responder.result);     
     token.addEventListener(XdriveAPIEvent.API_FAILURE, responder.fault);
}

In the Command notice how I map the payload (user) on the API result to the Model. This is explained in the documentation for the library, but basically the payload contains a return variable(s) name on the payload event that almost always represents the object you send to the library method.

public function result( event : Object ) : void         
{                                     
     var resultEvent : XdriveAPIEvent = XdriveAPIEvent(event);           
     model.user = resultEvent.payload.user as User; // store value in model for binding                 
}

Example File (right-click to view source/download)

- Mister

Tags: , , ,

Custom RichTextEditor

April 24th, 2008  |  Published in Component, Flash, Flex Tips

One of the longest running post on my blog has been about creating XHTML output from the Flex RichTextEditor control. I always thought it would would be nice if someone took all the comments and suggestions for that post and created a customized control that outputs proper XTHML. Well, Axel Jenson has created such a custom control. I want to send many thanks to all those who posted over the past year and to Axel for stepping up and creating this custom control for Flex. Give the component a try and let Axel know what you think.

-Mr

Tags: ,

Building ASDocs and SWC with Flex Ant Tasks (flexTasks) for both Mac and PC

March 7th, 2008  |  Published in ASDocs, Ant, Flex Ant Tasks, flexTasks  |  3 Comments

I wanted to write a quick post because I am just exploring the ability to build ASDocs using ANT from within Flex Builder 3. Adobe Labs has a post about using Flex Ant Tasks to manage builds from Flex. With this tool you can compile Flex applications, modules, libraries, and HTML wrapper code, as well as build your documentation with ASDocs. I created a sample Flex Library project that uses the compc feature of the Flex Ant tasks to compile my library into a SWC. I also use exec command in the same build file to create the ASDocs for the project. The path names in the build file have been abstracted into properties file. This properties file can be changed to build the project on both Mac and PC (thanks Joseph and Avani for your help).

The only problems I ran into for this project was pointing to the path of my Mac when compared to the paths on a PC and the name for the asdocs file. The asdoc (asdoc.exe on PC and just asdoc on Mac) doesn't like space in the path names to the output and source directories. So if you place these values direclty in the build XML file you have to use single quotes for the values, something like this:

<target name="asDocs">

        <exec executable="${FLEX_HOME}/bin/asdoc" failonerror="true">
   
        <arg line="-doc-sources '/Users/xmritchie/Documents/Flex Builder 3/as3xdrivelib/com'"/>
   
        <arg line="-window-title 'Xdrive AS3 Library'"/>
   
        <arg line="-output='/Users/xmritchie/Documents/Flex Builder 3/as3xdrivelib/docs'"/>
   
        <arg line="-external-library-path='/Users/xmritchie/Documents/Flex Builder 3/as3xdrivelib/libs'"/>
   
        </exec>
   
        <echo>docs created</echo>

    </target>

However, if you abstract these value into a properties file you won't have any issues with paths. The build.xml file and the properties file go into the root level of your library project. You need to download and install the flexTasks.jar file from Adobe into your Flex Builder 3 plugins folder. On my machine, I placed the jar file into /Applications/Adobe Flex Builder 3/plugins/org.apache.ant_1.7.0.v200706080842. I also placed the flexTasks.jar file into /Applications/Adobe Flex Builder 3/sdks/3.0.0/ant/lib. Grab the files from http://labs.adobe.com/wiki/index.php/Flex_Ant_Tasks.

Here is the code for the build.xml:

<?xml version="1.0" encoding="utf-8"?>

<!-- Flex Library Project ASDocs -->

<project name="ASDocsTest" default="compile" basedir=".">
   
    <!-- import our build properties file -->
    <property file="./build.properties" />
   
    <!-- Flex Ant Tasks used to perform compc and mxml compiling more info at http://labs.adobe.com/wiki/index.php/Flex_Ant_Tasks -->
    <taskdef resource="flexTasks.tasks" classpath="${basedir}/flexTasks/lib/flexTasks.jar" />
   
    <target name="setup" description="perform an setup operations"/>
   
    <!-- Execute the ASDoc Compile wich runs 3 seperate tasks in a series -->
    <target name="compile" description="series of tasks to create docs and swc">

        <antcall target="cleanDir" description="clean the docs directory"/>
   
        <antcall target="asDocs" description="full build of asdocs"/>
   
        <antcall target="buildSWC" description="build the SWC file"/>

    </target>

    <target name="deploy" description="perform an deployment operations"/>

    <target name="install" description="perform an installation operations"/>

    <!--

    DELETE the existing output folder and files and then re-generate the output folder

    -->

    <target name="cleanDir" description="DELETE the existing output folder and files and then re-generate the output folder">

        <delete dir="${basedir}/${docsoutputfolder}" failOnError="true" includeEmptyDirs="true"/>
   
        <mkdir dir="${basedir}/${docsoutputfolder}"/>
       
        <!-- echo dumps output to the console window -->
        <echo>doc directory cleaned</echo>

    </target>

    <!--

    Run the ASDoc executable and generate the ASDocs to the new output folder

    -->

    <target name="asDocs" description="Run the ASDoc executable and generate the ASDocs to the new output folder">

        <exec executable="${asdoc.exe}" failonerror="true">
   
            <arg line="-doc-sources ${domainextensions}"/>
       
            <arg value="-window-title" />
            <arg value="'${title}'"/>
       
            <arg value="-output" />
            <arg value="${basedir}/${docsoutputfolder}"/>
       
            <arg value="-external-library-path" />
            <arg value="${basedir}/${libpath}" />
   
        </exec>
   
        <echo>docs created</echo>

    </target>

    <!--

    Compile the  SWC file library including libs folder and the path to our classes, we use compc for library, but we
    would use mxml for MXML files, check the docs for Flex Ant Tasks, http://labs.adobe.com/wiki/index.php/Flex_Ant_Tasks.

    -->

    <target name="buildSWC" description="Compile the SWC file for the Librayr Project">

        <compc output="${basedir}/${liboutputfolder}/${liboutputfile}">
           
            <!--
                Include the path to any external SWC files used in our document, you may have to place name of SWC (corelib.swc) at end of path
                I didn't inlcude it because I didn't want to redistribute the corelib.swc. So file path would be file="${basedir}/${libpath}/corelib.swc"
             -->
            <include-libraries file="${basedir}/${libpath}/" />
       
            <source-path path-element="${basedir}" />
           
            <!-- include our Class packages into the build (com folder) -->
            <include-sources dir="${basedir}/${domainextensions}" includes="*" />
   
        </compc>
   
        <echo>SWC created</echo>

    </target>

</project>

Here is the code for the build.properties:

# Window and document title for the documentation
title = ASDocs Test Library

# Class-folders you want to search for classes to be included in the docs, seperated by spaces (for example   ../com/ ../net/  )
# to include every .as and .mxml file within your project, just state   ../
domainextensions = ./com

# The Location of deployment library on your Computer (PC/Mac) for compiled SWC file
liboutputfolder = bin
liboutputfile = ASDocsTest.swc
libpath = libs

# The Location of the output folder for your generated documents
docsoutputfolder = bin/docs

# Home directory for flex sdk 3, change this to build for Mac or PC using # as comment
# FLEX_HOME = C:/Program Files/Adobe/Flex Builder 3/sdks/3.0.0
FLEX_HOME = /Applications/Adobe Flex Builder 3/sdks/3.0.0
   
# The location of your asdoc.exe, change this to build for Mac or PC using # as comment
#asdoc.exe = C:/Program Files/Adobe/Flex Builder 3/sdks/3.0.0/bin/asdoc.exe
asdoc.exe = /Applications/Adobe Flex Builder 3/sdks/3.0.0/bin/asdoc

Once you have the Ant plugin installed for Flex Builder 3 and you restart, go to Widows --> Other Views and select Ant. Drag the build.xml file to the Ant dialog window, then double-click on compile to run the script. This is how you build the docs and the SWC file. You can also use those same build and properties files in the SDK.

Here is the example file, just download the file directly from link, unzip the file, and import into your Flex builder.

ASDocsTest.zip

- Mister

Tags: , , ,

Megadeth is alive and well in Bangaluru

March 4th, 2008  |  Published in Misc

And so am I.... I am entering my third week of my month long stay in Bangalore (Bangaluru to the locals). Bangalore can best be described as being under construction or in a transitional state. The big IT boom started here about 5-6 years ago. This caused a major influx of IT professionals and large companies building new remote locations within the city. The office is located at the new Ecospace location and within these walls the office feels like any other office. The water cooler gurgles away, the coffee machine is in frequent use. Herman miller chairs and cubes as far as you the eye can see. Outside the office is somewhat different. The streets are a bit teaming with cars, people and animals unconcerned with what happens in these walls. The environment is not what you might be used to coming from Los Angeles. Traffic rules are not often followed, everything is a bit chaotic, and services I have come accustomed to are missing or completely different. Bangalore is not a place to walk freely or easily cross the streets, its dangerous for pedestrians. Bangalore has a lot of economic differences between those in the shiny new offices and those many living outside the campus border, this is evident as you travel around the city.

Megadeth, Bangalore

However, this is changing rapidly, Bangalore is going through great growth from all the companies moving here and trying to establish themselves. New services and jobs are sprouting up in all directions. Next month there will be a brand new airport opening and soon to follow will be a metro transit system. I am not sure I can judge Bangalore as good or bad, but only that is outside my perception, I do not possess the cultural or historic knowledge to view Bangalore other than as an outsider. The people I work with are generous and thoughtful, they are happy and that happiness must come from some knowledge about Bangalore that I don't yet posses, but I can accept that there is something more below the surface.

One thing I do know, they like Megadeth and I think hard bands in general. The weekend I depart Bangalore, Megadeth will arrive. So the mystery of this bands longevity has been solved at least for me, they are here, where IT is flocking, where people are merging to live lives and work. Megadeth, like me, are remnants of the 80's, and here we both are. I suspect as people read this post they assume I am completely "off my head", or that the vegetarian diet has deprived my brain of essential fatty substances to function. Rest assured I am my wits and I am just letting this post flow from me as I too am just going through a transitional state. One can not visit a place for so long without some inevitable change.

I have some time left here to ponder and see where my mind is upon my return. I will then have a moment to reflect back to this time in Bangalore and perhaps find something more meaningful to say. Unfortunately, my departure in a week or so means I will miss Megadeth. But for those of you from the west traveling here at the end of the month, pick up some tickets, see what happens when you mix hard rock with new tech and just a dash of old culture...its got to be interesting.

- Mister

Tags: ,

Omniture Tracking in AIR Applications

January 27th, 2008  |  Published in AIR, Flex, Omniture  |  3 Comments

If you are developing Enterprise level applications chances are you are either used Omniture or you will be asked to integrate with Omniture tracking services. This post is not really about the specifics of Omniture tracking, but rather about the implementation methods for Flash, Flex, and AIR (with Flex). Usually, Omniture tracking events are sent using JavaScript on a web page. An HTML page would include the Omniture JavaScript library and events in your web application would call methods in the libary.

When creating RIA with Flex or Flash you have a couple of options. Ominture provides a proprietary ActionScript library, called ActionSource, for easy integration. ActionSource is a Flash MXP component library that transmits analytic data to Omniture without relying on calling JavaScript methods. This implementation is best for situation when your have a Flash file that can not call JavaScript. Brightcove has a good article describing this type of implemenation. Integrating Omniture services with Flex does not require the ActionSource library.

Flex has the ability to write and call JavaScript directly. With Flex, you can use the ExternalInterface call to call JavaScript functions on the page. So a typical call might look like this:

ExternalInterface.call("trackOmniture", omnitureType);

Where the value for the omnitureType represents the information you want to transmit to Omniture for analysis and "trackOmniture" is the name of a JavaScript method on the HTML embed page for the Flex application that calls Omniture methods in the included JS library from Oniture for HTML integration. Just setup your account information and other parameters in the Omniture JS library like you normally would for tracking Omniture in HTML-based applications.

Adobe AIR introdocues some new challenges and new solutions for Omniture tracking. An AIR application does not run in a web browser, its a desktop application that can make HTTP requests. Using ExternalInterface calls to an embed HTML page is not an option in this scenario. Now, you can create an entire AIR application with just HTML and JavaScript. However, in our implemenation we want to use Flex in AIR to create the application.

Lets talk a little bit about the new AIR security model introduced with Beta 3. According to Adobe :

One of the main threats facing HTML applications, whether desktop or web, are injection attacks which result in malicious code execution. Code is usually injected via a few common vectors such as via URL handling (“javascript:” and other dangerous schemes), eval() and assigning external HTML content to DOM elements such as innerHTML and outerHTML.

As a result of the security vulnerability AIR now uses two kinds of sandboxes, one is called the Application sandbox wich allows full access to AIR API's but disables the JavaScript parser after the initial load to prevent exposure of the API to possibly harmful JavaScript calls (like eval()). The other sandbox is the "Non-application" sandbox. This is where you can execute JavaScript like you would in a browser, but it does not have direct access to the AIR system API's and therefore can't raise havoc on your application. Now to talk between the two sandboxes you need to use Adobe's new SandboxBridge mechanism which can expose methods in both the parentSandboxBridge (the application sandbox) and the childSandboxBridge (the non-application sandbox location). So what does this mean for handling Omniture tracking in AIR?

Well, the Omniture JavaScript file is pulled from a separate domain and contains JavaScript deemed a security risk. This means that Omniture needs to run in the non-application sandbox, but the application reporting calls need to be made in the application sandbox.

This makes for an interesting implementation. Adobe does offer a good example of scripting between content in different domains. In this method, the main HTML content is loaded into the initial window of the application security sandbox, while the content from another domain is loaded into a <iframe> that specifies the content is placed in a non-application security sandbox.

Our application is built Flex within an AIR project, so we need to change the example from Adobe just a little bit to fit our specific implementation since they are building an AIR application with HTML. We are going to create an MXML file that loads an HTML page from a directory within our application called "omniture". We do this using an HTMLLoader that loads our main HTML content (track.html). We use the instance of the HTMLoader to call a method within the loaded HTML window called "track". Loading HTML into an HTMLLoader control will execute the HTML code on that page. So this page contains basic HTML and JavaScript that does not have a security risk. This loaded HTML page is not displayed inside our application the HTMLLoader is used merely to load and execute the HTML code, and also serve as a way to reference the JS methods of the loaded content.

private var html:HTMLLoader; // omniture tracker
/**
* @private
* Create an HTML object to handle Omniture tracking within
* the application.
**/

private function createHTMLTracker():void
{
    html = new HTMLLoader();
    //html.addEventListener(Event.COMPLETE, onHTMLLoadComplete);
    html.load(new URLRequest("omniture/tracking.html"));
}
           
/**
*Tracks Ominture events by callint a JavaScript function within the page
*loaded into an HTML control.
*@private
**/

private function omnitureHandler(omnitureType):void
{
 if(omnitureType != null){
 try{
 html.window.track(omnitureType);
 } catch(e:Error){trace(e.message);}
 }
 }

The track.html page also loads another HTML page into an <iframe> tag and establishses our SandboxBridge between the track.html page and the loaded HTML page. This second HTML paged (ui.html) loaded into the iframe includes our Omniture JavaScript library. The ui.html page can contain the JS library because we specify in the properties of the iframe that the content be placed in a non-application security sandbox. Our project includes a sub-directory called "omniture" that has two HTML pages, the tracking.html and the ui.html page. It also includes one more file, the AIRAliases.js file, which allows us to use AIR shortcuts within the tracking.html file. Below is the code for the tracking.html page this page does stray too far away from the example provided by Adobe, just notice the iframe tag settings for loading pages from a sub-directory within your AIR project.

    track.html
<html>
    <head>
         <script type="text/javascript" src="AIRAliases.js">
            // include the AIRAlias.js file to access AIR short cuts, like air.trace
         </script>
        
         <script type="text/javascript">
            // create an object that will be used to expose AIR functionality
            // (developer functions) to the browser sandbox
            var Exposed = {};
                
            // expose the trace method
            Exposed.trace = function(str) {
                    air.trace(str)// this trace shows up in debugger
            }
                
            function doLoad() {
                // place Exposed on the parentSandboxBridge property of the browser sandbox (iframe)
                var frame = document.getElementById('child').contentWindow.parentSandboxBridge = Exposed;
                    
                // get the functions exposed from the Browser Sandbox
                window.track = document.getElementById('child').contentWindow.childSandboxBridge.trackPage;
             } 
         </script>
    </head>
    <body onload="doLoad();">
        <iframe
            id="child"
            src="http://localhost/omniture/ui.html"
            sandboxRoot="http://localhost/omniture/"
            documentRoot="app:/omniture/">
        </iframe>
    </body>
</html>

The ui.html page is loaded within the iframe, who sets it to load the content in the non-application security box (sandboxRoot="http://localhost/omniture/"). The ui.html page uses JS to include the Omniture library (my_omniture_endpoint is specific to each client for Omniture tracking):

<script src="http://my_omniture_endpoint/omniunih.js" type="text/javascript"/>

The page also establishes our Omniture suite name:

var s_account = "mysuitename";

The method track is exposed to the track.html window; this is in turn called from our MXML application, passing in Omniture tracking information. The ui.html page makes all the necessary calls to Omniture within the track method. Everything is secure and AIR is happy. Below is the ui.html code, I commented out the client specific information.

    ui.html
<html>
    <head>
        <script>
            var s_account = ""; // your omniture suite name
        </script>
       
        <script type="text/javascript">
            // developer function implemented in the browser sandbox
            function track(pagename)
            {
                // Call your Omniture JS library methods here.....
               
                // trace out the return in the parent
                parentSandboxBridge.trace("tracked: " + pagename);
               
            }
                
            function doLoad()
            {
                // expose this function for the application sandbox by writing to the childSandboxBridge property of the window
                childSandboxBridge = {'trackPage': track};
            }
        </script>
       
       <script src="http://my_omniture_endpoint/omniunih.js" type="text/javascript"/>
       
    </head>
    <body onload="doLoad();">
    </body>
</html>

With this implementation method you can perform Omniture tracking from inside your AIR applications just like you would if they were web-based applications or Flex applications using ExternalInterface calls. The added benefit of including the Omniture JS library dynamically within the ui.html window is that the application always gets the latest version of the JS library. We could have taken the entire JS library and wrote it to a local .js file within our project. We could have also rewritten the JS library to exclude the extensive use of "eval()" and run it within the application security sandbox. However, I think this implementation allows the most flexibility. The new AIR security model doesn't prevent us from running JavaScript; it just requires us to take some extra steps to work with content from other domains, all while being secure.

- Mister

Tags: , , , , ,

Get ready to breathe some AIR with Oxygen

January 11th, 2008  |  Published in AIR, API, Adobe, Bluestring, Flex, Xdrive  |  3 Comments

Xdrive is coming out with a new desktop client called Oxygen that will manage their online storage service using Adobe AIR and FlexLarry Drolet and I first discussed Oxygen at a recent FITC conference in Hollywood. Oxygen was first designed as a proof of concept application using Flex, AIR, and the Xdrive JSON API version 1.2.  Since that event, I and other dedicated individuals have been heads-down coding for the past few months, taking full advantage of the pre-release version of Adobe AIR and Flex 3.   Our hope is to have a viable client delivered by sometime in February.   Oxygen will do basic file management and easy file sharing of any file type and folders of files.  It was designed to be simpler to use than the current Xdrive web interface, but its not meant to replace the current backup application for Xdrive. Oxygen introduce a cleaner and easier way for managing your storage and sharing needs.  Basic management features include new folder, rename, delete, move, upload, and download.

 

Xdrive interface showing your remote Xdrive files and your local drives.  You can drag files between the two views to perform upload and download.

Xdrive sharing features allow you to email assets, embed assets in a blog or web page, and check manage your shared assets.

In addition, this would be the first ever cross-platform desktop application built by Xdrive.   Users can install the AIR runtime environment for Mac or PC and run the Oxygen application.    This gives Mac users (which I am one) the ability to manage their Xdrive storage and share from the desktop. AIR allows us to do auto-updates, asynchronous file transfer and queuing, drag-n-drop files from the desktop, local file system browsing, folder upload with zip.   AIR also gives the ability to build our application using Flex. This makes for a better user experience and we could reuse existing code from Bluestring.      

Xdrive file viewer widget allows you to embed a single asset or a folder of assets on your blog.

Another feature we wanted to provide was the ability for users to embed individual assets or folders of assets on their personal blog or web page.  The new file viewer widget allows users to share all file types (zip, PDF, image, word, excel, video).  Users can download the files directly from the widget.   The widget has a list view and simple navigation system.  To get access to the widget from Oxygen, simple click and select embed code from any file or folder. 

So that everyone knows that I am not giving away the farm, the information in this post is within the public domain.  Oxygen was just shown at the recent CES show in Las Vegas and you can find a lot of positive articles on the new direction Xdrive is taking with Oxygen and Flex.  Here is a small list of articles about Oxygen:

Download Squad

PC Magazine

Webware

I can't say any exact dates or even confirm any exact features since we are still under development.   I can say that we don't plan on stopping with just the basic features of AIR, there is a lot to explore with AIR and we will be going forward with additions to Oxygen in the future.   

-Mr

Issue with using SWF files within TileList control

November 26th, 2007  |  Published in Flex, Flex 2, Flex Development, SWF

I ran into a small annoyance using SWF files within the Flex TileList control.   Using a SWF image in the itemRenderer seems to block the broadcasting a double-click ListEvent, however using a image format (JPG, PNG, GIF) works fine. To demonstrate the issue I created a very simple example:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="540" height="600" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
     import mx.controls.Alert;

     [Bindable]
     [Embed(source="assets/swffile.swf")]
     public var swfClass:Class;

     [Bindable]
     [Embed(source="assets/jpgfile.jpg")]
     public var graphicClass:Class;

     private function handleListDoubleClick():void
     {
          Alert.show("List item was double-clicked");
     }

]]>
</mx:Script>
<mx:TileList width="500" height="240" doubleClickEnabled="true" doubleClick="handleListDoubleClick()" columnWidth="220" rowHeight="220" y="44" horizontalCenter="0">
<mx:dataProvider>
     <mx:Array>
          <mx:Object label="SWF" icon="{swfClass}"/>
          <mx:Object label="JPEG" icon="{graphicClass}"/>
    </mx:Array>
</mx:dataProvider>
</mx:TileList>
</mx:Application>

In the example above we have two items in the list, one uses a jpeg image, the other uses a swf file.  I have set the doubleClickEnabled propertie to true and listen for the doubleClick event.    Double-clicking the jpeg image files and alert message.  Doing the same on the swf image does not fire the event.

The solution I derived for getting the the event to fire was to create a "cover" on top of the SWF image.  I used a Canvas with a transparent border and fill color.  This seems to proprogate the double-click event:

List ItemRenderer:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="220" height="220">
<mx:Image source="{data.icon}" width="200" height="200" horizontalCenter="0" top="0"/>
<mx:Label text="{data.label}" bottom="0" horizontalCenter="0" textAlign="center"/>
<mx:Canvas width="100%" height="100%" borderStyle="solid" backgroundAlpha="0" backgroundColor="0x000000" borderColor="#000000" alpha="0"/>
</mx:Canvas>

Application MXML:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="540" height="600" viewSourceURL="srcview/index.html">

 
<mx:Script>
<![CDATA[import mx.controls.Alert;
[Bindable]
[Embed(source="assets/swffile.swf")]
public var swfClass:Class;[Bindable]
[Embed(source="assets/jpgfile.jpg")]
public var graphicClass:Class;private function handleListDoubleClick():void
{
Alert.show("List item was double-clicked");
}

]]>
</mx:Script>
<mx:TileList width="500" height="240" doubleClickEnabled="true" doubleClick="handleListDoubleClick()" columnWidth="220" rowHeight="220" y="44" horizontalCenter="0">
<mx:dataProvider>
<mx:Array>
<mx:Object label="SWF" icon="{swfClass}"/>
<mx:Object label="JPEG" icon="{graphicClass}"/>
</mx:Array>
</mx:dataProvider>
</mx:TileList>
<mx:TileList width="500" height="240" doubleClickEnabled="true" doubleClick="handleListDoubleClick()" columnWidth="220" rowHeight="220" y="321" itemRenderer="listItemRenderer" x="20">
<mx:dataProvider><mx:Array>
<mx:Object label="SWF" icon="{swfClass}"/>
<mx:Object label="JPEG" icon="{graphicClass}"/>
</mx:Array>
</mx:dataProvider></mx:TileList>
<mx:Label x="20" y="295" text="TileList with SWF &amp; JPEG images (double-click), ItemRenderer with simple cover" fontWeight="bold"/>
<mx:Label x="20" y="18" text="TileList with SWF &amp; JPEG images (double-click)" fontWeight="bold"/>
</mx:Application>

This is the best solution and simplest I have come up with so far.   I will post the example and see if anyone else might have a bette workaround.  

Source Code
Example

 

-Mr

Tags: , ,