Warning: include() [function.include]: Unable to access /var/www/html/rogue-development/blog2/wp-content/advanced-cache.php in /var/www/html/rogue-development/blog2/wp-settings.php on line 62

Warning: include(/var/www/html/rogue-development/blog2/wp-content/advanced-cache.php) [function.include]: failed to open stream: No such file or directory in /var/www/html/rogue-development/blog2/wp-settings.php on line 62

Warning: include() [function.include]: Failed opening '/var/www/html/rogue-development/blog2/wp-content/advanced-cache.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in /var/www/html/rogue-development/blog2/wp-settings.php on line 62

Notice: add_option was called with an argument that is deprecated since version 2.3 with no alternative available. in /var/www/html/rogue-development/blog2/wp-includes/functions.php on line 3468

Notice: register_sidebar_widget is deprecated since version 2.8! Use wp_register_sidebar_widget() instead. in /var/www/html/rogue-development/blog2/wp-includes/functions.php on line 3382

Notice: register_widget_control is deprecated since version 2.8! Use wp_register_widget_control() instead. in /var/www/html/rogue-development/blog2/wp-includes/functions.php on line 3382
flex « Marc’s Musings

Archive for the 'flex' Category

Page 2 of 6

The Essential Guide to Open Source Flash Development

The book I’ve been working on, The Essential Guide to Open Source Flash Development, is now out in stores.  It’s hard to believe that I started working on it about 11 months ago!  It’s really great to see all of that hard work finally in print.

So What is it about?

The book does a few things.  First, about a third of the book introduces you to some open source tools for doing flash development.  Things like FlashDevelop, MTASC, SwfMill, ANT, and ASDT.  It’ll show you how to create an AS2 and an AS3 based flash application using completely free and open software.  This goes all the way from installing the tools, creating a sample app, writing up some unit tests for it, and then to publishing it to the web.  Along the way it’ll give you a brief introduction to each tool, explain what it does, and then give a quick example of how to use it.  (That’s the 5 chapters I wrote)

The remaining 2/3 of the book dedicates a chapter to various open source projects going into a little more detail about them.  There’s a chapter on Papervision 3D, SWX, FUSE/Go, HAXE, AMFPHP, two for Red5 and a couple more.

This was a lot of fun to work on, and my only regret is not getting to know the other authors better.

If you’re looking for a place to buy it, check out Bookpool.   I worked for them for a year and they’re really stellar guys.  They offer good prices, but more importantly;  as long as the book is in stock, they do their damndest to get it on a truck the day you order it. (Of course, you’re at the mercy of the publisher if it’s out of stock)

Talk tonight on ObjectHandles+Degrafa (Attend online!)

As mentioned in a previous post, I’m giving a talk to the BFAIG group on creating a simple diagramming application in Flex using ObjectHandles and Degrafa.  It’s been delayed until tonight at 7:00pm EST.  If you’re interested in seeing it, this is a joint in-person and online meeting.  I’m actually attending from a remote location, so the online experience should be pretty good.  You can get info about how to attend online at the BFAIG Blog post.

Printing horrors

Printing in a Flash/Flex application can be tricky to get right.  I’ve been working on a very print-heavy application recently.  We’ve been having a long standing intermittent bug where we print a large document, but some things on that document that were created through the Flash drawing API wouldn’t be on the paper.

We added a

var options:Object = canvas.prepareToPrint( canvas );

Before the print and a

canvas.finishPrint( options, canvas);

After the print, and it seems to have solved our problem even though the documentation says we shouldn’t ordinarily need to explicitly call those.

Xray Viewer updated

The XRayViewer has been broken for quite some time. I had originally done it with an AIR beta, and that has since stopped working. So there’s now a newly compiled version just waiting for you to grab.

What is the XRayViewer?

So you may be asking yourself What exactly is the XRayViewer?

All this little app does is host the XRay connector and let you load a local swf. Then it displays the swf with some simple controls to play/stop/advance/back. The big benefit is you can then use XRay (By John Grden + Others) to inspect the swf without changing any code around.

There’s three new (very minor) features in this version:

  1. There’s a button to launch the XRay interface in your default browser.
  2. The path to the loaded swf is displayed in the top toolbar. (You can copy & paste that into Xray so you don’t have to navigate as far into the hierarchy)
  3. New logo / icons

Now Open Source!

The entire project is now licensed under the MIT license. If you install the application and then right click on it you can “View Source” to get the source code for it.

Creating a simple diagramming application

Tonight, I’ll be giving a short talk to BFAIG on using ObjectHandles and Degrafa to make a simple diagramming application.  This application demonstrates the basics of working with Degrafa, and also shows how easy it is to make an interface that allows users to resize and move objects on screen.

The example application has source attached, and is licensed under the MIT license.  I hope someone can take it and make some really great application out of it.  Please drop me an email if you do!

I recorded a practice-run I did, so here it is…

 

New Object Handles release

I just whipped up a new ObjectHandles release.  Couple notable things…

They now automatically remove themselves from the SelectionManager when removed from the stage (they add themselves back when added as well).  This fixes a fairly serious memory leak.

I’m deprecating the useage of the Handle class.  It draws the handles through the drawing API.  Now, instead use the ImageHandle class which draws the handles from an embedded image.  There was some wonky logic in there that turned clipping on or off depending on this setting and that was just plain dumb.  Lets just do it one way.  I included sample .png files to simulate the old look.

I fixed some weird flickering when resizing to the left or up.  It only happened on certain cases, and I’m not sure why.  I think it had to do with delayed execution of the Flex layout manager.  Now, I delay the setting of x,y,width, and height and force a validateNow() right after setting those.  Let me know if this causes any problems in your application.

I still don’t have conditional compile for Flex2/Flex3 in there, so this will only compile under Flex 3.  Does anyone know how to get that conditional compiling to detect Flex version without having to pass an extra compiler param?

 

AIR / Browser API example

AIR / Browser API example
The other day I blogged about a wrapper class that I wrote to handle the AIR / Browser integration API.  Below is an example of another class that I wrote that uses that wrapper class.  It’s capable of detecting two different AIR applications, installing them, and running them.  It relies on the wrapper class to do all the heavy lifting, but I thought this might be a good example to help people get started.

package com.agileagenda.web
{

import com.roguedevelopment.air.AIRBrowserRuntime;

import com.roguedevelopment.air.AIRBrowserRuntimeEvent;

import flash.events.IOErrorEvent;

public class InstalledApplications

{

public static function get instance() : InstalledApplications

{

if( _instance == null ) { _instance = new InstalledApplications(); }

return _instance;

}

protected static var _instance:InstalledApplications = null;

protected var api:AIRBrowserRuntime;  

protected static const PUBLISHER_ID:String = “F49A4D8DF78A1FEE7A3BE440DC11BAB18D922274.1″;

protected static const TRACKER_ID:String =  “com.agileagenda.AgileTracker”;

protected static const MAIN_APP_ID:String = “com.agileagenda.AgileAgenda”;

protected static const AGILE_AGENDA_INSTALL:String =  “http://www.agileagenda.com/download/AgileAgenda.air”;

protected static const AGILE_TRACKER_INSTALL:String = “http://www.agileagenda.com/download/AgileTracker.air”;

[Bindablepublic var isReady:Boolean=false;

[Bindablepublic var hasAIR:Boolean;

[Bindablepublic var agileAgendaVersion:String = “”;

[Bindablepublic var agileTrackerVersion:String = “”;

public function InstalledApplications()

{

  api = new AIRBrowserRuntime();

  api.addEventListener(AIRBrowserRuntimeEvent.READY, onReady );

  api.addEventListener(IOErrorEvent.IO_ERROR, onAirFail );

  api.load();

}

protected function onAirFail(event:IOErrorEvent) : void

{

  trace( event.text );

}

protected function onReady(event:AIRBrowserRuntimeEvent ) : void

{

  hasAIR = api.getStatus() == ‘installed’;

  isReady = true;

  api.addEventListener(AIRBrowserRuntimeEvent.APP_VERSION_RESULT, onTrackerVersionResult );

  api.getApplicationVersion( TRACKER_ID, PUBLISHER_ID );

}

protected function onTrackerVersionResult(event:AIRBrowserRuntimeEvent) : void

{

  trace(“Tracker version installed: “ + event.detectedVersion );

  agileTrackerVersion = event.detectedVersion

  api.removeEventListener(AIRBrowserRuntimeEvent.APP_VERSION_RESULT, onTrackerVersionResult );

  api.addEventListener(AIRBrowserRuntimeEvent.APP_VERSION_RESULT, onAppVersionResult );

  api.getApplicationVersion( MAIN_APP_ID, PUBLISHER_ID );

}

protected function onAppVersionResult(event:AIRBrowserRuntimeEvent ) : void

{

  trace(“Application version installed: “ + event.detectedVersion );

  agileAgendaVersion  = event.detectedVersion

  api.removeEventListener(AIRBrowserRuntimeEvent.APP_VERSION_RESULT, onAppVersionResult );

}

public function launchAgileAgenda( args:Array ) : void

{

  api.launchApplication( MAIN_APP_ID, PUBLISHER_ID, args );

}

public function launchAgileTracker( args:Array ) : void

{

  api.launchApplication( TRACKER_ID, PUBLISHER_ID, args );

}

public function installAgileAgenda(args:Array) : void

{

  api.installApplication(AGILE_AGENDA_INSTALL, “1.0″, args );

}

public function installAgileTracker(args:Array) : void

{

  api.installApplication(AGILE_TRACKER_INSTALL, “1.0″, args );

}

}

}

Interacting with an AIR app from a browser based app

Interacting with an AIR app from a browser based app

We’ve all seen the AIR installation badges that let you install an AIR application from a website. But the API exposed to do that lets you do more than just a simple badge. I’ve been working on a web-based service for AgileAgenda. One of the components of that is to manage the list of files you’ve saved to the service and be able to open those in the desktop AIR application. So right from within the online Flex based app it sure would be nice to detect if the application is installed, give the user the option to install, and then launch it for them and automatically open the desired file. Something like this…

To implement that we need to:
  1. Detect whether or not the application is installed.
  2. Display the version number if it is. (Disable the “Open schedule in…” button if it’s not)
  3. When clicking on the “Open” link, launch the application with a few parameters so it know what to open.
  4. When clicking on the “Install” link, install the application and pass a few parameters so it know what to open when it launches directly after the install.
Doing things like that falls outside of the normal AS3 web development API. But Adobe provides a swf that you can load at runtime and them make calls to. You can read all about that over here: http://livedocs.adobe.com/air/1/devappshtml/help.html?content=distributing_apps_3.html.
But there’s an annoying thing with that. You need to load up that remote swf and then access it like a dynamic object. No compiler-time type checking. Not the standard event mechanism. And no code-completion from within Flex Builder.
Luckily for you, I went through and made a wrapper class that you can download from here:
This will handle the loading of the remote swf, dispatching events when it loads (or fails to load) and then wraps the API for the entire process. There’s nothing revolutionary in there, it’s mostly takendirectly from that livedocs page above.
So now that you have that handy-dandy wrapper class, lets look at how to actually get something done.
Setting up your AIR application to work with your website (Important)
First thing, go into your AIR application’s -app.xml file and make sure allowBrowserInvocation is set to true. By default it’s set to false.

<allowBrowserInvocation>true</allowBrowserInvocation>

If you don’t do this, you won’t even be able to query version information on your application. But, be careful. By doing this you’re letting any website launch your AIR application from a web page. You need to be careful in how much your app trusts command line arguments passed to it. For instance, you should never pass a file to delete on the command line.

Now that you’ve set allowBrowserInvocation to true, create a new .air file and post that to your website somewhere.

Using the wrapper class to interact with your application

Either open an existing Flex or Actionscript project in FlexBuilder and put the source to the AIRBrowserRuntime.as somewhere that the compiler will find it. Somewhere in your main application, create an AIRBrowserRuntime object, set some event listeners, and call the load() method to load the air.swf file from Adobe’s servers.

var api:AIRBrowserRuntime;

api = new AIRBrowserRuntime();

api.addEventListener(AIRBrowserRuntimeEvent.READY, onReady );

// Optional: api.addEventListener(IOErrorEvent.IO_ERROR, onAirFail );

api.load();

Once the READY event is dispatched, you can start calling methods to query application versions, install apps, or launch applications.

Checking if AIR is installed

To see if the AIR runtime is installed, call the getStatus() method. It will return one of three values.

available – The AIR runtime can be installed on this computer but currently it is not installed

unavailable – The AIR runtime cannot be installed on this computer.

installed – The AIR runtime is installed on this computer.

Example:

switch( api.getStatus() )

{

case “available”: trace(“AIR is available, but not installed.”); break;

case “unavailable”: trace(“AIR is not available for this computer.”); break;

case “installed”: trace(“AIR is already installed on this computer.”); break;

}

Checking the version of an installed application

The getApplicationVersion method will check to see if a given application is installed and give you the version if it is. This method operates asynchronously so you have to create an event listener before you call it. If you look at the method signature…

public function getApplicationVersion(applicationID:String, publisherID:String) : void

You’ll see that it takes an applicationID and a publisherID. The applicationID is just value of the <id> tag of your application descriptor (the -app.xml file). It’ll probably be something like this, but you need to make sure to make each application unique:

<id>com.agileagenda.AgileTracker</id>

The publisherID is a little trickier to find. That doesn’t get assigned until you actually sign your .AIR file with your code-signing key. I know of 2 ways of finding it, but there’s probably an easier way (please leave a comment if you know how).

Warning about Publisher ID:  If you change the key your sign your app with, the publisher ID will change.

Option 1: Get it at runtime.

In your application’s main MXML throw up an Alert message with the publisher ID. Then build a .air, install the .air and run it. Copy & Paste the result.  Example:

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml

    creationComplete=”init()” >

  <mx:Script>

  <![CDATA[

    protected function init() : void 

    {

      Alert.show( nativeApplication.publisherID);

    }

  ]]>

  </mx:Script>

</mx:WindowedApplication>

Which results in something resembling:

It’s hard to see in that picture, but the publisher ID is: F49A4D8DF78A1FEE7A3BE440DC11BAB18D922274.1 as it wraps to two lines.
Option 2: Get it after install
This is probably a bit easier, but I’m not 100% sure where this directory goes on a windows box.
On OSX (maybe on Windows, I’m not sure exactly where) the application storage directory that gets created for your application has the publisher ID appended to it. So if you look in your user directory -> Library -> Preferences, you should see a directory that starts with your application ID and ends with your publisher ID.
As you can see there, we get the same publisher ID value as Option #1.
On to checking installed version
Now that you have your application ID and your publisher ID you can make a call from your web application to the API to request the installed version of your AIR application.  This operates asynchronously so you’ll need an event listener for the result.
Example:

api.addEventListener(AIRBrowserRuntimeEvent.APP_VERSION_RESULT, onTrackerVersionResult );

api.getApplicationVersion( “com.agileagenda.AgileTracker”, “F49A4D8DF78A1FEE7A3BE440DC11BAB18D922274.1″ );

protected function onTrackerVersionResult(event:AIRBrowserRuntimeEvent) : void

{

trace(“Tracker version installed: “ + event.detectedVersion );

}

The event.detectedVersion that comes back will be the value in your application descriptor version tag.  Mine looks like this:

<version>v1</version>

So the trace output looks like this:

Tracker version installed: v1

Launching an installed AIR application

Assuming you followed along in the previous section, you have an Application ID and a Publisher ID ready. To launch an AIR app from a web app you just call

api.launchApplication( “com.agileagenda.AgileTracker”, “F49A4D8DF78A1FEE7A3BE440DC11BAB18D922274.1″ );

launchApplication has a third, optional, parameter called arguments and is typed as an array. Anything you pass into that will be passed along to the application in an INVOKE event. This way you can pass information from the web application to the AIR application. Example:
api.launchApplication( “com.agileagenda.AgileTracker”, “F49A4D8DF78A1FEE7A3BE440DC11BAB18D922274.1″, ["Argument1","Argument2" ] );
Be careful, the arguments are very restrictive in what characters can be passed. Things like dashes, percent signs, underscores, all cause a runtime exception on the web application. You’re probably safest only using alphanumeric characters. I haven’t found a comprehensive list of what characters are or are not valid. If someone has that, please leave a comment below.  This was done for security reasons.
Installing an AIR application
To install an AIR application, all you need to do is call the installApplication with the absolute URL to the .air file you want to install. Example:
api.installApplication(“http://www.agileagenda.com/download/AgileTracker.air”);

This will take care of installing the AIR runtime, installing the application, and launching it for the first time. installApplication has two more optional parameters. The AIR runtime that the application requires, and arguments that can be passed to the application for it’s first run. Example:

api.installApplication(“http://www.agileagenda.com/download/AgileTracker.air”,”1.0″,["Arg1","Arg2"]);
That’s all there is to it.
So that’s it, pretty simple, huh? You might also want to look into using a LocalConnection to do realtime communication between your web-app and your air-app once the air-app has been launched. Recap of the links:
Wrapper Class:
Adobe’s Documentation:
My Blog (where any updates to the wrapper class will be posted)
The actual air.swf that does all the heavy lifting (This URL is wrong in some of the docs!)

http://airdownload.adobe.com/air/browserapi/air.swf

ObjectHandles Demo

Here’s a short demo of some of the stuff that ObjectHandles (my Flex library for moving & resizing stuff) can do with a very minimal amount of code.  

The custom things I did:
  • Has a MOVING / RESIZING event handler to show a custom tooltip (hides the tooltip on MOVED / RESIZED)
  • Has custom resize handle images that look like grey horizontal bars
  • Only allows vertical resizing (allowHResize = false).
  • On a MOVED event, the objects have an animation that snaps them to a column.
It’s a little hard to see in that video, but the duration & start time in the tooltip update as you move or resize the boxes around.

Custom Cursors + Multiple Windows

Quick tip.  Don’t use CursorManager.setCursor anymore.  It works fine for single-window flex apps, but breaks once you have an AIR app with multiple windows.

UIComponent now has a cursorManager property that references the cursor manager for the current window.  If you use that it’ll work all the time, not just in web Flex apps.
I understand why they left CursorManager in there for backwards compatibility, but it’s a shame we can’t mark it as @deprecated like in the Java world.