Tag Archives: AIR

Flex 4.6 and SplitViewNavigator in AIR : Demo application – Finnkino Movies

Adobe Flex 4.6 includes several new and updated mobile-optimized Spark components (for example SplitViewNavigator, CallOutButton, SpinnerList, DateSpinner, Text Enhancements and ToggleSwitch).

In this example I used SplitViewNavigator to show Finnkino's Theatres in the first view of the SplitViewNavigator. Second view shows the selected Theatre's movies (what are running at this day) or all movies that are running in Finnkino's Theatres.

<s:SplitViewNavigator id="splitNavigator" width="100%" height="100%" autoHideFirstViewNavigator="true">
  <s:ViewNavigator id="areasView" width="200" height="100%" firstView="views.AreaCategory"/>
  <s:ViewNavigator id="detailView" width="100%" height="100%" firstView="views.MoviesTileView">
    <s:actionContent.portrait>
    <s:Button id="navigatorButton" label="Valitse Teatteri"
         click="splitNavigator.
         showFirstViewNavigatorInPopUp(navigatorButton);"/>
    </s:actionContent.portrait>
  </s:ViewNavigator>
</s:SplitViewNavigator>

Movies in Landscape mode

SplitViewNavigator is working very nicely and it hide's the first view in navigator when tablet is rotated to Portrait. You should add resize event handling to your application and use states in ActionContent in ViewNavigator.

<s:Application resize="resizeHandler(event);">
protected function resizeHandler(event:ResizeEvent):void {
  currentState = aspectRatio;
}
<s:states>
  <s:State name="portrait"/>
  <s:State name="landscape"/>
</s:states>

Movies in Portrait mode

List and ItemRenderers
I have used List to show all the movie items and own made ItemRenderer to display some basic information (or just movie image when all the movies are selected from the main list).

<s:List id="moviesList"
    dataProvider="{data}"
    itemRenderer="comps.MovieAreaTile"
    horizontalCenter="0"
    verticalCenter="0"
    width="100%" height="100%"
    contentBackgroundColor="#FFFFFF"
    change="moviesList_changeHandler(event)"
    >
  <s:layout>
    <s:TileLayout id="tileList"
     paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5" horizontalGap="5" verticalGap="5"
    columnAlign="justifyUsingWidth"
   />
  </s:layout>
</s:List>
// MovieAreaTile
<s:HGroup>
  <s:Image source="{data.Images.EventSmallImagePortrait}" />
  <s:VGroup>
    <s:Label id="title" styleName="title" text="{data.Title}" />
    <s:Label id="startTime" styleName="data" text="{data.dttmShowStart}" />
    <s:Label id="length" styleName="data" text="{data.LengthInMinutes} min" />
    <s:Label id="theatre" styleName="data" text="{data.TheatreAndAuditorium}" />
    <s:Label id="genre" styleName="data" text="{data.Genres}" />
  </s:VGroup>
</s:HGroup>

All the Movies in Finnkino's Theatres

SQLite: Automatically display same Theatre which was used in previous time
In this example I store theatreID and theatreName to SQLite database every time when a new Theatre is selected. Those values are used when application is started again.

Connection to database:

private function connect(type:Number):void {
  dbConn = new SQLConnection();
  if (type == CREATE_DB) dbConn.addEventListener(SQLEvent.OPEN, createTable);
  else dbConn.addEventListener(SQLEvent.OPEN, getTheatreData);
  dbConn.addEventListener(SQLErrorEvent.ERROR, errorHandler);
  dbConn.openAsync(dbFile);
}

Create a table:

private function createTable(event:SQLEvent):void {
  var dbStatement:SQLStatement = new SQLStatement();
  dbStatement.sqlConnection = dbConn;
  dbStatement.addEventListener(SQLEvent.RESULT, theatreTableCreated);
  dbStatement.addEventListener(SQLErrorEvent.ERROR, errorHandler);
  dbStatement.text = "CREATE TABLE IF NOT EXISTS movieTable (id INTEGER PRIMARY KEY AUTOINCREMENT, theatreID TEXT, theatreName TEXT);";
  dbStatement.execute();
}

Table is created a first time -> add some sample data to table

private function theatreTableCreated(event:SQLEvent):void {
  var dbStatement:SQLStatement = new SQLStatement();
  dbStatement.sqlConnection = dbConn;
  dbStatement.addEventListener(SQLEvent.RESULT,theatreDataAdded);
  dbStatement.addEventListener(SQLErrorEvent.ERROR,errorHandler);
  // just set default data when table is created
  theatreID = 1015;
  theatreName = "Jyväskylä";
  dbStatement.text = "INSERT INTO movieTable (theatreID, theatreName) VALUES('"+theatreID+"','"+theatreName+"');";
  dbStatement.execute();
}

Theatre (id and name) data is added first time, load events with HTTPService

<fx:Declarations>
  <s:HTTPService id="finnkinoArea" showBusyCursor="true" url="{url}"
      result="areaHandler(event)"
      fault="areaFaultHandler(event)"
   />
</fx:Declarations>

private function theatreDataAdded(event:SQLEvent):void {
  loadEvents(theatreID);
}

private function getTheatreData(event:SQLEvent):void {
  var dbStatement:SQLStatement = new SQLStatement();
  dbStatement.sqlConnection = dbConn;
  dbStatement.addEventListener(SQLEvent.RESULT,theatreResult);
  dbStatement.addEventListener(SQLErrorEvent.ERROR,errorHandler);
  dbStatement.text = "SELECT theatreID, theatreName FROM movieTable where id=1";
  dbStatement.execute();
}

TheatreID and name is loaded from SQLite, start loading XML data from Finnkino's service

private function theatreResult(event:SQLEvent):void {
  var dbResult:SQLResult = SQLStatement(event.target).getResult();
  theatreID = dbResult.data[0].theatreID;
  theatreName = dbResult.data[0].theatreName;
  // load previous used theatre
  loadEvents(theatreID);
}
public function loadEvents(theatreID:uint):void {
  // one Teatre
  if (theatreID != -1) url = urlBase + theatreID;
  // all events from Finnkino
  else url = "http://www.finnkino.fi/xml/Events/";
  // call HTTPService
  finnkinoArea.send();
}

Movie Details
Movie details are shown when user clicks MovieTile from TileView. Finnkino's Service offers a lot of information about the movie. In this example I only display some sample data and movie trailer. Movie trailer can be played in fullscreen mode too (use this only in Landscape - there a some issues in portrait mode - in this example :-)

<s:Scroller width="100%" height="100%">
  <s:VGroup width="100%">
    <s:HGroup>
      <s:Image source="{data.Images.EventLargeImagePortrait}" />
      <s:VGroup>
        <s:Label text="{data.Title} ({data.OriginalTitle})" styleName="title"/>
        <s:Label text="{data.RatingLabel}" styleName="data" />
        <s:Label id="startTime" styleName="data" text="{data.dttmShowStart}" />
        <s:Label id="length" styleName="data" text="{data.LengthInMinutes} min" />
        <s:Label id="genre" styleName="data" text="{data.Genres}" />
        <s:Label id="showUrl" styleName="dataBlue" text="{data.ShowURL} - (osta liput)" click="showUrl_clickHandler(event)" />
        <s:Label id="eventUrl" styleName="dataBlue" text="{data.EventURL} - (lisätietoa elokuvasta)" click="eventUrl_clickHandler(event)" />
     </s:VGroup>
   </s:HGroup>
   <s:Label text="{movieData.Synopsis}" width="100%" styleName="synopsis" />
   <s:VideoPlayer id="myVideo" source="{movieData.Videos.EventVideo.Location}" autoPlay="false" visible="{isVideo}" width="{this.width-20}"/>
   <s:Button label="Takaisin" id="takaisin" click="takaisin_clickHandler(event)"/>
   </s:VGroup>
</s:Scroller>

Movie details in Landscape mode

Movie details in Portrait mode

Movie trailer in fullscreen

Source codes
- Movies.fxp, Movies.zip

Note
There are a few bugs in this example (too). Sometimes the date is missing from first views, and VideoPlayer is messing up if it is in fullscreen mode in Landscape and you turn the device to portrait.. but now it is working for my purposes :-) and finally I have only tested this in Samsung Galaxy Tab 10.1 not in mobile...

This example uses Finnkino's Services

Developing AIR application to run in different screen sizes

A few days ago I got Samsung Galaxy S 2 and Tab 10.1 in my hands and I want to test how to make an AIR application that runs in both of these (with different screen sizes). This demo application uses SQLite database to collect information about Interesting Places (place information and picture).

I decided to make mobile application with s:Application and first test is application running in mobile or tablet. After that different states are used in s:ViewNavigator to launch MainPhoneView or MainTabletView view depending on used device (mobile or tablet). (Michael Chaize has wrote excellent article about Adaptive UI: Mobile vs. Tablet).

<s:ViewNavigator
  id="mainNavigator"
  left="0" top="0"
  right="0" bottom="0"
  firstViewData="{places}"
  firstView="views.MainPhoneView" firstView.tablet="views.MainTabletView"
  resize="mainNavigator.scrollRect = new Rectangle(0, 0, mainNavigator.width, mainNavigator.height)">

MainPhoneView
MainPhoneView
draws PlaceList component (which is used in the left side of MainTabletView view also) which displays some already embeded Interesting Places from SQLite database (title and time) by default. PlaceView is opened when users clicks PlaceList item and place information data is show with PlaceComp component (which is used in the right side of MainTabletView view also). Sample SQLite database is embeded into Flex Project.

<comps:PlacesList id="placesList"
  places="{data}"
  width="100%" height="100%"
  click="placeslist1_clickHandler(event)"/>


MainTabletView

MainTabletView holds Placelist and PlaceComp components in a same view in tablet. So there are not View changes in tablet version of this application. All needed information can be show in one View.

<s:HGroup width="100%" height="100%">
  <comps:PlacesList id="placesList" places="{data}"
	width="33%" height="100%" click="placesList_clickHandler(event)">
  <comps:PlaceComp id="place" width="67%">
<s:HGroup>


Add Place

User can write some lines from interesting place and take one picture. All of these information are saved into SQLite database. Same component are used in mobile and tablet view.

Conclusion
Using your own made components in different views helps application development into different screen sizes. This is working quite a nicely in AIR development. Like in this small example same components are used in Tablet and Mobile views to build this example.

Files
- MobileInterestingPlaces.apk
- MobileInterestingPlaces.fxp

Note
There are a few bugs and limitations in this example.. I did this just for a testing purposes :-)

Publish AIR applications from Adobe Flash CS5 to RIM PlayBook

There is a quite a many steps to follow to get AIR applications from Flash CS5 published to BlackBerry App World for distribution. I will cover here all needed parts in one blog post. Hope you find these information useful and get your application published to BlackBerry App World.

1 Create your content with Adobe Flash CS5 and publish to Adobe AIR
- go and get Adobe Flash CS5 from Adobe and create your application
- stage size: 1024x600, Publish settings: Adobe AIR 2.5, AS 3.0
- Adobe AIR 2.5 Settings: Normal AIR settings to generate SWF and XML file

2 Download the BlackBerry® Tablet OS SDK for Adobe® AIR®
- go to http://us.blackberry.com/developers/tablet/adobe.jsp (a lot of useful information)
- install SDK (for example to Adobe Flash Builder Burrito folder if you have that one and want to use SDK with Burrito too)
- blackberry-tablet-sdk-0.9.4 will be stored to Burrito's sdks folder

3 Download VMware® (required to run the BlackBerry PlayBook Simulator)
- go to https://www.vmware.com/tryvmware/?p=player&lp=1 and get VMware Player
- register and install player

4 Download the BlackBerry PlayBook Simulator
- go again to http://us.blackberry.com/developers/tablet/adobe.jsp
- install simulator and open (if you want to test it now) it to VMware player (mem 1GB, enable 3D acceleration)
- it took several minutes to launch simulator (just wait for it)
- enable Development Mode in Simulator (remember your password and device IP)

5 Register for Code Signing Keys (you have to sign your application)
- go to https://www.blackberry.com/SignedKeys/ and fill the form
- for me it took a few hours to get CSJ file via email

6 Initialize communication with the RIM® Signing Authority by using the blackberry-signer tool
- type: blackberry-signer -csksetup -cskpass <create_a_password>
- you should see: CSK file created message

The password you specify in the -storepass parameter allows you to access the RIM Signing Authority using your identity. You should protect the password. Remember include full path to blackberry-signer command if it is not in your PATH.

7 Register with the RIM Signing Authority using your CSJ registration file by using the blackberry-signer tool
- type: blackberry-signer -register -csjpin <PIN> -cskpass <csk password from step 6> <CSJ_file>
- you should see: Successfully registered with server message

When installing your keys, ensure that you have correctly entered the registration PIN number that you entered into the order form. If you enter an incorrect PIN 5 times, your keys will be deactivated.

8 Create a Developer Certificate by using the blackberry-keytool tool (if you don't have one)
- type: blackberry-keytool -genkeypair -keystore <output_file.p12> -storepass <create_a_password> -dname "cn=<company_name>" -alias author
- you should see generated p12 file in your directory

The value for cn in the dname parameter must match exactly the name you entered in the Company field on the web form when you requested permission to sign applications.

9 Package your application from the command line
- type: blackberry-airpackager -package <output_filename.bar> project_name-app.xml project_name.swf any_other_project_files

Include your additional files like icons (look your app-xml file). You should see your bar file in your directory.

10 Ask the RIM® Signing Authority to sign your BAR file
- type: blackberry-signer -verbose -cskpass <your CSK password> -keystore <your P12 file> -storepass <your P12 file password> <BAR_file.bar> RDK
- you should see bar signed message

11 Sign the BAR file using your code signing certificate
- type: blackberry-signer -keystore <your P12 file> -storepass <your p12 file password> <BAR_file.bar> author
- you should see bar signed message

12 Test your application with PlayBook Simulator
- start simulator
- type: blackberry-deploy -installApp -device <ip address of simulator> -package <BAR_file.bar> -password <simulator password>
- test your application in PlayBook Simulator

13 Publish the BAR file to BlackBerry App World™ for distribution
- create a vendor account to BlackBerry App World
- submit your application for evaluation by RIM

You can find more detailed information here:
- BlackBerry - Tablet OS SDK for Adobe AIR
- Signing your application
- Configure application signing from the command line
- Adobe AIR

Hope these steps helps you to publish your Adobe Flash CS5 made AIR content to BlackBerry App World.

BlackBerry PlayBook (RIM) with Flash and AIR support

A lot of new opportunities are available for Adobe Flash and AIR developers nowadays. RIM's new BlackBerry PlayBook tabled will support Adobe Flash and AIR with many nice features. Unfortunately I didn't get time to attend MAX this year to actually see those BlackBerry/RIM sessions but luckily sessions are available via Adobe TV - Developing Adobe AIR Apps for the BlackBerry Tabled OS.

In developer view - there are a lot of new informations available in BlackBerry web site:

1. Tablet OS Application Development
2. All the specs on the PlayBook
3. PlayBook Developer Webinars (weekly webcast)
4. Build and submit your app, change to win PlayBook

I am not so familiar with RIM devices, but hope I got some in my hands someday.

Send pictures from Android device to Windows with AIR applications

In this example I will show how you can take and send pictures from Android phone to Windows application using Adobe AIR in Android and Windows.

Create AIR for Android application (to take and send picture)

In Adobe AIR 2.5 there is Camera UI class that allows you to capture a still image or video using the default camera application on a device. First check that Camera is supported and create a new Camera UI object and register MediaEvent.COMPLETE to call onComplete function when picture is taken.

After that establish a new NetConnection for P2P in the local network (LAN). This can be done by specifying connection string as "rtmpf:" in NetConnection.

private function init():void {
  if (CameraUI.isSupported){
    myCam = new CameraUI();
    myCam.addEventListener(MediaEvent.COMPLETE, onComplete);
  }
  nc = new NetConnection();
  nc.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
  nc.connect("rtmfp:");
}

When connection is made netStatus function will be called. If NetConnection is ok it is time to setup own group for multicasting. You can set up your own NetGroup with GroupSpecifier. Enable local connections between peers and set your IP address and port for multicasting. You can find more details about multitasking for example here: www.flashrealtime.com (Tom Krcha - thanks about your excellent demos and work with Flash based Technologies).

private function netStatus(event:NetStatusEvent):void {
  switch(event.info.code) {
    case "NetConnection.Connect.Success":
       var groupspec:GroupSpecifier = new GroupSpecifier("fi.ptm.imageApp/myChannel");
       groupspec.postingEnabled = true;
       groupspec.ipMulticastMemberUpdatesEnabled = true;
       groupspec.addIPMulticastAddress("225.225.0.1:30000");
       group = new NetGroup(nc,groupspec.groupspecWithAuthorizations());
       group.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
       break;
  }
}

A new picture is taken when user presses Take Picture Button in device's screen. Picture is taken with Android Native Camera Application (using with Camera UI class) and onComplete will be called when picture is taken.

In this example image is shown with <mx:Image id="image"> and image URL is used when image is sent to Windows AIR application.

private function takePicture():void {
  image.filters = [];
  if (CameraUI.isSupported) myCam.launch(MediaType.IMAGE);
}

private function onComplete(evt:MediaEvent):void{
  image.source = evt.data.file.url;
  imageURL = evt.data.file.url;
}

Image is converted to ByteArray and all of those bytes is sent to Windows AIR application when user clicks send button in the devices screen.

private function sendPicture():void {
  var fs:FileStream = new FileStream();
  fs.open(new File(imageURL), FileMode.READ);
  var bytes:ByteArray = new ByteArray();
  fs.readBytes(bytes);
  fs.close();
  if (bytes == null) {
    trace("Image bytes is null!");
  } else {
    // create a new object with image data and image count (sent)
    var message:Object = new Object();
    message.bytes = bytes;
    message.images = ++imagesSent;
    group.post(message);
    trace("Pictures sent:"+imagesSent);
  }
}

Create AIR local client in Windows (receive images)

Windows AIR application is connected to same group and it is waiting for images from AIR for Android application. If we get a new netStatus with NetGroup.Posting.Notify message then a new image is received from Android. First new Sprite is created and all of those image bytes is loaded with Loader.

public function receivePicture(message:Object):void {
  imageContainer = new Sprite();
  var loaderContext:LoaderContext = new LoaderContext();
  loaderContext.allowCodeImport = false;
  var loader:Loader = new Loader();
  loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onPicLoaded,false,0,true);
  loader.loadBytes(message.bytes, loaderContext);
  // add a new sprite to hold image
  imageSprite = new Sprite();
  imageSprite.addChild(loader);
  imageContainer.addChild(imageSprite);
  wrapper.addChild(imageContainer);
}

ImageSprite is a wrapped for a new image sprite to get Parallel image animation into it (scaling, positioning and sizing) for 4 seconds.

private function onPicLoaded(e:Event):void {
  imageSprite.x = -imageContainer.width / 2;
  imageSprite.y = -imageContainer.height / 2;
  imageContainer.x += imageContainer.width / 2;
  imageContainer.y += imageContainer.height / 2;
  startX = imageContainer.width / 2;
  startY = imageContainer.height / 2;
  targetX = Math.floor(Math.random()*frame.width);
  var borderSize:uint = 100;
  if (targetX < borderSize) targetX = borderSize;
  if (targetX > (frame.width-borderSize)) targetX = frame.width - borderSize;
  targetY = Math.floor(Math.random()*frame.height);
  if (targetY < borderSize) targetY = borderSize;
  if (targetY > (frame.height-borderSize)) targetY = frame.height - borderSize;
  startRotation = Math.floor(Math.random()*360);
  if (Math.random() > .5) targetRotation = startRotation + 180;
  else targetRotation = startRotation - 180;
  imageAnimation.play();
}

Here is a basic parallel animation used to make a cool sliding effect with a new images. All previous taken images will be visible there too.

<mx:Parallel id="imageAnimation" target="{imageContainer}" >
  <mx:AnimateProperty property="scaleX" fromValue="1" toValue="0.2" duration="4000" />
  <mx:AnimateProperty property="scaleY" fromValue="1" toValue="0.2" duration="4000" />
  <mx:AnimateProperty property="x" fromValue="{startX}" toValue="{targetX}" duration="4000" />
  <mx:AnimateProperty property="y" fromValue="{startY}" toValue="{targetY}" duration="4000" />
  <mx:AnimateProperty property="rotation" fromValue="{startRotation}" toValue="{targetRotation}" duration="4000" />
</mx:Parallel>

Testing and source codes

You have to install Flex 4.1 (or newer) and AIR 2.5 SDK to try these examples (just copy/overwrite AIR SDK 2.5 to Flash Builder sdks/4.1.0 folder, or make copy from 4.1.0 to for example FlexAIR2.5 folder and overwrite there).

AIRSendingImageFromAndroid.zip
AIRReceiveImageFromAndroid.zip

Adobe® Developer Week

Adobe is holding free Developer Week at May 10 - 14, 2010. Live webinars includes new Adobe CS5 technologies like: Adobe Flash Platform, Flex, Flash Builder, AIR and interactive Q&A sessions.

Full session list:

  • Welcome and Introduction to the Adobe® Flash® Platform
  • Building Multi-Screen Applications with the Adobe® Flash® Platform and Adobe® Creative Suite® 5
  • New Features in Adobe® Flex® 4 Framework
  • New Features in Adobe® Flash® Builder™ 4
  • Adobe® Flash® Catalyst™ CS5 for Developers
  • Building Mobile Applications with Adobe® Flash® Professional™ CS5: Create a Game
  • What's New in Adobe® Flash® Builder™ for Java Developers
  • New Features in Adobe® Flash® Player 10.1
  • Creating Sexy Applications with the Adobe® Flash® Platform: Designer-Developer Workflow
  • A Basic CRUD Application with Zend AMF and Adobe® Flex 4
  • Adobe® Flash® Builder™ 4 and Adobe® ColdFusion® 9
  • Advanced Features In Adobe® Flex®
  • AS34J: ActionScript 3 for Java Developers
  • Adobe® Flash® Platform and Salesforce.com
  • Adobe® Flash® Builder™ for Adobe® Dreamweaver® CS5 Users
  • What’s New in Adobe® Flash® Builder™ for PHP Developers
  • Using Advanced Features with Adobe® Flex® & PHP
  • Adobe® ColdFusion® 9 and Adobe® Flash® Catalyst™ CS5
  • Adobe® Flex® and BlazeDS Integration with SpringSource
  • Building Desktop Applications with Adobe® AIR® 2.0

More informations and registration.

SWF2NFL – Application for packaging Flash Lite apps to Series 40 Devices

Dale has released an AIR application that allows you to create NFL files for Nokia Series 40 devices. SWF2NFL application is really easy to install directly from Moket's site. Application is also really easy to use, just drop your SWF content folder and icon to application and press Build - and that is all, your NFL file is ready.

I tested it to my old Flash Lite project - qTile.
You can download qTile.nfl for Nokia Series 40 here.

qTile for Nokia Series 60 devices