Tour de Flex - a new tool for Flex/AIR developers

A new tool for Flex developer was launched last night: Tour de Flex. It is an AIR application, and an Eclipse plugin that gives you:

“Tour de Flex includes over 200 runnable samples, each with source code, links to documentation, and other details. Topics include the Flex Core Components, Flex Data Access, AIR Desktop Capabilities, Cloud APIs, Data Visualization, Mapping, and a growing collection of custom components, effects, skins, etc.”

Screenshot_0

PS. If you are attending the MAX event and you want get the Tour de Flex on a memory stick, just look for a Platform Evangelist and ask us.

AIR 1.5 is out!

AIR 1.5 is out, just go to http://get.adobe.com/air/. You can read more about it here, and here.

PS. I am proudly and shamelessly brag about my articles on the Update Framework for AIR, which now works with AIR apps created in Flash too: Using the Adobe AIR update framework, and the article about the BlackBookSafe - an AIR app created entirely with HTML/JavaScript that uses Pixel Bender, 3D transformations and encrypted local database. Enjoy them!

Later Update: Almost forgot! AIR1.5 means also JavaScript debugging support. More to come.

Flash Player 10 and FileReference

Flash Player 10 introduced some user-initiated action requirements (for example, in order to call the method for browse for a file, the user has to initiate this action with a mouse or key press). These were introduced to enhance the security of Flash Player.

If you want to learn more about, here are two great resources: http://www.adobe.com/devnet/flashplayer/articles/fplayer10_uia_requirements.html?devcon=f2 and http://theflashblog.com/?p=463.

Enjoy!

Just a teaser for AIR 1.5

My friends from the AIR team created an interesting AIR application that illustrates the coolest features from AIR 1.5. We will present this application at MAX San Francisco; you have to wait a little bit. We will give the source code of the application too, so you can see how was made it. In the same spirit, I will post an article explaining the bits.

Can’t wait!

AIR: Encrypted local storage and Update framework

In a previous entry I wrote about the different ways you have to store data in an AIR application. One way is to use the encrypted local storage. The signature of the method that you use to place something in “vault” is:

EncryptedLocalStore.setItem(name:String, data:ByteArray, stronglyBound:Boolean = false):void

Usually, you use just the first two arguments: name - the key you use to store and to retrieve the data, and data - what you want to store. However the third argument is very interesting. If you set this to true, then the stored item is strongly bound to the digital signature and bits of the AIR application in addition to the publisher id. This means that if the bits of the installed application are changing, then the previous stored information in the encrypted storage cannot be read anymore.

This is great because it gives you all the security you might need - if somehow, some malicious files are injected into your application, the data are protected and cannot be read.

But in some situations, it is not so great. If you use the update framework, after each update the bits are changed and thus the information is lost. So, I spent some time thinking on this issue and I came to the conclusion that the only solution is this:

So, this is the only solution I came with for this. I am curious; how do you handle this?

Storing data locally in AIR

If someone asked me how to store data locally in AIR (on the user’s machine), I think this would be my answer in short:

  1. Store data in the SQLite database
  2. Store ActionScript objects in files
  3. Store assets in files
  4. Store ActionScript objects in the encrypted local store

Let’s talk about each one and see how you can use it.

Store data in the SQLite database

Every AIR application has access to an embedded SQL database (SQLite to be precise). This database supports (almost :D) ACID transactions, implements most of SQL-92, and more importantly, you don’t have to configure a thing to use it. It is there and if you need it, you can use it. All the database information is stored in a single file.

If you are not sure why you would use this feature, I’ll give you an example. Suppose your application has a feedback form, and because it is an AIR application the user can use it whether he has Internet connectivity or not. Imagine that the user is offline and he submits the form. What should the application do? I think one reasonable answer would be to save the data locally and then send it to the server the next time when the application is online. In this situation SQLite could be the local data store.

Below is sample code that let’s you write some data to a table and read the data. Basically when you want to work with SQLite, you need to create a connection (SQLConnection) and open the connection pointing to the file that stores the database. After this, you use a SQLStatement yo create queries and execute them. Thus, you can create tables, insert/update/delete/read data.

//create the database and table
private function createDb():void {
    //create the database file in the application storage folder
    var file:File = File.applicationStorageDirectory.resolvePath("my.db");
    sqlConnectionSync = new SQLConnection();//create a connection object
    sqlConnectionSync.open(file, SQLMode.CREATE);//create the database
    if(file.exists)
        return;
    //create a statement
    var createDb:SQLStatement = new SQLStatement();
    //create a table
    createDb.text = "CREATE TABLE messages (id INTEGER PRIMARY KEY AUTOINCREMENT, subject VARCHAR(64), message VARCHAR(255))";
    createDb.sqlConnection = sqlConnectionSync; //set the connection that will be used
    createDb.execute();//execute the statement
}
//write the data
private function writeData():void {
    var insert:SQLStatement = new SQLStatement(); //create the insert statement
    insert.sqlConnection = sqlConnectionSync; //set the connection
    insert.text = "INSERT INTO messages (subject, message) VALUES (?, ?)";
    insert.parameters[0] = subject.text;
    insert.parameters[1] = message.text;
    insert.execute();
    Alert.show("The data was saved into the table!");
}
//read the data
private function readData():void {
    var read:SQLStatement = new SQLStatement(); //create the read statemen
    read.sqlConnection = sqlConnectionSync; //set the connection
    read.text = "SELECT id, subject, message FROM messages ORDER BY id";
    read.execute();
    var result:SQLResult = read.getResult(); //retrieve the result of the query
    myDatagrid.dataProvider = result.data; //display the array of objects into the data grid
}

This example uses synchronous calls. You can use asynchronous calls. See the documentation for SQLConnection. Also, you can execute the queries in transactions if you want.

Store ActionScript objects in files

You might have objects that you want to save locally and you don’t want to use the SQLite for this. With AIR you can save any ActionScript object to a file. You can save anything from simple objects to arrays of value objects. However there is a catch: when the object you stored has a type (let’s say for example VOPerson), then  when you retrieve it is safe to cast to the original type. But if you save an ArrayCollection of VOPerson, then when you retrieve them you will have an ArrayCollection of Objects. More on this here.

Below is an example of code that writes/reads an Object to and from a file. You use the File class to create a file under the application storage folder, and then you use a FileStream to write the Object to this file. When you read, you use a File to get the file and a FileStream to read the contents of the file.

//write an Object to a file
private function writeObject():void {
    var object:Object = new Object();//create an object to store
    object.value =  asObject.text; //set the text field value to the value property
    //create a file under the application storage folder
    var file:File = File.applicationStorageDirectory.resolvePath("myobject.file");
    if (file.exists)
        file.deleteFile();
    var fileStream:FileStream = new FileStream(); //create a file stream
    fileStream.open(file, FileMode.WRITE);// and open the file for write
    fileStream.writeObject(object);//write the object to the file
    fileStream.close();
}
//read an object stored into a file
private function readObject():void {
    //read the file
    var file:File = File.applicationStorageDirectory.resolvePath("myobject.file");
    if (!file.exists) {
        Alert.show("There is no object saved!");
        return;
    }
    //create a file stream and open it for reading
    var fileStream:FileStream = new FileStream();
    fileStream.open(file, FileMode.READ);
    var object:Object = fileStream.readObject(); //read the object
    Alert.show("The text member has this value: " + object.value);
}

Store assets in files

If you have an AIR application that frequently uses the same pictures, sounds, movies, or other SWF files you might want to save these assets locally, and thus you the overhead of downloading these resources each time. The workflow is almost the same as the previous one for storing ActionScript objects in files. Only this time you don’t write an ActionScript object, you write the bytes to the disk. So instead of fileStream.writeObject(myObject), I use fileStream.writeBytes(bytes, 0, bytes.length). Below is some sample code that asks for a resource URL (such as an JPG file for example) and using an URLLoader, downloads the resource and saves it locally.

<mx:Script>

/**
 * loader object used to load content from web
*/
private var loader:URLLoader = new URLLoader();

/**
 * This is the click handler for the button that save the web resource locally.
 * Just trigger the load and disable the button so no other 
 * request can be sent while it is already running one.
 */
private function saveResource():void {
    loader.load(new URLRequest(resURL.text));
    btn.enabled = false;
}

/**
 * This function gets called when the resource was loaded.
 * We just extract the name of the resource and call the saveLocally() function
 * to write the object on the local machine.
 */
private function onLoad(event:Event):void {
    btn.enabled = true; //enable the button so we can send another request.
    //get the file name of the resource
    var fileName:String = new String(resURL.text).split("/").pop();
    //call the saveLocally function; loader.data has the bytes of the loaded resource
    saveLocally(fileName, loader.data);
} 

/**
 * Error handler for loader object. 
 * I just display the error message and enable the button for new requests.
 */
private function onError(event:ErrorEvent):void {
    btn.enabled = true;
    Alert.show(event.text, "Error");
} 

/**
 * This function does the actual saving.
 * @param file name to be used for saving
 * @param bytes to be written
 */
private function saveLocally(fileName:String, data:ByteArray):void {
    //create a file under the application storage directory using the filename argument
    var file:File = File.applicationStorageDirectory.resolvePath(fileName);
    if (file.exists)
        file.deleteFile(); //delete it if exists
    //create a file stream to be able to write the content of the file    
    var fileStream:FileStream = new FileStream();
    //open the file stream and set for Write
    fileStream.open(file, FileMode.WRITE);
    //writes the bytes
    fileStream.writeBytes(data, 0, data.length);
    //close the stream
    fileStream.close();
    //display the path of the saved resources    
    showLocalPath.text = file.nativePath;
}

</mx:Script>

<mx:VBox left="10">    <mx:Label text="Store assets in files" fontSize="20"/>    <mx:Label text="Enter a resource URL such as http://mysite/image.jpg"/>    <mx:HBox>        <mx:TextInput id="resURL" text=""  width="600"/>        <mx:Button id="btn" click="saveResource()" label="Save locally" />    </mx:HBox>    <mx:Label id="showLocalPath" /></mx:VBox>

You can read more on this here.

Store ActionScript objects in the encrypted local store

Sometimes you have sensitive data, such as personal information or passwords and you want to save them locally. But you don’t want this data to be read by other users or applications. The solution for this problem is to use the encrypted local store. This is a built in functionality in AIR, and each AIR application has its own encrypted local store. Thus you can very safely store any sensitive information you might have. You have access to this store using EncryptedLocalStore object. You have setItem(), getItem() and removeItem() methods. The data saved and read are ByteArray, and when you store or retrieve something you use a key. Below is a code sample:

<mx:Script>
//store the text in the encrypted local store 
private function encrypt():void {
    var data:ByteArray = new ByteArray();
    data.writeUTFBytes(encryptText.text);//create a byte array out of the text we want to encrypt
    EncryptedLocalStore.setItem("myEncryptedData", data); //store the byte array into the encrypted local storage 
}
//read the data from the encrypted local store
private function decrypt():void {
    //using the same key I used when I wrote the data, I reead the bytes back
    var bytes:ByteArray = EncryptedLocalStore.getItem("myEncryptedData");
    if(bytes)
        Alert.show(bytes.readUTFBytes(bytes.bytesAvailable));
    else
        Alert.show("There was no value to be read!");
}
</mx:Script>
<mx:VBox left="10">
    <mx:Label text="Store ActionScript objects in the encrypted local store" fontSize="20"/>
    <mx:TextInput id="encryptText" />
    <mx:Button label="Encrypt" click="encrypt()" />
    <mx:Button label="Decrypt" click="decrypt()" />
</mx:VBox>

You can read more on this subject here.

Final words

You can download the exported project from here. You can import the project in Flex Builder using Import > Flex Builder > Flex Project.

360|Flex and AMP

If you haven’t the chance to participate in a 360|Flex conference, you can watch 15 sessions from one event using AMP (Adobe Media Player). It is very simple:

  1. Install AMP
  2. Click “My Favorites” menu at the top.
  3. Click “Add RSS Feed” at the bottom.
  4. Paste: http://sessions.onflex.org/1733261879.xml

Enjoy!

Functional Testing Framework for AIR AJAX apps based on Selenium

My friends from the Romanian Adobe AIR team have been very busy lately. Marius and Alex have been working on, was to modifying Selenium so you can run automated / functional tests on AIR applications developed with HTML/JS.

To be honest, I am quite amazed about what they managed to pull off in so little time. And because we need your feedback, I have an archive ready for download here, so you can start playing with it in minutes. Here is what it is in ZIP:

  1. demo movies - two movies that show you the workflow supported for now
  2. sample tests - this is the interesting part. You have two applications and some tests files to test these applications with this framework. The first test uses a Selenium based model for dispatching UI events, the second uses a Java AWT robot to generate mouse and key events.
  3. selenium recorder - this lets you record your UI interaction with the application. Then with a little editing on the resulting test files you can create the test cases for your automated testing.
  4. selenium server - this is the heart of this framework. It acts as a proxy between the requests that are made from tests and the AIR application.
  5. readme.txt - for a quick start on trying the samples; this files describes how to create a Java project and run the tests from Eclipse as a JUnit Test Case.
  6. SeleniumWithAIR.pdf - the first draft of documentation for this framework, which includes the known issues.

It should work on Win and Mac, with Java 1.5 or newer and AIR SDK 1.1.

Here is a short explanation of how to quickly run the tests from the ZIP file:

  1. Download the ZIP, and unzip somewhere on your disk
  2. Create a system variable named ADL_EXE and set its value to the full path of the “adl” executable from your local AIR SDK
    Create ADT_EXE env variable
  3. Start the server: selenium/selenium server/runServer.bat
    The Selenium server is started
  4. Run the tests: selenium/sample tests/runTest.bat
    First test runSecond test run

Here is a snapshot of the Eclipse project to run the tests from the ZIP:

Running the tests from Eclipse

So, that’s it! Try these goodies and let me know what you think!

How to create a generic function for parsing an XML into an Array Collection of Objects of a certain type

The Problem

Over time, I found that it is more productive and less error prone (at least for me :D) to work with structures of data that are rather collections of typed Objects than collections of Objects or XML. Here are some advantages when you work with typed objects: gets support from Flex Builder tool (code completion, compile time checking), the code is easier to read and maintain, and somehow I think is more elegant.

Back to earth now, let’s say I have a Flex application that reads data from a server and format of the data is XML. For example, my Flex application receives some data like this:

<?xml version="1.0" encoding="utf-8"?>
<root>
    <person>
        <name>John</name>
        <age>25</age>
    </person>
    <person>
        <name>Mike</name>
        <age>27</age>
    </person>
    <person>
        <name>Michele</name>
        <age>22</age>
    </person>
    <person>
        <name>Julia</name>
        <age>29</age>
    </person>
</root>

I could use this data like an XML object in my application (for example if I use a HTTPService to bring them, I can set the resultFormat property to “XML”), or I can create an ActionScript class VOPerson:

package org.example {

    public class VOPerson {

        public var name:String;
        public var age:int;

    }
}

And I can transform this XML in an Array or Array Collection of VOPerson.

In an AIR application you can save on disk any object you might have. Suppose I have an Array Collection of VOPerson, I can use AIR API to save this object on disk. But, there is an issue: when I read the object from disk back into my application, the type of objects from Array Collection is lost. So instead of an Array Collection of VOPerson, I will have an Array Collection of Objects. This could be a problem for me, as my methods expect a VOPerson object and not a generic Object.

The Solution

The solution is to create a “generic” function that knows how to transform a collection (could be an Array, an Array Collection or a XMLList) of generic objects into an Array Collection of typed Objects. I could use this function when I read serialized objects from disk in AIR or when I use HTTPService to get XML from server in a Flex application. In order to have a simple solution I need to use the same names for the XML nodes and my typed object properties.

Let’s start with the HTTPService and XML code. This service has a property xmlDecode that can be used to set a function that is called right before the result handler gets called. This function must return an Object, and the return object will be passed in the event. result property to the result handler. The XML received from server is passed as an argument to the function, as XMLNode object. Pay attention that this function is called only when you set the resultFormat property to “object”.

So let’s take a look at how the code might look:

<mx:HTTPService id="myService" url="http://corlan.org/downloads/examples/get_data.html"
        method="POST" resultFormat="object" showBusyCursor="true"
        xmlDecode="serializeXMLToVO"
        result="resultListener(event)" fault="faultListener(event)"/>

Let’s define the three functions I set up in the HTTPService object (resultListener, faultListener, serializeXMLToVO):

private function resultListener(event:ResultEvent):void {
    myCollection = event.result as ArrayCollection;
}

private function faultListener(event:FaultEvent):void {
    Alert.show(event.fault.faultDetail, "Error");
}

private function serializeXMLToVO(node:XMLNode):Object {
    var xml:XML = new XML(node);     //make an XML object to be able to work with E4X
    var list:XMLList = xml..person; //get a XML List with all the nodes person
    //call the generic function that knows to make from a generic collection of dynamic objects
    //an array collection of value objects
    return serializeToVO(list, VOPerson);
}

As you can see in the resultListener I’ve just saved the event.result cast as ArrayCollection to myCollection variable (I use this variable as data provider for my data grid). So although the server sends me an XML, the result is translated to a proper object. All this transformation is done by the serializeXMLToVO function (this function is called by HTTPService to process the result and after this the ResultEvent is dispatched). In serializeXMLToVO first I create an XML object out of XMLNode (it is far more easy to work with E4X to get my data out of XMLNode) and then I create an XMLList with all the person nodes. The next step is to call my generic function (serializeToVO) and pass to this function my XMLList and the class I want to be used for storing one person node (VOPerson is my class in this case). This function will walk through and will do the “dirty” job. I wanted to use a generic function so I can reuse it whenever I have similar needs and also I can pass any object type I want to use.

Let’s take a look at this function:

private function serializeToVO(collection:Object, claz:Class):ArrayCollection {
    var arr:ArrayCollection = new ArrayCollection();
    //use reflection to find all the members from our claz object
    var tmp:XML = describeType(new claz());
    //save just the variables into am XML List using an E4X expression
    //(I want to get all the atributes "name" from all the nodes "variable")
    var properties:XMLList = tmp..variable.@name;
    //iterate our collection
    for each (var item:Object in collection) {
        var temp:Object = new claz();
        //iterate our properties and use the values from the current item 
        //to initializa our value object
        for each(var member:Object in properties) {
            temp[member] = item[member];
        }
        arr.addItem(temp); //add the item to collection
    }
    return arr; //return the array collection
}

Let’s take a look at the arguments: we have an Object (collection) which in fact should be an instance of XMLList, Array or ArrayCollection. The other one is a Class object, this is the type I want to be used for saving an item from the collection. Because I don’t know what properties might have the claz object, I need to use reflection and find what members I have in the claz:

var tmp:XML = describeType(new claz());
var properties:XMLList = tmp..variable.@name;

flash.utils.describeType does this reflection; it returns an XML with all the information I need. From this XML using an E4X expression I can create an XMLList with the members I am looking for. Now I need to loop through my collection and for each item I need to loop through my list of members and read the value from the item and write the value into my typed object (this works because I have the same names both for XML and my type objects; I have a node name in the person, and I have a property name in VOPerson). In ActionScript you don’t have iterators, or you can say you have but the iterator is the construct for each (item:object in collection). So in this way I can iterate on any Array, XMLList or ArrayCollection or on an Object.

That’s it for the HTTPService. The code for writing and reading the array collection to disk is much simpler. I have a saveData function and a readData function and in readData I make use again of my function serializeToVO. This time, on the ArrayCollection retrieved from disk.

private function saveData():void {
    var file:File = File.applicationStorageDirectory.resolvePath(fileName);
    if (file.exists)
        file.deleteFile();
    var fileStream:FileStream = new FileStream();
    fileStream.open(file, FileMode.WRITE);
    fileStream.writeObject(myCollection);
    fileStream.close();
    Alert.show("the data was saved in " + file.nativePath, "File Path");
}

private function readData():void {
    var file:File = File.applicationStorageDirectory.resolvePath(fileName);
    if (!file.exists)
        return;
    var fileStream:FileStream = new FileStream();
    fileStream.open(file, FileMode.READ);
    var myObject:ArrayCollection = fileStream.readObject() as ArrayCollection;
    fileStream.close();
    myCollection = serializeToVO(myObject, VOPerson);
}

Sample Code

As usually :), I created an AIR project with all the code. You can download it from here and you can import it in Flex Builder using Import > Flex Builder > Flex Project. When you run the application, you will see a data grid and four buttons:

My AIR sample app

If you want to have a quick view on the code:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
        <![CDATA[
            import flash.utils.describeType;
            import mx.collections.IList;
            import mx.controls.List;
            import org.example.VOPerson;
            import mx.collections.ArrayCollection;
            import mx.controls.Alert;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;

            [Bindable]
            private var myCollection:ArrayCollection; //this is collection used to store the data

            private var fileName:String = "myObject"; //this is file name used to saved locally the data

            /**
             * The result listener for the HTTPService. 
             * Saves the result into <code>myCollection</code> array collection
             * @param event
             */
            private function resultListener(event:ResultEvent):void {
                myCollection = event.result as ArrayCollection;
            }

            /**
             * Fault listener for HTTPService. It's just displaying the error message
             * @param event
             */
            private function faultListener(event:FaultEvent):void {
                Alert.show(event.fault.faultDetail, "Error");
            }

            /**
             * This function is called by the HTTPService when the result is available
             * and before the listener for result gets called. Thus I can parse the XML
             * returned by the server into an Array Collection of value objects VOPerson
             * @param node
             * @return an Array collection of VOPerson
             */
            private function serializeXMLToVO(node:XMLNode):Object {
                var xml:XML = new XML(node);     //make an XML object to be able to work with E4X
                var list:XMLList = xml..person; //get a XML List with all the nodes person
                //call the generic function that knows to make from a generic collection of dynamic objects
                //an array collection of value objects
                return serializeToVO(list, VOPerson);
            }

            /**
             * This is a generic function that can transform a collection of dynamic objects into
             * an array collection of a certain object type (the given <code>claz</code>)
             * @param collection to be parsed; could be an array collection or an XMLList or an array
             * @param claz to be used for storing one item
             * @return an array collection of <code>claz</code>
             */
            private function serializeToVO(collection:Object, claz:Class):ArrayCollection {
                var arr:ArrayCollection = new ArrayCollection();
                //use reflection to find all the members from our claz object
                var tmp:XML = describeType(new claz());
                //save just the variables into am XML List using an E4X expression
                //(I want to get all the atributes "name" from all the nodes "variable")
                var properties:XMLList = tmp..variable.@name;
                //iterate our collection
                for each (var item:Object in collection) {
                    var temp:Object = new claz();
                    //iterate our properties and use the values from the current item 
                    //to initializa our value object
                    for each(var member:Object in properties) {
                        temp[member] = item[member];
                    }
                    arr.addItem(temp); //add the item to collection
                }
                return arr; //return the array collection
            }

            /**
             * Save the value of <code>myCollection</code> into a file inside the
             * local application storage directory
             */
            private function saveData():void {
                //create the file to store the data
                var file:File = File.applicationStorageDirectory.resolvePath(fileName);
                if (file.exists)
                    file.deleteFile();
                //use a File Stream to write to file    
                var fileStream:FileStream = new FileStream();
                fileStream.open(file, FileMode.WRITE);
                //write the data as an object to file
                fileStream.writeObject(myCollection);
                fileStream.close();
                //display the path to the file I wrote it on disk
                Alert.show("the data was saved in " + file.nativePath, "File Path");
            }

            /**
             * Read the serialized object on the hard-disk and transform it into 
             * an array collection of VOPerson
             * Save array collection of VOPerson into the local <code>myCollection</code>
             * variable
             */
            private function readData():void {
                //get the file use
                var file:File = File.applicationStorageDirectory.resolvePath(fileName);
                if (!file.exists)
                    return;
                var fileStream:FileStream = new FileStream();
                fileStream.open(file, FileMode.READ);
                var myObject:ArrayCollection = fileStream.readObject() as ArrayCollection;
                fileStream.close();
                //use my generic serializing function to get an array collection of VOPerson
                myCollection = serializeToVO(myObject, VOPerson);
            }
        ]]>
    </mx:Script>

    <mx:HTTPService id="myService" url="http://corlan.org/downloads/examples/get_data.html"
        method="POST" resultFormat="object" showBusyCursor="true"
        xmlDecode="serializeXMLToVO"
        result="resultListener(event)" fault="faultListener(event)"/>

    <mx:VBox left="25" top="25">
        <mx:Button label="Get Data" click="myService.send()"/>
        <mx:Button label="Save Data Locally" click="saveData()" />
        <mx:Button label="Clear data from server" click="myCollection = new ArrayCollection()"/>
        <mx:Button label="Read Data Saved Locally" click="readData()" />

        <mx:DataGrid id="myGrid" dataProvider="{myCollection}"/>
    </mx:VBox>
</mx:WindowedApplication>

I am curious if you run into the same issues and what solution did you find for.

What has been happening lately in the Adobe RIA space

So, what has been going on over the last few weeks in the Adobe RIA space? Actually a lot of things. And I think with all these developments, we will have a great MAX this year. And remember, there is much more news to come (remember Thermo and Gumbo, just to give an example).

LiveCycle Data Services ES 2.6

LCDS 2.6 version was released on 8th July. What’s new: improved server performance and scalability, alignment with Flex 3 and Flex Builder 3, Data Management improvements (paging, offline, subtype support, Hibernate support). You can read release information here.

Flex 4 SDK (code named “Gumbo”)

On 14th July Flex team published information about the next version of Flex 4 (code named “Gumbo”). As you probably already know, Gumbo has three main themes: Design in Mind, Developer Productivity and Framework Evolution. You can see a 14 minutes presentation on Gumbo here, or you could watch Ely Greenfield discuss developer/designer improvements, and here are more details on the themes. You can download nightly builds of the Flex SDK from here: http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4.

Flash Player 10 beta 2

On 2nd July, the Flash Player 10 beta 2 was released on Adobe labs. It targets all three platforms: Windows, Mac and Linux and it has new features (custom filters and effects, 3D effects, new text engine, visual performance improvements, and enhanced sound API just to name some of them) and bug fixes.

Acrobat Reader 9

This version brings some interesting features: it has native Flash support (you can display SWF and FLV inside of an Acrobat document), it can use services from acrobat.com. With Acrobat Reader, you also get Adobe AIR (you need the runtime in order to use acrobat.com services - you can create PDF files online, edit with others, upload and share PDF). And of course it has better performance (improved launch time) and security (with support for 256-bit AES encryption and new digital signature functionality). And by the way, on 2nd July PDF became the latest ISO standard, you can read an article here on this subject.

Adobe Media Player 1.1

A new version of the AMP was launched last week. The welcome screen is redesigned and there are a lot of changes under the hood. I plan to do an interview this week with a couple of engineers from the AMP team, so stay tuned.

flex.org

flex.org has a new layout that should give a better user experience.

max2008_badge_169x66

I guess the best way to see presentations and demos on all these products and on many more, is to come to Adobe MAX 2008 - either in San Francisco, in Milan, or in Tokyo :D.

Next Page →