Archive for the ‘AIR’ tag
Multitouch development with Flash and Flex (e-seminar this Wednesday)
This Wednesday (17.03.2010, 11:00am CET) I will be doing an online e-seminar about “Multitouch development with Flash and Flex”. You will learn how you can start immediately building multitouch enabled applications, what new APIs are coming with FP10.1 and AIR 2.0, what the differences are, and what is supported on underlying operating systems. Please register here: http://multitouchflex.eventbrite.com
This will be 30-minute session followed by a 15-minute Q&A part, done via Adobe Connect room: http://my.adobe.acrobat.com/multitouchflex/
Adobe AIR 2.0 Beta 2 released!
Adobe AIR 2.0 Beta 2 was just released; it is available here on Adobe Labs!
New features:
- Enhanced Printer Interaction: New printing support allows finer control of the way content is printed from an AIR application, including the choice of printer, paper size, and number of copies. New APIs allow a developer to retrieve additional printing information, such as printable area, whether the printer will print in color, and whether the print job is currently active. It is also possible to print without displaying the print dialog.
- Support for TLS/SSL socket communication: You can now connect to a server that requires TLSv1 or SSLv3 for socket communications.
- IME API and IME Text Input Enhancement: There are several new features added in this release to support better text input handling with IME software. The new API enhancements are designed for use with the new Flash Text Engine (FTE).
You can find updated sample applications here.
IMPORTANT: Applications built against Adobe AIR 2 Beta 1 will not run using the AIR 2 beta 2 runtime. In order for an AIR 2 Beta 1 application to run on the AIR 2 Beta 2 runtime, the namespace of the Beta 1 application descriptor file must first be updated to “2.0beta2″ and the application must be recompiled using the AIR 2 Beta 2 SDK.
Java-AS3 serialization with AMF
A few weeks ago I published Flerry project a Flex-Java bridge for AIR 2.0. In this post I wanted to explain how it works and what I used on the Java side to do AS3/Java AMF de/serialization. First of all Flerry uses the new NativeProcess API that comes with AIR 2.0. This new API allows communication between AIR application and any native application running on a user’s machine with standard input, output or error streams. Almost any type of data can be transferred over those streams, ranging from simple strings, numbers, Booleans, bytes and byte arrays to AMF serialized AS3 objects. Of course if AMF serialized AS3 objects are streamed then receiving side of the communication needs to understand this format. In the case of Flerry, the receiving side is Java and obviously it doesn’t have a built-in AMF deserializer. In order to enable AMF on the Java side I decided to investigate Adobe’s Open Source BlazeDS project (BlazeDS is the server-based Java remoting and web messaging technology that enables developers to easily connect to back-end distributed data and push data in real-time to Adobe® Flex® and Adobe AIR™ applications for more responsive rich Internet application (RIA) experiences). As BlazeDS is designed to work by default inside of JEE containers it must have had it already implemented.
After a short investigation it turned out that I was right and BlazeDS comes with very simple to use AMF de/serializer. What you actually need to make it work are two jar files: flex-messaging-common.jar and flex-messaging-core.jar, which are in the BlazeDS package available for download from here. In those jars I found two classes Amf3Input and Am3Output that allowed me to send and receive AMF serialized AS3/Java objects over input/output/error streams.
So how does it all work?
On the AS3 side I used the NativeProcess.standardInput.writeObject(message) function where the message object is of the type flex.messaging.messages.RemotingMessage that maps to equivalent flex.messaging.messages.RemotingMessage on the Java side with the proper RemoteClass metadata. Of course any POAO (Plain Old ActionScript Object) could be used instead of RemotingMessage. In my case I just wanted to reuse standard classes out of the Flex SDK that wrap transferred content and add properties required for asynchronous communication.
Below is actual code from Flerry that writes AMF serialized AS3 objects to the standard input stream (source code of NativeObject.as class is available here):
protected function call(method:NativeMethod, ... args):AsyncToken { if (!nativeProcess) initialize(); var message:RemotingMessage = new RemotingMessage(); message.operation = method.name; message.source = source; message.headers = {SINGLETON_HEADER:singleton}; if (args.length == 1) message.body = args[0]; nativeProcess.standardInput.writeObject(message); var token:AsyncToken = new AsyncToken(message); tokens[message.messageId] = token; return token; }
On the Java side I used the Amf3Input class to deserialize received data from the input stream (System.in) into proper Java objects (source code of NativeObject.java class is available here):
public void init() { Amf3Input amf3Input = new Amf3Input(SerializationContext.getSerializationContext()); amf3Input.setInputStream(System.in); try { Object object; while ((object = amf3Input.readObject()) != null) { if (object instanceof RemotingMessage) { RemotingMessage message = (RemotingMessage) object; try { if (message.getSource() != null) { Object sourceObject = null; if (singleton) sourceObject = singletonObject; if (sourceObject == null) { sourceObject = sourceClass.newInstance(); if (singleton) singletonObject = sourceObject; } Object[] params = (Object[]) message.getBody(); Class[] paramsTypes = new Class[params.length]; for (int i = 0; i < paramsTypes.length; i++) { paramsTypes[i] = params[i].getClass(); } Object result = sourceObject.getClass().getMethod(message.getOperation(), paramsTypes).invoke(sourceObject, params); respond(result, message.getMessageId()); } else { Boolean stopProcess = (Boolean) message.getHeader(NativeObject.STOP_PROCESS_HEADER); if (stopProcess != null && stopProcess) { break; } } } catch(Exception e) { handleException(e, message.getMessageId()); } } else { handleException(new Exception( "Received object is not RemotingMessage type!"), null); } } } catch (Exception e) { handleException(e, null); } finally { try { amf3Input.close(); } catch (IOException e) { handleException(e, null); } } }
To send data back to AIR I used the Amf3Output class. With a little help from ByteArrayOutputStream Java objects are serialized to AMF format and written back to System.out.
protected void respond(Object object, String correlationId) { try { Amf3Output amf3Output = new Amf3Output(SerializationContext.getSerializationContext()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); amf3Output.setOutputStream(baos); AcknowledgeMessage message = new AcknowledgeMessage(); message.setBody(object); message.setCorrelationId(correlationId); amf3Output.writeObject(message); System.out.write(baos.toByteArray()); amf3Output.close(); } catch (Exception e) { handleException(e, correlationId); } }
File promises with Adobe AIR 2.0
File promises is one of the new AIR 2.0 features already available in beta on Adobe Labs. “A file promise is a drag-and-drop clipboard format that allows a user to drag a file that does not yet exist out of an AIR application. AIR uses the methods and properties defined by the IFilePromise interface to access the data to be written when the file promise is dropped.” To check out how it works, I built a sample application that allows you drag an AIR icon out of the app and drop it somewhere in your system (e.g. on your desktop). Dropping it will initiate a download of the current stable AIR runtime installer (at this point in time it is AIR 1.5.3). The downloaded file containing the runtime will be appropriate for your operating system. This logic is implemented in the fileUrl function and it uses the Capablities.os property with a simple regexp search for “win”, “lin”, or “mac” patterns.
Here is how the application looks:

Below is the source code:
<?xml version="1.0" encoding="utf-8"?> <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/mx" xmlns:halo="library://ns.adobe.com/flex/halo" currentState="START_STATE" height="200" width="300" viewSourceURL="srcview/index.html"> <s:layout> <s:VerticalLayout horizontalAlign="center" gap="15"/> </s:layout> <fx:Script> <![CDATA[ import flash.events.Event; import flash.events.MouseEvent; import flash.system.Capabilities; protected var filePromise:URLFilePromise; protected function imgAirIcon_mouseDownHandler(event:MouseEvent):void { // Instantiating new file promise filePromise = new URLFilePromise(); // Registering OPEN event listener, to switch to // DOWNLOAD_STATE when downloading starts filePromise.addEventListener(Event.OPEN, onOpen); // Setting URLRequest pointing to remote file filePromise.request = new URLRequest(fileUrl); // Setting relativePath with fileName to be saved locally filePromise.relativePath = fileName; // Array of promises with single promise in this case var promises:Array = new Array(); promises.push(filePromise); // Instantiating clipboard object pointing to the promise var clipboard:Clipboard = new Clipboard(); clipboard.setData(ClipboardFormats.FILE_PROMISE_LIST_FORMAT, promises); // Dragging with NativeDragManager NativeDragManager.doDrag(imgAirIcon, clipboard); } protected function onOpen(event:Event):void { currentState = "DOWNLOAD_STATE"; prgBar.source = filePromise; } protected function get fileUrl():String { // Returns remote file URL based on current operating system if (Capabilities.os.search(/mac/i) > -1) return "http://airdownload.adobe.com/air/mac/download/latest/AdobeAIR.dmg"; else if (Capabilities.os.search(/win/i) > -1) return "http://airdownload.adobe.com/air/win/download/latest/AdobeAIRInstaller.exe"; else return "http://airdownload.adobe.com/air/lin/download/latest/AdobeAIRInstaller.bin"; } protected function get fileName():String { var fileUrl:String = fileUrl; return fileUrl.slice(fileUrl.lastIndexOf("/") + 1); } ]]> </fx:Script> <s:states> <s:State name="START_STATE"/> <s:State name="DOWNLOAD_STATE"/> </s:states> <halo:Image id="imgAirIcon" source="assets/air_icon_special.gif" mouseDown="imgAirIcon_mouseDownHandler(event)" toolTip="{fileUrl}" /> <s:Label text="(Drag it out to start download)" /> <halo:ProgressBar id="prgBar" bottom="10" horizontalCenter="0" visible="false" visible.DOWNLOAD_STATE="true" label="Downloading {int(prgBar.percentComplete)}%"/> </s:WindowedApplication>
Flerry: Flex-Java bridge for Adobe AIR 2.0
Today I published Flerry on Google Code, a Flex-Java bridge that uses NativeProcess API from Adobe AIR 2.0. Using it is very simple and similar to RemoteObject API in Flex. In fact I reused libraries from BlazeDS to do AMF de/serialization on Java side and on Flex side classes like AsyncToken, RemotingMessage, AcknowledgeMessage and ResultEvent/FaultEvent for eventing.
Here is short snippet how you can use it directly in MXML:
<?xml version="1.0" encoding="utf-8"?> <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/mx" minWidth="300" minHeight="200" xmlns:flerry="net.riaspace.flerry.*"> <fx:Script> <![CDATA[ import flash.events.MouseEvent; import mx.controls.Alert; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; protected function nativeObject_faultHandler(event:FaultEvent):void { Alert.show(event.message.toString()); } protected function addMethod_resultHandler(event:ResultEvent):void { txtResult.text = event.result.toString(); } protected function btnAdd_clickHandler(event:MouseEvent):void { nativeObject.add(parseInt(txtValue1.text), parseInt(txtValue2.text)); } ]]> </fx:Script> <fx:Declarations> <!-- Declaring NativeObject that points to net.riaspace.flerrydemo.MyJavaObject class in ./jars/flerry-demo.jar --> <flerry:NativeObject id="nativeObject" binPath="./jars/flerry-demo.jar" source="net.riaspace.flerrydemo.MyJavaObject" fault="nativeObject_faultHandler(event)"> <!-- Declaring NativeMethod with custom result handler, for this example actually result from NativeObject could be used --> <flerry:NativeMethod id="addMethod" name="add" result="addMethod_resultHandler(event)" /> </flerry:NativeObject> </fx:Declarations> <s:HGroup verticalAlign="middle" textAlign="center" horizontalCenter="0" verticalCenter="0"> <s:TextInput id="txtValue1" text="2" width="30" /> <s:Label text="+" width="30" /> <s:TextInput id="txtValue2" text="3" width="30" /> <s:Button id="btnAdd" label="=" width="30" click="btnAdd_clickHandler(event)" /> <s:TextInput id="txtResult" width="30" editable="false" /> </s:HGroup> </s:WindowedApplication>
Some NativeObject properties:
- binPath – points to jar file with your Java application
- source – points to java class that this NativeObject maps to
- singleton – sets source class object as singleton
- startupInfoProvider – custom implementation of net.riaspace.flerry.IStartupInfoProvider, by default Flerry comes with JavaStartupInfoProvider that tries to detect where Java is installed on users machine, in other case Error is thrown
In following posts I will explain in details how it all works, for now you can download flerry Flash Builder project with all the source code from here.
ipla lite: Adobe AIR based client of video content from Polsat group
ipla lite application was recently released, this is an Adobe AIR/Flex based client of video content published by Polsat group. Polsat is one of the biggest TV stations in Poland. This release is really great news for Mac and Linux users because ipla’s original version released in 2008 was only available for Windows platforms. In this case Adobe AIR brings outstanding capabilities like: video, audio and cross-platform support. At the moment this release allows watching live streaming content, prerecorded TV shows in VOD mode, do video search and navigate through video categories. I know there are other plans to extend it with features known from full Windows version like social networking. Personally I would also like to see capabilities like local video caching (for offline mode watching), Twitter integration, video deeplinking (maybe something similar as we recently introduced for the content from Adobe MAX) – time will show
Here are some screens with application UI:
R-Quick: first Adobe AIR based e-Banking solution from Raiffeisen Bank Polska
Raiffeisen Bank Polska has just released first ever full e-Banking solution based on Adobe AIR technology. This release brings full functionality of what was always available via browser built with Flex. This is great news especially for those that have really slow or low bandwidth internet connections at home, office or are connecting with GSM modems, unfortunately this is still reality among Polish internet providers. This application lowers the required bandwidth to the actual minimum, no graphics no layout no html has to be transferred over the wire, only pure data.
This application is a great example of AIR technology itself but also possibility of deploying Flex browser code on the desktop. I’m hoping we will see more integration with desktop in upcoming releases. I can immediately imagine things like: direct desktop notifications of account transactions, stocks rates alerts, upcoming payments alerts, data caching for complex data intensive calculations and analysis (using encrypted SQLite), drag-and-drop transaction confirmations directly from AIR app to desktop or other applications in formats like PDF, XLS etc.
Here are couple of screens with running application:
Application can be downloaded and installed via badge from this location: https://www.r-bank.pl/web/en/air/ this applies only to Polish Raiffeisen Bank customers of course.
e-Government RIA manifesto
Last week I was presenting at MAX 2009 in LA where I talked about Flex and Adobe AIR in Government, it was based on e-Deklaracje Desktop application released by Polish Ministry of Finance (you can find more info about e-Deklaracje Desktop in one of my previous posts here). Important here is to note that e-Deklaracje received Honorable Mention in Adobe 2009 MAX Awards. My presentation was focusing on requirements, challenges and solutions that Ministry of Finance faced during implementation of e-Deklaracje. Here is the link to video of my presentation online. At the end I concluded with my personal manifesto that I believe e-Government Rich Internet Applications should be guided by to achieve success! Manifesto is made of four mandatory bullets that are in totally random order but each as equal in its value:
Consistency – application should work consistently on all major operating systems, browsers or even hardware without compromises. Citizens should have freedom of using different environments and achieve same user experience.
Openness – the architecture of the application should be open and available to the public. Preferably it should use communication channels and formats based on open standards e.g. HTTP, SOAP, AMF or JASON. These commodities will make integration and extensions easier for the public.
Experience – citizens’ experience should be as straightforward as possible and that applies to anything like usage, accessibility or installation procedures. Actually experience should engage and drive the user through the application.
Security – this is the most obvious one but shouldn’t be forgotten. User data should be secure not only on server side or over communication channels but also on the client side. Encryption to storage or access should be applied where possible, giving the user confidence while using the application.
Any comments are most likely welcome. RIA is trend now that whole industry is going towards and as citizens and users of e-Government applications we should expect same from public institutions solutions.
Flex Challenge Academic Tour

Już jutro wybieram się na konferencję Adobe MAX! W związku z tym chciałem jeszcze przed samym wyjazdem ogłosić coś nad czym pracowałem ostatnie parę tygodni. Mianowicie będzie to cykl warsztatów poświęconych tematyce RIA w tym oczywiście Flex’owi i Adobe AIR. Założenie moje jest takie żeby objechać wszystkie główne ośrodki akademickie w Polsce i przeprowadzić otwarte wykłady i warsztaty dla wszystkich zainteresowanych. W tej chwili na mapie są: Gdańsk, Gliwice, Kraków, Lublin, Łódź, Poznań, Toruń, Warszawa i Wrocław. Jednak same wykłady i warsztaty to nie wszystko, na zakończenie odbędzie się specjalna edycja konkursu Flex Challenge dedykowana dla studentów! Będą super nagrody, myślę, że podobnie jak w roku ubiegłym uda nam się ufundować jakieś produkty firmy Adobe i Apple
Objazd po uczelniach oraz konkurs będą się kryły pod nazwą Flex Challenge Academic Tour. W najbliższym czasie wszystkie potrzebne informacje znajdziecie na stronie Flex Challenge oraz powiązaną z częścią wykładową Akademią Pana Flexa.
Wszystkich zainteresowanych wsparciem w organizowaniu wydarzenia Flex Challenge Academic Tour gorąco zachęcam do pisania pod adres wspolpraca [małpa] flexchallenge [kropka] pl. Wszelka pomoc się przyda, do tego dodam jeszcze, że lista miast, które planuję odwiedzić nie jest zamknięta!
Organizatorem tegorocznych zawodów jest polski oddział Adobe wraz wybranymi partnerami i grupami Adobe w danych lokalizacjach, natomiast sponsorami wydarzenia jest polski oddział Adobe oraz Janmedia.
Desktop and Browser Facebook Apps with AIR and Flex
Last week I was presenting at AUG Poznań and today remotely via Adobe Connect at AUG Trójmiasto, where I showed how to build applications usign AS3 API for Facebook that is available at Google Code. My goal was not only to show how to use Facebook API but also how you can build desktop and browser versions of the same app using conditional compilation in Flex. I took the approach of two Flash Builder projects one with minimum set of Flex (browser) code and AIR (desktop) project with common sources for both. Flex project actually links to AIR project src-common folder in Flash Builder. The structure of both projects looks like this:

Below you can also find example of MainView.mxml code that is common for both projects. Within that example you can see sections that will be compiled based on specified options for each project. I actually defined two compiler options CONFIG::AIR and CONFIG::FLEX. You can find more info about defining compiler options and conditional compilation here. Another very good approach especially in more complex cases is described here on Adobe Developer Connection.
Here is the link to my Demo of AS3 API on Facebook and also below is the badge with desktop version.
You can look at the source code of each example by right-clicking and choosing View Source option. Also you can download Flash Builder project archives from here and here, one with Flex and the other with AIR code.
<?xml version="1.0" encoding="utf-8"?> <s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" width="100%" height="100%" currentState="CONNECTING"> <fx:Script> <![CDATA[ import com.facebook.Facebook; import com.facebook.commands.batch.BatchRun; import com.facebook.commands.friends.GetFriends; import com.facebook.commands.users.GetInfo; import com.facebook.data.batch.BatchCollection; import com.facebook.data.batch.BatchResult; import com.facebook.data.friends.GetFriendsData; import com.facebook.data.users.FacebookUser; import com.facebook.data.users.GetInfoData; import com.facebook.data.users.GetInfoFieldValues; import com.facebook.events.FacebookEvent; import com.facebook.net.FacebookCall; import flash.net.URLRequest; import flash.net.navigateToURL; import mx.collections.ArrayCollection; import mx.controls.Alert; CONFIG::AIR { // DesktopSessionHelper import required for AIR and Facebook Connect import com.facebook.utils.DesktopSessionHelper; import com.facebook.session.DesktopSession; // Session variable when running as AIR app protected var session:DesktopSessionHelper; } CONFIG::FLEX { // FacebookSessionUtil import required for Flex (browser) app import com.facebook.utils.FacebookSessionUtil; // Session variable when running as Flex (browser) app protected var session:FacebookSessionUtil; } /** * Facebook class used to access FB remote API's */ protected var fbook:Facebook; /** * Logged-in FB user */ [Bindable] protected var user:FacebookUser; /** * Collection of logged-in user friends */ [Bindable] protected var userFriends:ArrayCollection; /** * AIR init function */ CONFIG::AIR public function init():void { // Creating session based on API key session = new DesktopSessionHelper("795c0879607643aa3c5a363d09ce5384"); // Registering handler when connected session.addEventListener(FacebookEvent.CONNECT, onConnect); } /** * Flex init function */ CONFIG::FLEX public function init():void { // When running in iFrame API key and secret values are not necessary // these are passed with flashVars from GET parameters session = new FacebookSessionUtil(null, null, loaderInfo); // Registering handler when connected session.addEventListener(FacebookEvent.CONNECT, onConnect); // Setting fbook from session properties fbook = session.facebook; // Checking if fb_sig_session_key was set from IFrame GET params if(loaderInfo.parameters.fb_sig_session_key) { // Verifying FB session session.verifySession(); } else { // This application cannot run as external FB app Alert.show("You cannot run this application outside Facebook!"); } } protected function onConnect(event:FacebookEvent):void { var uid:String; // Checking if connection was successful if (event.success) { // Conditional block that sets fbook variable after connection CONFIG::AIR { // Creating instance of Facebook class fbook = new Facebook(); // Starting new FB session with apiKey and session secret // received after connection in session variable fbook.startSession(new DesktopSession( session.apiKey, session.sessionData.secret, session.sessionData.session_key)); uid = session.sessionData.uid; } CONFIG::FLEX { uid = fbook.uid; } // Creating batch to make two data request at one FB call var batchRun:BatchRun; var batch:BatchCollection = new BatchCollection(); // Batch item GetInfo returning info about current user batch.addItem(new GetInfo([uid], [GetInfoFieldValues.ALL_VALUES])); // Batch item GetFriends returning friends list of current user batch.addItem(new GetFriends(null, uid)); // Posting batch to FB batchRun = fbook.post(new BatchRun(batch)) as BatchRun; // Registering batch run results handler batchRun.addEventListener(FacebookEvent.COMPLETE, onBatchRunResult); } else { // In case of not successful connection Alert.show(event.error.errorMsg); } } /** * Handler for batch run results */ protected function onBatchRunResult(event:FacebookEvent):void { // Setting results variable from event object var batchResult:BatchResult = event.data as BatchResult; // Getting data from first batch result item var userInfo:GetInfoData = batchResult.results[0] as GetInfoData; // Setting current user object user = userInfo.userCollection.source[0] as FacebookUser; // Getting data from second batch result item var friendsInfo:GetFriendsData = batchResult.results[1] as GetFriendsData; // Building array of returned friends ID's var friendsUids:Array = new Array(); for each(var friend:FacebookUser in friendsInfo.friends.source) { friendsUids.push(friend.uid); } // Doing another call to FB to get friends detailed info var call:FacebookCall = fbook.post(new GetInfo(friendsUids, [GetInfoFieldValues.ALL_VALUES])); // Registering handler call.addEventListener(FacebookEvent.COMPLETE, onGetFriendsInfo); // Changing state to connected currentState = "CONNECTED"; } /** * Friends details request handler */ protected function onGetFriendsInfo(event:FacebookEvent):void { userFriends = new ArrayCollection((event.data as GetInfoData) .userCollection.source); } ]]> </fx:Script> <s:states> <s:State name="CONNECTING"/> <s:State name="CONNECTED" /> </s:states> <mx:ProgressBar includeIn="CONNECTING" label="Connecting..." indeterminate="true" labelPlacement="center" horizontalCenter="0" verticalCenter="0"/> <s:Label text="Hello {user.first_name}!" horizontalCenter="0" verticalCenter="-100" fontSize="29" includeIn="CONNECTED"/> <mx:AdvancedDataGrid id="adgFriends" dataProvider="{userFriends}" designViewDataType="flat" horizontalCenter="0" verticalCenter="19" includeIn="CONNECTED"> <mx:columns> <mx:AdvancedDataGridColumn headerText="Name" dataField="first_name"/> <mx:AdvancedDataGridColumn headerText="Last Name" dataField="last_name"/> </mx:columns> </mx:AdvancedDataGrid> <mx:LinkButton label="Get AIR (desktop) version..." horizontalCenter="0" verticalCenter="150" color="#ABA5A5" fontWeight="bold" textDecoration="underline" fontSize="14" click="navigateToURL(new URLRequest('http://www.riaspace.net/examples/fb-as3demo/air/'))"/> </s:Group>







