Prerequisite knowledge
This series of tutorials is designed for developers with an intermediate to advanced understanding of ActionScript 3 and building Adobe AIR applications. Familiarity with Flash Builder, Java, and Objective-C will also be helpful. You should have already read Part 1 of this tutorial series.
Required products
Flash Builder (Download trial)
Additional required other products
Volume Native Extension
User Level

This part of the tutorial series on building a volume control native extension for iOS and Android covers the parts of the extension that are written in ActionScript. These include the main library and the default library. If you have not already done so, read Building a native extension for iOS and Android — Part 1 before proceeding.

Creating the ActionScript library

The main library is an ActionScript library that serves as an interface to the AIR application. This library is also responsible for interacting with the extension context generated by AIR. The extension context is used as a bridge between AIR and the native platform.
The following steps, used to create the library, are explained in more detail below:
  1. Create a Flex Library project.
  2. Create a Controller class that extends EventDispatcher.
  3. Initialize the extension context.
  4. Expose the interface methods and call native methods on the extension context.
  5. Add listeners on the extension context.
  6. Create the extension.xml file.
See the sample files of this article for the complete implementation of the ActionScript library,
Create a Flex Library project
Open Flash Builder and choose File > New > Flex Library Project to get started.
Create a Controller class that extends EventDispatcher
In the code, begin by creating a class that extends EventDispatcher:
public class VolumeController extends EventDispatcher { public function VolumeController( enforcer:SingletonEnforcer ) { super(); } }
Initialize the extension context
The extension context is the bridge between the AIR application and the native platform. It is automatically created. The developer never does anything to invoke it and the runtime hides it for both iOS and Android. The createExtensionContext() method accepts two parameters. The first parameter is the id of the native extension package. The native extension package is defined in the extension.xml file (covered below in Create The Extension.xml File). The second parameter, which is optional, defines the particular behavior desired from the native extension. For simple native extensions this won't apply, but in more complex native extensions this serves as a valuable way to separate invoke different features in the native code. For example, if you had a native extension to send notifications, you may have a local implementation and a push implementation.
The volume native extension is fairly basic, so the code simply passes the package path and leaves the second parameter empty.
public function VolumeController( enforcer:SingletonEnforcer ) { extContext = ExtensionContext.createExtensionContext("net.digitalprimates.volume", "" ); if ( !extContext ) { throw new Error( "Volume native extension is not supported on this platform." ); } }
If the result from createExtensionContext() is null there isn't an implementation in the native extension for the native platform on which the AIR application is currently running. In that case you should throw an error.
Expose the interface methods and call native methods on the extension context
The class VolumeController has two native methods: init() and setVolume():
private function init():void { "init" ); } public function setVolume(newVolume:Number):void { if ( isNaN(newVolume) ) { newVolume = 1; } if ( newVolume < 0 ) { newVolume = 0; } if ( newVolume > 1 ) { newVolume = 1; } "setVolume", newVolume ); systemVolume = newVolume; }
VolumeController is a singleton, because it doesn't make sense to need more than one object controlling the system volume within your application. The init() method is called when the singleton instance is created.
The setVolume() method expects a value between zero and one, so basic validation is performed and then the volume value is passed to the native extension.
View the file in the VolumeLib Flash Builder project in the sample files for this tutorial to see all of the ActionScript code including the necessary code to make this class a singleton.
Add listeners on the extension context
Later we'll want the native code to send events to AIR when certain events happen (such as the user pressing the volume hard buttons). To listen for the events in AIR we need to add an event listener on the extensionContext that is listening for StatusEvent.STATUS . We'll discuss dispatching these events in the native code in later tutorials.
extContext = ExtensionContext.createExtensionContext( "net.digitalprimates.volume", "" ); extContext.addEventListener( StatusEvent.STATUS, onStatus ); private function onStatus( event:StatusEvent ):void { systemVolume = Number(event.level); dispatchEvent( new VolumeEvent( VolumeEvent.VOLUME_CHANGED, systemVolume, false, false ) ); }
Create the extension.xml file
The extension.xml file defines where the code for each platform exists in the native extension (you can see the complete extension.xml file in the VolumeLib project in the sample files). This is how the native extension knows which platforms are supported and how to access the native libraries.
<extension xmlns=""> <id>net.digitalprimates.volume</id> <versionNumber>0.0.1</versionNumber> <platforms> <platform name="Android-ARM"> <applicationDeployment> <nativeLibrary>libAndroidVolumeLib.jar</nativeLibrary> <initializer>net.digitalprimates.volume.VolumeExtension</initializer> </applicationDeployment> </platform> <platform name="iPhone-ARM"> <applicationDeployment> <nativeLibrary>libIOSVolumeLib.a</nativeLibrary> <initializer>VolumeExtensionInitializer</initializer> <finalizer>VolumeExtensionFinalizer</finalizer> </applicationDeployment> </platform> <platform name="default"> <applicationDeployment/> </platform> </platforms> </extension>
The <id> (in this case net.digitalprimates.volume ) is the same id that is a required parameter of the createExtensionContext() method which is called to initialize the extension context as explained above.
In the platforms node you should include one platform node for each platform you plan on supporting. The file above supports Android and iOS. Other possible platforms are OS X, Windows, and AIR TV.
The nativeLibrary node points to the location of the native library.
The initializer node points to the initializer for the native library.
The finalizer node points to the finalizer for the native library.
The initializer and finalizer will be implemented differently depending on the platform. I'll cover that in more detail later. For now, it's enough to know that the initializer and finalizer are chunks of code that get called when the native extension is started and when it is destroyed, respectively.
Note: On iOS the finalizer is not required and it won't ever get called, even if it is present.
Later articles in this tutorial series will cover writing an initializer and a finalizer in more detail.
The default platform defines an ActionScript implementation to use on any platform that isn't defined in the extension.xml file. Generally this will be some sort of stub implementation, because it's often difficult to reproduce native behavior in ActionScript (part of the reason for writing a native extension in the first place). It's important to add a default implementation so that your application can be tested in the Flash Builder simulator.

Creating the default library

The default library will be used on any platform that isn't specifically supported by the native extension. The main reason I include this is so that the native extension will work in the Flash Builder simulator. Development would be slow and difficult if you had to deploy to the device every time you wanted to test a small change. However, creating a default library is also important if you're building an extension that's targeted specifically at one platform but the main application supports multiple platforms.
Creating the default library is fairly straightforward, because the default library is an exact copy of the main ActionScript library with all of the methods stubbed out. It's important that every class that exists in the main ActionScript library also exists in the default library.
In the Volume native extension, I let the default library continue to update the systemVolume property whenever setVolume() is called. Also, the systemVolume property defaults to a value of 1 . This allows the native extension to "work" in the simulator, emulating what would happen if the user never touched the hardware volume buttons. The native extension is unable to change the volume of the simulator though, so UI elements will appear to work but will not actually change the volume.
View the file in the VolumeDefault Flash Builder project in this tutorial's sample files to see all of the ActionScript code.
Here are the basics steps in creating a default library:
  1. Create a new Flex Library Project.
  2. Make a copy of each class in the main ActionScript library project.
  3. Stub out the methods for each class. Be sure that there are no references to an extension context.

Where to go from here

At this point the main and default libraries are done. You have defined the interface to interact with the native extension, you have connected to the extension context bridge, and you've created the extension.xml file to define the available platforms and their options. In the next two tutorials in this series—Building a native extension for iOS and Android – Part 3: Building the iOS library and Building a native extension for iOS and Android – Part 4: Building the Android library—you will develop the iOS code and the Android code.