Finding the username in an AIR app, and a quick binding tip
Gary asked me an interesting question relating to AIR applications. He wanted to know if there was a way for an AIR application to know the username of the current user. I would have guessed that this would be part of the AIR API, but after a bit of searching and asking around, it turns out that this is not the case.
While there isn't a direct API, there is a nice workaround that I found on Stack Overflow (Get the current logged in OS user in Adobe Air). The solution simply assumes that all users have a base directory that includes their username. On my Mac, it is "/Users/ray". On my Windows box (yes, I'm ashamed, I still keep one around), the directory is "c:\documents and settings\administrator". So this technique seems like a good one. You could certainly use it to suggest the current username. Here is a simple demo that Gary cooked up using this technique:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" horizontalAlign="center" verticalAlign="middle">
<mx:Script>
<![CDATA[
public function currentOSUser():String {
var userDir:String = File.userDirectory.nativePath;
var userName:String = userDir.substr(userDir.lastIndexOf(File.separator) + 1);
return userName;
}
]]>
</mx:Script>
<mx:Text text="{currentOSUser()}" fontSize="75" horizontalCenter="true"/>
<mx:Text text="is a winner!" fontSize="20" />
</mx:WindowedApplication>
I modified it a bit just to simplify things. Running it on my Mac I see:

On my PC (oh, and I loved how the AIR installer noticed my PC was a bit behind on the AIR SDK and updated itself) it displayed:

Ok, one last tip. Gary was trying to use binding with this method and had trouble getting it working. Let's look at what he did.
public function currentOSUser():String {
var userDir:String = File.userDirectory.nativePath;
var userName:String = userDir.substr(userDir.lastIndexOf(File.separator) + 1);
return userName;
}
protected function list_creationCompleteHandler(event:FlexEvent):void {
getMyUnitResult.token = Widget.getMyUnit("{currentOSUser()}");
}
This didn't work for him. Notice the binding inside the function? That's simply not a place where you can use binding. In this case the solution was simpler code:
getMyUnitResult.token = Widget.gyMyUnit(currentOSUser())
My understanding is that you can only use bindings in the attributes of components. However, don't take my word for it. I found a nice article at Adobe specifically on binding: Using data binding
Hope this helps!
Follow up - Flash Builder 4, ColdFusion CFCs, and AIR
I've done a few blog entries recently showing simple examples of connecting to a ColdFusion CFC via Flex. My last blog entry demonstrated this using Flash Builder 4. A reader there asked me to create an example of this in AIR. It just took a few minutes, so let me walk you through the process (and please forgive me but let me give the normal disclaimer, I'm new to Flex, blah blah blah).
More playtime with Flex, AIR, ColdFusion, and Flex Messaging
Since I wrapped GameOne, and begun work on GameTwo, I've been thinking a lot about messaging about AIR/ColdFusion applications. I had an idea today for a simple application. Imagine your web site sells widgets. Every time you sell a widget, your boss wants an alert. You could easily use email, but emails tend to get ignored. How about writing a quick AIR application instead?
Liked GameOne? Want to learn more about BlazeDS/ColdFusion/Flex/AIR?
Just wanted to give a quick shout out to Aaron West and his blog entry on a class he will be giving at CFUNITED this year:
AIR Messaging: Integrating AIR, BlazeDS, and ColdFusion
I blogged about this myself recently with the release of my first proof of concept game built using AIR/Flex/CF/BlazeDS. I've got to say this is very exciting stuff. If you are attending CFUNITED, be sure to check out his talk. (I know I will be, if I'm not speaking at the same time.)
Code and Design of Gameone
So now that I'm done enjoying Festival International, I thought I'd do a quick write up about the code behind Gameone, the POC (proof of concept) AIR/Flex/Blaze/ColdFusion multiplayer game I released last week. I want to be sure folks remember that this is probably quite far away from 'Best Practice'. It was a learning experience for me, so please keep that in mind while reviewing the code and reading my comments. I've attached the full source code to both the front end and the server side. Note that the ColdFusion code was written very quickly and would benefit from ColdSpring. Anyway, enough with the foreplay, let's get down to it.
As I mentioned, the front end was built with Flex and AIR. I learned a heck of a lot while at Broadchoice. One of the tools we used there was the Swiz framework created by Chris Scott. Swiz helps solve one of the biggest issues I had with Flex - events. I don't mean stuff like click handlers for buttons, but more... communication between multiple files. Flex, then language, is pretty simple. But I found that once I got into multiple files, it became difficult for me to understand how best to have them work with even other.
Swiz let me set up the application almost like a Model-Glue/ColdSpring site. I created folders for the major sections of the application (which for me was just application and authentication) and within each I set up controller, model, and view folders. Swiz let me inject (copy in) code from one part of the application into another. For example, this line of code from the file that renders the Buy/Sell portion:
[Autowire(bean="stockController")]
public var stockController : StockController;
Swiz finds the controller via a Beans file that contains the stockController:
<!-- stock controller -->
<stockControl:StockController id="stockController"/>
I was also able to define my remoteObject here:
<mx:RemoteObject id="authenticationService" destination="ColdFusion" source="remoteservices.authenticationService"
showBusyCursor="true" channelSet="{myAmfChannel}" />
As for events, I can define both listeners and broadcasts as well - very much like ModelGlue. So for example, I want my register form to listen for a 'registration failed' event so it could tell people when their registration failed:
Swiz.addEventListener(AuthenticationController.REGISTRATION_FAILED, registrationFailed)
The flip side, broadcasting an event, is also trivial:
private function getStockPricesResult(result:ResultEvent):void {
var event:DynamicEvent = new DynamicEvent(StockController.STOCKS_LOADED)
trace('back from get stocks')
var stockData:ArrayCollection = result.result as ArrayCollection
event.stockData = stockData
Swiz.dispatchEvent(event);
}
Swiz does a heck of a lot more than what you will see in the code, but I can't describe how much of a relief it was to have this framework. It removed what was probably the biggest barrier to my Flex development.
The other interesting aspect was the integration with BlazeDS. Well, not terribly interesting. The code actually is pretty trivial. The server setup was the worst part. (I talked about it more here.) The Flex code for chatting came down to something like 10 lines of code. So for example, to tell the app to listen to and broadcast to the server begins with two declarations:
<mx:Producer id="producer" destination="ColdFusionGateway" channelSet="{myAmfPollingChannel}" fault="Alert.show(event.faultDetail)"/>
<mx:Consumer id="consumer" message="handleMessage(event)" channelSet="{myAmfPollingChannel}" destination="ColdFusionGateway" />
Sending a chat is just:
private function sendMessage():void {
if(chatMsg.text == '') return
var msg:AsyncMessage = new AsyncMessage();
msg.headers.gatewayid = 'GameOne';
msg.body = { msg : chatMsg.text, user : authController.currentUser.username };
producer.subtopic='chat'
producer.send(msg);
chatMsg.text=''
}
And code to handle listening (with the consumer) is first this in the init():
private function init():void {
consumer.subtopic='chat'
consumer.subscribe();
}
and this for handling the actual message:
private function handleMessage(e:MessageEvent):void{
var body :Object = e.message.body
if(body.user != null) {
chatWindow.htmlText = '<b>['+fmtDate.format(body.timestamp)+'] '+body.user + ' says: ' + body.msg + '</b>\n' + chatWindow.htmlText;
} else chatWindow.htmlText = '['+fmtDate.format(body.timestamp)+'] ' + body.msg + '\n' + chatWindow.htmlText;
}
Most of the code here is UI crap, and I think you can see that there really isn't a lot here. The one thing I'd point out is the subtopics. This is basically a 'filter' for the messages sent back and forth between the server. My game had 2 types of communications - stock updates and chat. So I simply used a different channel for each. The stock data view has a consumer as well:
stockConsumer.subtopic='stockupdate'
stockConsumer.subscribe();
Because it uses a different subtopic, it will ignore the chat messages going back and forth.
This is probably a good time to switch to the server side. The ColdFusion code is rather simple. I'll only point out the stuff specific to messaging. Stock updates are run every minute and cover both the actual price updates (which is game logic and I'll leave to those who download the code) and messaging. Here is the actual remote call invoked by the CF Scheduler.
<cfset application.stockService.updateStocks()>
<cfset application.messageService.notifyStockUpdate(application.stockService.getStockPrices())>
The updateStock methods changes the prices and the messaging service will handle the broadcast.
<cffunction name="notifyStockUpdate" access="public" returnType="void" output="false">
<cfargument name="stockdata" type="any" required="true">
<cfset var packet = StructNew()>
<cfset packet.body = {}>
<cfset packet.body["data"] = arguments.stockdata>
<cfset packet.destination = "ColdFusionGateway">
<cfset packet["headers"]["DSSubtopic"] = "stockupdate" />
<cfset SendGatewayMessage("GameOne", packet)>
</cffunction>
To be honest, I don't remember where I found the docs that said I should use "DSSubtopic" but it works. The last piece was the DataServicesMessaging event gateway. I set that up as a simple based on the documentation in the developer's guide.
Ok, so this is going on a bit long. It may make sense to stop now and work on a simpler example for later in the week. But here is the thing. It is freaking cool as surfboading ninja zombies to see my ColdFusion scheduler run some code that then updated stuff on my AIR application. Let's wrap this now. Take a look at the code (please don't laugh too hard at any simple Flex mistakes I made), and let's talk again later in the week about some simpler examples (I have a few good ideas coming to me now).
GameOne Released
Ok, if you have been following me on Twitter, you know I've been working on a project involving BlazeDS, Flex, AIR, and ColdFusion. The game idea I had was pretty complex, so I thought it would make sense to start with a simple game, and focus on the technologies first. So with that in mind, I'm proud to announce the release of GameOne. Yes, I know, great name, eh? Well, I don't care how creative the name is, I'm just happy it works (mostly).
The game is basically a stock simulator. The stocks are fake and update every few minutes. When you buy/sell stocks, it is announced to all active players. You can chat as well. So for example:

So, it's late. I'm tired. And there is a lot I want to talk about this game, especially in regards to messaging and LCDS/BlazeDS+CF. (Seriously, why aren't people using it more?) But for now I figured I'd let folks download and play with it. I'll post the full source code (client+server side) later in the week. Enjoy. Oh, and for those of you who are competitive, you can view a dump of all users sorted by funds here: http://gameone.coldfusionjedi.com/
My first jQuery/AIR Application: Selecter Tester
A few days ago I was speaking with Ben Nadel and Rey Bango about a problem I had with jQuery Selectors. I've only recently become a bit comfortable with them, but they can still be a bit confusing to use. I wondered if there was a way to create a test script. Currently I'll write my selector and do hide() (for those of you who don't use jQuery, you can probably guess that will hide the matched items) or apply a class that adds a border. I'll do a bunch of "edit/save/alt-tab/reload" tests which isn't always fun.
With that in mind, I built a simple testing application with jQuery and AIR. When it launches, you are presented with a simple UI:

You can then enter some code:

and switch to the render tab:

Now comes the fun part. You can type a selector value in the form above. As you do, jQuery will attempt to find, and match, the items in your page.
For example, I knew I could match li:first, but wasn't sure about li:last. Yep, it works:

You can install it here - and hopefully this badge thing will work.
(The badge isn't working on my blog preview. Folks, if you read this entry immediately after I post it, give me a few if the badge is broken. FYI - badge is definitely wonkey. Use the download link below. Update 7:39PM - badge works now.)
I used Aptana's AIR support plugin. It is nice, but has a few issues. I was never able to get it to run the AIR application. It wouldn't error - it just refused to do anything. I used the command line so it wasn't too bad. It did do a real nice job of laying down the files for a new project. I really appreciate that. It also handled creating the certs/.air file nicely as well.
I'm planning another modification to this soon, based on a suggestion Ben had. I'd also like to allow people to change the 'highlight' CSS in case a red border doesn't make sense.
Shoot, I don't know if this is useful or not, but, enjoy. :)
p.s. Download the app using the link below. It is a zipped version of the air file. The badge is just not working. Maybe I need to bug the Aptana folks more. :)
Flex Builder Question - Not seeing files during AIR export
I've got a weird Flex Builder question I hope my readers can help me with. I'm working with an open source Flex project (ShareFire) and need to build the code myself. I created a new project in FB and then manually copied the files over from ShareFire's SVN drop.
When I did a debug build, I noticed that the non-code files (one SQL xml file and a CSS file) were not copied to bin-debug. Not thinking much of it, I just copied the files manually to bin-debug.
But this wasn't a real solution and it's now biting me on the rear. When I go to export a release build, the AIR File Contents screen refuses to list the other files:

I thought perhaps I had messed something up by manually dragging files into the project, yet when I made a new file from within FB (readme.txt), it wasn't noticed either.
Any ideas folks?
p.s. Sorry for the lack of posts this week. Been fighting the flu the last few days and the flu is winning.
Latest Broadchoice Workspace now has a free edition
This was mentioned on the Broadchoice blog a few days ago, but I thought I'd mention it here as well. The latest version of Broadchoice Workspace was released recently and now includes a free edition. There are a few limitations in the free version, but you can now download it and use it without having to pay a dime. Check it out and let us know what you think. When you do - be sure to check out the snazzy iPhone edition as well.
Having issues with your BlazeDS/Flex app? Try changing the display-name
For days now (well, days in man hours, today is my first day back from vacation), I've been struggling with an issue concerning Flex and BlazeDS. Specifically an issue with the Flex front end receiving messages being sent by BlazeDS. I finally found the answer today, but before I go on, please note I'm a bit fuzzy on the details here - all I know is that it worked and I'm about as freaking relieved as a developer can get.
