« Importing REST web services in ActionScript with Flex Builder 4 | Main | ChangeEvent vs Bindable, multiple change events for binding »

How to enable communication between AIR and Skype through Merapi

Merapi is an interesting project that wants to create a bridge between Air and Java on the desktop, something that already exists between Flex and Java on the web thanks to BlazeDS/LCDS and other similar projects.
It's made up of an actionscript part and a java part, together they build a messaging bridge based on AMF so that you can send messages from java to Air and viceversa.
Merapi is currently in a private alpha state, though a first beta has just been released on their site. If you want to get involved and download the package you have to create an account on http://www.merapiproject.net/ and wait for their approval (usually doesn't take longer than a few days).
The APIs are very simple, you can see an example of their usage on: http://www.merapiproject.net/index.php?option=com_content&view=article&id=47&Itemid=64&limitstart=3.

The idea is simple and is based on sending messages from one endpoint to the other. Sending a message from ActionScript:

var message : Message = new Message();
message.data = "Hello from Merapi Flex.";
message.type = "Reply";
Bridge.instance.sendMessage( message );


Sending a message from Java:

Bridge bridge = Bridge.getInstance();
Message message = new Message();
message.setData("Hello from Merapi Java.");
bridge.sendMessage(message);

Receiving a message in Flex:

<merapi:BridgeInstance
id="bridge" result="handleResult(event)" />
<mx:Script>
<![CDATA[
private function handleResult( event : ResultEvent ) : void
{
var message : IMessage = event.result as IMessage;

Receiving a message in Java:

Bridge.getInstance().registerMessageHandler("Reply", messageHandlerInstance );
public void handleMessage( IMessage message )
{
System.out.println( message.getData() );
}

There is a forum: http://www.merapiproject.net/index.php?option=com_fireboard and some interesting video of application examples: http://www.merapiproject.net/index.php?option=com_content&view=article&id=51&Itemid=84.

I decided to start playing with Merapi creating a AIR client for the Skype chat; first of all I started searching for a Java implementation of the Skype communication API. I found this project "http://skype.sourceforge.jp/index.php?Skype%20API%20For%20Java%20(English)", officially supported by Skype https://developer.skype.com/wiki/Java_API
It's a well written and powerful library so it was very easy to understand.

In my application I used two message types:
- "skype" - for generic API commands
- "skypeChat" - for chat messages

then I created two specific payloads for the Merapi message dependent on its type:

SkypeCommand in ActionScript

package
{
import mx.collections.ArrayCollection;

[RemoteClass(alias="SkypeCommand")]
public class SkypeCommand
{
public static const GET_VERSION:String = "getVersion";
public static const GET_FRIENDS:String = "getFriends";

public var name:String;
public var data:Object;
}
}

SkypeCommand in Java

public class SkypeCommand {
public static final String GET_VERSION = "getVersion";
public static final String GET_FRIENDS = "getFriends";

private String name;
private Object data;

public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

SkypeChatMessage in ActionScript:

package
{
[RemoteClass(alias="SkypeChatMessage")]
public class SkypeChatMessage
{
public var skypeId:String;
public var message:String;
}
}

SkypeChatMessage in Java:

public class SkypeChatMessage {
private String skypeId;
private String message;

public String getSkypeId() {
return skypeId;
}
public void setSkypeId(String skypeId) {
this.skypeId = skypeId;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

As you can see, class mapping works exactly the same way it works in BlazeDS.
You have to initialize the bridge on ActionScript:

<merapi:BridgeInstance id="bridge"
  result="bridgeMessageHandler()" />

and on Java you have to register message type handlers:

Bridge.getInstance().registerMessageHandler( "skype", new SkypeMessageHandler() );
Bridge.getInstance().registerMessageHandler( "skypeChat", new SkypeMessageHandler() );

and a Chat message listener

Skype.addChatMessageListener(new SkypeChatListener());

On AIR creationComplete I send two skype commands to get version and friends list:

private function init():void {
var versionCmd:SkypeCommand = new SkypeCommand();
versionCmd.name = SkypeCommand.GET_VERSION;

var listCmd:SkypeCommand = new SkypeCommand();
listCmd.name = SkypeCommand.GET_FRIENDS;

bridge.send("skype", versionCmd);
bridge.send("skype", listCmd);
}

On Java side, in the SkypeMessageHandler, for "skype" type:

SkypeCommand cmd = (SkypeCommand)((Message) message).getData();
Message response = new Message();
response.setType("skype");
SkypeCommand respCmd = new SkypeCommand();

String commandName = cmd.getName();

if(commandName.equals(SkypeCommand.GET_VERSION)) {
respCmd.setName(SkypeCommand.GET_VERSION);
respCmd.setData(Skype.getVersion());
} else if(commandName.equals(SkypeCommand.GET_FRIENDS)) {
ContactList list = Skype.getContactList();
Friend[] friends = list.getAllFriends();
String[] friendIds = new String[friends.length];

for (int i = 0; i < friends.length; i++) {
friendIds[i] = friends[i].getId();
}

respCmd.setName(SkypeCommand.GET_FRIENDS);
respCmd.setData(friendIds);

}

response.setData(respCmd);

Bridge.getInstance().sendMessage(response);

I resend back a SkypeCommand of the same type so that in the AIR app I can recognize the response:

private function bridgeMessageHandler():void
{
if(bridge.lastMessage.type == "skype")
switch(bridge.lastMessage.data.name) {
case SkypeCommand.GET_VERSION:
versionLabel.text = "Skype version: "+bridge.lastMessage.data.data as String;
break;
case SkypeCommand.GET_FRIENDS:
var friendsArray:Array = bridge.lastMessage.data.data as Array;
friendsList.dataProvider = friendsArray;
break;
}

For chat messages sent by AIR, I only forward them to the Skype API:

SkypeChatMessage chatMsg = (SkypeChatMessage)((Message) message).getData();
Chat c = Skype.chat(chatMsg.getSkypeId());
c.send(chatMsg.getMessage());

For chat messages sent by a friend, in the SkypeChatListener I forward the message to the bridge:

public void chatMessageReceived(ChatMessage arg0) throws SkypeException {
SkypeClient.hideSkypeWindow();
Message response = new Message();
response.setType("skypeChat");

SkypeChatMessage msg = new SkypeChatMessage();
msg.setSkypeId(arg0.getSenderId());
msg.setMessage(arg0.getContent());

response.setData(msg);

try{
Bridge.getInstance().sendMessage(response);
} catch(Exception e) {
e.printStackTrace();
}
}

You have to start the java application first, then the AIR, otherwise you will get IOErrors on the bridge. And here is the AIR app in action:
AIRSkype.png

This is the AIR installer: Download file
and the runnable jar file: Download file

Waiting for the next version of AIR, that will let you call executable files, Merapi could be a good starting point to make your apps interoperate with AIR; there is a java implementation for nearly everything you will need, thus Merapi exponentially expands the AIR potential on desktop.

TrackBack

TrackBack URL for this entry:
http://blog.comtaste.com/mt-tb.cgi/77

Comments (3)

milos:

Great article! Thank you a lot!

kwade:

Great article. Very helpful. Can you explain how to pass an array of objects from java -> air and back? For example, if I have an array of my own Person objects that I want to pass from Java to AIR using merapi?

Emanuele Tatti:

Hi kwade,
you can pass object to the AIR application the same way you would do using BlazeDS/LCDS.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

About

This page contains a single entry from the blog posted on January 17, 2009 4:46 PM.

The previous post in this blog was Importing REST web services in ActionScript with Flex Builder 4.

The next post in this blog is ChangeEvent vs Bindable, multiple change events for binding.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.33