January 4, 2010

Setting HTML Focus From Flash or Flex Content in AIR

It is possible to set HTML focus from Flash or Flex content in AIR, but it takes a little finessing. The key is to focus the HTMLLoader before setting focus to something inside the HTMLLoader. The code below shows an HTML text input getting focus when the document loads, and it shows how to set focus on-demand from Flash or Flex. I'll let the code explain the rest:

Flex code:

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="onApplicationComplete();">
    <mx:Script>
        <![CDATA[
            private function onApplicationComplete():void
            {
                this.stage.focus = html.htmlLoader;
                html.location = "form.html?t=" + new Date().time; // Prevent cacheing while testing
            }
            
            private function onSetFocus():void
            {
                this.stage.focus = html.htmlLoader;
                this.html.htmlLoader.window.setFocus();
            }
        ]]>
    </mx:Script>
    <mx:VBox width="100%" height="100%">
        <mx:HTML id="html" width="100%" height="100%"/>
        <mx:Button label="Set focus from Flash" click="onSetFocus();"/>
    </mx:VBox>
</mx:WindowedApplication>

HTML code:

<html>
    <head>
        <script type="text/javascript">
            function setFocus() {
                var myText = document.getElementById('myText'); 
                myText.focus();
            }
        </script>
    </head>
    <body onLoad="setFocus();">
        <form id="myForm">
            <input type="text" id="myText" width="200"/>
            <br/>
            <button onClick="setFocus();">Set focus from HTML</button>
        </form>
    </body>
</html>

Posted by cantrell at 12:58 PM. Link | Comments (0) | References

December 18, 2009

Get the Full Version of Google Tasks on Your Desktop

I started using Google Tasks about a month ago in order to make it easier for me to use multiple computers and multiple operating systems. Google Tasks is a nice, simple, straightforward application with one small shortcoming: it's tied to the browser.

Download TaskWrapper

Running some applications exclusively in the browser is fine, but sometimes you really want to take them out of the browser and run them on the desktop. Google Tasks has the ability to "pop-out" of Gmail and open in its own browser window which is nice, but leads to an entirely new set of problems:

The answer is to run Google Tasks on the desktop with TaskWrapper.

Update (1/8/10): Thanks to a tip from robmcm, TaskWrapper is now using an even nicer version of Google Tasks.

Update (12/21/09): Thanks to Rob McM, TaskWrapper is even simpler. It now loads the iGoogle version of Google Tasks rather than making you log into Gmail first, and "pop" the task window out. TaskWrapper is now literally three lines of MXML code!

Posted by cantrell at 7:33 AM. Link | Comments (4) | References

December 10, 2009

Four New Properties Added to Socket

In response to feedback from the AIR 2 public beta, it looks like we're going to be adding four new properties to the flash.net.Socket class:

They're not yet available in the current public beta build, but I would expect them to be in the next one. As always, thanks for the feedback!

(I've added these properties to Exhaustive List of Everything That's New in AIR 2, so that's still the definitive list.)

Posted by cantrell at 10:17 AM. Link | Comments (3) | References

November 18, 2009

Demonstration of Gesture APIs in AIR 2

I don't have a multi-touch computer (yet), but I do have a MacBook with a multi-touch trackpad which means I can write AIR 2 applications that incorporate gestures. The video below demonstrates a few of the new gesture APIs in AIR 2:

The code below shows how to indicate that you want to receive gesture events (as opposed to multi-touch, or no touch events at all), and registers for zoom, rotate, and pan gesture events (the watch variable refers to a Sprite which contains the bitmap image of the watch):

Multitouch.inputMode = MultitouchInputMode.GESTURE;
watch.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);
watch.addEventListener(TransformGestureEvent.GESTURE_ROTATE, onRotate);
watch.addEventListener(TransformGestureEvent.GESTURE_PAN, onPan);

The three functions below show responding to each of the gesture events:

private function onZoom(e:TransformGestureEvent):void
{
    var watch:Sprite = e.target as Sprite;
    watch.scaleX *= e.scaleX;
    watch.scaleY *= e.scaleY;
}

private function onRotate(e:TransformGestureEvent):void
{
    var watch:Sprite = e.target as Sprite;
    watch.rotation += e.rotation;
}

private function onPan(e:TransformGestureEvent):void
{
    var watch:Sprite = e.target as Sprite;
    var watchBitmap:Bitmap = watch.getChildAt(0) as Bitmap;
    watchBitmap.x += e.offsetX;
    watchBitmap.y += e.offsetY;
}

For much more information on how multi-touch and gestures work in both AIR 2 and Flash Player 10.1 (including OS and hardware support, which gestures are supported where, and a thorough review of the APIs), and to download sample code, see Multi-touch and Gesture Support on the Flash Platform. Or, if you just want to see the code for this sample, you can download it here.

Posted by cantrell at 6:50 AM. Link | Comments (7) | References

November 16, 2009

AIR 2 Public Beta Resources

The AIR 2 public beta is now live! Below are all the links you'll need to learn more and get started:

Adobe Labs

Adobe Developer Center

Adobe TV

This Blog

Community

Posted by cantrell at 8:51 PM. Link | Comments (1) | References

November 13, 2009

A Demonstration of Encrypted Socket Support in AIR 2

I've been wanting to write my own email notifier in AIR for a long time, but without support for encrypted sockets, it wasn't easy to do. But now that AIR 2 added the new SecureSocket class, I was able to write a pretty functional email notifier in just a couple of days:

The new SecureSocket class extends Socket, so MenuMail can use either encrypted or non-encrypted sockets without really having to know the difference:

this.socket = (this.secure) ? new SecureSocket() : new Socket();

The MenuMail application, and all the code for MenuMail, will be available on Adobe Labs after the AIR 2 public beta launch (which is very close!).

Posted by cantrell at 7:20 AM. Link | Comments (5) | References

November 12, 2009

A Demonstration of the NativeProcess APIs in AIR 2

SearchCentral uses the new NativeProcess APIs in AIR 2 in order to integrate with Spotlight and provide very fast local file system search. Here's a demo:

The NativeProcess APIs are very simple to use. The code below invokes the mdfind command on OS X, and sets up an event listener to read the results from standard out:

private function onSearch():void
{
    var npInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
    npInfo.executable = this.mdfindFile; // A reference to the mdfind command
    var args:Vector.<String> = new Vector.<String>;
    args.push("-interpret");
    args.push(this.searchTerm.text);
    npInfo.arguments = args;
    this.processBuffer = new ByteArray();
    this.nativeProcess = new NativeProcess();
    this.nativeProcess.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onStandardOutputData);
    this.nativeProcess.addEventListener(NativeProcessExitEvent.EXIT, onStandardOutputExit);
    this.setStatus("Searching for " + this.searchTerm.text + "...", true);
    this.nativeProcess.start(npInfo);
}

The code below shows buffering the data from standard out:

private function onStandardOutputData(e:ProgressEvent):void
{
    this.nativeProcess.standardOutput.readBytes(this.processBuffer, this.processBuffer.length);
}

And finally, the code blow is an abbreviated version of the function that parses the data from standard out and puts it into the data grid:

private function onStandardOutputExit(e:Event):void
{
    var output:String = new String(this.processBuffer);
    var outputArray:Array = output.split("\n");
    var data:Array = new Array();
    for each(var path:String in outputArray)
    {
        var f:File = new File(path);
        if (!f.exists) continue;
        var o:Object = new Object();
        if (f.isDirectory)
        {
            o.name = "/" + f.name;
        }
        else
        {
            o.name = f.name;
        }
        o.type = f.extension;
        o.lastModified = f.modificationDate;
        o.path = f.nativePath;
        data.push(o);
    }
    var dp:ArrayCollection = new ArrayCollection(data);
    this.fileGrid.dataProvider = dp;
    this.setStatus(dp.length + " items found");
}

The code for SearchCentral will be available on on Adobe Labs as soon as the AIR 2 Beta is public (very soon!). The code for SearchCentral is now available on Google Code. If you have any questions, drop them in the comments below.

Posted by cantrell at 7:25 AM. Link | Comments (6) | References

November 11, 2009

A Demonstration of the New Storage Volume APIs in AIR 2

Below is a screencast of an application I wrote called FileTile in order to validate the new storage volume APIs in AIR 2:

Detecting the mounting and unmounting of a storage volume in AIR 2 is very easy. The code below shows using the new StorageVolumeInfo class to register for mount and unmount events:

StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT, onVolumeMount);
StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT, onVolumeUnmount);

Below are the two functions that react to the mounting and unmounting of storage volumes:

private function onVolumeMount(e:StorageVolumeChangeEvent):void
{
    if (e.storageVolume.isRemovable)
    {
        this.nativeWindow.activate();
        this.volumeName.text = e.storageVolume.name;
        this.targetVolume = e.storageVolume.rootDirectory;
        this.currentVolumeNativePath = this.targetVolume.nativePath;
        this.start();
    }
}

private function onVolumeUnmount(e:StorageVolumeChangeEvent):void
{
    if (this.currentVolumeNativePath == e.rootDirectory.nativePath)
    {
        this.reset();
        this.mainView.selectedChild = instructionBox;
    }
}

FileTile is a free, open-source application. The code can be downloaded from Google Code.

Posted by cantrell at 6:52 AM. Link | Comments (2) | References

November 4, 2009

A Demonstration of the ServerSocket API in AIR 2

In order to validate the new ServerSocket APIs in AIR 2, I wrote an application called HTTPeek. HTTPeek is a proxy server that sits between your browser and the network, and can show you HTTP request and response headers. It can handle compressed content, chunked content, binary content, etc. Check out the video below to see it in action:

Most of the HTTPeek code is dedicated to implementing just enough of the HTTP protocol to be an effective proxy, but the socket portion of the code is actually not all that complex. And the creation of the ServerSocket itself is very simple. The function below gets called when the user clicks on the "Listen" button:

private function onListen():void
{
    this.sockets = new Array();
    this.server = new ServerSocket();
    this.server.addEventListener(Event.CONNECT, onConnect);
    try
    {
        this.server.bind(Number(this.portNumber.text), String(this.interfaces.value));
        this.server.listen();
        this.listenButton.label = "Close";
    }
    catch (e:Error)
    {
        Alert.show(e.message, "Error", Alert.OK);
        return;
    }
    this.debugButton.enabled = true;
}

HTTPeek is open-source and hosted here on Google Code. Feel free to check out the source to see how it works.

Posted by cantrell at 8:20 AM. Link | Comments (10) | References

November 3, 2009

Some Interesting AIR Marketplace Statistics

A little over a year ago, I wrote a Python script to screen scrape the entire Adobe AIR Marketplace, download all the listed AIR applications, crack open the installers, and extract some statistics. My primary interest was how many HTML-based AIR applications were listed versus SWF-based applications, but I recorded some other interesting stats, as well.

I ran the script again the other day (after some updating since screen scraping scripts always break), and here's what I found:

Statistic Data Description
Total AIR apps on the Marketplace 724 Total number of AIR applications listed on the Adobe AIR Marketplace as of 10/29/2009.
Total testable apps 536 The total number of applications I was able to download and test. 121 applications don't have direct links to their AIR files on the marketplace, and the rest couldn't be tested because of 404s or network timeouts.
Total SWF-based AIR apps 443 (82.6%) Total number of applications that use a SWF as their main content. There is no distinction between Flash and Flex apps.
Total confirmed AS2 apps 2 Total number of SWF-based applications that use ActionScript 2 rather than ActionScript 3. (I'd be alarmed if this were much higher.)
Total HTML-based AIR apps 93 (17.4%) Total number of applications that use an HTML file as their main content.

Some things to keep in mind about this data:

I plan on updating the script to look for any HTML/JS files in the application bundle in order to give an indication of the number of hybrid applications out there. I'll post the results when I have them.

So my questions for you guys are:

Posted by cantrell at 7:54 AM. Link | Comments (5) | References

October 30, 2009

A Screencast Explaining and Demoing File Promises

File promises are kind of a difficult concept to describe, so I decided to explain them using a video. Hopefully this clarifies what file promises are, and why it's such a cool new feature of AIR 2.0:

Here's the code that creates a URLFilePromise for every file to be downloaded and puts them on the clipboard. But first, some code context:

private function onDragStart(e:DragEvent):void
{
    if (bucketList.selectedIndex == 0 || objectList.selectedItems == null) return;
    var c:Clipboard = new Clipboard();
    var items:Array = objectList.selectedItems;
    this.filePromises = new Array();
    for each (var item:Object in items)
    {
        var fp:URLFilePromise = new URLFilePromise();
        var req:URLRequest = new URLRequest(s3.getTemporaryObjectURL(bucketList.selectedItem.name, item.key, 60));
        fp.request = req;
        fp.relativePath = item.key;
        this.filePromises.push(fp);
    }
    c.setData(ClipboardFormats.FILE_PROMISE_LIST_FORMAT, this.filePromises);
    NativeDragManager.doDrag(objectList, c, null, null, null);
}

Below is the function that gets called when the drop is complete. It's responsible for opening the ProgressWindow component and passing it the array of URLFilePromise objects which the ProgressWindow component hooks into in order to receive progress events.

private function onDragComplete(e:DragEvent):void
{
    var progressWindow:ProgressWindow = new ProgressWindow();
    progressWindow.open(false);
    progressWindow.setFilePromises(this.filePromises);
}

The code for S3E is open source and available on Google Code.

Posted by cantrell at 6:11 AM. Link | Comments (2) | References

October 29, 2009

Exhaustive List of Everything That's New in AIR 2.0

This morning, I was going to start making videos demoing some of the new features in AIR 2.0 when I realized that I should probably start with a list of everything that's new. Below is an exhaustive list of everything that we're planning on including in AIR 2.0. I use the word planning because even at this relatively late stage, things can change. Consider yourself warned.

Feel free to post questions in the comments.

I think that's everything. A beta of AIR 2.0 will be on Adobe Labs late this year, and we hope to release in the first half of 2010. Questions?

Posted by cantrell at 10:44 AM. Link | Comments (50) | References

October 28, 2009

Global Error Handling in AIR 2.0 and Flash 10.1

One of the most popular feature announcements during my MAX presentation was global error handling (GEH). GEH lets you handle all uncaught errors (both synchronous errors and asynchronous error events) in one place in your code. Here's an example of how it will work:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="onApplicationComplete();">

    <mx:Script>
        <![CDATA[
            private function onApplicationComplete():void
            {
                loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtError);
            }

            private function onUncaughtError(e:UncaughtErrorEvent):void
            {
                // Do something with your error.
                if (e.error is Error)
                {
                    var error:Error = e.error as Error;
                    trace(error.errorID, error.name, error.message);
                }
                else
                {
                    var errorEvent:ErrorEvent = e.error as ErrorEvent;
                    trace(errorEvent.errorID);
                }
            }
            
            private function onCauseError(e:MouseEvent):void
            {
                var foo:String = null;
                try
                {
                    trace(foo.length);
                }
                catch (e:TypeError)
                {
                    trace("This error is caught.");
                }
                
                // Since this error isn't caught, it will cause the global error handler to fire.
                trace(foo.length);
            }
        ]]>
    </mx:Script>
      
    <mx:Button label="Cause TypeError" click="onCauseError(event);"/>
 
</mx:WindowedApplication>

Registering for uncaught errors is hugely convenient, but it doesn't entirely excuse developers from handling errors where they're most likely to happen. For instance, you should still always register for things like IOError events and other errors that are likely to happen at some point, and that you can usually recover from. But starting with AIR 2.0 (and FP 10.1), you will also be able to register globally for errors that you weren't able to anticipate, and handle them at least somewhat gracefully.

The big question is what you do once you've caught an unhandled error. That will probably depend on the application. Since it's likely that a function exited without executing all the way through, there's no telling what state your app will be in. The safest thing to do is probably to log the error, show a very contrite dialog box, and then exit the app (after all, if the error had been predictable and easy to recover from, you should have caught it explicitly). You might try sending the error message to your server, including instructions for emailing the log file to a support email address, or if it's an internal application, provide a telephone extension to call for someone to come take a look. It's entirely up to you. We provide the API, you provide the solution.

Posted by cantrell at 10:46 AM. Link | Comments (7) | References

September 22, 2009

New ActionScript and Flex Spell Checking Engine on Adobe Labs

Adobe's Linguistic Services team recently launched a project code named Squiggly on Adobe Labs. From the project homepage:

Squiggly is a spell checking engine for Adobe® Flash® Player and Adobe AIR®. The Squiggly library allows you to easily add spell checking functionality in any Flex 3 based text control. The distribution package consists of a utility for building your own spelling dictionaries, a sample English dictionary, an ActionScript package that checks individual words for spelling accuracy, and sample code that demonstrates "check as you type" functionality.

The rest of the details can be found on Labs along with a simple demo.

Posted by cantrell at 9:56 AM. Link | Comments (0) | References

June 9, 2009

New version of PixelWindow

PixelWindow (was PixelPerfect) was one of the first AIR applications I wrote, and has turned out to be one of the most useful. I wrote it primarily as a test case for multi-window, custom chrome, transparent, pure ActionScript AIR applications, and I've been using it almost daily ever since.

If you're unfamiliar with PixelWindow, it's a simple, light-weight AIR application for measuring things on your screen. It creates a translucent window that acts like a pixel ruler with its width and height reported in the center. Just drag it around and resize it to measure whatever you want.

I finally got around to updating PixelWindow, and creating its own site and installer badge. I also added a few new features:

If you use an old version of PixelWindow (probably installed as PixelPerfect), I recommend that you grab the new version. And if you don't use it, it's an extremely useful app for any designer or developer to have around. And remember that PixelWindow is open source, so feel free to grab the code to fix bugs, add features, or just to see how it works.

Posted by cantrell at 9:53 AM. Link | Comments (3) | References

January 30, 2009

ShareFire: a new feed reader for Adobe AIR

Over the summer, I built a feed reader called Apprise with my intern, Dan Koestler. Dan is now working with me full-time, and the first thing he did when he started at Adobe was update the application and release a new version. The following changes were made for 1.5:

In case you're new to ShareFire, here are some other interesting features:

If you already have Apprise installed, you will get the update automatically (if you want to update manually, go to Settings > Application Updates > Check Now). If you don't have it installed, check it out here.

Let us know what you think!

Posted by cantrell at 9:44 AM. Link | Comments (8) | References

November 11, 2008

Revisiting Linux

It's been a long time since I've used Linux on a daily basis. Back in those days, my development tools were vim, make, and CVS. I ran an early free version of Red Hat with the best windowing manager there was at the time: FVWM. My browser was Netscape, my multi-protocol IM client was the text-based CenterICQ, and my mail client was Pine running inside of Screen. (My cell phone was a big plastic Nextel clamshell which the IT guys called the construction worker phone.)

But like many Linux users at the time, I was trying to exist in a Windows world. I used VMWare for testing web sites on IE, and various command line tools for converting Word documents that people insisted on emailing me into watered-down PDFs. So when OS X came out, I rejoiced and immediately jumped ship (before it even supported CD burning), and I've never looked back.

But when builds of AIR for Linux started appearing, I decided it was time to revisit Linux (specifically Ubuntu) to see what had changed. I made the mistake of installing it under VMWare on my MacBook initially which didn't impress me all that much since it wasn't able to access the graphics card directly, so yesterday I decided to set aside some time and install Ubuntu natively on my Mac.

From the time I got it in my head to give it a try to the time I was running AIR apps was probably about an hour. That included downloading Ubuntu 8.10, burning the ISO, using Boot Camp to make my Mac think I wanted to install Windows, and more or less following these instructions for installing Ubuntu on a new partition. Although I used an internal build of AIR for Linux, a public beta build is available here.

The experience of running Ubuntu natively really blew me away. The windowing effects are beautiful, and after using the OS for a few hours, I began to realize that Ubuntu even does a few things better than OS X. All my AIR apps ran beautifully (here's a screenshot of three of them running), and I began to realize that with Flex Builder for Linux, AIR for Linux, a few strategic AIR apps (Apprise Reader, TweetDeck, etc), Firefox, and with the amount of data that's moving to "the cloud," I could very easily start using Linux again day-to-day. I did encounter a few incompatibilities running Ubuntu on Mac hardware, but if I could get my hands on a decent ThinkPad, I think I just might be able to make the switch. Of course, I'll have to keep my Mac around for synching my iPhone since I did finally give up that Nextel i1000.

Posted by cantrell at 12:25 PM. Link | Comments (10) | References

September 2, 2008

Apprise 1.1: More sharing, more features, and more languages

We just released Apprise 1.1 with several new features:

We've also added some smaller features and polish, and fixed several bugs. If you're already running Apprise, it will auto-update. Otherwise, go check it out.

Posted by cantrell at 9:14 AM. Link | Comments (5) | References

August 6, 2008

How do you want to share with Apprise 1.1?

We're working Apprise 1.1, and we'd like some feedback. Apprise 1.0 allows you to share articles via AIM and Twitter, and for 1.1 (not out yet), we're adding the ability to share via email, as well. We're also adding Facebook, del.icio.us, Google Bookmarks, and Windows Live Favorites. What other services would you like to see us support? MySpace? Newsvine? Mixx? Let us know what you're using, and we'll do our best to get it in.

Posted by cantrell at 8:40 AM. Link | Comments (14) | References

July 28, 2008

Share stories right from your news reader

Over the summer, my intern Dan Koestler and I decided to get serious about Apprise, a news reader written for AIR. There are a lot of news readers out there, so the natural question is what sets Apprise apart? Here's a summery of what I believe are our most compelling features:

Apprise has several other cool features like realtime search accross all feeds, OPML import and export, Vi keys, and site view. And it finally even got a nice design so it no longer looks like a developer designed it.

You can find Appirse at apprisereader.com. If you already have an earlier version of Apprise installed, you should be able to click on the badge and replace it, but you also might need to uninstall the old version first. This version of Apprise adds auto-updated, so from now on, you'll get new features and bug fixes automatically.

Update: I should have mentioned that you should export your feeds before upgrading to the new version of Apprise so you can easily import them again. In future versions, all your feeds will be preserved.

Posted by cantrell at 8:04 AM. Link | Comments (4) | References

July 2, 2008

Closing all your application windows in AIR

Here's a little snippet of code I sometimes use in AIR applications to make sure all windows close when the main application window closes. I usually put it in the main application MXML file (the main window of the application). It stops the main window from closing, and closes any other opened application windows first in the opposite order in which they were opened (more recent windows first). Without code like this, if you have any other windows open, even little utility windows, and your main application window is closed, your application will not exit, even if NativeApplication.autoExit is set to true.

this.nativeWindow.addEventListener(Event.CLOSING,
  function(e:Event):void
  { 
    e.preventDefault();
    for (var i:int = NativeApplication.nativeApplication.openedWindows.length - 1; i >= 0; --i)
    { 
      NativeWindow(NativeApplication.nativeApplication.openedWindows[i]).close();
    } 
  });

Posted by cantrell at 9:28 AM. Link | Comments (3) | References

June 16, 2008

AIR 1.1 and Apprise 1.5

Note: Apprise is now available at apprisereader.com.

With the release of AIR 1.1 comes a new version of Apprise, a feed aggregator written for AIR. Apprise is a work in progress with lots of additional features to come, but here's what we have so far:

Apprise 1.5 adds the following features:

If you check the code out and build Apprise yourself, you can get even more features:

My new intern, Daniel Koestler, and I will be working on adding new features to Apprise this summer. And, of course, it's all cross-platform and open source, so anyone who wants to contribute features can.

The next version of Apprise (2.0) will enable the auto-update feature so you'll be able to pick up new features automatically. We consider 1.0 and 1.5 to be betas, so they have to be manually installed. Click on the badge below to have a look at what we have so far, and let us know in the comments what features you want to see next.

Posted by cantrell at 8:38 AM. Link | Comments (2) | References

April 23, 2008

The latest version of PixelPerfect (with screencast)

PixelPerfect is a simple AIR application for measuring things on your desktop. If you want to see how it works, check out this screencast. If you want to see the code, check out the PixelPerfect Google Code project. And finally, if you just want to install it, click on the badge below.

AIR Badge

Posted by cantrell at 9:12 AM. Link | Comments (4) | References

April 9, 2008

Maptacular 2.0: a desktop mapping application for AIR

Maptacular was one of the first applications I wrote for AIR. The first version parsed vCards from your local file system, and let you map the addresses it found inside. That was back when AIR was in still in alpha, so I didn't have APIs for things like drag and drop or copy and paste. Now that AIR 1.0 has shipped, I decided to rewrite Maptacular to to have the functionality that I originally envisioned.

I created a screencast of Maptacular 2.0 which you can watch here. If you want to install the app, just click on the badge below. If you want to see the source, it's all available on Google Code.

Maptacular is a good example of the following:

AIR Badge

Posted by cantrell at 9:43 AM. Link | Comments (3) | References

April 2, 2008

Lineup: an Exchange calendar viewer for AIR

I did a screencast of an Exchange calendaring application I wrote for AIR. Click here to watch it.

It's one of the most useful applications I've written so far, and probably the most popular within Adobe. It lets you view your Exchange calendar, and it notifies you of upcoming meetings. It also works offline, so you can view your calendar and get meeting details while you're between internet connections.

Lineup uses all the following open source ActionScript 3 libraries:

The complete source code for Lineup itself is also available on Google Code, or you can simply click on the badge below to install it.

AIR Badge

Posted by cantrell at 9:39 AM. Link | Comments (10) | References

March 26, 2008

Tips on getting a code signing certificate

I got my own certificate for signing AIR applications a couple of weeks ago, so I thought I'd share my experience in order to help others more easily navigate the process. My intention is not to provide an exhaustive tutorial on code signing or the process of obtaining a certificate (if that's what you're looking for, see Todd Prekaski's article entitled Digitally Signing Adobe AIR Applications). Rather, I just want to list a few things you should know about before starting the process in order to ensure that it goes smoothly.

I decided to get my certificate from Thawte, but you can also use VeriSign (and soon other Certificate Authorities who Adobe is working with). Both Thawte and VeriSign issue code-signing certificates to organizations as opposed to individuals, so the tips below assume that you already have a corporate entity established. We are currently working with CAs who can issue personal certificates, so if you don't have a business, you will soon be able to get a certificate issued in your own name.

If you decide to get your certificate through Thawte, here are some important things to know:

Those are all the specific issues that caused me problems. If you own a business, and take all four points above into account when applying for your code-signing certificate, you should be able to obtain one within a couple of days with no problem at all.

Posted by cantrell at 10:48 AM. Link | Comments (9) | References

March 13, 2008

Apprise: an RSS aggregator written for AIR

Note: Apprise is now available at apprisereader.com.

I did a screencast of the RSS aggregator called Apprise that I wrote for AIR. Click here to watch it. I didn't put any effort into designing the app, so it looks pretty plain, but it's very functional.

All the source code for apprise is available here on Google Code. You can install Apprise by clicking on the badge below.

Posted by cantrell at 7:06 AM. Link | Comments (8) | References

March 11, 2008

Detecting network connections in AIR

One of the biggest benefits of AIR is that applications can be written to easily function whether online or off. Features like file access and a local SQL database give developers the functionality they need to write applications that can pull data from remote data sources while connected, but can also rely on cached data while disconnected. Of course, writing an application that functions seamlessly both online and offline requires that you be able to reliably detect whether you have a connection or not. This article will tell you everything you need to know to reliably determine not only if your application is connected or not, but also whether it can reach the services it requires.

Let me start by saying that this isn't as simple and straightforward as it might sound. In fact, in today's world of VPNs and 3G wireless networks and virtual network interfaces, it's actually pretty complicated. We didn't realize how complicated it was until early on in AIR 1.0 development when the engineer who was responsible for the online/offline functionality in the runtime gave me a build to test. I started writing an application that needed to know whether it was connected or not, and the first time I yanked my network cable out, we were surprised to find that it didn't work as expected. The cable was disconnected, my AirPort card was turned off, but for some reason, my computer (and the AIR runtime) thought it was still connected. After some investigation, we realized that the problem was a couple of virtual network interfaces that Parallels set up in order to be able to manage networking between OS X and Windows.

What ensued was an almost philosophical discussion about what it meant to be online and offline. Theoretically, my application might have been trying to talk to a service on my Windows virtual machine which would have meant that my computer actually was connected even though I pulled my cord out. Or I might have had an internet connection, but the server my application needed to talk to might only be accessible while connected to a VPN. Or I might be on a public network that blocks certain services, packets, or ports that my application relies on in order to access data. Or you might be sitting in an underground concrete bunker with no connection to the outside world whatsoever, but you're developing an application the retrieves data from a server running on your local computer which means, from the application's point of view, it's online.

We eventually arrived at the conclusion that there really is no such thing as online or offline, or at least no simple and practical definition that worked for AIR developers, and that meant there was no way we were going to get away with a simple online/offline API (the first version of this API allowed for the simple registration of online and offline events on the static NativeApplication instance). In fact, we realized that it doesn't even matter if you're online or not which meant we were trying to answer the wrong question. All that matters is whether you can successfully access a particular service. And to figure that out, we were going to have to deliver a couple different APIs, and probably some best practices around using them.

The two APIs we decided to deliver with the runtime are the networkChangeEvent, and the ServiceMonitor class. You register for networkChangeEvents on the NativeApplication static instance. Each time a networkChangeEvent is thrown, you know that network connectivity has somehow changed, but you don't know how it's changed. For instance, the user might have connected, disconnected, logged into a VPN, joined a wireless network, etc. That's where the ServiceMonitor class comes in. Since we determined that there really is no absolute concept of online or offline, you can use the ServiceMonitor class to tell you something which is much more interesting: whether or not you can actually reach a specific service.

The ServiceMonitor class has two subclasses: SocketMonitor and URLMonitor. The SocketMonitor is for monitoring services that you connect to directly with a TCP socket like a mail or an XMPP server. The URLMonitor class is for monitoring HTTP services like REST based services. Both can be configured to listen for networkChangeEvents for you, or they can can be activated manually by calling the checkService method. Both also have various ways to configure them to work with your specific service. The service event thrown by the ServiceMonitor has a code property which indicates whether or not communication with the service was successful.

This is a pretty complete solution, but there's still one more scenario that we haven't accounted for. Let's say the end user plugs in a network cable which causes the networkChangeEvent to fire which, in turn, causes the ServiceMonitor to check the state of the service. And let's say the service responds favorably which causes the ServiceMonitor to throw its status event with a code property of "service.available". Your application responds by showing a big green light indicating to the end user that all is well, and that it's safe to submit data to or pull live data from your remote service.

The problem is that the service might go down between the time you last checked it and the time your application needs to interact with it. Or your ISP might have suddenly gone out of business. Or an underwater transatlantic cable could have been cut. Any number of things could prevent your application from successfully talking to its services. The ServiceMonitor can't help you now, so it's up to you to catch the IOErrorEvents and handle them gracefully. Since I typically use the Cairngorm Flex framework, I have a single boolean property in my ModelLocator called online which any part of my code can access. I set the online property inside of my ServiceMonitor event handlers and also within my IOErrorEvent handlers, as well (and I also set it to true every time I successfully interact with a service). That way, whether the end user has no connectivity or the service the application is trying to access is down, the application behaves consistently and appropriately.

Of course, having a robust solution to reliably determining whether you're online or not is only half the challenge since your application also has to know how to behave in both circumstances. Writing a complete online/offline application is beyond the scope of this article, but I will say that hinging application logic on the ModelLocator's online property is the key. If you want a detailed real-world example, I recommend taking a look at Lineup, my Exchange calendaring application. It works both online and off, and demonstrates all the principles described in this article.

Posted by cantrell at 10:12 AM. Link | Comments (1) | References

February 25, 2008

What you might not know about AIR (yet)

If you're reading my blog, I'm going to assume that you already know what AIR is. If you don't, have a look at the official AIR product page before reading any further. Rather than tell you what you probably already know about AIR, I'd like to mention a few things that people might not have realized yet.

AIR isn't just another Adobe product. It isn't just a new tool or utility. It's an entirely new way to develop, deploy, install, and use desktop applications. Entirely new. I really can't stress this point enough. In the world of software, new products are launched all the time (if you don't believe me, just follow TechCrunch for a few days — it's staggering), each with a marketing team that wants you to believe that their technology is nothing less than revolutionary. So when something completely different does come along, it's sometimes hard to distinguish. How successful AIR will be and how quickly the technology is adopted is an entirely different topic, but there is absolutely no doubt that it's new and disruptive. I could easily write an entire white paper on the importance of AIR, but for the sake of brevity, I'll focus on three main points:

I've been in the software business for 10 years, and I've worked on all kinds of projects. I've been fortunate that I've never had to work on a project that I didn't like or believe in. But I believe AIR is the most important project I've ever had the good fortunate to be involved with. I can't wait to see what the world does with it.

Posted by cantrell at 9:38 AM. Link | Comments (6) | References

February 21, 2008

AIR Development on Linux

I used to use Linux every day for about four years before coming to work for Macromedia/Adobe (over five years ago). My favorite tools were Screen, Pine, Centericq, XMMS, and Vim. My window manager of choice was FVWM which was doing fast and stable virtual desktops before anyone even knew what virtual desktops were. But I had to leave all that behind when I moved over to the world of Flash and Flex development. Fortunately I had OS X to help me make the transition, but as much as I've come to love Macs, I still sometimes miss the good old days of Linux.

So a couple of weeks ago when I heard that the Linux version of AIR was ready for testing, I dropped everything I was working on and set up Ubuntu, Flex Builder on Linux, and installed AIR. I keep all of my AIR application code on Google Code, so I installed SVN, checked out the latest versions of all my projects, and started running my AIR applications completely unchanged on Linux.

I use a MacBook Pro for development now, and I have Windows XP and Ubuntu virtual machines set up. I now have complete development environments including Flex Builder, AIR, and SVN on all three virtual machines, and all of my apps run across all three different operating systems (with the exception of some apps which use features we haven't implemented yet on Linux — it's still in pre-beta).

It's amazing enough that I'm writing desktop applications using web technologies like Flex, ActionScript, JavaScript, and HTML, but to be able to deploy them with no changes across Mac, Linux, and Windows is seriously revolutionary. The idea that I can actually go back to using Linux day to day, and still be able to do AIR and Flex development is very liberating. And now wherever there are gaps in Linux applications, I can just build them myself in AIR (like Lineup, an Exchange calendaring client that will enable me to view my meeting schedule on Mac and Linux as easily as on Windows).

As I said, the Linux version of AIR still needs more work, and is currently in pre-beta. If you're a Linux user, and you're interested in getting your hands on early builds and helping us do some testing, check out this post by James Ward to find out how to get involved.

Posted by cantrell at 12:14 PM. Link | Comments (6) | References

February 20, 2008

Transparent HTML in AIR

I keep forgetting how to do this, so I'm going to blog it once and for all.

First of all, it's possible to have transparent HTML in AIR. I don't just mean changing the alpha of the HTML Flex component. That's one way to do it, but that's not terribly useful. I'm talking about entirely knocking out the background of an HTMLLoader (formerly know as the HTMLControl) so that you can do very cool things like custom HTML chrome. I like it because I often use the HTMLLoader to render large amounts of text in AIR, like "help" or "about" pages, so making the background transparent can let me do some cool effects and presentations.

There are three things you need to know about making HTML transparent in AIR:

  1. Set the paintsDefaultBackground property of the HTMLLoader to false. This specifies whether the background of the HTML document is opaque or not. If you're using the Flex HTML component, you can do this using the paintsDefaultBackground attribute on the HTML MXML tag.
  2. Make sure that your HTML document does not have a background color explicitly specified. For instance, setting background-color to anything will prevent this from working.
  3. If you're using the Flex HTML component, make sure the backgroundAlpha property is set to 0 (this is the one I always forget). Also, don't mess with the opaqueBackground property.

Ok, now that I have saved cumulative years of searching and trial and error, I expect that time to be reinvested into building even more cool AIR apps!

Posted by cantrell at 9:31 AM. Link | Comments (7) | References

February 14, 2008

How to Make Application Icons Appear During AIR Development

If you've started building an AIR application, you probably noticed that your application icons don't appear when launching your app via ADL (ADL is the AIR Debug Launcher, and is what Flex Builder uses to launch and debug your apps). Platform specific icons are created from the PNG files referenced in your application descriptor file at install time which means they aren't available during development. It would obviously be too costly to generate those icons every time you wanted to launch or debug your code.

This typically isn't an issue because you usually don't need to see your application icons during development. However, there are times when you do. For instance, if your application uses a dynamic dock icon, it's nice to be able to see the "before" icon before seeing the "after" version. I've also found that it's nice to be able to try an icon out for a while during the development process to see how you like it over time which means it should appear every time you debug or launch your app.

Fortunately, I've found an easy way to make this work. Here's what you do:

  1. Make a copy of your application icon and name it something different. One version should be referenced by your application descriptor file, and the other will be compiled into your application. (You don't technically have to make a copy of the icon, but when generating a release build of your application, Flex Builder doesn't copy over embedded resources which means your application icon will be missing. Trust me when I tell you that it's easier to create a copy and avoid this whole issue.)
  2. Compile the copy of your application icon into your application using code like this: [Embed(source="assets/application.png")] public var appIconClass:Class;
  3. In your application's initialization code, create a Bitmap instance of your icon like this: var appIcon:Bitmap = new appIconClass();
  4. Set your icon like this: InteractiveIcon(NativeApplication.nativeApplication.icon).bitmaps = [appIcon];

This code is a little oversimplified because it doesn't take platform differences into account. A more complete implementation might do something like this:

  1. Check to see what kinds of icons the client supports. You can do this with the NativeApplication.supportsDockIcon and NativeApplication.supportsSystemTrayIcon APIs.
  2. Scale the Bitmap to the appropriate dimensions for the platform.
  3. Set the icon(s) using the NativeApplication's icon property.

Since I usually use the Cairngorm framework, I like to create an UpdateIconsCommand which takes care of creating and setting dynamic icons for me. I also like using the as3notificationlib project to help encapsulate some of the platform differences in setting icons.

For a real world example of setting dynamic icons, see my Exchange calendaring application called Lineup (currently being updated to work with Exchange 2007). It sets icons at runtime and even dynamically generates custom icons, all in a way that works well across platforms.

Posted by cantrell at 9:50 AM. Link | Comments (5) | References

January 23, 2008

Update on my AIR Applications and Libraries

The most important part of my job is writing AIR applications, and I've written quite a few. In addition to applications, my team and I have written a great deal of reusable ActionScript library code (Amazon S3 APIs, Exchange APIs, etc.). Everything I've written is completely free and open source.

The problem is that I haven't done a very good job in keeping these applications and libraries synchronized with the the publicly available version of AIR. It's not that I'm lagging behind, though. In fact, I have the opposite problem: most of this code has been updated to work with the the latest internal versions of AIR so that I can keep banging on, testing, and exercising the latest and greatest runtime.

I just wanted to let everyone know that I will be releasing all my apps the day we release AIR 1.0. If you can't wait that long, the source code is all available here in Google Code. In theory, all of the libraries should work with the public AIR beta (beta 3), and the applications should all work, as well, as long as the application descriptor's namespace is set to 1.0.M6 rather than 1.0. Anything that doesn't work, or any bugs that you find, will be fixed in the final 1.0 release.

Below is a list of almost everything I've written that's worth releasing (I've written dozens of test applications that will never see the light of day).

Applications:

Libraries:

The source code for all these applications and libraries is available here. You will need to use subversion to download the source.

Posted by cantrell at 9:45 AM. Link | Comments (14) | References

January 18, 2008

Converting Stage Coordinates to Screen Coordinates

In the Exchange calendaring application I'm working on (called Lineup), when the user double-clicks on an appointment, I open up an appointment detail window which shows you things like the location, full description, etc. Initially, I was just opening a utility window (a native window with smaller chrome), but after seeing the newest version of iCal, I was inspired to do something more creative. I still open a new window, but it's a customized transparent window which looks more like a dialog bubble extending from the exact point where the user clicked.

In order for the effect to work properly, I needed to place the window at very precise screen coordinates so that the window was clearly associated with the appointment that was clicked on. At first, I tried doing my own coordinate calculations using the stage x/y coordinates of the double click, and the coordinates of the application window. The problem was that I had no way to take native window chrome into account, so although I got it working on Mac, the window placement was off on Windows.

Fortunately, AIR has an API that does the work for you by taking the coordinates from a "local" mouse event (relative to the window stage) and converting them into screen or global coordinates. The function is NativeWindow.globalToScreen(), and it fixed my problem perfectly. Now, Lineup has iCal-like detail windows that appear right where they are supposed to on Mac, Windows, and eventually on Linux, as well.

Posted by cantrell at 10:12 AM. Link | Comments (1) | References

January 10, 2008

Managing Symbolic Links in AIR

I recently wrote an AIR application called SPF, or Screen Protection Factor. It's a screen saver that will fill however many monitors you have attached to your computer with random pictures from a specified directory. The specified directory is recursively traversed so SPF will find all supported file types (jpg, png, and gif), no matter how deeply nested they are.

While writing SPF, I realized that a carelessly placed symbolic link could easily put my directory traversal code into an infinite loop. Symbolic links (as they are known on Unix systems, including OS X) and junction points (as they are known on Windows) are pointers to files or directories which are mostly indistinguishable from whatever they point to. I use them frequently when doing web development when I don't want to keep my source code in my web root. Instead, I create a symbolic link in my web root which points to my source tree, and assuming my web server is configured correctly, it's just like having the same code in two places. Put a symbolic link in the wrong place, however, and you've created a circular reference on the file system. Normally this isn't a problem, until you have a piece of code that is recursively traversing that portion of your file system.

I tried a few stopgap measures to guard against circular references in my SPF code, but there really was no way to gracefully handle the problem without specific APIs. Fortunately, those APIs now exist.

To handle symbolic links, you need two File APIs: the isSymbolicLink property, and the canonicalize function.

isSymbolicLink simply returns a boolean which tells you if the File instance is a symbolic link or a junction point. Simple enough. But what does the symbolic link actually point to? That's where canonicalize comes in. Call canonicalize, and suddenly the file reference now points to the actual file referenced by the link. Call nativePath, and you get the path of the original file, not the link.

So how does this solve my SPF problem? While traversing the file system, I keep a map of file paths, and before adding a new image to the queue, I check to see if it is already in the map. Before I was able to resolve symbolic links, a circular reference would have me adding the same files infinitely because the path continued to grow. Now, I simply check to see if the file reference is a symbolic link, and if it is, I call canonicalize before adding it to the map. Problem solved.

These two APIs don't work on Mac aliases or Windows shortcuts, however they don't have to. AIR will not follow aliases or shortcuts like it will symbolic links, so they cannot get you into the same kind of trouble.

Posted by cantrell at 9:48 AM. Link | Comments (2) | References

January 3, 2008

Manual Data Binding in Flex

I'm currently working on an Exchange calendaring client for AIR (more details to come), and I decided early on to use the Cairngorm Flex framework. I'd worked on Cairngorm projects in the past, but I hadn't ever used Cairngorm in one of my own projects from start to finish. I decided it was time to put it to use, and to make sure it was 100% AIR friendly.

One of the first things you realize about Cairngorm is that it relies very heavily on data binding. Data binding is one of my favorite features of Flex, so I was very happy with how my application was coming together. But then I ran into a snag. I found myself needing to execute a block of code in response to a property change rather than just display the new property value, or update a component's data provider. Fortunately, Flex has anticipated this issue, and allows you to set up binding manually in a very flexible (naturally) way.

So here's the scenario:

I have a ControlBar component which has a busy, and a notBusy state. In the busy state, I show a progress bar, and in the notBusy state, the progress bar goes away, and in its place, I show the date and time of the last update. My ModelLocator has a busy property which makes it easy for me to to maintain a global "busy" state for my entire app. Whenever that properly changes, I want to change the state of the ControlBar component, as well as execute a small block of UI-related code.

The key to making this work is the mx.binding.utils.ChangeWatcher class. In the creationComplete handler of my component, I have the following code:

ChangeWatcher.watch(ModelLocator.getInstance(), "busy", onBusyChange);

Believe it or not, that's it. In my onBusyChange function, I change the state of my component, and do a couple of other small UI-related things. Now whenever the busy property of the ModelLocator changes, my app just does the right thing.

I found that the ChangeWatcher was really the key to making Cairngorm flexible enough to build any type of application. It allowed my team to throw together a pretty nice little Exchange Calendar viewer and notifier which I think will be a big hit with those who use Exchange in a corporate environment, but who aren't big fans of Outlook. I will release the application and the code with the next public release of AIR.

Posted by cantrell at 9:39 AM. Link | Comments (8) | References

December 4, 2007

Mapping File Extensions to MIME Types

When writing Salsa (an Amazon S3 client), I ran into a situation where I needed to map MIME types to file extensions and vice versa. So I wrote a simple but very handy utility to do it for me. The ActionScript code is here if you just want to copy it, or it's available in as3corelib if you want grab the entire library (with tons of other useful APIs).

Posted by cantrell at 8:57 AM. Link | Comments (1) | References

December 3, 2007

Multi-monitor Support in AIR

I've seen several people ask about multi-monitor support in AIR. The answer is: it's there, and it's very good.

Take a look at the flash.display.Screen class (AIR only -- not available in the Flash Player). It contains the following properties:

The Screen class also contains a very cool function called getScreensForRectangle that returns an array of screens that intersect the specified Rectangle.

We've also added a ScreenMouseEvent that is dispatched by SystemTrayIcon and gives you the coordinates of a click relative to the main screen as opposed to an application window.

For a relatively simple example of the Screen APIs in action, check out SPF on the Adobe Labs Sample Application page.

Posted by cantrell at 10:35 AM. Link | Comments (5) | References

October 19, 2007

AIR is Desktop 2.0

As I was putting together an Introduction to AIR presentation yesterday, something struck me about AIR that I hadn't fully realized before: AIR is desktop 2.0.

To me, web 2.0 means a new wave of innovation and attention to things like user experience. It's not just the technology (Ajax, Flex, etc.) or things like user-generated content or social networks. Rather, it's what all these things represent: a commitment to more effective, efficient, and engaging user experiences.

But why should such an important evolutionary step in application development be confined to the web? What about the desktop? In many ways, desktop applications have stagnated worse than pre web 2.0 applications. In general, they tend to be bloated, and it doesn't seem to matter how fast my processor is or how much memory my computer has -- most desktop applications seem to find a way to use just about all available resources. They have too many features that not enough people use, and since updates are such a big deal, bug fixes are few and far between. Worst of all, convention seems to have replaced innovation. Just like on the web, we have certain expectations of how desktop applications should work, and we tend to forget to demand more.

That's not to say there hasn't been any innovation on the desktop. OS X was a huge step forward, and although Windows Vista doesn't represent any type of fundamental change in the desktop model, it's certainly the nicest looking OS Microsoft has ever released. I finally installed Office 2007 the other day, and I was impressed with Microsoft's obvious desire to break away from 17 years of user interface history in order to provide Office users with a more streamlined and intuitive experience.

So I believe the desire is there to create better desktop experiences, but not necessarily the tools. That's where I think AIR is really going to shine. In fact, I believe that AIR is going to be the catalyst for desktop 2.0.

The reason I'm coming to this realization now is that I've had enough time to really see what AIR can do. Of course, there are the most salient examples like the new eBay Desktop and AMP, but there are a lot of other smaller, more subtle clues that foreshadow the types of experiences we have in store for us. The other day, I wrote an application called AIRPressure just to see how hard it would be to generated dynamic dock and system tray icons using Flash drawing APIs. The little timer that appears in the dock or system tray is being drawn completely dynamically, and is updated once per second. The application took about a day to write (mostly because I had to familiarize myself with the Flash drawing APIs and enough trigonometry to to draw a dynamic pie chart) and it works perfectly on both Mac and Windows. The app itself isn't anything revolutionary, but the ease with which I was able to do something that has traditionally been difficult, and to do it in a cross-platform way, really starts to illuminate the possibilities.

My team has been working on an open source AIR notification framework and we decided the other day to see how far we could push it. Among many other things, the notification framework allows you show notification windows like "toasts" on Windows or the Google Notifier on Mac. Just for the fun of it, we decided to see if we could extend the AbstractNotification class and create a custom notification with a video in it. It took Prabs (an engineer on my team) about half a day to get it working beautifully, and most of his time was spent familiarizing himself with the Flash video APIs which he had never used before. (There's a sample in the Google Code project -- you just need an FLV on your machine to point the sample application to.)

And there are examples like the Allurent Desktop Connection, AOL's Top 100 Video application, all the amazing apps built for the Adobe AIR Developer Derby, and one of my personal favorites, Snippage.

In developing AIR, it was easy to get caught up in specific AIR APIs, and to think of AIR's capabilities in terms of those APIs. However, I've recently realized that those APIs don't even come close to defining what AIR is and what it can do. AIR's true capabilities lie somewhere in the intersection of all the breathtaking and innovative things millions of designers and developers can do with Flash, Flex, and Ajax, the power of desktop APIs, and the functionality unique to AIR like the embedded database. I think AIR is going to completely redefine our expectations of both developing for and interacting with desktop applications, and I think it's going to far exceed even our own expectations.

Posted by cantrell at 9:18 AM. Link | Comments (6) | References

October 10, 2007

Generating Dynamic Dock and System Tray Icons in AIR

Update (11/18/09): AIRPressure has been renamed to Timeslide. All links below have been update.

Someone asked me the other day if AIR applications can generate dynamic dock and system tray icons. The use case is something like Mail.app on Mac which always shows you the number of unread messages as part of the dock icon.

I'm happy to report that this is perfectly possible in AIR, and I created a sample application called AIRPressure to demonstrate. AIRPressure is a countdown timer which shows a dynamically generated timer icon in your dock or system tray to let you know how much time is left. It also has some other cool features:

The code for AIRPressure is probably more interesting than the application itself. For notifications (notification windows, bouncing the dock icon, flashing the task bar entry), and for changing the dock and system tray icons in a cross-platform manner, I use the as3notificationlib project that my team has been working on which makes it extremely simple. And for persisting preferences, I use the new as3preferencelib that my team also wrote which handles all file IO, encryption, and serialization automatically. AIRPressure is a pretty useful and comprehensive cross-platform application, and the entire app is only about 300 lines of code (thanks to all these libraries doing a lot of the heavy lifting).

Posted by cantrell at 1:11 PM. Link | Comments (17) | References

October 4, 2007

Make Text Fade in Flash Without Embedding Fonts

My team is working on a notification framework for AIR. It provides a cross platform API for managing notification windows, changing the dock or system tray icon, attaching menus to the dock or system tray icon, and bouncing the dock or flashing the task bar icon. It's open source and free, and available now on Google Code if you're interested in an early preview.

One of the challenges we ran into was getting the notification text to fade along with the notifications. My first reaction was that we were going to have to embed a font which is how I have gotten text to fade in Flex applications in the past. However, I didn't want to have to include a font in an open source project for both technical and legal reasons. Fortunately, one of the engineers on the AIR team (Stan Switzer) pointed me to one of the first AIR applications ever written called ApolloClock in which he had partially transparent dynamic text.

It turns out the answer is to apply a filter to the text. Applying a filter to a TextField results in the runtime generating a bitmap out of the device font which means you can change its alpha, rotation, etc. This was a huge help, and resulted in a very cool and smooth fade effect. It also allows the notification library to remain very small since it's written in 100% ActionScript (no Flex), and now does not contain an embedded font. (It might also be possible to use cacheAsBitmap to make this work, but I was happy enough with the filter approach that I didn't investigate further.)

I'll post more about the notification framework as we add features. I'll also post more about the application we're working on that we wrote the notification framework for. It, too, will be be free and open source, and will make Mac users who have to use Exchange calendaring very happy.

Posted by cantrell at 11:35 AM. Link | Comments (15) | References

August 21, 2007

My Embedded Database Presentation From the Bus Tour

I just finished my portion of the bus tour. I went from Atlanta through DC, presenting a total of about 7 or 8 times (I lost track). In addition to several demos, I presented a talk called "Introduction to the AIR Embedded Database".

It was a great trip, and and I really enjoyed hanging out with all the AIR developers here on the east coast. Every event was well attended and full of energy. The audience was engaged, enthusiastic, and had some very good questions and comments. In general, I would say the onAIR Bus Tour is by far the best developer event I've ever been a part of, and I'm very glad I was able to contribute.

We're going to post videos of all the presentations shortly, but I wanted to go ahead and get my slides and demo apps posted. A Flash version of my slides are available here, and a zip file of all my sample code is available here.

Keep in mind that my samples were built using a "daily build" rather than the public AIR beta. SQLExample2 should work in the public beta, but SQLExample and SchemaTest won't work until the next public drop (around the MAX timeframe). I've decided to include all the code anyway, however, to give you a sense of what's coming.

I'll post the URL to the video of my presentation when it's available.

Posted by cantrell at 10:36 AM. Link | Comments (5) | References

August 7, 2007

on AIR Bus Tour Picks Up Again Next Week

The second leg of the on AIR bus tour starts next week in Atlanta, and this time, I'll be there (I wasn't able to make the first leg). We're going to hit Atlanta, St. Luis, Cincinnati, Pittsburgh, Washington DC, Baltimore, Philadelphia, and Boston. I live outside of Washington DC now, so I'm going to head home after the DC event, but I'll be there for most of it. I'm going to be giving a talk called "Introduction to the Embedded Database API".

If you live or around any of these areas, come on out. You can learn everything you ever wanted to know about AIR, and it's entirely free. I can pretty much guarantee that a good time will be had by all, so go sign up!

Posted by cantrell at 9:38 AM. Link | Comments (4) | References

August 3, 2007

Milti-window Programming in AIR

Multi-window programming is something we've never had to worry about in Flash or Flex until now. AIR has a NativeWindow object which lets you write true multi-windowed desktop applications. Of course, along with additional power comes additional responsibility.

I just finished writing a screen saver application in AIR which I call SPF (Screen Protection Factor). It works basically like the picture viewer screen saver on OS X with the following features:

I wrote a ScreenSaver class which extends NativeWindow, and when the user has been idle for the specified amount of time, I create a new ScreenSaver for each available monitor and show it at full screen. The ScreenSaver class has a timer which picks images at random and displays them at the full resolution of the monitor. When input is detected again (when the user is no longer idle), the code iterates through the collection of ScreenSaver instances and closes each one.

Pretty straightforward. Except for one thing.

Thanks to some debug trace statements, I discovered that after all the ScreenSaver instances were closed, their timers were still running and trying to display images. I figured one of two things was happening:

  1. I found a bug.
  2. The timers had references to the window, which were preventing them from being garbage collected.

Turns out I was wrong on both counts.

When you close a window and null out your references to it, you are making it eligible for GC, but that doesn't mean it's immediately GCed. It might be GC right away, or it might be around for another hour. It just so happens that we're currently seeing unusually long intervals between collections on Mac right now (so to my credit, I did find partial bug, I guess), but regardless, AIR makes no guarantees of when your references are going to get cleaned up. That means you are still responsible for code that runs in those windows until they are finally reclaimed.

This should only be an issue for things like timers, or for asynchronous processes which might have been kicked off before the window was closed. If you working with any such code, be sure to listen for the closing event, and take care of any loose ends. Stop timers, and cancel all asynchronous processes that shouldn't have a chance to return. If you're extending NativeWindow, it's as easy as adding an event listener to yourself, and adding a few lines of housekeeping code.

I will release SPF with the next public beta along with a few other fun apps I've been tinkering with.

Posted by cantrell at 11:57 AM. Link | Comments (2) | References

June 12, 2007

Apollo (now AIR) Sample Applications

By now you have probably heard that:

  1. Apollo is now officially AIR (Adobe Integrated Runtime), and
  2. The AIR beta went live this week, and is available for download.

I did a lot more product definition for this release, and much less coding, but I did manage to write one interesting new application:

Salsa

Salsa is an Amazon S3 client which lets you easily manage your buckets and files in the S3 cloud. You can create and delete buckets as well as upload and download, drag and drop, and copy and past between your computer and Amazon S3. You can even manage multiple files at once. Salsa uses the open-source as3awss3lib ActionScript 3 S3 library that I wrote, so if you don't like the way Salsa works, you can write your own client.

Salsa, along with the other Adobe AIR sample applications (and their source code), is available here.

In addition to Salsa, I managed to update some of my old sample apps from the alpha:

MapCache

MapSnap is now MapCache. It lets you look up addresses using Yahoo! Maps, and save the maps for offline use. I used to call it MapSnap because all it let you do is create a snapshot of a map and save it, but now you can save the map, copy it to your clipboard, or drag and drop the map to your desktop. It's a simple but handy little app, and demonstrates the following:

Scout

I added forward and back buttons in Scout using the HTMLControl's new history management functionality. Otherwise, it's the same.

PixelPerfect

PixelPerfect is an example of a pure ActionScript 3 project (no Flex, no HTML, no JavaScript). I think it's actually the most useful AIR application that I've written so far, and I find myself using it on a regular bases. It pops up transparent rulers on your screen which you can use to measure things in pixels. For this release, I added multi-window support (you can open multiple rulers at once), and now the rules are always on top of other windows so they don't get lost. (I don't have a help dialog yet, but you can also change the transparency using your mouse wheel, move rulers with your arrow keys, and resize them with shift+arrow keys.)

All the beta sample apps, along with their source code, are available here on Adobe Labs.

Posted by cantrell at 6:48 AM. Link | Comments (20) | References

May 30, 2007

ActionScript Library for Amazon S3

I've been working on an Amazon S3 client in Apollo, and I finally got around to extracting the S3 library and making it its own project. I put it up on Google Code under the name as3awss3lib.

The library will only work in Apollo because there are too many restrictions in the browser player for it to work. Since I knew it wouldn't work in the browser anyway, I went ahead and used some Apollo APIs. It also has dependencies on As3Crypto, as3corelib, and Flex (you can use the free Flex SDK).

I haven't implemented everything S3 supports yet. Specifically, I haven't implemented access control (everything is private) and the bucket listing pagination functionality. But all the hard protocol work is there so the rest is easy to add. Right now, you can:

I'll release the application I'm building on top of the library as soon as we update the public release of Apollo. It allows you to drag and drop and copy and paste files to and from S3.

Posted by cantrell at 8:46 AM. Link | Comments (27) | References

April 23, 2007

Important Information on Maptacular

Maptacular is an Apollo application I wrote that lets you map addresses on your desktop (in vCard format) using Google Maps. You can download the public alpha binary from the Apollo application page on the Adobe Labs wiki, and if you're interested in the source code, read the entry previous to this one.

Some important information on Maptacular (which I should have mentioned when I first released the app):

Posted by cantrell at 11:11 AM. Link | Comments (4) | References

April 20, 2007

Apollo Sample Application Source Code

I finally got around to releasing the source code for my sample Apollo applications. Sorry it took so long. The applications and the source code can be downloaded here.

A couple of important notes:

Posted by cantrell at 11:27 AM. Link | Comments (5) | References

March 22, 2007

Constraining the Dimensions of an Apollo Application Window

I saw this issue come up on the Apollo forums the other day. If you want to constrain the dimensions of an Apollo application window, use the maxSize and the minSize properties of NativeWindow. I usually do it in an initialization function after setting the bounds of my window, like this:

var win:NativeWindow = systemManager.stage.window;
win.width = 800;
win.height = 600;
win.maxSize = new Point(800, 600);
win.minSize = new Point(800, 600);
win.visible = true;

You can also use the bounds property of NativeWindow to set its size and location at the same time.

Posted by cantrell at 2:13 PM. Link | Comments (31) | References

March 21, 2007

Using HTML and Script Bridging in Apollo

The video of the presentation I did at Apollo Camp the other day on using HTML and Script Bridging in Apollo is available on video.onflex.org. Props to Mike Chambers for enduring the extremely long encoding process.

I will be releasing the source code for all the applications I showed in this presentation shortly. I just need to find the time to clean up the code a bit, and to fix a couple of bugs.

Posted by cantrell at 10:40 AM. Link | Comments (2) | References

March 19, 2007

MapSnap: An Apollo Example Application

MapSnap was one of the first Apollo applications I wrote which incorporates HTML. It lets you look up addresses on a Yahoo map, and save the maps as a PNGs to your desktop. It's a very simple application, but I think it makes for an excellent example.

MapSnap demonstrates the following:

If you just want to run the app, all you need is the Apollo runtime, and the MapSnap AIR file. If you want to see the source code, here is the Flex Builder project. There's a lib directory in the project with corelib.swc in it which you need to link in to your project in order to get the PNG encoding support.

Posted by cantrell at 1:44 PM. Link | Comments (12) | References

March 18, 2007

Apollo alpha is live

The Apollo Alpha is now available on Labs, along with a lot of good resources on the Labs wiki.

Yes, I know my blog has been silent recently, but that's primarily because I've been working with technology that hadn't been released yet. Now that the Apollo Alpha is live, I can start posting all kinds of tips and tricks, sample applications, and code snippets.

Posted by cantrell at 11:23 PM. Link | Comments (4) | References

December 19, 2006

Apollo Application Demos

Mike Chambers interviewed me the other day about what I do on the Apollo team, and asked me to show a couple apps I've been working on. You can check it out the Google Video, or download the mp4.

Posted by cantrell at 1:01 PM. Link | Comments (27) | References

Copyright © 2009 Adobe Systems Incorporated. All rights reserved.
Use of this website signifies your agreement to the Terms of Use and Online Privacy Policy (updated 07-14-2009).