Home >
Developing Google Wave Gadgets with Adobe Flex
Introduction
Google Wave is a collaboration platform that was announced by Google last year. It is a web based platform where participants can come together and collaborate both for work and fun. The developers of Wave have kept the platform open for extension where developers can write automated robots and gadgets (similar to widgets) that can participate in a wave and add value to the collaboration. In this article we will cover how you can start developing gadgets for Google Wave using Flex as your primary development environment.
We will cover the following:
- What is Google Wave?
- What are Google Wave Extensions?
- "Hello World" Google Wave Gadget
- A Twitter Search Google Wave Widget written in Flex
- Sharing State in a Google Wave Gadget
What is Google Wave?
Google Wave as mentioned is a web-based collaboration platform released by Google. The fundamental concept is that of a Wave. A Wave is a threaded conversation between one or more participants. The participants are users like us or can be automated participants (known as Robots). The users can create messages in a Wave and embed media like documents, pictures, videos, etc. You can apply Google Wave to all sorts of communication. Examples include Event Planning, Trip Planner, Project Discussion Notes, To Do List between a team, etc.
The Google Wave Preview is available at http://wave.google.com. To use Google Wave, you need to have a Google Wave Account, which currently are being given only by invitation.
This article will not delve in the specifics of Google Wave from a user perspective. There is an excellent reference available for the same called Complete Wave Guide if you want to read up in detail on how you can effectively use Google Wave. There are also some excellent use cases of Google Wave present over here : http://mashable.com/2009/11/14/google-wave-use-cases/
What are Google Wave Extensions?
The developers of Google Wave kept the platform open for extension. A developer can write an extension that can participate in a Wave. Extensions are the mechanism by which you can extend Google Wave by adding your own creations. Extensions are of two types: Robots and Gadgets. Let us cover them in brief now.
Robot
A Robot is an automated participant in a Wave. It can be aware of most things happening in the Wave by subscribing to Events. It can react to an Event by modifying the state of the Wave. Modification could mean modifying the message that the participant typed, adding a new message, post the entire Wave contents to a blog, Tweet a particular message, etc. A robot is typically invisible and reacts to events. Example of a Robot is the Stocky Robot that looks for a participant typing $StockSymbol and adds the current stock price of the Symbol.
Gadgets
Gadgets are like mini-applications that run within the Google Wave client. They can be typically thought of as a UI which several participants can share at the same time. This is a key point because all participants share the same state in the Gadget. Participants can change the state of a Gadget and a Gadget can react to that by rendering the data accordingly. Example of a Gadget is the Yes/No/Maybe Gadget that you can add in Wave to let users vote their preference for an item. Another excellent example of a Wave Gadget written in ActionScript is the Napkin Gadget that allows you to sketch your ideas to other participants in the Wave.
In this article, we will focus on writing a Gadget and our choice for writing the Gadget will be the Flex development environment. Let us first look at the structure that is expected when writing a Google Wave Gadget.
"Hello World" Google Wave Gadget
A Gadget definition in Google Wave is a XML file that is structured as shown below:
<Module>
<ModulePrefs title="My Flex Gadget">
<Require feature="flash" />
</ModulePrefs>
<Content type="html">
<![CDATA[
]]>
</Content>
</Module>
At the root we have a Module element that contains the preferences and the content. In the ModulePrefs, you need to include one or more feature libraries that your Gadget will require. Google Wave supports several feature libraries like flash, tabs, dynamic-height,etc. A complete list is found here.
In the list above, we have included only one feature library i.e. flash. We need flash because we are going to embed a Flash movie i.e. SWF in the Gadget. Each Gadget is 200 pixels in height by default. In case you wish to modify that and provide another static height value to your Gadget, then you can use the attribute height for the The Content element is specified as HTML and this contains the core of the Gadget that you will include. Note that the Content is put inside of the CDATA element and it typically comprised of HTML, Javascript and CSS stuff. In our case, we will be using the OBJECT HTML tag to insert the Flash Movie i.e. SWF that we will develop. We will keep things pretty simple over here since the focus is on first getting the process right i.e. development, deployment and then testing. We will develop a simple MXML application that has a single label that just says "Hello World". Launch your Flex Builder and generate a simple Hello World application of your choice. My main MXML file is called HelloWorldGadget.mxml. The source code for my application is shown below: We build the above project and generate the HelloWorldGadget.swf Flash movie file. The next step is to write our Gadget XML since this is what will be understood by Google Wave. I first repeat the skeleton structure of the Google Wave Gadget XML that was covered before. It is listed here again for your reference. The only change is that I have modified the title to "Hello World Gadget". Now we need to insert our HelloWorldGadget.swf movie inside the Content element by using the The code is straightforward and you will find that the HelloWorldGadget.swf movie file has been referenced in the Object tag. The URL is from the site that will be hosting (serving) your files. Keep in mind that Google Wave platform is a web based platform and if you want others to use your Gadget, it must be made available on a publicly available server. In my case I have used the Google App Engine hosting environment to serve my files, but it could be served from your personal site or any other publicly addressable server. This step is a logical step that was mentioned above. Once you have your .SWF file and the Gadget XML file ready, you need to put them up on a Server that is publicly addressable. Make sure that the appropriate SWF url is specified in the You will need to have a Google Wave Account to do these steps but even if you dont, you will be able to see the entire process below. The steps are: In this section we will write another Wave Gadget in Flex. It will sort of mimic the process that we did in the earlier section except that it will be much more than "Hello World". In this Gadget, that we will call the Twitter Search Gadget, we can enter a search term, then it will do a search across Twitter for tweets matching that term. The first step involves writing the Flex application. The main application file : BasicTwitterSearchGadget.mxml is shown below. Let us go through the main parts of the source code: Listed below is the Google Gadget definition in XML. This should be familiar by now. The only slight change that I have introduced here is the addition of the height attribute to the ModulePrefs element. I have provided this static value for the height so that our Gadget when displayed in Google Wave will be bigger than the default height value of 200 pixels. The rest of the XML definition is simply referencing our SWF file inside of the We add the Gadget to the Wave in the same manner as outlined earlier by using the Insert Gadget by URL option in the Wave client. In case you wish to try this gadget, you can try inserting the Gadget by using the following url : http://wavegadgets4u.appspot.com/basictwittersearchgadget.xml. A sample run of Twitter Search for the term "insideria" shows up the results in the Gadget embedded inside of Wave. Google Wave as you know is a collaborative platform. A wave typically consists of several messages by participants and even if you come back to it after several days, it still saves its State. What this means is that an extension present in the Wave for e.g. the Gadget that we just wrote should also be able to save its state. To understand this a little better, think about the following points: To enable our Gadget to be state aware, we will need to work with the State API that the Wave Programming model exposes. Fortunately for those of us, who prefer to use ActionScript to write the Gadget, there is an API already available that wraps the Google Wave State Javascript API with an ActionScript Wrapper. The Project is open sourced and available at http://code.google.com/p/wave-as-client/ Download the wave-as-client04.swc from the Downloads page. Add this .SWC file to the Project LibraryPath. This is as simple as copying it to the libs folder of your Project.
We are going to modify the BasicTwitterSearchGadget.mxml Application that we wrote to incorporate state functionality. The main application file : TwitterSearchGadget.mxml is shown below. Let us go through the main parts of the source code: By making these changes, we have changed the behaviour of our Gadget to incorporate the following: The final part is that of the Gadget XML definition, which is shown below. The only changes are the addition of a couple of Javascript files that are needed to act as a bridge with the WaveAsClient.swc that we used and the Javascript libraries that manage interaction with the Wave for us. For more information on this, you can refer to http://code.google.com/p/wave-as-client/. Addiitonally we have an extra feature library included "rpc". This feature provides support for operations for making remote procedure calls for gadget-to-container, container-to-gadget, and gadget-to-gadget communication. In case you wish to try this gadget, you can try inserting the Gadget by using the following url : http://wavegadgets4u.appspot.com/twittersearchgadget.xml. Even if you come back later to the Wave, you will find that the term is still shown that you or any of the other participants last used. This concludes the article and hope that you have a good time writing Google Wave Gadgets using Flex. Hope to see your Gadgets being featured in the Samples Gallery.Developing our Hello World SWF
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Label text="Hello World"/>
</mx:Application>
Authoring the Hello World Google Gadget
<Module>
<ModulePrefs title="Hello World Gadget">
<Require feature="flash" />
</ModulePrefs>
<Content type="html">
<![CDATA[
]]>
</Content>
</Module>
<Module>
<ModulePrefs title="Hello World Gadget">
<Require feature="flash" />
</ModulePrefs>
<Content type="html">
<![CDATA[
<object id="HelloWorldGadget" name="HelloWorldGadget"
type="application/x-shockwave-flash"
data="http://wavegadgets4u.appspot.com/HelloWorldGadget.swf"
width="100%" height="100%">
<param name="movie" value="http://wavegadgets4u.appspot.com/HelloWorldGadget"/>
<param name="quality" value="high" />
<param name="wmode" value="transparent" />
<param name="allowScriptAccess" value="always" />
<param name="allowNetworking" value="all" />
</object>
]]>
</Content>
</Module>
Deploying the Hello World Google Gadget
Inserting the Gadget inside Wave
A Twitter Search Google Wave Widget written in Flex
BasicTwitterSearchGadget.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" backgroundColor="#323232">
<mx:HTTPService id="YQLService" resultFormat="object" method="GET" result="onYQLResult(event)"/>
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;
[Bindable]
private var results:ArrayCollection = new ArrayCollection();
public function searchTwitterviaYQL():void {
var yahooURL:String = "";
var term : String = searchterm.text;
yahooURL = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20twitter.search%20where%20q%3D'"+term+"'%3B&format=xml&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys";
YQLService.url = yahooURL;
YQLService.send();
}
public function onYQLResult(event:ResultEvent):void {
results = event.result.query.results.results as ArrayCollection;
}
]]>
</mx:Script>
<mx:TextInput id="searchterm" width="200" color="#000000" x="10" y="10"/>
<mx:Button id="btn" label="Search" click="searchTwitterviaYQL()" x="218" y="10"/>
<mx:DataGrid id="dgData" dataProvider="{results}" variableRowHeight="true"
selectionColor="#6496C3" x="10" y="40">
<mx:columns>
<mx:DataGridColumn headerText="Image" width="60" >
<mx:itemRenderer>
<mx:Component>
<mx:Image source="{data.profile_image_url}" horizontalAlign="center" verticalAlign="middle"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn dataField="from_user" headerText="Screen Name" width="95"/>
<mx:DataGridColumn dataField="text" headerText="Status" width="500" wordWrap="true"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>
The Gadget Definition - basictwittersearchgadget.xml
<Module>
<ModulePrefs title="Hello World Gadget" height="400">
<Require feature="flash" />
</ModulePrefs>
<Content type="html">
<![CDATA[
<object id="BasicTwitterSearchGadget" name="BasicTwitterSearchGadget"
type="application/x-shockwave-flash"
data="http://wavegadgets4u.appspot.com/BasicTwitterSearchGadget.swf" width="100%" height="100%">
<param name="movie" value="http://wavegadgets4u.appspot.com/BasicTwitterSearchGadget.swf"/>
<param name="quality" value="high" />
<param name="wmode" value="transparent" />
<param name="allowScriptAccess" value="always" />
<param name="allowNetworking" value="all" />
</object>
]]>
</Content>
</Module>
Twitter Search in Action
Sharing State in a Google Wave Gadget
Adding the Wave As Client SWC to your project
TwitterSearchGadget.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" backgroundColor="#323232" creationComplete="initGadget()">
<mx:HTTPService id="YQLService" resultFormat="object" method="GET" result="onYQLResult(event)"/>
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.utils.StringUtil;
import mx.collections.ArrayCollection;
import com.nextgenapp.wave.gadget.WaveState;
import com.nextgenapp.wave.gadget.Wave;
[Bindable]
private var results:ArrayCollection = new ArrayCollection();
//Wave Instance
private var _wave:Wave;
private function initGadget():void {
_wave = new Wave();
_wave.setStateCallback(stateupdated);
}
private function stateupdated(state:Object):void {
var strSearchTerm:String = _wave.getState().getStringValue("mysearchterm");
if (strSearchTerm) {
searchterm.text = strSearchTerm;
var yahooURL:String = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20twitter.search%20where%20q%3D'"+strSearchTerm+"'%3B&format=xml&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys";
YQLService.url = yahooURL;
YQLService.send();
}
}
public function searchTwitterviaYQL():void {
var delta:Object = new Object();
delta.mysearchterm = searchterm.text;
_wave.submitDelta(delta);
}
public function onYQLResult(event:ResultEvent):void {
results = event.result.query.results.results as ArrayCollection;
}
]]>
</mx:Script>
<mx:TextInput id="searchterm" width="200" color="#000000" x="10" y="10"/>
<mx:Button id="btn" label="Search" click="searchTwitterviaYQL()" x="218" y="10"/>
<mx:DataGrid id="dgData" dataProvider="{results}" variableRowHeight="true"
selectionColor="#6496C3" x="10" y="40">
<mx:columns>
<mx:DataGridColumn headerText="Image" width="60" >
<mx:itemRenderer>
<mx:Component>
<mx:Image source="{data.profile_image_url}" horizontalAlign="center" verticalAlign="middle"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn dataField="from_user" headerText="Screen Name" width="95"/>
<mx:DataGridColumn dataField="text" headerText="Status" width="500" wordWrap="true"/>
</mx:columns>
</mx:DataGrid></mx:Application>
The Gadget Definition - twittersearchgadget.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Twitter Search" height="400">
<Require feature="rpc" />
<Require feature="flash" />
</ModulePrefs>
<Content type="html">
<![CDATA[
<script type="text/javascript"
src="http://wave-api.appspot.com/public/wave.js"></script>
<script type="text/javascript"
src="http://wave-as-client.googlecode.com/svn/trunk/example/wave_simple_state_example/web/wave-as-client.js"></script>
<object id="TwitterSearchWidget" name="TwitterSearchWidget"
type="application/x-shockwave-flash"
data="http://wavegadgets4u.appspot.com/TwitterSearchWidget.swf"
width="100%" height="100%">
<param name="movie" value="http://wavegadgets4u.appspot.com/TwitterSearchWidget.swf"/>
<param name="quality" value="high" />
<param name="wmode" value="transparent" />
<param name="allowScriptAccess" value="always" />
<param name="allowNetworking" value="all" />
</object>
]]>
</Content>
</Module>
- @RIARadio Episode 18: IntelliJ
- FITC Day 3 - Afternoon: Ralph Hauwert (take 2), Theo Watson and Emily Gobeille, Brendan Dawes and Yugo Nakamura
- FITC Day 3 - Morning: Kristin Henry, Grant Skinner and Jeremy Thorpe
- FITC Day 2 - Afternoon / Evening: Jared Ficklin, Eric Natzke and Robert Hodgin
- FITC 2 - Morning: Ralph Hauwert, Jim Corbett and Joa Ebert










Facebook Application Development
I built what was probably one of the first Flex Wave Gadgets for the Wave Developer Day in Sydney (a week or two after the Wave launch at I/O last year) - haven't done much with it since, but the source is available on Google Code:
http://code.google.com/p/napkin-wave-gadget/
Of interest is that my Wave-AS bridge allows for limited testing "offline", ie if you're not embedded in a Wave you can still test the app and set/retrieve state to make sure things are working. This saves having to go through the whole deployment process every time, and allows easy use of the Flash Builder Debugger.
Oops, missed the bit of the article where my Napkin Gadget was linked. Well, there you go :)
Does anyone use Google Wave? Best quote I ever heard about it: "Google wave was invented to show young people how old people feel when trying to use the internet."
Debt Help
I like the flex and the mode of delivery and present things. Especially for users who do not really know the network and easily bored. I am one of them, then I say Bravo to FLEX. I really appreciate your work.
Lots of people will really use Google wave because it is easy to use, real time gratification and the speed. The functionality of Google Wave is undoubtedly great for users. Even though its new terminology the functionality of Google Wave is very familiar to users who already use email, IM, wikis, web docs, and gizmos. Diamonds