0% found this document useful (0 votes)
21 views

Air Devappsflex

Uploaded by

andrexcaverito
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
21 views

Air Devappsflex

Uploaded by

andrexcaverito
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 304

1

Developing AIR Applications with Adobe Flex


© 2007 Adobe Systems Incorporated. All rights reserved.
Long Book Title
If this guide is distributed with software that includes an end user agreement, this guide, as well as the software
described in it, is furnished under license and may be used or copied only in accordance with the terms of such
license. Except as permitted by any such license, no part of this guide may be reproduced, stored in a retrieval system,
or transmitted, in any form or by any means, electronic, mechanical, recording, or otherwise, without the prior
written permission of Adobe Systems Incorporated. Please note that the content in this guide is protected under
copyright law even if it is not distributed with software that includes an end user license agreement.
The content of this guide is furnished for informational use only, is subject to change without notice, and should not
be construed as a commitment by Adobe Systems Incorporated. Adobe Systems Incorporated assumes no responsi-
bility or liability for any errors or inaccuracies that may appear in the informational content contained in this guide.
Please remember that existing artwork or images that you may want to include in your project may be protected
under copyright law. The unauthorized incorporation of such material into your new work could be a violation of
the rights of the copyright owner. Please be sure to obtain any permission required from the copyright owner.
Any references to company or person names in sample templates are for demonstration purposes only and are not
intended to refer to any actual organization or person.
Adobe, the Adobe logo, Dreamweaver, Flash, ColdFusion, and JRun are either registered trademarks or trademarks
of Adobe Systems Incorporated in the United States and/or other countries.
Microsoft and Windows are either registered trademarks or trademarks of Microsoft Corporation in the United
States and/or other countries. Apple and Mac OS are trademarks of Apple Inc., registered in the United States and
other countries. All other trademarks are the property of their respective owners.
Adobe Systems Incorporated, 345 Park Avenue, San Jose, California 95110, USA.
Notice to U.S. Government End Users. The Software and Documentation are “Commercial Items,” as that term is
defined at 48 C.F.R. §2.101, consisting of “Commercial Computer Software” and “Commercial Computer Software
Documentation,” as such terms are used in 48 C.F.R. §12.212 or 48 C.F.R. §227.7202, as applicable. Consistent with
48 C.F.R. §12.212 or 48 C.F.R. §§227.7202-1 through 227.7202-4, as applicable, the Commercial Computer Software
and Commercial Computer Software Documentation are being licensed to U.S. Government end users (a) only as
Commercial Items and (b) with only those rights as are granted to all other end users pursuant to the terms and
conditions herein. Unpublished-rights reserved under the copyright laws of the United States. Adobe Systems Incor-
porated, 345 Park Avenue, San Jose, CA 95110-2704, USA. For U.S. Government End Users, Adobe agrees to comply
with all applicable equal opportunity laws including, if appropriate, the provisions of Executive Order 11246, as
amended, Section 402 of the Vietnam Era Veterans Readjustment Assistance Act of 1974 (38 USC 4212), and Section
503 of the Rehabilitation Act of 1973, as amended, and the regulations at 41 CFR Parts 60-1 through 60-60, 60-250,
and 60-741. The affirmative action clause and regulations contained in the preceding sentence shall be incorporated
by reference.
1

Contents
Installation instructions
Installing Adobe AIR

Setting up for Flex Builder 3

Setting up the Flex 3 SDK

Getting started
Introducing Adobe AIR

Creating your first Flex AIR application in Flex Builder

Creating your first AIR application with the Flex SDK

Using the AIR development tools


Developing AIR applications with Flex Builder

Creating an AIR application using the command line tools


Compiling an AIR application with the amxmlc compiler
Compiling an AIR component or library with the acompc compiler
Debugging using the AIR Debug Launcher
Exporting an AIR installation file using the AIR Developer Tool
Creating a self-signed certificate with ADT
Using Apache Ant with the AIR SDK tools

Using the Flex AIR components


About file system controls
About the HTML control
About window containers

Application development essentials


AIR Security
Installation and Updates
Sandboxes
Writing to disk
Working securely with untrusted content
Best security practices for developers
Code Signing
2

Setting application properties

Working with windows and menus


Working with windows
AIR window basics
Creating windows
Manipulating windows
Listening for window events
Using full-screen window mode

Screens
Screen basics
Enumerating the screens

Working with native menus


AIR menu basics
Creating native menus

Working with files and data


Working with the file system
AIR file basics
Working with File objects
Getting file system information
Working with directories
Working with files
Using the encrypted local store

Drag and Drop


Drag and drop basics
Supporting the drag-out gesture
Supporting the drag-in gesture
HTML Drag and drop

Copy and Paste


Copy-and-paste basics
Reading from and writing to the system clipboard
Clipboard data formats

Working with local SQL databases


About local SQL databases
Common SQL database tasks
Creating and modifying a database
Using synchronous and asynchronous database operations
Strategies for working with SQL databases
3

Adding content to AIR applications


Adding HTML content to SWF-based applications
About the HTMLControl class
Loading HTML content from a URL
Loading HTML content from a string
Display properties of HTMLControl objects
Scrolling HTML content
Events dispatched by an HTMLControl object
Accessing the HTML history list from ActionScript
Cross-scripting between ActionScript and JavaScript
Accessing runtime classes and functions from JavaScript
The AIRAliases.js file
Defining browser-like user interfaces for HTML content
Creating subclasses of the HTMLControl class
Unsupported JavaScript functionality

Rendering PDF content in AIR applications

Interacting with the operating system


Interacting with the operating system
Application invocation
Capturing command line arguments
Registering file types
Reading the application descriptor file
Getting the runtime version and patch level
Detecting AIR capabilities
Tracking user presence
Taskbar icons
Application termination

Networking and communications


Monitoring network connectivity

URL requests
Using the URLRequest class
Changes to the URLStream class
Opening a URL in the default system web browser

Scripting and communications between applications and content


Using the LocalConnection class in AIR applications
Scripting between content in different domains
4

Distributing and updating applications


Distributing AIR applications
Digitally signing an AIR file
Distributing and installing using the seamless install feature
Distributing and installing an AIR file without using the seamless install feature

Updating AIR applications programmatically

New functionality in Adobe AIR


New runtime classes
Runtime classes with new functionality
New Flex components
Service monitoring framework classes

AIR Quick Starts


Building the quick-start sample applications with Flex

Building a text-file editor

Building a directory search application

Reading and writing from an XML preferences file

Interacting with a window

Creating a transparent window application

Launching native windows

Dragging, copying, and pasting data

Building a JPEG file uploader

Adding native menus to an AIR application

Creating toast-style windows

Controlling the display order of windows

Creating resizable, non-rectangular windows

Measuring the virtual desktop

Using the system tray and dock icons

Creating and working with a local SQL database

Compressing files and data

Index
5

Part 1: Installation instructions


Installing Adobe AIR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6
Setting up for Flex Builder 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8
Setting up the Flex 3 SDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10
6

Chapter 3: Installing Adobe AIR


Adobe AIR allows you to build rich Internet applications that run on the desktop. To begin, you must install the
runtime on your computer. Once you've installed the runtime, download the sample applications to see AIR in
action and to evaluate the underlying code.

Install the runtime


Use the following instructions to download and install the Windows and Mac OS X versions of AIR. You only need
to install the runtime once for each computer that will run AIR applications.

Install the runtime on a Windows computer


1 Download the runtime installation file (AIR1_win_beta2.exe) from Adobe Labs
(http://labs.adobe.com/downloads/air.html).
2 Double-click the AIR1_win_beta2.exe file.
3 In the installation window, follow the prompts to complete the installation.
4 If you have created or modified the mms.cfg file used by Flash Player, remove it while running AIR applications.
On Windows, the file is located in the Macromedia Flash Player folder within the system directory (for example,
C:\winnt\system32\macromed\flash\mms.cfg on a default Windows XP installation).

Install the runtime on Mac OS


1 Download the runtime installation file (AIR1_mac_beta2.dmg) from Adobe Labs
(http://labs.adobe.com/downloads/air.html).
2 Double-click the AIR1_mac_beta2.dmg file.
3 In the installation window, follow the prompts to complete the installation.
4 If the Installer displays an Authenticate window, enter your Mac OS user name and password.
5 If you have created or modified the mms.cfg file used by Flash Player, remove it while running AIR applications.
On Mac OS, the file is located at /Library/Application Support/Macromedia/mms.cfg.

Install and run the AIR sample applications


The Beta 2 release of AIR includes a number of sample applications.
1 Download the sample AIR file from Adobe Labs (http://labs.adobe.com/technologies/air/samples/).
2 Double-click the AIR file.
3 In the Installation window, select installation options, and then click Continue.
4 After the installation is complete, open the application:
• On Windows, double-click the application's icon on the desktop or select it from the Windows Start menu.
ADOBE PRODUCT X.0 7
User Guide

• On Mac OS, double-click the application's icon, which is installed in the Applications sub-directory of your user
directory (for example, in Macintosh HD/Users/JoeUser/Applications/) by default.
The application opens in the runtime environment.
Note: Check the AIR Beta 2 release notes for updates to these instructions, which are located here:
http://labs.adobe.com/wiki/index.php/AIR:Release_Notes.

Run an AIR application


Once you have installed the runtime and the AIR application you want to run, running the application is as simple
as running any other desktop application:
• On a Windows computer, double-click the application's icon (which may be installed on the desktop or in a
folder), or select the application from the Start menu.
• On Mac OS, double-click the application in the folder in which it was installed. The default installation directory
is the Applications subdirectory of the user directory.
8

Chapter 4: Setting up for Flex Builder 3


To develop Adobe® AIR™ applications with Flex, you have the following options:
• You can download and install Adobe Flex Builder 3, which provides integrated tools to create Adobe AIR
projects and test, debug, and package your AIR applications.
• You can download the Adobe Flex 3 SDK and develop Flex AIR applications using your favorite text editor and
the command-line tools. See “Setting up the Flex 3 SDK” on page 10.
This topic describes how to download, install, and set up Flex Builder 3.

Downloading and installing Flex Builder 3


Flex Builder 3 (currently in beta release) provides integrated support for developing AIR applications. This means
that you can use it to develop Flex-based AIR applications without having to download or install any additional plug-
ins. If you haven’t already done so, follow these instructions to download and install the beta version of Flex Builder
3.
Adobe AIR support in Flex Builder includes the following:
• A wizard for creating new AIR application projects
• Automatic creation and management of the application.xml file
• Running and debugging of AIR applications from Flex Builder
• Automated exporting of your AIR application project to an AIR file for distribution to users

Download and install Flex Builder 3


1 The beta release version of Flex Builder 3 is available on Adobe Labs (http://labs.adobe.com/technol-
ogies/flex/flexbuilder3/). Download the installation file. Flex Builder 3 is available for both Windows and the Mac
OS.
2 After you’ve downloaded the installation file, begin the Flex Builder 3 installation process and follow the
prompts.
Note: If you already have an earlier version of Flex Builder installed, you can either install the beta version over it (not
recommended) or you can install Flex Builder 3 separately and use it to develop AIR applications during this beta phase.

Moving from Flex Builder 2.0.1 to Flex Builder 3


AIR applications developed using Flex Builder 2.0.1 and the Alpha version of AIR (referred to by its code name
Apollo), are not supported in Flex and Flex Builder 3.

Use Flex Builder to port AIR applications to the new version


1 Create an AIR project in Flex Builder. See “Developing AIR applications with Flex Builder” on page 1.
ADOBE PRODUCT X.0 9
User Guide

2 Copy and paste the application code from your old Apollo project’s main MXML file into the new AIR project
MXML file. Note that the AIR application container (ApolloApplication) has been changed to WindowedAppli-
cation.
3 Add your project’s assets into the new project using the Flex Builder project navigator.
4 Update any remaining code that refers to the ApolloApplication container.

Use the Flex SDK to port AIR applications to the new version
1 Recreate the application.xml file. For details of this file format, see “Setting application properties” on page 62.
2 Update your code with the new name for the AIR application container (WindowedApplication).

See also
• “Creating your first Flex AIR application in Flex Builder” on page 15
• “Developing AIR applications with Flex Builder” on page 25
• “Using the Flex AIR components” on page 40
10

Chapter 5: Setting up the Flex 3 SDK


To develop Adobe® AIR™ applications with Flex, you have the following options:
• You can download and install Adobe Flex Builder 3, which provides integrated tools to create Adobe AIR
projects and test, debug, and package your AIR applications. See “Setting up for Flex Builder 3” on page 8.
• You can download the Adobe Flex 3 SDK and develop Flex AIR applications using your favorite text editor and
the command-line tools.
This topic describes how to download, install, and set up the Flex SDK 3.
Each of the command-line tools you use to create an Adobe AIR application calls the corresponding tool used to
build Flex applications:
• amxmlc calls mxmlc to compile application classes

• acompc calls compc to compile library and component classes

The only difference between the Flex and the AIR versions of the utilities is that the AIR versions load the configu-
ration options from the air_config.xml file instead of the flex_config.xml file.
The Flex SDK tools and their command-line options are fully described in Building and Deploying Flex 3
Applications (http://www.adobe.com/go/learn_flex3_building) in the Flex 3 documentation library. The Flex SDK
tools are described here at a basic level to help you get started and to point out the differences between building Flex
and building AIR applications.

Install and configure the Flex 3 SDK


Building AIR applications with the command-line tools requires that Java be installed on your computer. You can
use the Java virtual machine from either the JRE or the JDK (version 1.4.2 or newer). The Java JRE and JDK are
available at http://java.sun.com.
Note: Java is not required for end users to run AIR applications.
The Flex 3 SDK provides you with the AIR API and command-line tools that you use to package, compile, and debug
your AIR applications.
1 If you haven't already done so, download the Flex 3 SDK, which is available on Adobe Labs.
2 Place the contents of the SDK into a folder (for example, Flex 3 SDK).
3 The command line utilities are located in the bin folder.

Compiler setup
You typically specify compilation options both on the command line and with one or more configuration files. The
global Flex SDK configuration file contains default values that are used whenever the compilers are run. You can edit
this file to suit your own development environment. There are two global Flex configuration files located in the
frameworks directory of your Flex 3 SDK installation. The air_config.xml file is used when you run the amxmlc
compiler. This file configures the compiler for AIR by including the AIR libraries. The flex_config.xml file is used
when you run mxmlc.
ADOBE PRODUCT X.0 11
User Guide

The default configuration values are suitable for discovering how Flex and AIR work, but you should examine the
available options more closely when you embark on a full-scale project. You can supply project-specific values for
the compiler options in a local configuration file that will take precedence over the global values for a given project.
For a full list of the compilation options and for the syntax of the configuration files, see About configuration
files (http://livedocs.macromedia.com/flex/2/docs/00001490.html) in the Flex 2 documentation library.
Note: No compilation options are used specifically for AIR applications, but you do need to reference the AIR libraries
when compiling an AIR application. Typically, these libraries are referenced in a project-level configuration file, in a tool
for a build tool such as Ant, or directly on the command line.

Debugger setup
AIR supports debugging directly, so you do not need a debug version of the runtime (as you would with Flash
Player). To conduct command-line debugging, you use the Flash Debugger and, optionally, the AIR Debug
Launcher.
The Flash Debugger is distributed in the Flex 3 SDK directory. The native versions, for example fdb.exe on Windows,
are in the bin subdirectory. The Java version is in the lib subdirectory. The AIR Debug Launcher, adl.exe or ADL, is
in the bin directory of your Flex SDK installation (there is no separate Java version).
Note: You cannot start an AIR application directly with FDB, because FDB attempts to launch it with Flash Player.
Instead, you must let the AIR application connect to a running FDB session.

Application packager setup


The AIR Developer Tool (ADT), which packages your application into an installable AIR file, is a Java program. No
setup is required other than setting up your environment so that you can conveniently run the utility.
The SDK includes a script file in the SDK bin directory for executing ADT as a command. You can also run ADT as
a Java program, which may be convenient when using build tools such as Apache Ant.

See also
• “Creating your first AIR application with the Flex SDK” on page 19
• “Creating an AIR application using the command line tools” on page 28
• “Debugging using the AIR Debug Launcher” on page 30
• “Exporting an AIR installation file using the AIR Developer Tool” on page 32
12

Part 1: Getting started


Introducing Adobe AIR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13
Creating your first Flex AIR application in Flex Builder. . . . . . . . . . . . . . . . . . . . . . . .15
Creating your first AIR application with the Flex SDK . . . . . . . . . . . . . . . . . . . . . . . . .19
13

Chapter 1: Introducing Adobe AIR


Adobe® AIR™ is a cross-operating system runtime that allows you to leverage your existing web development skills
(Flash, Flex, HTML, JavaScript, Ajax) to build and deploy Rich Internet Applications (RIAs) to the desktop.
AIR enables you to work in familiar environments, to leverage the tools and approaches you find most comfortable,
and by supporting Flash, Flex, HTML, JavaScript, and Ajax, to build the best possible experience that meets your
needs.
For example, applications can be developed using one or a combination of technologies below:
• Flash / Flex / ActionScript
• HTML / JavaScript / CSS / Ajax
• PDF can be leveraged with any application
As a result, AIR applications can be:
• Based on Flash or Flex: Application whose root content is Flash/Flex (SWF)
• Based on Flash or Flex with HTML or PDF. Applications whose root content is Flash/Flex (SWF) with HTML
(HTML, JS, CSS) or PDF content included
• HTML-based. Application whose root content is HTML, JS, CSS
• HTML-based with Flash/Flex or PDF. Applications whose root content is HTML with Flash/Flex (SWF) or PDF
content included
Users interact with AIR applications in the same way that they interact with native desktop applications. The runtime
is installed once on the user's computer, and then AIR applications are installed and run just like any other desktop
application.

Since AIR is an application runtime, it has little or no visible user interface and you have complete control over the
application and the experience it provides to users. The runtime provides a consistent cross-operating system
platform and framework for deploying applications and therefore eliminates cross-browser testing by ensuring
consistent functionality and interactions across desktops. Instead of developing for a specific operating system, you
target the runtime. This has a number of benefits:
• Applications developed for AIR run across multiple operating systems without any additional work by you. The
runtime ensures consistent and predictable presentation and interactions across all the operating systems supported
by AIR.
• Applications can be built faster by enabling you to leverage existing web technologies and design patterns. This
allows you to extend your web based applications to the desktop without learning traditional desktop development
technologies or the complexity of native code. This is easier than using lower level languages such as C and C++, and
does away with the need to learn complex low-level APIs specific to each operating system.
When developing applications for AIR, you can leverage a rich set of frameworks and APIs:
• APIs specific to AIR provided by the runtime and the AIR framework
• ActionScript APIs used in SWF files and Flex framework (as well as other ActionScript based libraries and
frameworks)
ADOBE PRODUCT X.0 14
User Guide

AIR delivers a new paradigm that dramatically changes how applications can be created, deployed, and experienced.
You gain more creative control and can extend your Flash, Flex, HTML, and Ajax-based applications to the desktop,
without learning traditional desktop development technologies.
15

Chapter 2: Creating your first Flex AIR


application in Flex Builder
For a quick, hands-on illustration of how Adobe® AIR™ works, use these instructions to create and package a simple
SWF file-based AIR “Hello World” application using Adobe Flex Builder 3.
If you haven’t already done so, download and install Flex Builder 3. For more information, see “Setting up the Flex
3 SDK” on page 10.

Run Flex Builder and create an AIR project


Flex Builder 3 includes the tools you need to develop and package AIR applications. You begin to create AIR appli-
cations in Flex Builder in the same way that you create other Flex-based application projects, by defining a new
project.

Create the project in Flex Builder


1 Open Flex Builder 3.
2 Select File > New > Flex Project.
3 Enter the project name as AIRHelloWorld.
4 In Flex, AIR applications are considered an application type. You have two type options: a Flex application that
runs on the Web in Flash Player and an AIR application that runs on the desktop in the Adobe AIR runtime. Select
Desktop Application as the application type.
5 You won’t be using a server technology, so select None and then click Next.
6 Select the folder in which you want your compiled application to be located. The default is the bin folder. Click
Finish to create the project.
CLOSE PROCEDURE
AIR projects initially consist of two files: the main MXML file and an application XML file (referred to as the appli-
cation descriptor file), the latter of which specifies parameters for identifying, installing, and launching AIR appli-
cations. There will be occasions when you will want to manually edit this file. For now, be aware that it exists.

Write the AIR application code


To write the “Hello World” application code, you edit the application's MXML file (AIRHelloWorld.mxml), which
should be open in the editor. If it isn't, use the Project Navigator to open the file.
All Flex AIR applications are contained within the MXML WindowedApplication tag, which creates a simple
window that includes basic window controls such as a title bar and close button.

Add the code


1 Add a title attribute to the WindowedApplication component, and assign it the value "Hello World":
ADOBE PRODUCT X.0 16
User Guide

<?xml version="1.0" encoding="utf-8"?>


<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
title="Hello World">

</mx:WindowedApplication>

2 Add a Label component to the application (place it inside the WindowedApplication tag), set the text property
of the Label component to "Hello AIR", and set the layout constraints to keep it centered, as shown here:
<?xml version="1.0" encoding="utf-8"?><?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
title="Hello World">
<mx:Label text="Hello AIR" horizontalCenter="0" verticalCenter="0"/>
</mx:WindowedApplication>

3 Add the following style block immediately after the opening WindowedApplication tag and before the label
component tag you just entered:
<mx:Style>
Application
{
background-image:"";
background-color:"";
background-alpha:"0.5";
}
</mx:Style>

These style settings apply to the entire application and render the window background a slightly transparent gray.
The entire application code should now look like the following:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
title="Hello World">
<mx:Style>
Application
{
background-image:"";
background-color:"";
background-alpha:"0.5";
}
</mx:Style>
<mx:Label text="Hello AIR" horizontalCenter="0" verticalCenter="0"/>
</mx:WindowedApplication>

CLOSE PROCEDURE

Test the AIR application


To test the application code that you’ve just written, you can run it in debug mode.
1 Click the Debug button in the main Flex Builder toolbar.
You can also select the Run > Debug > AIRHelloWorld command.
The resulting AIR application should look something like the following example (the green background is the
user's desktop):
ADOBE PRODUCT X.0 17
User Guide

2 Using the horizontalCenter and verrticalCenter properties of the Label control, the text is placed in the center of
the window. Move or resize the window as you would any other desktop application.
Note: If the application does not compile, fix any syntax or spelling errors that you may have inadvertently entered into
the code. Errors and warnings are displayed in the Problems view in Flex Builder.
CLOSE PROCEDURE

Package, sign, and run your AIR application


You are now ready to use Flex Builder to package the "Hello World" application into an AIR file for distribution. An
AIR file is an archive file that contains the application files (all of the files contained in the project's bin folder - in
this simple example, the SWF and application XML files). You distribute the AIR package to users who then use it
to install the application. A required step in this process is to digitally sign it.

Packaging an AIR application


1 Ensure that the application has no compile errors and runs as expected.
2 Select Project > Export Release Version.
3 If you have multiple projects and applications open in Flex Builder, you may have to select the specific AIR
project you want to package.
4 Select the Export and Sign an AIR File with a Digital Certificate option.
5 If you have an existing digital certificate, click Browse to locate and select it.
6 If you need to create a new self-signed digital certificate, select Create.
7 Enter the required information and click OK.
8 Click Finish to generate the AIR package, which will be named AIRHelloWorld.air.
ADOBE PRODUCT X.0 18
User Guide

You can now run the application from the Project Navigator in Flex Builder or from the file system, as your users
would, by double-clicking the AIR file.
CLOSE PROCEDURE

See also
• “Developing AIR applications with Flex Builder” on page 25
• “Using the Flex AIR components” on page 40
19

Chapter 3: Creating your first AIR


application with the Flex SDK
For a quick, hands-on illustration of how AIR works, use these instructions to create a simple SWF-based AIR "Hello
World" application using the Flex SDK. You will learn how to compile, test, and package an AIR application with the
command-line tools provided with the SDK.
If you haven’t already done so, download and install the Flex 3 SDK. For more information, see “Setting up the Flex
3 SDK” on page 10.

Create the application XML file


Each AIR application requires an application descriptor file. This XML file defines various properties of the appli-
cation, and is embedded in the AIR package that is distributed to users.
1 Create an XML file named HelloAIR-app.xml with the following structure:
<application>
<name>…</name>
<initialWindow>
<content>…</content>
<visible>…</visible>
</initialWindow>
</application>

2 Set the following attributes for the <application> element:


appId="samples.HelloAIR" Uniquely identifies your application. The recommended form is a dot-delimited,
reverse-DNS-style string, such as "com.company.AppName". The application id is used for installation, access
to the private application file-system storage directory, access to private encrypted storage, and interapplication
communication.
Note: In this beta release of AIR, if two applications use the same appId value, installing the second application
overwrites the first application.
xmlns="http://ns.adobe.com/air/application/1.0.M5" The AIR namespace. The last segment of the
namespace specifies the version of the runtime required by the application.
version="0.1" Helps users to determine which version of your application they are installing.

3 Add the <name> element:


<name>Hello AIR!</name> Identifies your application to users.

4 Set the following elements in <initialWindow> to specify how your application window will be displayed:
<visible>true</true> Makes the window visible when it opens. In some cases, you might want to initialize
the visible element to false so that you can alter the window before showing it to your user.
<content>HelloAIR.html</content> Identifies the root html file for AIR to load.

5 Save the file. Your complete application descriptor file should look like this:
<?xml version="1.0" encoding="UTF-8"?>
ADOBE PRODUCT X.0 20
User Guide

<application xmlns="http://ns.adobe.com/air/application/1.0.M5"
appId="samples.HelloWeb" version="0.1">
<name>Hello AIR!</name>
<initialWindow>
<content>HelloAIR.html</content>
<visible>true</visible>
</application>

See also
• “Setting application properties” on page 62

Write the application code


Note: SWF-based AIR applications can use a main class defined either with MXML or with ActionScript. This example
uses an MXML file to define its main class. The process for creating an AIR application with a main ActionScript class
is similar. Instead of compiling an MXML file into the SWF, you compile the ActionScript class file. The main Action-
Script class must extend the flash.display.Sprite class.
Like all Flex applications, AIR applications built with the Flex framework contain a main MXML file. AIR applica-
tions, however, can use the WindowedApplication component as the root element instead of the Application
component. The WindowedApplication component provides the window and basic window control chrome needed
to run your application on the desktop. The following procedure creates the Hello World application.
1 Using a text editor, create a file named AIRHelloWorld.mxml and add the following MXML code:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" title="Hello World">
</mx:WindowedApplication>

2 Next, add a Label component to the application (place it inside the WindowedApplication tag) and set the text
property of the Label component to "Hello AIR" and set the layout constraints to always keep it centered, as shown
here:
<?xml version="1.0" encoding="utf-8"?><?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
title="Hello World">
<mx:Label text="Hello AIR" horizontalCenter="0" verticalCenter="0"/>
</mx:WindowedApplication>

3 Add the following style block:


<mx:Style>
Application
{
background-image:"";
background-color:"";
background-alpha:"0.5";
}
</mx:Style>

These style settings apply to the entire application and render the window background a slightly transparent gray.
The entire application code now looks like this:
<?xml version="1.0" encoding="utf-8"?>
ADOBE PRODUCT X.0 21
User Guide

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"


title="Hello World">
<mx:Style>
Application
{
background-image:"";
background-color:"";
background-alpha:"0.5";
}
</mx:Style>
<mx:Label text="Hello AIR" horizontalCenter="0" verticalCenter="0"/>
</mx:WindowedApplication>

Compile the application


Before you can run and debug the application, you must compile the MXML into a SWF file. (Make sure you have
already added the AIR command-line tools to your class path.)
1 Open a command prompt in Windows or a terminal in the Mac OS and navigate to the source location of your
AIR application.
2 Enter the following command:
amxmlc AIRHelloWorld.mxml

Note: If the application does not compile, fix syntax or spelling errors. Errors and warnings are displayed in the console
window used to run the amxmlc compiler.

See also
• “Compiling an AIR application with the amxmlc compiler” on page 28

Test the application


To run and test the application from the command line, use the AIR Debug Launcher (ADL) to launch the appli-
cation using its application descriptor file.
❖ From the command prompt, enter the following command:
adl AIRHelloWorld-app.xml

The resulting AIR application looks something like this (the green background is the user's desktop):
ADOBE PRODUCT X.0 22
User Guide

Using the horizontalCenter and verticalCenter properties of the Label control, the text is placed in the center of the
window. Move or resize the window as you would any other desktop application.

See also
• “Debugging using the AIR Debug Launcher” on page 30

Create the AIR installation file


When your application runs successfully, you can use ADT to package the application into an AIR installation file
for distribution or testing from the desktop. An AIR installation file is an archive file that contains all the application
files. You can distribute the AIR file to users.
All AIR installation files must be signed. For development purposes, you can generate a basic self-signed certificate
with ADT or another certificate generation tool, or you can buy a commercial code-signing certificate from VeriSign
or Thawte. When you use a self-signed AIR file, the publisher will be displayed as “unverified” in the installation
process. This is because a self-signed certificate only guarantees that the AIR file has not been changed since it was
created. There is nothing to prevent someone from self-signing a masquerade AIR file and presenting it as your
application. For publicly released AIR files, a verifiable, commercial certificate is strongly recommended.
When you create a self-signed certificate with ADT, the tool generates a random key. Because of this, no two certif-
icates generated by ADT will ever be the same. If you wish to create an update for an application signed with an ADT
certificate, you must use the same certificate file to sign the update.
Note: this Beta release of AIR does not use the certificate to help establish the identity of an application, but a future
version will. If you are planning to release a beta version of your AIR application, you should sign it with a commercial
certificate now, so that it will be possible for users to update to the release version when AIR 1.0 is released.
ADOBE PRODUCT X.0 23
User Guide

To generate a self-signed certificate:


❖ From the command prompt, enter the following command:
adt –certificate -cn SelfSigned 1024-RSA sampleCert.pfx samplePassword

This example uses the minimum number of attributes that can be set for a certificate. You can use any values for the
parameters in italics. The key type must be either 1024-RSA or 2048-RSA.

To create the AIR installation file:


❖ From the command prompt, enter the following command (on a single line):
adt –package -certificate sampleCert.pfx -password samplePassword AIRHelloWorld.air
AIRHelloWorld-app.xml AIRHelloWorld.swf

The HelloAIR.air argument is the AIR file that ADT produces. HelloAIR-app.xml is the application descriptor
file. The subsequent arguments are the files used by your application. This example only uses a single file, but you
can include any number of files and directories.
After the AIR package is created, you can double-click, ctrl-click, or type the AIR filename as a command in the shell
to install and run the application.

See also
• “Exporting an AIR installation file using the AIR Developer Tool” on page 32
24

Part 1: Using the AIR development tools


Developing AIR applications with Flex Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25
Creating an AIR application using the command line tools . . . . . . . . . . . . . . . . . . . . .28
Using the Flex AIR components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .40
25

Chapter 5: Developing AIR applications


with Flex Builder
Adobe Flex Builder 3 provides you with the tools you need to create Adobe AIR projects, work with the Flex AIR
components, and debug and package your Adobe AIR applications. The application development workflow for
developing Flex-based applications in Flex Builder is similar to typical Flex applications.
Flex Builder provides the following support for developing AIR applications:
• Create AIR projects
• Debug AIR projects
• Package AIR applications for distribution
• Allow users to view the application’s source code
• Digitally sign your AIR applications
• Create AIR library projects
• Use the Flex AIR components to build user interfaces

Creating AIR projects with Flex Builder


If you have not already done so, install AIR and Flex Builder 3.
1 Open Flex Builder 3.
2 Select File > New > Flex Project.
3 Enter the project name.
4 In Flex, AIR applications are considered an application type. You have two type options: a Flex application that
runs on the Web in Flash Player and an AIR application that runs on the desktop in the Adobe AIR runtime. Select
Desktop Application as the application type.
5 Select the server technology (if any) that you want to use with your AIR application. If you’re not using a server
technology, select None and then click Next.
6 Select the folder in which you want your compiled application to be located. The default is the bin folder. Click
Next.
7 Modify the source and library paths as needed and then click Finish to create your AIR project.
CLOSE PROCEDURE

See also
• “Setting up for Flex Builder 3” on page 8
• “The application descriptor file structure” on page 62
ADOBE PRODUCT X.0 26
User Guide

Debugging AIR applications with Flex Builder


Flex Builder provides full debugging support for AIR applications. For more information about the debugging
capabilities of Flex Builder, refer to Flex Builder Help.
1 Open a source file for the application (such as an MXML file) in Flex Builder.
2 Click the Debug button on the main toolbar .
You can also select Run > Debug.
The application launches and runs in the ADL application (the AIR Debugger Launcher). The Flex Builder debugger
catches any breakpoints or runtime errors and you can debug the application like any other Flex application.
You can also debug an application from the command line, using the AIR Debug Launcher command-line tool.
CLOSE PROCEDURE

See also
• “Debugging using the AIR Debug Launcher” on page 30

Packaging AIR applications with Flex Builder


When your application is complete and ready to be distributed (or tested running from the desktop), you package it
into an AIR file. Packaging consists of the following steps:
• Selecting the AIR application you want to publish
• Optionally allowing users to view the source code and then selecting which of your application’s files to include
• Digitally signing your AIR application using a Verisign or Thwate digital certificate or by creating and applying
a self-signed signature
• Optionally choose to create an intermediate AIR file, which can be signed at a later time

Packaging an AIR application


1 Open the project and ensure that the application has no compile errors and runs as expected.
2 Select Project > Export Release Version.
3 If you have multiple projects and applications open in Flex Builder, you may have to select the specific AIR
project you want to package.
4 Optionally select Enable View Source if you want users to be able to see the source code when they run the appli-
cation. You may select individual files to exclude by selecting Choose Source Files. By default all the source files are
selected. For more information about publishing source files in Flex Builder, see the Flex Builder Help.
5 You may also optionally change the name of the AIR that will be generated. When you’re ready to continue, click
Next to digitally sign your application.

Digitally signing your AIR applications


Before continuing with the Export Release Version, you must decide how you will digitally sign your AIR appli-
cation. You have several options. You can sign the application using a Verisign or Thwate digital certificate, you can
create and use a self-signed digital certificate, or you can choose to package the application now and sign it later.
ADOBE PRODUCT X.0 27
User Guide

Digital certificates provided by VeriSign and Thwate give your users some assurance as to your identity as a publisher
and verification that the installation file has not been altered since you signed it. Self-signed digital certificates serve
the same purpose but they are not validated by a third-party.
You also have the option of packaging your AIR application without a digital signature by creating an intermediate
AIR file (.airi). An intermediate AIR file is not valid in that it cannot be installed. It is instead used for testing (by the
developer) and can be launched using the AIR ADT command line tool. This capability is provided because in some
development environments signing is handled by a particular developer or team, which insures an additional level
of security in managing digital certificates.
For more information about signing applications, see “Digitally signing an AIR file” on page 206.

Digitally sign your AIR application


1 Continuing from the Digital Signature step of the Export Release Version wizard, you can digitally sign your AIR
application by selecting an existing digital certificate or by creating a new self-signed certificate. Select the Export
and Sign an AIR File with a Digital Certificate option.
2 If you have an existing digital certificate, click Browse to locate and select it.
3 If you need to create a new self-signed digital certificate, select Create.
4 Enter the required information and click OK.
5 Click Next to optionally select files to exclude from the exported AIR file. By default, all the files are included.
6 Click Finish to generate the AIR file.

Create an intermediate AIR file


• Select Export an Intermediate AIRI File that will be Exported Later option. Click Finish to generate the interme-
diate file.
After you have generated an intermediate AIR file, it can be signed using the ADT command line tool. For more
information, see “Signing an AIR intermediate file with ADT” on page 34.

See also
• “Exporting an AIR installation file using the AIR Developer Tool” on page 32

Create an AIR Library project


To create an AIR code library that can be used by multiple AIR projects, you create an AIR library project using the
standard Flex library project wizard.
1 Select File > New > Flex Library Project.
2 Specify a project name.
3 Select the Add Adobe AIR Libraries and then click Next.
Note: The Flex SDK version you select must support AIR. The Flex 2.0.1 SDK does not.
4 Modify the build path as needed and then click Finish. For more information about creating library projects, see
“About library projects” in the Flex Builder Help.
28

Chapter 6: Creating an AIR application


using the command line tools
The Adobe AIR command-line tools provide an alternative to Flex Builder for compiling, debugging, and packaging
Adobe AIR applications. You can also use these tools in automated build processes. The command-line tools are
included in both the Flex and AIR SDKs.
This section contains the following topics:
• Compiling an AIR application with the amxmlc compiler
• Compiling an AIR component or library with the acompc compiler
• Debugging using the AIR Debug Launcher
• Exporting an AIR installation file using the AIR Developer Tool
• Creating a self-signed certificate with ADT
• Using Apache Ant with the AIR SDK tools

Compiling an AIR application with the amxmlc


compiler
You can compile the ActionScript and MXML assets of your AIR application with the command-line MXML
compiler (amxmlc):
amxmlc [compiler options] -- MyAIRApp.mxml

where [compiler options] specifies the command-line options used to compile your AIR application.
The amxmlc command invokes mxmlc with an additional parameter, +configname=air, which instructs the
compiler to use the air-config.xml file instead of the flex_config.xml file. Using amxmlc is otherwise identical to
using mxmlc. The mxmlc compiler and the configuration file format are described in Building and Deploying Flex
2 Applications in the Flex 2 documentation library.
The compiler loads the air-config.xml configuration file specifying the AIR and Flex libraries typically required to
compile an AIR application. You can also use a local, project-level configuration file to override or add additional
options to the global configuration. Typically the easiest way to create a local configuration file is to edit a copy of
the global version. You can load the local file with the -load-config option:
-load-config=project-config.xml Overrides global options.

-load-config+=project-config.xml Adds additional values to those global options that take more than value,
such as the -library-path option. Global options that only take a single value are overridden.
You can also use a special naming convention for the file and the amxmlc compiler will load the local configuration
file automatically. For example, if your application’s main MXML file is RunningMan.mxml, then name the config-
uration file: RunningMan-config.xml. To compile the application you only need to type:
amxmlc RunningMan.mxml

Note: The compiler commands use relative paths to find configuration files and executables. See “Setting up the Flex 3
SDK” on page 10
ADOBE PRODUCT X.0 29
User Guide

Examples

The following examples demonstrate use of the amxmlc compiler. (Only the ActionScript and MXML assets of your
application need to be compiled.)
Compile an AIR MXML file:
amxmlc myApp.mxml

Compile and set the output name:


amxmlc –output anApp.swf -- myApp.mxml

Compile an AIR ActionScript file:


amxmlc myApp.as

Specify a compiler configuration file:


amxmlc –load-config config.xml -- myApp.mxml

Add additional options from another configuration file:


amxmlc –load-config+=moreConfig.xml -- myApp.mxml

Add an additional libraries on the command line (to those already in the configuration file):
amxmlc –library-path+=/libs/libOne.swc,/libs/libTwo.swc -- myApp.mxml

Compile an AIR MXML file without using a configuration file (Win):


mxmlc -library-path [AIR SDK]/frameworks/libs/air/airframework.swc, ^
[AIR SDK]/frameworks/libs/air/airframework.swc, ^
-library-path [Flex 2 SDK]/frameworks/libs/framework.swc ^
-- myApp.mxml

Compile an AIR MXML file without using a configuration file (Mac OS X):
mxmlc -library-path [AIR SDK]/frameworks/libs/air/airframework.swc, \
[AIR SDK]/frameworks/libs/air/airframework.swc, \
-library-path [Flex 2 SDK]/frameworks/libs/framework.swc \
-- myApp.mxml

Compile an AIR MXML file to use a runtime-shared library


amxmlc -external-library-path+=../lib/myLib.swc -runtime-shared-libraries=myrsl.swf --
myApp.mxml

Compiling from Java (with the class path set):


java flex2.tools.Compiler +flexlib [Flex SDK 2]/frameworks +configname=air [additional
compiler options] -- myApp.mxml

The flexlib option identifies the location of your Flex SDK frameworks directory, enabling the compiler to locate the
flex_config.xml file.
Compiling from Java (without the class path set):
java -jar [Flex SDK 2]/lib/mxmlc.jar +flexlib [Flex SDK 2]/frameworks +configname=air
[additional compiler options] -- myApp.mxml

Compiling an AIR component or library with the


acompc compiler
Use the component compiler, acompc, to compile AIR libraries and independent components. The component
compiler behaves like the amxmlc compiler, with the following exceptions:
• You must specify which classes within the code base to include in the library or component.
ADOBE PRODUCT X.0 30
User Guide

• acompc does not look for a local configuration file automatically. To use a project configuration file, you must
use the –load-config option.
acompc is identical to compc, except that it loads configuration options from the air-config.xml file instead of the
flex_config.xml file.

Component Compiler Configuration File


You can use a compiler configuration file with the acompc compiler by specifying the path to a local config file with
the -load-config option. By adding the path to the source files and specify the classes to include within the configu-
ration file you avoid having to type them on the command line.
The following example illustrates a configuration for building a library with two classes, ParticleManager and
Particle, both in the ActionScript package, com.adobe.samples.particles, and located in the
source/com/adobe/samples/particles folder (relative to the working directory when the compiler is run).
<flex-config>
<compiler>
<source-path>
<path-element>source</path-element>
</source-path>
</compiler>
<include-classes>
<class>com.adobe.samples.particles.ParticleManager</class>
<class>com.adobe.samples.particles.Particle</class>
</include-classes>
</flex-config>

To enter the same command entirely on the command line, type:


acompc -source-path source -include-classes com.adobe.samples.particles.Particle
com.adobe.samples.particles.ParticleManager

(Type the entire command on one line, or use the line continuation character for your command shell.)

Examples

These examples assume you are using a configuration file.


Compile an AIR component or library:
acompc -load-config myLib-config.xml -output lib/myLib.swc

Compile a runtime-shared library:


acompc -load-config myLib-config.xml -directory -output lib

(Note, the folder lib must exist and be empty before running the command.)
Reference a runtime-shared library:
acompc -load-config myLib-config.xml -output lib/myLib.swc

Debugging using the AIR Debug Launcher


Use the AIR Debug Launcher (ADL) to run both Flex-based and HTML-based applications during development.
Using ADL, you can run an application without first packaging and installing it. The runtime does not need to be
installed to use ADL (just the AIR SDK).
ADOBE PRODUCT X.0 31
User Guide

The debugging support provided by ADL is limited to the printing of trace statements. If you're developing a Flex-
based application, use the Flash Debugger (or Flex Builder) for complex debugging issues in SWF-based applica-
tions.

Launch an application with ADL


❖ Use the following syntax:
adl [-runtime runtime-directory] [-nodebug] application.xml [root-directory] [-- arguments]

-runtime runtime-directory Specify the directory containing the runtime to use. If not specified, the runtime
directory in the same SDK as the ADL program will be used. (If ADL is moved out of its SDK folder, then the runtime
directory must always be specified.) On Windows, the directory to specify is typically Adobe AIR. On Mac OSX, the
directory to specify is typically Adobe AIR.framework.
-nodebug Turns off debugging support. If used, the AIR application cannot connect to the Flash debugger and
dialogs for unhandled exceptions will be suppressed. Trace statements are still printed to the console window.
Turning off debugging will allow your application to run a little faster and will also more closely match the execution
mode of an installed application.
application.xml The application descriptor file.

root-directory The root directory of the application to run. If not specified, the directory containing the appli-
cation descriptor file is used.
-- arguments Any character strings appearing after "--" are passed to the application as command-line arguments.

Note: When you launch an AIR application that is already running, a new instance of that application is not started.
Instead, an invoke event is dispatched to the running instance. In this case, the ADL root-directory and runtime param-
eters used for the subsequent launches must match those of the initial launch or the command-line arguments cannot be
delivered to the running instance of the application.

Print trace statements


❖ To print trace statements to the console used to run ADL, use the trace() function:
trace("debug message");

ADL Examples

Run an application in the current directory:


adl myApp-app.xml

Run an application in a subdirectory of the current directory:


adl source/myApp-app.xml release

Run an application and pass in two command-line arguments, "tick" and "tock":
adl myApp-app.xml -- tick tock

Run an application using a specific runtime:


adl -runtime /AIRSDK/runtime myApp-app.xml

Set breakpoints with the Flash Debugger


To debug a SWF-based AIR application with the Flash Debugger, you must start an FDB session and then launch a
debug version of your application. The debug version of a SWF file will automatically connect to a "listening" FDB
session.
1 Start FDB. The FDB program can be found in the bin directory of your Flex SDK folder.
ADOBE PRODUCT X.0 32
User Guide

The console displays the FDB prompt: <fdb>


2 Execute the Run command: <fdb>run [Enter]
3 In a different command or shell console, start a debug version of your application:
adl myApp-debug.xml
4 Using the FDB commands, set breakpoints as desired.
5 Type: continue [Enter]
6 Set any additional breakpoints.
7 Type: continue [Enter]

Exporting an AIR installation file using the AIR


Developer Tool
Export an AIR installation file for your application with the AIR Developer Tool (ADT). ADT creates installation
packages for both HTML-based and SWF-based AIR applications. (If you are using Flex Builder to create your appli-
cation, you can use the Flex Builder Export wizard to build the AIR file package.)
ADT is a Java program that you can run from the command line or a build tool such as Ant. The SDK includes
command-line scripts that execute the Java program for you. See “Setting up the Flex 3 SDK” on page 10 for infor-
mation on configuring your system to run the ADT tool.

Every AIR application must, at a minimum, have an application descriptor file and a main SWF or HTML file. Any
other application assets to be installed with the application must be packaged in the AIR file as well.
All AIR installer files must be signed using a digital certificate. You can use a code signing certificate from the
VeriSign or Thawte certificate authorities, or a self-signed certificate. A VeriSign or Thawte certificate provides users
of your application with some assurance as to your identity as publisher and verification that the installation file has
not been altered since you signed it. A self-signed certificate verifies that the AIR file has not been altered since it
was signed, but cannot verify the identity of the signer.
You can package and sign an AIR file in a single step, or you can create an intermediate, unsigned package in one
step, and sign the intermediate package to produce an installation file in a separate step.
Note: The settings in the application descriptor file determine the identity of an AIR application and its default instal-
lation path. See “The application descriptor file structure” on page 62 for more information.

Package and sign AIR file in one step:


❖ Use the following syntax (on a single command line):
adt -package [-certificate pfx_file] [-password password] air_file app_xml
[ file_or_dir | -C dir file_or_dir ... ] ...

pfx_file the certificate file. (You can use the adt -certificate option to generate a self-signing certificate before
creating an AIR file). If no certifcate is provided, then ADT will look for a file named air.pfx in the user directory
password the certificate password.

air_file The name of the AIR file to be created.


ADOBE PRODUCT X.0 33
User Guide

app_xml The path to the application descriptor file. No matter what name is assigned to the application descriptor
file, it will be renamed to “application.xml” in the package. The path can be specified relative to the current directory
or as an absolute path.
file_or_dir The files and directories to package in the AIR file. Any number of files and directories can be
specified, delimited by whitespace. If you list a directory, all files and subdirectories within, except hidden files, are
added to the package. (In addition, if the application descriptor file is specified, either directly, or through wildcard
or directory expansion, it is ignored and not added to the package a second time.) The files and directories specified
must be in the current directory or one of its subdirectories. Use the -C option to change the current directory.
Important: Wildcards cannot be used in the file_or_dir arguments following the –C option. (The command shell
expands wildcards before passing the arguments to ADT, which results in ADT looking for files in the original working
directory instead of the directory specified by the –C option.)
-C dir Changes the working directory to dir before processing subsequent files and directories added to the appli-
cation package. The files or directories are added to the root of the application package. The –C option can be used
any number of times to include files from multiple points in the file system. If a relative path is specified for dir, the
path is always resolved from the original working directory.
As ADT process the files and directories to be included in the package, the relative paths between the current
directory and the target files are stored. These paths are expanded into the application directory structure when the
package is installed. Thus specifying "-C release/bin lib/feature.swf" would place the file
"release/bin/lib/feature.swf " in the "lib" subdirectory of the root application folder.
Note: You must make sure that the <content> element of the application descriptor file specifies the final path to the
main application file relative to the root application directory. This may be an issue if the main application file is not
located in the current directory when you run ADT.

ADT Examples

Package specific application files in the current directory:


adt –package -certificate cert.pfx -password password myApp.air myApp.xml myApp.swf
components.swc

Package all files and subdirectories in the current working directory:


adt –package myApp.air myApp.xml .

Package only the main files and an images subdirectory:


adt –package –package -certificate cert.pfx -password password myApp.air myApp.xml myApp.swf
images

Package the application.xml file and main SWF located in a working directory (release\bin):
adt –package -certificate cert.pfx -password password myApp.air release\bin\myApp.xml –C
release\bin myApp.swf

The following example shows how to package assets from more than one place in your build file system.
Suppose the application assets are located in the following folders prior to packaging:
/devRoot
/myApp
/release
/bin
myApp.xml
myApp.swf
/artwork
/myApp
/images
image-1.png
...
ADOBE PRODUCT X.0 34
User Guide

image-n.png
/libraries
/release
/libs
lib-1.swf
...
lib-n.swf

The following ADT command is run from the /devRoot/myApp directory:


adt –package -certificate cert.pfx -password password myApp.air release/bin/myApp.xml –C
release/bin myApp.swf
–C ../artwork/myApp images –C ../audio

That command results in this package structure:


/myAppRoot
/META-INF
/AIR
application.xml
hash
myApp.swf
mimetype
/images
image-1.png
...
image-n.png
/libs
lib-1.swf
...

lib-n.swfRun ADT as a Java program (without setting the classpath):


java –jar {AIRSDK}\lib\ADT.jar –package -certificate cert.pfx -password password myApp.air
myApp.xml myApp.swf

Run ADT as a Java program (with the Java classpath set to include the ADT.jar package):
java com.adobe.air.ADT –package -certificate cert.pfx -password password myApp.air myApp.xml
myApp.swf

Creating an unsigned AIR intermediate file with ADT


When your workflow centralizes code signing and the management of certificates, you can create unsigned AIR
intermediate files and send them to the unit of your organization responsible for signing.
To create an unsigned AIR intermediate file with ADT, use the -prepare command instead of the -package command.
The -prepare command takes the same flags and parameters as the -package command (except for the certificate and
password arguments). The only difference is that the output file is not signed, and, so, cannot be installed. The inter-
mediate file will be generated with the file extension: airi.
To sign an AIR intermediate file, use the adt -sign command. (See Signing an AIR intermediate file with ADT.)
ADT Examples
adt –prepare unsignedMyApp.airi myApp.xml myApp.swf components.swc

Signing an AIR intermediate file with ADT


To sign an AIR intermediate file with ADT, use the -sign command. The signing command only works with AIR
intermediate files. A signed AIR file cannot be re-signed.
ADOBE PRODUCT X.0 35
User Guide

To create an unsigned AIR intermediate file, use the adt -prepare command. (See Creating an unsigned AIR inter-
mediate file with ADT.)

Sign an AIRI file:


Use the following syntax:
adt -sign -certificate pfx_file -password password airi_file air_file

pfx_file the certificate file. (You can use the adt -certificate option to generate a self-signing certificate before
creating an AIR file). If no certifcate is provided, then ADT will look for a file named air.pfx in the user directory
password the certificate password.

airi_file The path to the unsigned AIR intermediate file to be signed.

air_file The name of the AIR file to be created.

ADT Examples
adt –sign -certificate code_cert.pfx -password secretPassword unsignedMyApp.airi myApp.air

Creating a self-signed certificate with ADT


Self-generated certificates allow you to produce a valid AIR installation file, but do not provide any security assur-
ances to your users. When a self-signed AIR file is installed, the publisher information will be displayed to the user
as unknown. A certificate generated by adt will be valid for five years.
If you create an update for an AIR application that was signed with a self-generated certificate, you must use the same
certificate to sign both the original and update AIR files. The certificates that adt produces are always unique, even
if the same parameters are used. Thus, if you wish to self-sign updates with an adt-generated certificate, you must
take care to preserve the original certificate in a safe location. In addition, you will not be able to produce an update
AIR file after the original adt-generated certificate expires. (You can roll out new applications with a different certif-
icate, but not new versions of the same application.)
Important: Because of the limitations of self-signed certificates, Adobe strongly recommends using a commercial certif-
icate from VeriSign or Thawte for signing publicly released AIR applications.

Generating a digital ID certificate for self-signing AIR files:


❖ Use the following syntax (on a single command line):
adt -certificate -cn name [-ou org_unit][-o org_name][-c country] key_type pfx_file password

-cn name the string assigned as the common name of the new certificate.

-ou org_unit a string assigned as the organizational unit issuing the certificate. (Optional.)
-o org_name a string assigned as the organization issuing the certificate. (Optional.)
-c country a two-letter ISO-3166 country code. A certificate will not be generated if an invalid code is supplied.
(Optional.)
key_type the type of key to use for the certificate, either “1024-RSA” or “2048-RSA”.
pfx_file the path for the certificate file to be generated.

password the password for the new certificate. The password is required when signing AIR files with this certificate.
ADOBE PRODUCT X.0 36
User Guide

Certificate generation examples:


adt -certificate -cn SelfSign -ou QE -o "Example, Co" -c US 2048-RSA newcert.pfx 39#wnetx3tl
adt -certificate -cn ADigitalID 1024-RSA SigningCert.pfx 39#wnetx3tl

Using Apache Ant with the AIR SDK tools


This section provides examples of using the Apache Ant build tool to compile, test, and package AIR applications.
Note: this discussion does not attempt to provide a comprehensive outline of Apache Ant. For Ant documentation, see
http://Ant.Apache.org.

Using Ant for simple projects


This example illustrates building an AIR application using Ant and the AIR command-line tools. A very simple
project structure is used with all files stored in a single directory.
Note: this example assumes that you are using the separate AIR SDK rather than Flex Builder. The tools and configu-
ration files included with Flex Builder are stored in a different directory structure.
To make it easier to reuse the build script, these examples use several defined properties. One set of properties
identifies the installed locations of the command-line tools:
<property name="SDK_HOME" value="C:/AIRSDK"/>
<property name="MXMLC.JAR" value="${SDK_HOME}/lib/mxmlc.jar"/>
<property name="ADL" value="${SDK_HOME}/bin/adl.exe"/>
<property name="ADT.JAR" value="${SDK_HOME}/lib/adt.jar"/>

The second set of properties are project specific. These properties assume the naming convention that application
descriptor and AIR files are named based on the root source file, but other conventions are easily supported. The
properties also define the MXMLC debug parameter as true (by default).
<property name="APP_NAME" value="ExampleApplication"/>
<property name="APP_ROOT" value="."/>
<property name="MAIN_SOURCE_FILE" value="${APP_ROOT}/${APP_NAME}.mxml"/>
<property name="APP_DESCRIPTOR" value="${APP_ROOT}/${APP_NAME}-app.xml"/>
<property name="AIR_NAME" value="${APP_NAME}.air"/>
<property name="DEBUG" value="true"/>
<property name="CERT" value="ExampleCert.pfx"/>
<property name"CERT_PASSWORD" value="39#wnetx3tl"/>

Invoking the compiler

To invoke the compiler, the example uses a Java task to run mxmlc.jar:
<target name="compile">
<java jar="${MXMLC.JAR}" fork="true" failonerror="true">
<arg value="-debug=${DEBUG}"/>
<arg value="+flexlib=${SDK_HOME}/frameworks"/>
<arg value="+configname=air"/>
<arg value="-file-specs=${MAIN_SOURCE_FILE}"/>
</java>
</target>

When invoking mxmlc using Java, you must specify the +flexlib parameter. The +configname=air parameter
instructs mxmlc to load the supplied AIR configuration file along with the normal Flex config file.

Invoking ADL to test an application

To run the application with ADL, use an exec task:


ADOBE PRODUCT X.0 37
User Guide

<target name="test" depends="compile">


<exec executable="${ADL}">
<arg value="${APP_DESCRIPTOR}"/>
</exec>
</target>

Invoking ADT to package an application

To package the application use a Java task to run the adt.jar tool:
<target name="package" depends="compile">
<java jar="${ADT.JAR}" fork="true" failonerror="true">
<arg value="-package"/>
<arg value="-certificate"/>
<arg value="${CERT}"/>
<arg value="-password"/>
<arg value="${CERT_PASSWORD}"/>
<arg value="${AIR_NAME}"/>
<arg value="${APP_DESCRIPTOR}"/>
<arg value="${APP_NAME}.swf"/>
<arg value="*.png"/>
</java>
</target>

If your application has more files to package, you could add additional <arg> elements.
Important: You can leave the certificate password parameter out of the build file. If no password is provided, ADT will
prompt you for one on the command line. Since the certificate is only as secure as the password, you should not put a
valued password in a plain-text build file.

Using Ant for more complex projects


Because few applications would keep all files in a single directory, the following example illustrates a build file used
to compile, test, and package an AIR application which has a more practical project directory structure.
This sample project stores the application source files and other assets like icon files within a src directory. The build
script creates the following working directories:
build Stores the release (non-debug) versions of compiled SWF files.
debug Stores an unpackaged debug version of the application, including any compiled SWFs and asset files. The
ADL utility runs the application from this directory.
release Stores the final AIR package

The AIR tools require the use of some additional options when operating on files outside the current working
directory:
Compiling The -output option of the mxmlc compiler allows you to specify where to put the compiled file, in this
case, in the build or debug subdirectories. To specify the output file, the line:
<arg value="-output=${debug}/${APP_ROOT_FILE}"/>
is added to the compilation task.
Testing The second argument passed to adl specifies the root directory of the AIR application. To specify the appli-
cation root directory, the line:
<arg value="${debug}"/>
is added to the testing task.
ADOBE PRODUCT X.0 38
User Guide

Packaging Packaging files from subdirectories that should not be part of the final package structure (such as the src
directory) requires using the -C directive to change the adt working directory. When you use the -C directive, files
in the new working directory are packaged at the root level of the air file. Thus "-C build file.png" places file.png at
the root, and "-C assets icons" places the icon folder at the root level, and packages all the files within the icons folder
as well. For example, the following sequence of lines in the package task adds the icons directory directly to the root
level of the application package file:
<arg value="-C"/>
<arg value="${assets}"/>
<arg value="icons"/>
<?xml version="1.0" ?>
<project>
<!-- SDK properties -->
<property name="SDK_HOME" value="C:/FlexSDK"/>
<property name="MXMLC.JAR" value="${SDK_HOME}/lib/mxmlc.jar"/>
<property name="ADL" value="${SDK_HOME}/bin/adl.exe"/>
<property name="ADT.JAR" value="${SDK_HOME}/lib/adt.jar"/>

<!-- Project properties -->


<property name="APP_NAME" value="ExampleApplication"/>
<property name="APP_ROOT_DIR" value="."/>
<property name="MAIN_SOURCE_FILE" value="${APP_ROOT_DIR}/src/${APP_NAME}.mxml"/>
<property name="APP_ROOT_FILE" value="${APP_NAME}.swf"/>
<property name="APP_DESCRIPTOR" value="${APP_ROOT_DIR}/${APP_NAME}-app.xml"/>
<property name="AIR_NAME" value="${APP_NAME}.air"/>
<property name="build" location="${APP_ROOT}/build"/>
<property name="debug" location="${APP_ROOT_DIR}/debug"/>
<property name="release" location="${APP_ROOT_DIR}/release"/>
<property name="assets" location="${APP_ROOT_DIR}/src/assets"/>
<property name="CERT" value="ExampleCert.pfx"/>
<property name"CERT_PASSWORD" value="secret_password"/>

<target name="init" depends="clean">


<tstamp/>
<mkdir dir="${build}"/>
<mkdir dir="${debug}"/>
<mkdir dir="${release}"/>
</target>

<target name="debugcompile" depends="init">


<java jar="${MXMLC.JAR}" fork="true" failonerror="true">
<arg value="-debug=true"/>
<arg value="+flexlib=${SDK_HOME}/frameworks"/>
<arg value="+configname=air"/>
<arg value="-file-specs=${MAIN_SOURCE}"/>
<arg value="-output=${debug}/${APP_ROOT_FILE}"/>
</java>
<copy todir="${debug}">
<fileset dir="${assets}"/>
</copy>
</target>

<target name="releasecompile" depends="init">


<java jar="${MXMLC.JAR}" fork="true" failonerror="true">
<arg value="-debug=false"/>
<arg value="+flexlib=${SDK_HOME}/frameworks"/>
<arg value="+configname=air"/>
<arg value="-file-specs=${MAIN_SOURCE_FILE}"/>
<arg value="-output=${build}/${APP_ROOT_FILE}"/>
</java>
ADOBE PRODUCT X.0 39
User Guide

</target>

<target name="test" depends="debugcompile">


<exec executable="${ADL}">
<arg value="${APP_DESCRIPTOR}"/>
<arg value="${debug}"/>
</exec>
</target>

<target name="package" depends="releasecompile">


<java jar="${ADT.JAR}" fork="true" failonerror="true">
<arg value="-package"/>
<arg value="-certificate"/>
<arg value="${CERT}"/>
<arg value="-password"/>
<arg value="${CERT_PASSWORD}"/>
<arg value="${release}/${AIR_NAME}"/>
<arg value="${APP_DESCRIPTOR}"/>
<arg value="-C"/>
<arg value="${build}"/>
<arg value="${APP_ROOT_FILE}"/>
<arg value="-C"/>
<arg value="${assets}"/>
<arg value="icons"/>
</java>
</target>

<target name="clean" description="clean up">


<delete dir="${build}"/>
<delete dir="${debug}"/>
<delete dir="${release}"/>
</target>
</project>
40

Chapter 7: Using the Flex AIR


components
When building an AIR application in Flex, you can use any of the controls and other components that are part of
Flex. In addition, Flex includes a set of components that are specifically for AIR applications. The Flex AIR compo-
nents can be divided into a few groups:
File system controls A set of user-interface controls that provide information about and tools to interact with the
file system of the local computer on which the AIR application is running, such as controls for displaying lists of
files in tree or grid format, controls for choosing directories or files from a list or combo box, and so on. See “About
file system controls” on page 40.
HTML control A control that is used to display an HTML web page within a Flex application, for example to
combine HTML and Flex content in a single application. See “About the HTML control” on page 46.
Window containers Two components that can be used as containers for defining the layout of windows in applica-
tions. These include the ApplicationWindow, a substitute for the Application container to use as the main or initial
window of an AIR application; and the Window, for application windows that are opened after the initial loading
of the application. “About window containers” on page 46.
For more information on these components, see the Flex 3 Language Reference.

About file system controls


The Flex file system components combine the functionality of other Flex controls, such as Tree, DataGrid,
ComboBox, and so forth, with pre-built awareness of the file system on the application user’s computer. These
controls duplicate the functionality of user interface controls that are commonly used in desktop applications for
browsing and selecting files and directories. You can use one or two of them to directly include file-related function-
ality in a screen of your application, or combine several of them together to create a full-featured file browsing or
selection dialog.
With the exception of the FileSystemHistoryButton control, each of the Flex file system controls displays a view of
the contents of a particular directory in the computer’s file system. For instance, the FileSystemTree displays the
directory’s contents in a hierarchical tree (using the Flex Tree control) and the FileSystemComboBox displays the
directory in the text field of a ComboBox control, with the directory’s containing directories appearing in the combo
box’s menu.

Setting and retrieving the displayed directory


For any of the Flex file system controls except the FileSystemHistoryButton control, you use the control’s
directory property to change the currently selected directory for a control (the directory that displays in the
control, or whose contents are displayed in the control). You can also use the directory property to retrieve the
current directory, such as if the user selects a directory in the control.
ADOBE PRODUCT X.0 41
User Guide

FileSystemComboBox control
A FileSystemComboBox defines a combo box control for selecting a location in a file system. The control always
displays the selected directory in the combo box’s text field. When the combo box’s drop-down list is displayed, it
shows the ancestor directories of the selected directory—the hierarchy of directories that contain the selected
directory, up to the computer root directory. This allows a user to select a higher-level directory, providing an alter-
native to the FileSystemTree, FileSystemList, and FileSystemDataGrid controls that display (and allow selection of)
a directory that’s contained by the current directory.
For more information on the FileSystemComboBox control, see the Flex 3 Language Reference.

Creating a FileSystemComboBox control


You use the <mx:FileSystemComboBox> tag to define a FileSystemComboBox control in MXML, as the following
example shows. Specify an id value if you intend to refer to a component elsewhere in your MXML, either in
another tag or in an ActionScript block.
You specify the currently displayed directory using the control’s directory property. The directory property can
be set in MXML by binding the value to a property or variable, or by setting the property in ActionScript. Specifying
a value for the directory property automatically causes the data provider of the underlying combo box to be
populated. By default the directory property is set to the root “Computer” directory, which has no ancestor direc-
tories and hence shows no selectable directories in the combo box’s drop-down list.
The following example shows four variations on the basic FileSystemComboBox. Each combo box is initially set to
the user’s desktop directory, in the application’s creationComplete handler. The distinct characteristics of the
combo boxes are as follows:
• The first combo box simply displays the selected directory
• The second combo box’s showIcons property is set to false, so no icon appears next to the items in the combo
box’s list
• The third combo box’s indent property is set to 20, which is larger than the default, so the items in the combo
box’s list are more indented than normal
• The fourth combo box has an event handler defined for the directoryChange event. When the selected
directory in the combo box changes, it calls the setOutput() method, which writes the selected directory’s path to
a TextArea control named output.
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="init();">
<mx:Script>
<![CDATA[
import flash.filesystem.File;

private function init():void


{
fcb.directory = File.desktopDirectory;
fcbIndent.directory = File.desktopDirectory;
fcbNoIcons.directory = File.desktopDirectory;
fcbChange.directory = File.desktopDirectory;
}

private function setOutput():void


{
output.text = fcbChange.directory.nativePath;
}
]]>
ADOBE PRODUCT X.0 42
User Guide

</mx:Script>
<mx:FileSystemComboBox id="fcb"/>
<mx:FileSystemComboBox id="fcbNoIcons" showIcons="false"/>
<mx:FileSystemComboBox id="fcbIndent" indent="20"/>
<mx:FileSystemComboBox id="fcbChange" directoryChange="setOutput();"/>
<mx:TextArea id="output" width="200" height="50"/>
</mx:WindowedApplication>

FileSystemComboBox user interaction


The user interaction supported by the FileSystemComboBox is the same as that of a standard combo box control:
the control displays a directory in its selection field, the user clicks the button (or uses the keyboard) to open a drop-
down list containing the names of the hierarchy of directories that contain the selected directory, and the user can
select one of the directories, causing the drop-down list to close and the selected directory to become the current
directory. When the user selects a directory, the control dispatches the directoryChange event, and its directory
property changes to the newly selected directory.

FileSystemTree control
A FileSystemTree control displays the contents of a file system directory as a tree. The tree can display the directory’s
files, its subdirectories, or both. For files, file names can be displayed with or without extensions.
For more information on the FileSystemTree control, see the Flex 3 Language Reference.

Creating a FileSystemTree control


You use the <mx:FileSystemComboBox> tag to define a FileSystemComboBox control in MXML, as the following
example shows. Specify an id value if you intend to refer to a component elsewhere in your MXML, either in
another tag or in an ActionScript block.
You specify the currently displayed directory using the control’s directory property. The directory property can
be set in MXML by binding the value to a property or variable, or by setting the property in ActionScript. Specifying
a value for the directory property automatically causes the data provider of the underlying tree control to be
populated. The specified directory isn’t displayed in the tree—its child files or directories are shown as the top-level
nodes of the tree. By default the directory property is set to the root “Computer” directory, so its children (the
drive or drives attached to the computer) are displayed as the top branches of the tree.
The following example demonstrates creating a FileSystemTree control that displays all files and folders (the
default) including hidden files (specified by setting the showHidden property to true).
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:FileSystemTree showHidden="true"/>
</mx:WindowedApplication>

FileSystemTree user interaction


The FileSystemTree control supports the same types of user interaction that are supported by the standard Flex Tree
control. If the user double-clicks a closed directory node, or clicks its disclosure icon, the control dispatches a
directoryOpening event; if the user double-clicks an open directory node, or clicks its disclosure icon, the control
dispatches a directoryClosing event; and if the user double-clicks a file node, the control dispatches a select
event.
ADOBE PRODUCT X.0 43
User Guide

FileSystemList control
A FileSystemList control displays the contents of a file system directory as selectable items in a scrolling list (a Flex
List control). The displayed contents can include subdirectories and files, with additional filtering options as well.
A FileSystemList control can be linked to a FileSystemHistoryButton control, meaning that the button can be used
to move to a previously displayed directory.
For more information on the FileSystemList control, see the Flex 3 Language Reference.

Creating a FileSystemList control


You use the <mx:FileSystemList> tag to define a FileSystemList control in MXML, as the following example
shows. Specify an id value if you intend to refer to a component elsewhere in your MXML, either in another tag or
in an ActionScript block.
You specify the currently displayed directory using the control’s directory property. The directory property can
be set in MXML by binding the value to a property or variable, or by setting the property in ActionScript. Specifying
a value for the directory property automatically causes the data provider of the underlying list control to be
populated. The specified directory isn’t displayed in the list—its child files or directories are shown as the items in
the list. By default the directory property is set to the root “Computer” directory, so its children (the drive or drives
attached to the computer) are displayed as the items in the list.
The following example demonstrates creating a FileSystemList control that displays all files and folders (the default).
The sample also includes a button for navigating up one level in the directory hierarchy. The button is enabled if the
currently-displayed directory has a parent directory, by binding the button’s enabled property to the FileSystemList
control’s canNavigateUp property, and the button navigates up one level by calling the FileSystemList control’s
navigateUp() method when it is clicked.

<?xml version="1.0" encoding="utf-8"?>


<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Button label="Up" click="fileList.navigateUp();"
enabled="{fileList.canNavigateUp}"/>
<mx:FileSystemList id="fileList"/>
</mx:WindowedApplication>

FileSystemList user interaction


The FilesSystemList control provides standard scrolling list functionality for files: a user can scroll through the list
of files and select one or multiple files or directories. When the user double-clicks on a directory, the FileSystemList
control automatically sets that directory as the control’s directory property, and it becomes the directory whose
contents are displayed in the list.

FileSystemDataGrid control
A FileSystemDataGrid displays file information in a data-grid format. The file information displayed can include
the file name, creation date, modification date, type, and size. These are automatically created as columns in the
underlying DataGrid control, and can be removed or customized in the same way that you customize DataGrid
columns. The displayed contents can include subdirectories and files, with additional filtering options as well. A
FileSystemList control can be linked to a FileSystemHistoryButton control, meaning that the button can be used to
move to a previously displayed directory.
For more information on the FileSystemDataGrid control, see the Flex 3 Language Reference.
ADOBE PRODUCT X.0 44
User Guide

Creating a FileSystemDataGrid control


You use the <mx:FileSystemDataGrid> tag to define a FileSystemDataGrid control in MXML, as the following
example shows. Specify an id value if you intend to refer to a component elsewhere in your MXML, either in
another tag or in an ActionScript block.
You specify the currently displayed directory using the control’s directory property. The directory property can
be set in MXML by binding the value to a property or variable, or by setting the property in ActionScript. Specifying
a value for the directory property automatically causes the data provider of the underlying data grid control to be
populated. The specified directory isn’t displayed in the grid—its child files or directories are shown as the rows in
the grid. By default the directory property is set to the root “Computer” directory, so its children (the drive or
drives attached to the computer) are displayed as the items in the grid.
The following example demonstrates creating a FileSystemDataGrid control that displays all files and folders (the
default). The sample also includes a button for navigating up one level in the directory hierarchy. The button is
enabled if the currently-displayed directory has a parent directory, by binding the button’s enabled property to the
FileSystemDataGrid control’s canNavigateUp property, and the button navigates up one level by calling the
FileSystemDataGrid control’s navigateUp() method when it is clicked.
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Button label="Up" click="fileGrid.navigateUp();"
enabled="{fileGrid.canNavigateUp}"/>
<mx:FileSystemDataGrid id="fileGrid"/>
</mx:WindowedApplication>

FileSystemDataGrid user interaction


The FileSystemDataGrid control includes standard DataGrid functionality such as scrolling through the grid,
selecting grid rows, reordering grid columns, and sorting grid data by clicking on the grid headers. In addition, the
FileSystemDataGrid provides some file-specific functionality. A FileSystemDataGrid allows a user to navigate to
other directories using the mouse or keyboard. The user can change the directory by double-clicking a subdirectory,
by pressing Enter or Ctrl-Down when a subdirectory is selected, by pressing Ctrl-Up when the control isn't
displaying the COMPUTER directory, by pressing Ctrl-Left when there is a “previous” directory to navigate back
to, or by pressing Ctrl-Right when there is a “next” directory to navigate forward to. If the user attempts to change
the directory being displayed, the control dispatches a cancelable directoryChanging event. If the event isn’t
cancelled, the control displays the contents of the new directory and its directory property changes. Whenever the
directory property changes for any reason, the controls dispatches a directoryChange event.

FileSystemHistoryButton control
The FileSystemHistoryButton control works in conjunction with a FileSystemList or FileSystemDataGrid control
(or similar control with a property containing an array of File objects) to let the user move backwards or forwards
through the navigation history of the control. The FileSystemHistoryButton is a PopUpMenuButton with a button
for navigating back or forward one step in the history and a list of history steps from which one step can be chosen
as well.
To link a FileSystemHistoryButton to a control, the button’s dataProvider property should be bound to a property
containing an array of File objects representing a sequence of directories in a file system browsing history. For
instance, the dataProvider property can be bound to the forwardHistory or backHistory property of a FileSys-
temList or FileSystemDataGrid control in order to use the button to navigate the display history of that control.
Then you set the click and itemClick event handlers of the button to call the navigateForward() or
navigateBack() method of the control.

For more information on the FileSystemHistoryButton control, see the Flex 3 Language Reference.
ADOBE PRODUCT X.0 45
User Guide

Creating a FileSystemHistoryButton control


You use the <mx:FileSystemHistoryButton> tag to define a FileSystemHistoryButton control in MXML, as the
following example shows. Specify an id value if you intend to refer to a component elsewhere in your MXML, either
in another tag or in an ActionScript block.
You specify the property to which the button is bound by setting the dataProvider property.
The following example demonstrates creating two FileSystemHistoryButton controls that are linked to the display
history of a FileSystemList control. Each button is enabled if the currently-displayed directory can navigate in the
appropriate direction, by binding the button’s enabled property to the FileSystemList control’s canNavigateBack
or canNavigateForward property. The button actually does the navigation by calling the FileSystemList control’s
navigateBack() or navigateForward() method.

<?xml version="1.0" encoding="utf-8"?>


<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:HBox>
<mx:FileSystemHistoryButton label="Back"
dataProvider="{fileList.backHistory}"
enabled="{fileList.canNavigateBack}"
click="fileList.navigateBack();"
itemClick="fileList.navigateBack(event.index)"/>
<mx:FileSystemHistoryButton label="Forward"
dataProvider="{fileList.forwardHistory}"
enabled="{fileList.canNavigateForward}"
click="fileList.navigateForward();"
itemClick="fileList.navigateForward(event.index)"/>
</mx:HBox>
<mx:FileSystemList id="fileList"/>
</mx:WindowedApplication>

FileSystemHistoryButton user interaction


The FileSystemHistoryButton is based on the Flex PopUpMenuButton, so their core functionality is the same.
When the user clicks the main button the click event is dispatched (normally moving backward or forward one step
in the history). In addition, by clicking the pull-down menu button, a list of the history steps is displayed, allowing
the user to navigate directly to a specific step in the history.

Example: Displaying a directory structure with Flex AIR


The following example uses the WindowedApplication container and the FileSystemTree and FileSystemDataGrid
controls. In this example, the FileSystemTree control displays a directory structure. Clicking a directory name in the
FileSystemTree control causes the FileSystemDataGrid control to display information about the files in the selected
directory:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:HDividedBox>
<mx:FileSystemTree id="tree"
width="200" height="100%"
directory="{new File('C:\\')}"
enumerationMode="directoriesOnly"
change="dataGrid.directory = File(tree.selectedItem);"/>
<mx:FileSystemDataGrid id="dataGrid"
width="100%" height="100%"
directory="{new File('C:\\')}"/>
</mx:HDividedBox>
</mx:WindowedApplication>
ADOBE PRODUCT X.0 46
User Guide

About the HTML control


An HTML control Displays HTML web pages in your application. It is designed to be used to render specific
external HTML content within your AIR application. It offers functionality similar to a lightweight web browser,
including loading HTML pages, navigation history, and the ability to access the raw HTML content. The HTML
control is not designed or intended to be used as a replacement for the Text or TextArea controls for displaying
formatted text, or as an item renderer for displaying short runs of text.

Creating an HTML control


You use the <mx:HTML> tag to define an HTML control in MXML, as the following example shows. Specify an id
value if you intend to refer to a component elsewhere in your MXML, either in another tag or in an ActionScript
block.
You specify the location of the HTML page to display by setting the location property.
The following example demonstrates the use of an HTML control in a simple application. The HTML control’s
location property is set to “http://labs.adobe.com/”, so that URL is opened in the control when it loads. In addition,
“back” and “forward” buttons call the control’s historyBack() and historyForward() methods when clicked. A
TextInput control allows the user to enter a url location and, when a third “go” button is clicked, the HTML control’s
location property is set to the text property of the input text field.

<?xml version="1.0" encoding="utf-8"?>


<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:ControlBar width="100%">
<mx:Button label="&lt; Back" click="content.historyBack();"/>
<mx:Button label="Forward &gt;" click="content.historyForward();"/>
<mx:TextInput id="address" text="{content.location}" width="100%"/>
<mx:Button label="Go!" click="content.location = address.text"/>
</mx:ControlBar>
<mx:Canvas width="100%" height="100%">
<mx:HTML id="content" location="http://labs.adobe.com/"/>
</mx:Canvas>
</mx:WindowedApplication>

HTML control user interaction


For a user interacting with an HTML control, the experience is similar to using a web browser with only the content
window and no menu bar or navigation buttons. The HTML page content displays in the control. The user can
interact with the content through form fields and buttons and by clicking hyperlinks. Any action that normally
causes a browser to load a new page (such as clicking a link or submitting a form) also causes the HTML control to
display the content of the new page and changes the value of the control’s location property.

About window containers


Flex container components define the content, sizing and positioning for a specific part of an application. For AIR
applications, Flex includes two specific window containers that serve as containers whose visible area consists of an
operating system window. Both the WindowedApplication and the Window components can be used to define the
contents of an operating system window, as well as providing the means to define and control characteristics of the
window itself, such as the window’s size, its position on the user’s screen, and the presence of window chrome.
ADOBE PRODUCT X.0 47
User Guide

Controlling window chrome


The WindowedApplication component and the Window component allow you to control the presence and
appearance of the window chrome that is used by the operating system when creating the actual window on the
screen.
These components provide the following window controls:
• A title bar
• A minimize button
• A maximize button
• A close button
How these controls are represented depends on the setting of the systemChrome value, which is defined in the appli-
cation.xml file for a WindowedApplication component, or in the Window component’s systemChrome property.
If systemChrome is set to “standard” in the application.xml file (or
flash.display.NativeWindowSystemChrome.STANDARD in ActionScript) the operating system renders the
chrome for the title bar and border of the window. However, if systemChrome is set to “none”
(NativeWindowSystemChrome.NONE) the Flex Window or WindowedApplication component draws its own title
bar, including the buttons listed before.
The title bar area of the window chrome (either the operating system or Flex chrome) includes a title message and
an icon that can be set and modified programmatically. In addition to the operating system chrome, Flex adds
additional common window chrome elements including a status bar with a definable status message and a resize
gripper in the bottom-right corner of the window. All the Flex-drawn chrome can be hidden as a group by setting
the showFlexChrome property to false, or a single chrome element can be hidden by setting the showTitleBar,
showGripper or showStatusBar property to false.

The window that a WindowedApplication or Window component defines conforms to the standard behavior of the
operating system. The user can move the window by dragging the title bar and resize the window by dragging on
any side or corner of the window. The components also include a set of properties that allow you control over
window sizing, including minimumHeight, minimumWidth, maximumHeight, and maximumWidth.

WindowedApplication container
The WindowedApplication container defines an application container that you use to create Flex applications for
AIR that use the native operating system chrome. The WindowedApplication container adds window-related
functionality and desktop application-specific functionality to the Flex Application container, which you can use
when you build AIR applications.

Creating and using a WindowedApplication container


The <mx:WindowedApplication> container component defines an AIR application object that includes its own
window controls. In an MXML AIR application, a <mx:WindowedApplication> tag replaces the
<mx:Application> tag.

By default, the WindowedApplication component creates an application window for which windowMode is set to
systemChrome, and visibility is set to true. These settings are made in the application.xml file for the AIR appli-
cation. To eliminate the system chrome and window controls that the WindowedApplication component creates by
default, use a standard Flex Application container instead of a WindowedApplication container as the root MXML
tag for the application and, in the application.xml file, set systemChrome to none.
The following simple application shows a simple use of the WindowedApplication container:
ADOBE PRODUCT X.0 48
User Guide

<?xml version="1.0" encoding="utf-8"?>


<mx:WindowedApplication
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">
<mx:Text text="Hello World" />
</mx:WindowedApplication>

For more information about the WindowedApplication container, see the Flex 3 Language Reference.

Window container
The Window component is a Flex container that is used to define the content and layout of operating system
windows that are opened after an application launches—in other words, windows other than the initial or main
window of the application (which is a WindowedApplication component or Application component). In addition
to the common functionality that is common with the WindowedApplication component, a Window component
allows you to define characteristics of the window, such as the type of window, the type of chrome, whether certain
actions (such as resizing and maximizing) are permitted for the window, and more. These characteristics are
accessed as properties that can be set when the component is initially created (at which point the actual operating
system window is not yet displayed). However, once the actual window is opened, the properties can no longer be
set and can only be read.

Creating and using a Window container


The <mx:Window> container component defines an AIR application object that includes its own window controls.
In an MXML AIR application, a <mx:Window> tag is used as the top-level tag of an MXML component, with the
windows content defined in the body of the MXML component document. However, unlike other MXML compo-
nents, the Window component cannot be used in another MXML document. Instead, you create an instance of the
component in ActionScript.
Because several of the properties of the Window component can only be set before the window is actually opened,
they can be set as properties in the <mx:Window> MXML tag or can be set using ActionScript, either in an
<mx:Script> block in the window’s MXML document or in code that creates an instance of the window.

Once the window’s initial properties are set, you call the Window component’s open() method to actually cause the
operating system window to appear on the user’s display.
The following example shows a basic use of the Window component. The example includes two MXML files: the
first uses an Application container and is the initial window of the application, while the second uses the Window
container to define a secondary window for the application. In this example, the main window simulates a “splash
screen” for the application, and after a set time (3 seconds) it closes the splash screen and opens the second window.
Thee following code defines the main application MXML file, which contains the initial window (the splash screen)
that opens automatically when the application is run:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
creationComplete="init();">
<mx:Script>
<![CDATA[
private const LOAD_DELAY:int = 3;
private var timeElapsed:int = 0;
private var loadTimer:Timer;

private var splashScreen:NativeWindow;


private var docWindow:DocumentWindow;

private function init():void


ADOBE PRODUCT X.0 49
User Guide

{
// center the window on the screen
splashScreen = Shell.shell.openedWindows[0];
var screenBounds:Rectangle = Screen.mainScreen.bounds;
splashScreen.x = (screenBounds.width - splashScreen.width) / 2;
splashScreen.y = (screenBounds.height - splashScreen.height) / 2;

// start the timer, which simulates a loading delay


loadTimer = new Timer(1000);
loadTimer.addEventListener(TimerEvent.TIMER, incrementTime);
loadTimer.start();

updateStatus();
}

private function incrementTime(event:TimerEvent):void


{
timeElapsed++;

updateStatus();

// if the loading delay has passed, stop the timer,


// close the splash screen, and open the document window
if ((LOAD_DELAY - timeElapsed) == 0)
{
loadTimer.stop();
loadTimer.removeEventListener(TimerEvent.TIMER, incrementTime);
loadTimer = null;

splashScreen.close();

// open a new instance of the document window


docWindow = new DocumentWindow();
docWindow.open();
}
}

private function updateStatus():void


{
loadStatusMessage.text = "initializing... " + (LOAD_DELAY -
timeElapsed).toString() + " seconds remaining.";
}
]]>
</mx:Script>
<mx:VBox horizontalCenter="0" verticalCenter="0">
<mx:Text text="My Splash Screen" fontFamily="Courier New" fontSize="36"/>
<mx:Text id="loadStatusMessage" text="initializing..."/>
</mx:VBox>
</mx:Application>

The incrementTime() method is called each second and when the appropriate time is reached, a
DocumentWindow instance is created and its open() method is called. The DocumentWindow class is defined in
a separate MXML document; its base MXML tag is the <mx:Window> tag so it is a subclass of the Window class (the
Window component). Here is the source code for the DocumentWindow MXML file:
<?xml version="1.0" encoding="utf-8"?>
<mx:Window xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
title="Document window"
width="550" height="450">
<mx:Text text="This is a document window." horizontalCenter="0" verticalCenter="0"/>
ADOBE PRODUCT X.0 50
User Guide

</mx:Window>

For more information about the Window container, see the Flex 3 Language Reference.
51

Part 1: Application development


essentials
AIR Security. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .52
Setting application properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .62
52

Chapter 9: AIR Security


Although the AIR security model is an evolution of the Flash Player security model, the security contract is quite
different from the security contract applied to content in a browser. This contract offers developers a secure means
of broader functionality for rich experiences with freedoms that would be inappropriate for a browser-based appli-
cations.
AIR applications run with the same user privileges as native applications. In general, these privileges allow for broad
access to operating system capabilities such as reading and writing files, starting applications, drawing to the screen,
communicating with the network, etc. Operating system restrictions that apply to native applications, such as user-
specific privileges, equally apply to AIR applications.
AIR applications are written using either compiled bytecode (Flash/Flex content) or interpreted script (JavaScript,
HTML) so that memory management is provided by the runtime. This minimizes the chances of AIR applications
being affected by vulnerabilities related to memory management, such as buffer overflows and memory corruption.
These are some of the most common vulnerabilities affecting desktop applications written in native code.
This section contains the following topics:
• “Installation and Updates” on page 52
• “Sandboxes” on page 55
• “Writing to disk” on page 57
• “Working securely with untrusted content” on page 58
• “Best security practices for developers” on page 59
• “Code Signing” on page 60

Installation and Updates


AIR applications are distributed via AIR installer files which use the air extension. When the AIR runtime is
installed and an AIR installer file is opened, the runtime administers the installation process.
Note: Developers can specify a version, and application name, and a publisher source, but the initial application instal-
lation workflow itself cannot be modified. This restriction is advantageous for users because all AIR applications share
a secure, streamlined, and consistent installation procedure administered by the AIR runtime. If application customi-
zation is necessary, it can be provided when the application is first executed.
AIR applications first require the AIR runtime to be installed on a user's computer, similar to how SWF files first
require the Flash Player browser plug-in to be installed.
The runtime is installed to the following location on a user's computer:
• Mac OS X: /Library/Frameworks/
• Windows: C:\Program Files\Common Files\Adobe AI
The runtime can be installed in two ways: in an express install or a manual install.
ADOBE PRODUCT X.0 53
User Guide

Express install (runtime and application)


The express install provides developers which a streamlined install experience for users who do not have Adobe AIR
installed yet. In the express install method, the developer creates a SWF file that presents the application for instal-
lation. When a user clicks in the SWF file to install the application, the SWF file attempts to detect the runtime. If
the runtime cannot be detected it will be installed, and the runtime is activated immediately with the installation
process for the developer's application.

Manual install
Alternatively, the user can manually download and install the runtime before opening an AIR file. The developer can
then distribute an AIR file by a number of means (e-mail, an HTML link on a website, etc.) When the AIR file is
opened, the runtime is activated and begins to process the application install.
For more information on this process, see “Distributing AIR applications” on page 206.

Application Installation Flow


The AIR security model allows users to decide whether to install an AIR application. The AIR install experience
provides several improvements over native application install technologies that make this trust decision easier for
users:
• The AIR runtime provides a consistent install experience on all web browsers on all operating systems. Most
native application install experiences depend upon the browser or other application to provide security information,
if it is provided at all.
• The AIR application install experience identifies the source of the application and information about what privi-
leges will be available to the application if the user allows the installation to proceed.
• The install process of an AIR application is administered by the AIR runtime, which cannot be manipulated by
the application being installed.
In general, users should not install any desktop application that comes from a source that they do not trust, or that
cannot be verified. The burden of proof on security for native applications is equally true for AIR applications as it
is for other installable applications.

Application Destination
The installation directory of an AIR applications is determined by the first of three possible options:
1 The user customizes the destination during installation. The application installs to wherever the user specifies.
2 If the user does not change the install destination, the application installs to the default path as determined by
the runtime:
• Mac OS X: ~/Applications/
• Windows XP and earlier: C:\Program Files\
• Windows Vista: ~/Apps/
If the developer specifies an installFolder setting in the application descriptor file, the application is installed to
a subpath of this directory.
ADOBE PRODUCT X.0 54
User Guide

The AIR file system


The install process for AIR applications copies all files that the developer has included within the AIR installer file
onto the user's local machine. The installed application is composed of:
• Windows: A directory containing all files included in the AIR installer file. The AIR runtime also creates an exe
file as during the installation of the AIR application.
• OS X: An app file. This file's contents include all of the contents of the AIR installer file. It can be inspected using
the "Show Package Contents" option in Finder. The AIR runtime creates this app file as part of the installation of the
AIR application.
An AIR application is run by:
• Windows: Running the install folder’s .exe file, or a shortcut that corresponds to this file (i.e. Start Menu,
Desktop)
• OS X: Running the .app file or an alias that points to it.
The application file system also includes subdirectories related to the function of the application. For example, infor-
mation written to encrypted local storage is saved to a subdirectory in a directory named after the application
identifier (appId) of the application.

AIR application storage


AIR applications have privileges to write to any location on the user's hard drive; however, developers are encouraged
to use the app-storage:/ path for local storage related to their application. Files written to app-storage:/ from
an application will be located in a standard location depending on the user's operating system:
• On Mac OS X: the storage directory of an application is <appData>/<appId>/Local Store/ where <appData>
is the user's "preferences folder," typically /Users/<user>/Library/Preferences
• On Windows: the storage directory of an application is <appData>\<appId>\Local Store\ where
<appData> is the user's CSIDL_APPDATA "Special Folder," typically C:\Documents and
Settings\<userName>\Application Data

You can access the application storage directory via the air.File.applicationStorageDirectory property. You can access
its contents using the resolvePath() method of the File class. For details, see “Working with the file system”
on page 100.

Updating Adobe AIR


Adobe AIR does not automatically update for the beta release. The newest version of the runtime can be found at
http://labs.adobe.com/downloads/air.html.

Updating AIR applications


Development and deployment of software updates are one of the biggest security challenges facing native code appli-
cations. The AIR API provides a mechanism to improve this: the Updater.update() method can be invoked upon
launch to check a remote location for an AIR file. If an update is appropriate, the AIR file is downloaded, installed,
and the application restarts. Developers can use this class not only to provide new functionality but also respond to
potential security vulnerabilities.
ADOBE PRODUCT X.0 55
User Guide

Note: Developers can specify the version of an application by setting the version property of the application descriptor
file. AIR does not interpret the version string in any way. Thus version “3.0” is not assumed to be more current than
version “2.0.” It is up to the developer to maintain meaningful versioning. For details, see “Defining properties in the
application descriptor file” on page 63.

Uninstalling an AIR application


AIR applications can be uninstalled by:
• On Windows: Using the Add/Remove Programs panel to remove the application.
• On Mac OS X: Deleting the app file from the install location.
Removing an AIR application removes all files in the application directory, but does not remove files that the appli-
cation may have written to the local disk outside of the application directory, including files written to local storage.
Removing AIR applications does not revert changes the AIR application has made to files outside of the application
directory.

Uninstalling Adobe AIR


AIR can be uninstalled:
• On Windows: by running Add/Remove Programs from the Control Panel, selecting Adobe AIR and selecting
"Remove"
• On Mac OS X: by running the Adobe AIR Uninstaller application in the Applications directory.

Sandboxes
AIR provides a comprehensive security architecture that defines permissions accordingly to each file in an AIR
application, both internal and external. Permissions are granted to files according to their origin, and are assigned
into logical security groupings called sandboxes.
The AIR runtime security model of sandboxes is composed of the Flash Player security model with the addition of
the application sandbox. Files that are not in the application sandbox have security restrictions similar to those
specified by the Flash Player security model.
A chart of each sandbox and their characteristics is below:
application The file resides in the application directory and operates with
the full set of AIR privileges
remote The file is from an Internet URL, and operates under domain-
based sandbox rules analogous to those of remote files in the
Flash Player.
local-trusted The file is a local file and has been trusted by the user, using
either the Settings Manager or a Flash Player trust
configuration file. The file can both read from local data
sources and communicate with the Internet, but does not
have the full set of AIR privileges.
local-with-networking The file is a local SWF file published with a networking
designation, but has not been explicitly trusted by the user.
The file can communicate with the Internet but cannot read
from local data sources.
ADOBE PRODUCT X.0 56
User Guide

local-with-filesystem The file is a local scripting file that was not published with a
networking designation and has not been explicitly trusted by
the user. This includes JavaScript files that have not been
trusted. The file can read from local data sources but cannot
communicate with the Internet.
The runtime uses these security sandboxes to define the range of data that a file may access and the operations it may
execute. To maintain local security, the files in each sandbox are isolated from the files of other sandboxes. For
example, a SWF file loaded into an AIR application from an external Internet website is placed into the remote
sandbox, and does not by default have permission to script into files that reside in the application directory, which
are assigned to the application sandbox.
This topic focuses primarily on the application sandbox and its relationship to other sandboxes in the AIR appli-
cation. Developers that use content that will be assigned to other sandboxes should read further documentation on
the Flash Player security model. See the “Flash Player Security” chapter in the Programming ActionScript 3.0
documentation and the Adobe Flash Player 9 Security white paper.

The application sandbox


When an application is installed, all files included within an AIR installer file are installed onto the user's computer
into an application directory. Developers can reference this directory in code through the app-resource:/ URL
scheme (see “Using AIR URL schemes in URLRequest object URLs” on page 200). All files within the application
directory tree are assigned to the application sandbox when the application is run and are blessed with the full
privileges available to an AIR application, including interaction with the local file system.
Many AIR applications use only these locally installed files to run the application, but AIR applications are not
restricted to just the files within the application directory — they can load any type of file from any source. This
includes files local to the user's computer as well as files from available external sources, such as those on a local
network or on the Internet. File type has no impact on security restrictions; loaded HTML files have the same
security privileges as loaded SWF files from the same source.
Content in the application security sandbox has access to AIR APIs that are content in other sandboxes are prevented
from using. For example, the Shell.shell.applicationDescriptor property, which returns the contents of the
application descriptor file for the application, is restricted to content in the application security sandbox. Another
example of a restricted API is the FileStream class, which contains methods for reading and writing to the local file
system.
APIs that are restricted to content in the application security sandbox are indicated in with the AIR logo Apollo
ActionScript 3.0 Language Reference.

Privileges of content in non-application sandboxes


Files loaded from a network or Internet location are assigned to the remote sandbox. Files loaded from outside the
application directory are assigned to either the local-with-filesystem, local-with-networking, or the
local-trusted sandbox, depending on how the file was created and if the user has explicitly trusted the file through
the Flash Player Global Settings Manager. Unlike content in the application security sandbox, content in a non-appli-
cation security sandbox can call the eval() function to execute dynamically generated code at any time.
Code in a non-application sandbox does not have access to the window.runtime object, and as such this code
cannot execute AIR APIs. If content in a non-application security sandbox calls the following code, the application
throws a TypeError exception:
try {
window.runtime.flash.system.Shell.shell.exit();
ADOBE PRODUCT X.0 57
User Guide

}
catch (e)
{
alert(e);
}

The exception type is TypeError (undefined value), because content in the non-application sandbox does not
recognize the window.runtime object, so it is seen as an undefined value.

Writing to disk
Applications running in a web browser have only limited interaction with the user's local file system. This is because
web browsers implement security policies that ensure that a user's computer cannot be compromised by web content.
For example, SWF files running through Flash Player in a browser cannot directly interact with files already on a
user's computer. Shared objects can be written to a user's computer for the purpose of maintaining user preferences
and other data, but this is the limit of file system interaction. Because AIR applications are natively installed, they
have a different security contract, one which includes the capability to read and write across the local file system.
This freedom comes with high responsibility for developers. Accidental application insecurities jeopardize not only
the functionality of the application, but the integrity of the user's machine. For this reason, developers are strongly
advised to read this section as well as the “Best security practices for developers” on page 59 section.

Network files and the remote sandbox


AIR developers can access and write files to the local file system using several path conventions:
app-resource:/

An alias to the application directory. Files accessed from this path will be assigned the application sandbox and
have the full privileges granted by the runtime.
app-storage:/

An alias to the local storage directory, standardized by the runtime. Files accessed from this path will be assigned a
non-application sandbox.
file:///

An alias that represents the root of the user's hard disk. Files accessed from this path are assigned an application
sandbox if it exists in the application directory, and a non-application sandbox otherwise.
Note: AIR applications cannot modify content in the app-resource:/ directory, due to security concerns and because
administrator settings may not allow it.
Unless there are administrator restrictions to the user's computer, AIR applications are privileged to write to any
location on the user's hard drive. Developers are advised to use the app-storage:/ path for local storage related to
their application. Files written to app-storage:/ from an application will be located in a standard location
depending on the user's operating system:
• On Mac OS X: the storage directory of an application is <appData>/<appId>/Local Store/ where <appData>
is the user's preferences folder. This is typically /Users/<user>/Library/Preferences
ADOBE PRODUCT X.0 58
User Guide

• On Windows: the storage directory of an application is <appData>\<appId>\Local Store\ where <appData>


is the user's CSIDL_APPDATA Special Folder. This is typically C:\Documents and
Settings\<userName>\Application Data

If an application is designed to interact with existing files in the user's file system, the developer is advised to read
the “Best security practices for developers” on page 59 section.

Working securely with untrusted content


Content not assigned to the application sandbox can provide additional scripting functionality to your application,
but only if it meets the security criteria of the runtime. This section explains the AIR security contract with non-
application content.

Security.allowDomain()
AIR applications restrict scripting access for non-application content more stringently than the Flash Player 9
browser plug-in restricts scripting access for untrusted content. For example, in Flash Player in the browser, when a
SWF file that is assigned to the local-trusted sandbox calls the System.allowDomain() method, scripting access
is granted to any SWF loaded from the specified domain, reassigning this remote file from the remote sandbox to
the local-trusted sandbox. The analogous approach is not permitted from application content in AIR applica-
tions, since it would grant unreasonable access unto the non-application file into the user's file system. Remote files
cannot directly access the application sandbox, regardless of calls to the Security.allowDomain() method.

Scripting between application and non-application content


AIR applications that script between application and non-application content have more complex security arrange-
ments. Files that are not in the application sandbox are only allowed to access the properties and methods of files
in the application sandbox through the use of a sandbox bridge. A sandbox bridge acts as a gateway between appli-
cation content and non-application content, providing explicit interaction between the two files. When used
correctly, sandbox bridges provide an extra layer of security, restricting non-application content from accessing
object references that are part of application content.
The benefit of sandbox bridges is best illustrated through example. Suppose an AIR music store application wants to
provide an API to advertisers who want to create their own SWF files to be added to the store application. The store
wants to provide advertisers with methods to look up artists and CD's from the store, but also wants to isolate some
methods and properties from the third-party SWF for security reasons.
A sandbox bridge can provide this functionality. By default, content loaded externally into an AIR application at
runtime does not have access to any methods or properties in the main application. With a custom sandbox bridge
implementation, a developer can provide services to the remote content without exposing these methods or
properties. Consider the sandbox bridge as a pathway between trusted and untrusted content, providing communi-
cation between loader and loadee content without exposing object references.
For more information on how to securely use sandbox bridges, see “Scripting between content in different domains”
on page 202
ADOBE PRODUCT X.0 59
User Guide

Best security practices for developers


Although AIR applications are built using web technologies, it is important for developers to note that they are not
working within the browser security sandbox. This means that it is possible to build AIR applications that can do
harm to the local system, either intentionally or unintentionally. AIR attempts to minimize this risk, but there are
still ways where vulnerabilities can be introduced. This section covers important potential insecurities.

Risk from importing files into the application security sandbox


Files that exist in the application directory are assigned to the application sandbox and have the full privileges of the
runtime. Applications that write to the local file system are advised to write to app-storage:/. This directory exists
separately from the application files on the user's computer, hence the files are not assigned to the application
sandbox and present a reduced security risk. Developers are advised to consider the following:
• Include a file in an AIR file (in the installed application) only if absolutely necessary.
• Include a scripting file in an AIR file (in the installed application) only its behavior is fully understood and
trusted.
• Do not write to or modify content in the application resource directory. The AIR runtime prevents applications
from writing or modifying files and directories using the app-resource:/ URL scheme by throwing a Security-
Error exception.
• Do not use data from a network source as parameters to methods of the AIR API that may lead to code execution.
This includes use of the Loader.loadBytes() method and the JavaScript eval() function.

Risk from using an external source to determine paths


An AIR application can be compromised when using external data or content — so you must take special care when
using data from the network or file system. The onus of trust is ultimately up to the developer and the network
connections they make, but loading foreign data is inherently risky, and should not be used for input into sensitive
operations. Developers are advised against the following:
• Using data from a network source to determine a file name
• Using data from a network source to construct a URL that you will use to send private information

Risk from using, storing, or transmitting unsecure credentials


Storing user credentials on the user's local file system inherently introduces the risk that these credentials may be
compromised. Developers are advised to consider the following:
• If credentials must be stored locally, to encrypt the credentials when writing to the local file system.
• Do not transmit unencrypted user credentials to a network source unless that source is trusted.
• Never specify a default password in credential creation — let users create their own. Users who leave the default
expose their credentials to an attacker that already knows the default password
The AIR runtime provides an encrypted storage unique to each installed application, via the EncryptedLocalStore
class. For details, see “Using the encrypted local store” on page 122.
ADOBE PRODUCT X.0 60
User Guide

Risk from a downgrade attack


During application install, the runtime checks to ensure that a version of the application is not currently installed. If
an application is already installed, the runtime compares the version string against the version that is being installed.
If this string is different, the user can choose to upgrade their installation. There runtime does not guarantee that the
newly installed version is actually newer than the older version, only that it is different. An attacker can distribute an
older version to the user in order to circumvent a security weakness. For this reason, the developer is advised to make
version checks when the application is run. It is a good idea to have applications check the network for required
updates. That way, even if an attacker gets the user to run an old version, that old version will recognize that it needs
to be updated. Also, using a very clear versioning scheme for your application makes it more difficult to trick users
into installing a downgraded version. For details on providing application versions, see “Defining properties in the
application descriptor file” on page 63.

Code Signing
All AIR installer files are required to be code signed. Code signing is a cryptographic process of confirming that the
specified origin of software is accurate. AIR applications can be signed either by linking a certificate from an external
certificate authority (CA) or by constructing your own certificate. Certificates are created using adt from the SDK
or using either Flash, Flex Builder, or another graphical front-end that uses adt for certificate generation.
For more general information about digitally signing AIR applications, see “Digitally signing an AIR file” on
page 206.

Signing AIR applications with an existing certificate


To associate an AIR installer file with an existing certificate, use the following syntax
adt -package ( -certificate <pfx-file> )? ( -password <password> )? <air-file> <app-desc>
<fileOrDir> ( -C <dir> <fileOrDir>+ )

<pfx-file> is the path and name to the existing file that contains the certificate to be associated with the application

<password> is the password assigned to the certificate during its creation

The other options are unrelated to security and are described in “Creating an AIR application using the command
line tools” on page 28.

Signing AIR applications with a self-signed certificate


Alternatively, application installer files can be constructed with a self-signed certificate. A self-signed certificate is
not backed by a certificate authority (CA) and provides no external confirmation that the origin of the installer file
is indeed what the runtime says it is. For AIR developers who choose not to purchase a certificate, this step is
required in order to create an installer file.
To create a self-signed certificate, using ADT, use the following syntax:
adt -certificate -cn <name> ( -ou <org-unit> )? ( -o <org-name> )? ( -c <country> )? <key-
type> <pfx-file> <password>

The following options are available to adt in the construction of a self-signed certificate:
<name> is the name of the application
ADOBE PRODUCT X.0 61
User Guide

<org-unit> (optional) is the department within an organization that is creating the installer file

<org-name> (optional) is the name of the organization that is creating the installer file.

<country> (optional) is the two-letter country code that represents the installer file’s country of origin

<key-type> is the type of encryption used to create the certificate. Acceptable options include “1024-RSA” and
“2048-RSA”

<pfx-file> is the path and name, including extension, of the file created that will contain the certificate

<password> is the password associated with the certificate. this password is later required when using adt to
associate an installer file to a certificate.
Note: When choosing a password, it is best to use a password that is a combination of uppercase letters, lowercase letters,
punctuation characters, and numbers. It is also safest to never store the password on the same computer that contains
the certificate, or to not store a password on a machine at all. One way to pick a long password is to thing of a memorable
short sentence or long phrase and then add some personally memorable numbers and punctuation (such as “3665
MELba !@# toast”).

See also
• “Scripting between content in different domains” on page 202
62

Chapter 10: Setting application properties


Aside from all the files and other assets that make up your Adobe AIR applications, an AIR application requires an
application descriptor, an XML file which defines the basic properties of the application.
When you use Flex Builder 3, the application descriptor file is automatically generated when you create a new AIR
project. If you're developing AIR applications using the Flex and AIR SDKs, you need to create this file manually. An
example descriptor file, application.xml, can be found in the templates/air directory of your SDK installation.

The application descriptor file structure


The application descriptor file, application.xml, contains the properties of the entire application, such as its name,
the version, copyright, and so on. Any file name can be used for the application descriptor file. When you package
the application with either Flex Builder or ADT, the application descriptor file is renamed application.xml and
placed within a special directory inside the package.
Here's an example of an application descriptor file:
<?xml version="1.0" encoding="utf-8" ?>
<application appId="com.adobe.air.examples.HelloWorld" version="2.0"
xmlns="http://ns.adobe.com/air/application/1.0.M5">
<name>AIR Hello World</name>
<title>HelloWorld -- AIR Example</title>
<description>
This is the Hello World sample file from the Adobe AIR documentation.
</description>
<copyright>Copyright © 2006</copyright>
<initialWindow>
<title>Hello World</title>
<content>
HelloWorld-debug.swf
</content>
<systemChrome>none</systemChrome>
<transparent>true</transparent>
<visible>true</visible>
<width>640</width>
<height>480</height>
</initialWindow>
<installFolder>Adobe/Examples</installFolder>
<icon>
<image16x16>icons/smallIcon.png</image16x16>
<image32x32>icons/mediumIcon.jpg</image32x32>
<image48x48>icons/bigIcon.gif</image48x48>
<image128x128>icons/biggestIcon.png</image128x128>
</icon>
<handleUpdates/>
<fileTypes>
<fileType>
<name>adobe.VideoFile</name>
<extension>avf</extension>
<description>Adobe Video File</description>
<contentType>application/vnd.adobe.video-file</contentType>
ADOBE PRODUCT X.0 63
User Guide

</fileType>
</fileTypes>
</application>

Defining properties in the application descriptor file


At its root, the application descriptor file contains an application property that has several attributes:
<application appId="com.adobe.air.HelloWorld" version="1.0"
xmlns="http://ns.adobe.com/air/application/1.0.M5">
appId A unique application identifier. The attribute value is restricted to the following characters:

• 0–9
• a–z
• A–Z
• . (dot)
• - (hyphen)
The value must contain 1 to 212 characters.
The appId string typically uses a dot-separated hierarchy, in alignment with a reversed DNS domain address, a Java
package or class name, or an OS X Universal Type Identifier. The DNS-like form is not enforced, and AIR does not
create any association between the name and actual DNS domains.
version Specifies the version information for the application. (It has no relation to the version of the runtime). The
version string is an application-defined designator. AIR does not interprete the version string in any way. Thus
version “3.0” is not assumed to be more current than version “2.0.” Examples: "1.0", ".4", "0.5", "4.9", "1.3.4a".
xmlns The AIR namespace, which you must define as the default XML namespace. The namespace will change with
each release of AIR. The last segment of the namespace, such as “1.0.M5,” indicates the AIR runtime version required
by the application.
minimumPatchLevel Together with the AIR namespace, determines the version of the AIR runtime required by the
application. Typically, the user will be prompted to download and install the required version or patch, if necessary.

Defining the application’s name, description, copyright, and installation folder

name The name of the application. You must define this element.

<name>TestApp</name>

In Windows, this is displayed in the application’s title bar and in the Windows Start menu. On Mac OS, it is displayed
in the menu bar when the application is running.
title (Optional) The title displayed by the AIR application installer.

description (Optional) Displayed in the AIR application installer.

<description>An MP3 player.</description>

copyright (Optional) The copyright information for the AIR application.


ADOBE PRODUCT X.0 64
User Guide

<copyright>Copyright © 2006 [YourCompany, Inc.]</copyright>

installFolder (Optional) Identifies the subdirectory of the default installation directory.

<installFolder>Acme</installFolder>

On Windows, the default installation subdirectory is the Program Files directory. On Mac OS, it is the ./Applications
directory. For example, if the installFolder property is set to "Acme" and an application is named "ExampleApp",
then the application will be installed in C:\Program Files\Acme\Example on Windows and in ./Applica-
tions/Acme/Example.app on MacOS.
Use the forward-slash (/) character as the directory separator character if you want to specify a nested subdirectory,
as in the following:
<installFolder>Acme/Power Tools</installFolder>

The installFolder property can contain any Unicode (UTF-8) character except the following, which are
prohibited from use as folder names on various file systems:

Character Hex Code

various 0x00 – x1F


* x2A

" x22

: x3A

> x3C

< x3E

? x3F

\ x5C

| x7C

The installFolder property is optional. If you specify no installFolder property, the application is installed in
a subdirectory of the default installation directory, based on the name property.
programMenuFolder (Optional) Identifies the location in which to place shortcuts to the application in the
Windows All Programs menu. (Ignored on other operating systems.) The restrictions on the characters that are
allowed in the value of the property are the same as those for the installFolder property.
<programMenuFolder>Acme/Applications</programMenuFolder>

Defining the properties of the initial application window


When an AIR application is loaded, the runtime uses the values in the initialWindow element to create the initial
window for the application. The runtime then loads the file specified in the content element into the window.
<initialWindow>
<content>AIRTunes.swf</content>
<title>AIR Tunes</title>
<systemChrome>none</systemChrome>
<transparent>true</transparent>
ADOBE PRODUCT X.0 65
User Guide

<visible>true</visible>
<minimizable>true</minimizable>
<maximizable>true</maximizable>
<resizable>true</resizable>
<width>400</width>
<height>600</height>
<x>150</x>
<y>150</y>
<minSize>300 300</minSize>
<maxSize>800 800</maxSize>
</initialWindow>
The children of the initialWindow element set the properties of the window into which the root content file will be
loaded.
content The value specified for the content element is the URL for the main content file of the application. This
may be either a SWF file or an HTML file. The URL is specified relative to the root of the application.xml file.
title The window title.

systemChrome If you set this attribute to standard, the standard system chrome supplied by the operating system
is displayed. If you set it to none, no system chrome is displayed. When using the Flex mx:WindowedApplication
component, the component applies its custom chrome if standard system chrome is not set. The system chrome
setting cannot be changed at run time.
transparent Set this to "true" if you want the application window to support alpha blending. The transprent
property of a window cannot be changed after the window has been created. A window with transparency may draw
more slowly and require more memory than otherwise. The transparent setting cannot be changed at run time.
Important: You can only set transparent="true" when systemChrome="none".
visible Set this to "true" if you want to have the main window be visible as soon as it is created. The default value
is "false". Note that the Flex mx:WindowedApplication component will automatically set the window to visible
immediately prior to the applicationComplete event, unless the showWindow attribute is set to false in the
MXML definition.
You may want to leave the main window hidden initially, so that changes to the window ‘s position, size, and the
layout of its contents are not shown. You can then display the window by setting the
stage.nativeWindow.visible property (for the main window) to true. For details, see “Working with windows”
on page 69.
width, height, x, y The initial bounds of the main window of the application. If you do not set these values,
the window size is determined by the settings in the root SWF file or, in the case of HTML, by the operating system.
minSize, maxSize The minimum and maximum sizes of the window. If you do not set these values, they will be
determined by the operating system.
minimizable, maximizable, resizable Specifies whether the window can be minimized, maximized, and
resized. By default, these settings default to true.
Note: On operating systems, such as Mac OS X, for which maximizing windows is a resizing operation, both maximi-
zable and resizable must be set to false to prevent the window from being zoomed or resized.

See also
• “Working with windows” on page 69
ADOBE PRODUCT X.0 66
User Guide

Specifying icon files


The icon property specifies one or more icon files to be used for the application. Including an icon is optional. If you
do not specify an icon property, the operating system will display a default icon.
The path specified is relative to the application root directory. The PNG, GIF, and JPEG formats are supported. You
can specify all of the following icon sizes:
<icon>
<image16x16>icons/smallIcon.png</image16x16>
<image32x32>icons/mediumIcon.jpg</image32x32>
<image48x48>icons/bigIcon.gif</image48x48>
<image128x128>icons/biggestIcon.png</image128x128>
</icon>

If an image is specified, it must be the size specified. If all sizes are not provided, the closest size will be scaled to fit
for a given use of the icon by the operating system.
Note: The icons specified are not automatically added to the AIR package. The icon files must be included in their correct
relative locations when the application is packaged.

Signaling the inclusion of an update interface


Normally, AIR installs and updates applications using the default installation dialog. However, you can define your
own mechanism for updating an application using the AIR Updater API. To indicate that your application should
handle the update process itself, you must include the handleUpdates element in the application descriptor file:
<handleUpdates/>

When the installed version of your application includes the handleUpdates element in the application descriptor
file and the user then double-clicks on the AIR file for a new version (the appId attributes must match), the runtime
opens the installed version of the application, rather than the default AIR application installer. Your application logic
can then determine how to proceed with the update operation.
Note: The handleUpdates mechanism only works when the application is already installed and the user double-clicks
the AIR file.
For more information, see “Updating AIR applications programmatically” on page 214.

Registering file types


The fileTypes property lets you define the file types that can be registered for the AIR application.
<fileTypes>
<fileType>
<name>adobe.VideoFile</name>
<extension>avf</extension>
<description>Adobe Video File</description>
<contentType>application/vnd.adobe.video-file</contentType>
<icon>
<image16x16>icons/AIRApp_16.png</image16x16>
<image32x32>icons/AIRApp_32.png</image32x32>
<image48x48>icons/AIRApp_48.png</image48x48>
<image128x128>icons/AIRApp_128.png</image128x128>
</icon>
</fileType>
</fileTypes>
ADOBE PRODUCT X.0 67
User Guide

The fileTypes element is optional. If present, you can specify any number of file type registrations.
The name and extension elements are required for each fileType definition that you include. Note that the
extension is specified without the preceding period. The description element is optional. The contentType
property is also optional.
Icons can be specified for the file extension, using the same format as the application icon element.
When a file type is registered with an AIR application, the application will be invoked whenever a user opens a file
of that type. If the application is already running, AIR will dispatch the invoke event to the running instance.
Otherwise, AIR will launch the application first. In both cases, the file name and location can be retrieved from the
InvokeEvent object dispatched by the application Shell object.

See also
• “Capturing command line arguments” on page 187
68

Part 1: Working with windows and menus


Working with windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .69
Screens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
Working with native menus. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
69

Chapter 12: Working with windows


You use the classes provided by the Adobe AIR windowing API to create and manipulate windows.

GETTING STARTED
• “AIR window basics” on page 69
• “Interacting with a window” on page 21
• “Creating a transparent window application” on page 26
• “Launching native windows” on page 31
• “Creating toast-style windows” on page 51
• “Creating resizable, non-rectangular windows” on page 60
• “Controlling the display order of windows” on page 56

DEVELOPMENT TASKS
• “Creating windows” on page 73
• “Manipulating windows” on page 76
• “Listening for window events” on page 81
• “Using full-screen window mode” on page 82

LANGUAGE REFERENCE
• NativeWindow
• NativeWindowInitOptions

MORE INFORMATION:
You may be able to find more information about working the AIR File API in the AIR Developer Center
(http://www.adobe.com/devnet/air/flex/). Search the site using the keywords ‘AIR windows’.

AIR window basics


The windowing API contains the following classes.
Package Classes

flash.display NativeWindow, NativeWindowInitOptions

Window string constants are defined in the following classes: NativeWindowDisplayState, NativeWindowResize,
NativeWindowSystemChrome, NativeWindowType

flash.events NativeWindowBoundsEvent, NativeWindowDisplayStateEvent, NativeWindowErrorEvent

AIR provides an easy-to-use, cross-platform window API for creating native operating system windows using Flash,
Flex, and HTML programming techniques.
ADOBE PRODUCT X.0 70
User Guide

Important: When you are building an application with the Flex framework, you should generally create windows using
the mx:WindowedApplication and mx:Window components. These components are designed as Flex containers and so
can accept Flex components directly whereas NativeWindow objects cannot. When necessary, NativeWindow properties
and methods can be accessed through the WindowedApplication and Window objects using the stage.nativeWindow
property. See About window containers for more information about the Flex components.
With AIR, you have a wide latitude in developing the look-and-feel of your application. The windows you create can
look like a standard desktop application, matching Apple style when run on the Mac, and conforming to Microsoft
conventions when run on Windows. Or you can use the skinnable, extendible chrome provided by the Flex
framework to establish your own style no matter where your application is run. You can even draw your own
windows with vector and bitmap artwork with full support for transparency and alpha blending against the desktop.
Tired of rectangular windows? Draw a round one.
AIR supports two distinct APIs for working with windows: the Flash-oriented NativeWindow class and the HTML-
oriented JavaScript Window class. Windows created with the NativeWindow class use the Flash stage and display list
directly. To add a visual object to a NativeWindow, you add the object to the display list of the window’s stage.
Windows created with JavaScript use HTML, CSS, and JavaScript to display content. To add a visual object to an
HTML window, you add that content to the HTML DOM.
Note: HTML windows are a special category of NativeWindow. The AIR host adds a nativeWindow property to HTML
windows that provides access to the underlying NativeWindow instance.
The first window of your application is automatically created for you by AIR based on the parameters specified in
the initialWindow element of the application descriptor file. If the root content is a SWF file, a NativeWindow
instance is created and the SWF is loaded into the window. If the root content is an HTML file, an HTML window
is created and the HTML page is loaded.
Native windows use an event-based programming model. Changing any properties or calling methods on the
window object that may affect the display or behavior of application components dispatches notification events to
which interested components can listen. For example, when the system chrome maximize button is clicked by a user
the following sequence of events occurs:
1 The user clicks the maximize button.
2 A displayStateChanging event is dispatched by the window
3 If no registered listeners cancel the event, the window is maximized
4 A displayStateChange event is dispatched by the window to notify listeners that the change has been made.
In addition, events for related changes are dispatched:
5 A move event is dispatched if the top, left corner of the window moved because of the maximize operation.
6 A resize event is dispatched if the window size changed because of the maximize operation
A similar sequence of events is dispatched for minimizing, restoring, closing, moving, and resizing a window.
For detailed information about the window API classes, methods, properties, and events, see the AIR ActionScript
3.0 Language Reference (http://www.adobe.com/go/learn_flex3_aslr). For general information about using the Flash
display list, see the “Display Programming” section of the Programming ActionScript
3.0 (http://www.adobe.com/go/programmingAS3) reference.

See also
• “The application descriptor file structure” on page 62
ADOBE PRODUCT X.0 71
User Guide

Styles of Native Windows


The basic appearance and functional style of window is determined by three properties: type, systemChrome, and
transparent. The type property sets the function of the window. The systemChrome property sets whether the
window uses chrome supplied by native operating system. The transparent property sets whether the supports alpha
blending with the desktop environment. These properties are set on the NativeWindowInitOptions object used to
create the window and cannot be changed. The properties of the initial window created automatically on application
start up are set in the application descriptor file. The type property cannot be set in the application descriptor and is
always normal.
Some settings of these properties are mutually incompatible: systemChrome cannot be set to standard when either
transparent is true or type is lightweight.

Window Type

The type property combines certain chrome and visibility attributes of the native operating system to create three
functional types of window. The type property can only be set when creating a new NativeWindow. AIR supports
the following window types:
Normal A typical window. Normal windows use the full-size chrome and appear on the Windows task bar and the
Mac OS X window menu.
Utility A tool palette. Utility windows use a slimmer version of the system chrome and do not appear on the
Windows task bar and the Mac OS-X window menu.
Lightweight Lightweight windows have little or no chrome and do not appear on the Windows task bar and the Mac
OS X window menu. In addition, lightweight windows do not have the System (Alt-Space) menu on Windows.
Lightweight windows are suitable for notification bubbles and controls such as combo-boxes that open a short-lived
display area. When the lightweight type is used, systemChrome must be set to “none”.

Window chrome

Window chrome is the set of controls that allow users to manipulate a window in the desktop environment. For an
AIR application, you have the following choices for window chrome:
System chrome System chrome displays your window within the set of standard controls created and styled by the
user’s operating system. Use system chrome to make your application look like a standard desktop application for
the operating system in which it is run. System chrome is managed by the system, your application has no direct
access to the controls themselves, but can react to the events dispatched when the controls are used. If system chrome
is used for a window, the transparency property must be set to false.
Note: In this Beta release, the Alternate systemChrome setting is not supported.
Flex chrome When using the Flex WindowedApplication and Window components, your window will be displayed
with chrome provided by the Flex framework. If the Flex components are used along with system chrome, the Flex
chrome is not displayed.
Custom chrome When you create a window with no system chrome and you do not use the Flex mx:WindowedAp-
plication of mxWindow components, then you must add your own controls to handle the interactions between a user
and the window. You are also free to make transparent, non-rectangular windows.

Window transparency

To allow alpha blending of a window with the desktop or other windows, set the window's transparent property to
true. The transparent property must be set before the window is created and cannot be changed. The transparent
property is set in the NativeWindowInitOptions object used to create a window. (For the initial window created for
your application, the initialization options are taken from attributes of the initialWindow element in the appli-
cation descriptor file.)
ADOBE PRODUCT X.0 72
User Guide

A transparent window has no default background. Any window area not occupied by a display object will be
invisible. If a display object has an alpha setting of less than one, then any thing below the object will show through,
including other display objects in the same window, other windows, and the desktop. Rendering large alpha-blended
areas can be slow, so the effect should be used conservatively.
Transparent windows are useful when you want to create:
• Applications with borders that are irregular in shape
• Applications that must “fade out” or appear to be invisible
Transparency cannot be used for windows with system chrome.

Transparency in an MXML application window

If your Flex application uses CSS style properties that set a background image or color, your window will not be
transparent. Likewise, if the Application tag in your MXML file defines a background color, this setting will override
a transparent window mode. To ensure the transparency of your application window, include the following code in
your style sheet or in the <mx:Style> element that is contained in your application MXML file:
Application
{
background-image:"";
background-color:"";
}

These declarations suppress the appearance of background color and image in the application window.

A visual window catalogue


The following table demonstrates the visual effects of different combinations of window property settings.
Type System Chrome Transparent Mac OS X Microsoft Windows
Standard normal Not supported

Utility normal Not supported

Any none false


ADOBE PRODUCT X.0 73
User Guide

Type System Chrome Transparent Mac OS X Microsoft Windows


Any none true

Any none true mx:WindowedApplication

Limitations in the Beta Release

The following window-related features are not supported in this Beta release:
• The windowing API does not currently support the Toolbars|OS X Toolbar or the OS X Proxy Icon.
• Alternate system chrome

Creating windows
AIR provides the following primary means for creating applications windows:
• AIR automatically creates the first window for every application. This window is initialized according to the
settings defined in the application descriptor file. If the root content identified in the descriptor is a SWF file, then
you can access the properties and methods of the window instance through the Stage.nativeWindow property and
the NativeWindow API. In addition, the main class of the SWF file must extend from Sprite or a subclass of Sprite.
(The Flex WindowedApplication and Application components, which are Sprite subclasses, are used as the main
class of Flex applications.) If the root content is an HTML file, then you can access the properties and methods of the
window through the JavaScript Window object’s nativeWindow property.
• You can create a new instance of the NativeWindow class. New NativeWindows are initialized by the NativeWin-
dowInitOptions object passed to the window constructor. You can access the properties and methods directly from
the object reference returned by the constructor.
• You can use the HTMLControl.createRootWindow() method to create a window to display HTML content.
• You can use the JavaScript Window.open() method to open new windows from JavaScript code. The properties
and methods of JavaScript-created windows can only be accessed through JavaScript and the window can only
display HTML. JavaScript-created HTML windows automatically include system chrome.
Note: You can create new NativeWindows from JavaScript code (through the AIR window.runtime property).
This section contains the following topics:
• “Creating a new NativeWindow” on page 74
• “Adding content to a window” on page 74
• “Example: Creating windows with ActionScript” on page 75
ADOBE PRODUCT X.0 74
User Guide

Creating a new NativeWindow


To create a new NativeWindow, create a NativeWindowInitOptions object and pass it to the NativeWindow
constructor.
var options:NativeWindowInitOptions = new NativeWindowInitOptions();
options.systemChrome = NativeWindowSystemChrome.STANDARD;
options.transparent = false;
var newWin:NativeWindow = new NativeWindow(options);

To avoid displaying the intermediate states of the window as you set its bounds, position, and contents, do not set
the NativeWindow.visible property to true or call the activate() method until you have finished initializing
the window. The NativeWindowInitOptions object sets those properties of a window that cannot be changed after
the window has been created.
Note: Setting systemChrome="standard" and transparent="true" is not a supported combination.
Once the window is created, you may initialize its properties and load content into the window using the stage
property and Flash display list techniques.
Note: To determine the maximum and minimum window sizes allowed on the current operating system, use the
following static NativeWindow properties:
var maxOSSize:Point = NativeWindow.systemMaxSize;
var minOSSize:Point = NativeWindow.systemMinSize;

Adding content to a window


To add content to a native window, add display objects to the window stage. You can create display objects dynami-
cally or load existing content with the flash.display.Loader class. Within an HTML window, you can add
content by changing the location property (to load a new page) or inserting HTML content into the DOM.
When you load SWF content, or HTML content containing JavaScript, you must take the AIR security model into
consideration. Any content in the application security sandbox, that is content installed with your application and
loadable with the app-resource: URL scheme, will have full privileges to access all the AIR APIs. Any content loaded
from outside this sandbox will be restricted both from accessing the security-restricted AIR APIs and from cross-
scripting other content. JavaScript content outside the application sandbox will not be able to use the nativeWindow
or htmlControl properties of the JavaScript Window object.
To allow safe cross-scripting, you can use a sandbox bridge to provide a limited interface between application content
and non-application content.

Loading a SWF or image

You can load Flash or images into the display list of a native window using the flash.display.Loader class.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
import flash.display.Loader;

public class LoadedSWF extends Sprite


{
public function LoadedSWF(){
var loader:Loader = new Loader();
loader.load(new URLRequest("visual.swf"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,loadFlash);
}

private function loadFlash(event:Event):void{


ADOBE PRODUCT X.0 75
User Guide

addChild(event.target.loader);
}
}
}

Visual Flash content added to an HTML window must be displayed either on top of the HTML content as an overlay,
or beneath transparent HTML content. Such content must also be positioned and resized separately from the HTML
content.
You can load a SWF file that contains library code for use in an HTML-based application. The simplest way to load
a SWF in an HTML window is to use the script tag, but you can also use the flash.display.Loader API directly.
Note: Older SWF files created using ActionScript 1 or 2 will share global state such as class definitions, singletons, and
global variables if they are loaded into the same window. If such a SWF relies on untouched global state to work correctly,
it cannot be loaded more than once into the same window, or loaded into the same window as another SWF using
overlapping class definitions and variables. This content can be loaded into separate windows.

Loading HTML content into a NativeWindow

To load HTML content into a NativeWindow, you can either add an HTMLControl object to the window stage and
load the HTML content into the HTMLControl, or create a new window that already contains an HTMLControl
object by using the HTMLControl.createRootWindow()method. The following example displays HTML content
within a 300 by 500 pixel display area on the stage of a native window:
//newWindow is a NativeWindow instance
var htmlView:HTMLControl = new HTMLControl();
html.width = 300;
html.height = 500;

//set the stage so display objects are added to the top-left and not scaled
newWindow.stage.align = "TL";
newWindow.stage.scaleMode = "noScale";
newWindow.stage.addChild( htmlView );

//urlString is the URL of the HTML page to load


htmlView.load( new URLRequest(urlString) );

To load an HTML page into a Flex application, you can use the Flex HTMLComponent.

Loading Flash content within an HTML page

In this Beta release, displaying Flash content embedded in an HTML page within an instance of a HTMLControl is
not supported. Any Flash objects in the page will be displayed as blank rectangles. You can, however, load or create
Flash content using the AIR APIs and add it to the HTML window as an overlay or underlay to the HTML layer.

See also
• “Adding HTML content to SWF-based applications” on page 162
• “Scripting between content in different domains” on page 202

Example: Creating windows with ActionScript


The following example illustrates how to create new windows using ActionScript.
public function createNativeWindow():void {
//create the init options
var options:NativeWindowInitOptions = new NativeWindowInitOptions();
options.transparent = false;
options.systemChrome = NativeWindowSystemChrome.STANDARD;
ADOBE PRODUCT X.0 76
User Guide

options.type = NativeWindowType.NORMAL;

//create the window


var newWindow:NativeWindow = new NativeWindow(options);
newWindow.title = "A title";
newWindow.width = 600;
newWindow.height = 400;

//add a sprite to the window


newWindow.stage.align = StageAlign.TOP_LEFT;
newWindow.stage.scaleMode = StageScaleMode.NO_SCALE;

//activate and show the new window


newWindow.activate();
}

Manipulating windows
This section highlights using the properties and methods of the NativeWindow class to manipulate the appearance
and behavior of application windows and includes the following topics:
• “Getting a NativeWindow instance” on page 76
• “Activating, showing and hiding windows” on page 77
• “Maximizing, minimizing, and restoring a window” on page 77
• “Changing the window display order” on page 78
• “Closing a window” on page 78
• “Allowing cancellation of window operations” on page 79
• “Example: Minimizing, maximizing, restoring and closing a window” on page 79
• “Example: Resizing and moving windows” on page 80

Getting a NativeWindow instance


To manipulate a window, you must first get the window instance. You can get a window instance from one of the
following places:
The window constructor That is, the window constructor for a new NativeWindow.
The window stage That is, stage.nativeWindow.
Any display object on the stage That is, myDisplayObject.stage.nativeWindow.
A window event The target property of the event object references the window that dispatched the event.
The global nativeWindow property of an HTMLControl or HTML window That is, window.nativeWindow.
The shell object Shell.shell.activeWindow references the active window of an application (but returns null if
the active window is not a window of this AIR application). The Shell.shell.openedWindows array contains all
of the windows in an AIR application that have not been closed.
Because the Flex Application, WindowedApplication, and Window objects are display objects, you can easily
reference the application window in an MXML file using the stage property, as follows:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
applicationComplete="init();">
<mx:Script>
<![CDATA[
import flash.display.NativeWindow;
ADOBE PRODUCT X.0 77
User Guide

public function init():void{


var appWindow:NativeWindow = this.stage.nativeWindow;
//set window properties
appWindow.visible = true;
}
]]>
</mx:Script>
</WindowedApplication
Note: Until the WindowedApplication or Window components is added to the window stage by the Flex framework, the
component's stage property will be null. This behavior is consistent with that of the Flex Application component, but
does mean that it is not possible to access the stage or the NativeWindow instance in listeners for events that occur earlier
in the initialization cycle of the WindowedApplication and Window components, such as creationComplete. It is safe
to access the stage and NativeWindow instance when the applicationComplete event is dispatched.

Activating, showing and hiding windows


To activate a window, call the NativeWindow.activate() method. Activating a window will bring the window to front,
give it keyboard and mouse focus, and, if necessary, make it visible by restoring the window or setting
visible=true. Activating a window will not change the ordering of other windows in the application.

To show a hidden window without activating it, set the visible property to true. This will bring the window to the
front, but will not take the keyboard or mouse focus.
To hide a window from view, set the NativeWindow.visible property to false. Hiding a window will suppress the
display of both the window, any related task bar icons, and, on MacOS X, the entry in the Windows menu.
Note: On Mac OS X, it is not possible to completely hide a minimized window that has a dock icon. If the visible
property is set to false on a minimized window, the dock icon for the window will still be displayed. If the user clicks
the icon, the window will be restored to a visible state and displayed.

Maximizing, minimizing, and restoring a window


To maximize the window, use the NativeWindow.maximize() method.
myWindow.maximize();

To minimize the window, use the NativeWindow.minimize() method.


myWindow.minimize();

Restoring a window returns it to the size that it was before it was either minimized or maximized.
To restore the window (that is, return it to the size that it was before it was either minimized or maximized), use the
NativeWindow.restore() method.
myWindow.restore();

Note: The behavior that results from maximizing an AIR window is different than the Mac OS X standard behavior.
Rather than toggling between an application-defined “standard” size and the last size set by the user, AIR windows toggle
between the size last set by the application or user and the full usable area of the screen.
ADOBE PRODUCT X.0 78
User Guide

Changing the window display order


AIR provides two display order groups for windows, controlled by the setting of the alwaysInFront property. A
window can be placed in the normal group, which should be used for almost all windows, by setting
alwaysInFront=false. Windows in the second group are always displayed in front of windows in the normal group,
even the windows of other applications. Because this behavior can be disruptive to a user, the “always in front” group
should only be used when necessary and appropriate. Examples of justified uses of this display order group are:
• Temporary pop-up display windows for controls such as tooltips, pop-up lists, custom menus, or comboboxes.
Because these windows should close when they lose focus, the annoyance of blocking a user from viewing another
window can be avoided.
• Screensavers
• Extremely urgent error messages and alerts. When an irrevocable change may occur if the user does not respond
in a timely manner, it may be justified to push an alert widow to the forefront. However, most errors and alerts can
be handled in the normal window display order. (Please refer to the AIR end user license agreement (EULA) for
information on AIR’s suitability for time-critical applications such as aircraft and nuclear power plant control.)
• Short-lived toast-style windows.
• Full-screen windows for games or video
Note: AIR does not enforce proper use of the alwaysInFront property. However, if your application disrupts a user’s
workflow, it is very likely to be consigned to that same user’s trash can.
The NativeWindow class provides the following methods for setting the display order of a window relative to other
windows:
alwaysInFront property Specifies in which display-order grouping a window will be placed. In almost all cases
alwaysInFront=false is the best setting. Changing the value from false to true will also bring the window to
the front of all windows (but will not activate it). Changing the value from true to false will put the window at the
front of the normal display order group (but behind all the windows in the always in front group). Setting the
property to the current value will not change the window display order.
orderToFront() Brings the window to the front of its display order group.
orderInFrontOf() Brings the window in front of another window.
orderToBack() Sends the window behind all other windows in its display order group.
orderBehind() Sends the window directly behind another window.
Note: If a window is hidden (visible=false) or minimized, then calling the display order methods will have no affect.

Closing a window
To close a window, use the NativeWindow.close method.
Closing a window unloads the contents of the window, but if other objects have references to this content, the content
objects will not be destroyed. The NativeWindow.close() method executes asynchronously, the application that is
contained in the window continues to run during the closing process. The close method dispatches a close event
when the close operation is complete. The NativeWindow object is still technically valid, but accessing most
properties and methods on a closed window will generate an IllegalOperationError. You cannot reopen a closed
window. Check the closed property of a window to test whether a window has been closed. To simply hide a window
from view, set the NativeWindow.visible property to false.
If the Shell.autoExit property is true, which is the default, then the application exits when its last window closes.
ADOBE PRODUCT X.0 79
User Guide

Allowing cancellation of window operations


When a window uses system chrome, user interaction with the window can be canceled by listening for, and
canceling the default behavior of the appropriate events. For example, when a user clicks the system chrome close
button, the closing event is dispatched. If any registered listener calls the preventDefault() method of the event,
then the window will not close.
When a window does not use system chrome, notification events for intended changes are not automatically
dispatched before the change is made. Hence, if you call the methods for closing a window, changing the window
state, or set any of the window bounds properties, the change cannot be canceled. To notify components in your
application before a window change is actually made, your application logic can dispatch the relevant notification
event using the dispatchEvent() method of the window.
For example, the following logic implements a cancelable event handler for a window close button:
public function onCloseCommand(event:MouseEvent):void{
var closingEvent:Event = new Event(Event.CLOSING,true,true);
dispatchEvent(closing);
if(!closingEvent.isDefaultPrevented()){
win.close();
}
}

Note that the dispatchEvent() method returns false if the event preventDefault() method is called by a
listener. However, it can also return false for other reasons, so it is better to explicitly use the
isDefaultPrevented() method to test whether the change should be canceled.

Example: Minimizing, maximizing, restoring and closing a window


The following short MXML application demonstrates the Window maximize(), minimize(), restore(), and
close() methods.
<?xml version="1.0" encoding="utf-8"?>

<mx:WindowedApplication
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical">

<mx:Script>
<![CDATA[
public function minimizeWindow():void
{
this.stage.nativeWindow.minimize();
}

public function maximizeWindow():void


{
this.stage.nativeWindow.maximize();
}

public function restoreWindow():void


{
this.stage.nativeWindow.restore();
}

public function closeWindow():void


{
this.stage.nativeWindow.close();
}
ADOBE PRODUCT X.0 80
User Guide

]]>
</mx:Script>

<mx:VBox>
<mx:Button label="Minimize" click="minimizeWindow()"/>
<mx:Button label="Restore" click="restoreWindow()"/>
<mx:Button label="Maximize" click="maximizeWindow()"/>
<mx:Button label="Close" click="closeWindow()"/>
</mx:VBox>

</mx:WindowedApplication>

Example: Resizing and moving windows


An AIR application can initiate a system-controlled operation to resize or move its containing window. The process
that is used to complete the operation depends on whether the call to the method that completes the operation is
made from within a mouseDown event.
Note: To resize or move a window, you must first obtain a reference to the NativeWindow instance. For information
about how to obtain a window reference, see “Getting a NativeWindow instance” on page 76.
To resize a window, use the NativeWindow.startResize() method. This method triggers a system-controlled
resizing of the window. When this method is called from a mouseDown event, the resizing process is mouse-driven
and completes when the operating system receives a mouseUp event. A call to the NativeWindow.startResize()
method that is not made from a mouseDown event triggers a system-controlled resizing operation that may be
mouse-driven or keyboard-driven but remains consistent with the default resizing sequence for the operating
system.
To move a window without resizing it, use the NativeWindow.startMove() method. Like the startResize()
method, when the startMove() method is called from a mouseDown event, the move process is mouse-driven and
completes when the operating system receives a mouseUp event. A call to the NativeWindow.startMove() method
that is not made from a mouseDown event triggers a system-controlled move that may be mouse-driven or keyboard-
driven but remains consistent with the default move sequence for the operating system.
For more information about the startResize and startMove methods, see the AIR ActionScript 3.0 Language
Reference.
The following example shows how to initiate resizing and moving operations on a window:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.display.NativeWindowResize;

public class NativeWindowResizeExample extends Sprite


{
public function NativeWindowResizeExample():void
{
// Fills a background area.
this.graphics.beginFill(0xFFFFFF);
this.graphics.drawRect(0, 0, 400, 300);
this.graphics.endFill();

// Creates a square area where a mouse down will trigger a resize.


var resizeHandle:Sprite = createSprite(0xCCCCCC, 20, this.width - 20,
this.height - 20);
resizeHandle.addEventListener(MouseEvent.MOUSE_DOWN, onStartResize);
ADOBE PRODUCT X.0 81
User Guide

// Creates a square area where a mouse down will trigger a move.


var moveHandle:Sprite = createSprite(0xCCCCCC, 20, this.width - 20, 0);
moveHandle.addEventListener(MouseEvent.MOUSE_DOWN, onStartMove);
}

public function createSprite(color:int, size:int, x:int, y:int):Sprite


{
var s:Sprite = new Sprite();
s.graphics.beginFill(color);
s.graphics.drawRect(0, 0, size, size);
s.graphics.endFill();
s.x = x;
s.y = y;
this.addChild(s);
return s;
}

public function onStartResize(event:MouseEvent):void


{
this.stage.nativeWindow.startResize(NativeWindowResize.BOTTOM_RIGHT);
}

public function onStartMove(event:MouseEvent):void


{
this.stage.nativeWindow.startMove();
}
}
}

Listening for window events


To listen for the events dispatched by a window, register a listener with the window instance. For example, to listen
for the closing event, register a listener with the window as follows:
myWindow.addEventListener(Event.CLOSING, onClosingEvent);

When an event is dispatched, the target property references the window sending the event.
Most window events have two related messages. The first message signals that a window change is imminent (and
can be canceled), while the second message signals that the change has occurred. For example, when a user clicks the
close button of a window, the closing event message is dispatched. If no listeners cancel the event, the window closes
and the close event is dispatched to any listeners.
Events in the flash.events.Event class:
• ACTIVATE
• DEACTIVATE
• CLOSING
• CLOSE
NativeWindowBoundsEvent:
Use the beforeBounds and afterBounds properties to determine the window bounds before and after the
impending or completed change.
• MOVING
• MOVE
ADOBE PRODUCT X.0 82
User Guide

• RESIZING
• RESIZE
NativeWindowDisplayStateEvent:
Use the beforeDisplayState and afterDisplayState properties to determine the window display state before
and after the impending or completed change.
• DISPLAY_STATE_CHANGING
• DISPLAY_STATE_CHANGE

Using full-screen window mode


Setting the displayState property of the Stage to StageDisplayState.FULL_SCREEN puts the window in full-
screen mode, and keyboard input is permitted in this mode. (In SWF content running in a browser, keyboard input
is not permitted). To exit full-screen mode, the user presses the Escape key.
For example, the following Flex code defines a simple AIR application that sets up a simple full-screen terminal:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
applicationComplete="init()" backgroundColor="0x003030" focusRect="false">
<mx:Script>
<![CDATA[
private function init():void
{
stage.displayState = StageDisplayState.FULL_SCREEN;
focusManager.setFocus(terminal);
terminal.text = "Welcome to the dumb terminal app. Press the ESC key to
exit..\n";
terminal.selectionBeginIndex = terminal.text.length;
terminal.selectionEndIndex = terminal.text.length;
}
]]>
</mx:Script>
<mx:TextArea
id="terminal"
height="100%" width="100%"
scroll="false"
backgroundColor="0x003030"
color="0xCCFF00"
fontFamily="Lucida Console"
fontSize="44"/>
</mx:WindowedApplication>
83

Chapter 13: Screens


Use the classes provided by the AIR screen API to discover information about the desktop display screens attached
to a computer.

GETTING STARTED
• “Screen basics” on page 83
• “Measuring the virtual desktop” on page 1

DEVELOPMENT TASKS
• “Enumerating the screens” on page 84

LANGUAGE REFERENCE
• Screen

MORE INFORMATION
You may be able to find more information about working the AIR File API in the AIR Developer Center
(http://www.adobe.com/devnet/air/flex/). Search the site using the keywords ‘AIR screens’.

Screen basics
The screen API contains a single class, Screen, which provides static members for getting system screen information,
and instance members for describing a particular screen.
A computer system may have several monitors or displays attached, which may correspond to several desktop
screens arranged in a virtual space. The AIR Screen class provides information about the screens, their relative
arrangement, and their usable space. If more than one monitor maps to the same screen, only one screen exists. If
the size of a screen is larger than the display area of the monitor, there is no way to determine which portion of the
screen is currently visible.
A screen represents an independent desktop display area. Screens are described as rectangles within the virtual
desktop. The top-left corner of screen designated as the primary display is the origin of the virtual desktop
coordinate system. All values used to describe a screen are provided in pixels.
ADOBE PRODUCT X.0 84
User Guide

In this screen arrangement, two screens exist on the virtual desktop. The coordinates of the top-left corner of the main screen (#1) are always
(0,0). If the screen arrangement is changed to designate screen #2 as the main screen, then the coordinates of screen #1 will become negative.
Menubars, taskbars and docks are excluded when reporting the usable bounds for a screen.

For detailed information about the screen API class, methods, properties, and events, see the Flex ActionScript 3.0
Language Reference (http://www.adobe.com/go/learn_flex3_aslr).

Enumerating the screens


You can enumerate the screens of the virtual desktop with the following screen methods and properties:
Screen.screens Provides an array of Screen objects describing the available screens. Note that the order of the
array is not significant.
Screen.mainScreen Provides a Screen object for the main screen. On Mac OS X, this will be the screen displaying
the menu bar. On Windows, this will be the system-designated primary screen.
Screen.getScreensForRectangle() Provides an array of Screen objects describing the screens intersected by the
given rectangle. The rectangle passed to this method is in pixel coordinates on the virtual desktop. If no screens are
intersected by the rectangle, then the array will be empty.
You should not save the values returned by these methods and properties. The user or operating system can change
the available screens and their arrangement at any time.
ADOBE PRODUCT X.0 85
User Guide

Example: Moving a window between screens


This example uses the screen API to move a window between multiple screens in response to pressing the arrow keys.
To move the window to the next screen, the example gets the screens array and sorts it either vertically or horizon-
tally (depending on the arrow key pressed). The code then walks through the sorted array, comparing each screen
to the coordinates of the current screen. To identify the current screen of the window, the example calls
Screen.getScreensForRectangle(), passing in the window bounds.

package {
import flash.display.Sprite;
import flash.display.Screen;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.display.StageAlign;
import flash.display.StageScaleMode;

public class ScreenExample extends Sprite


{
public function ScreenExample()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;

stage.addEventListener(KeyboardEvent.KEY_DOWN,onKey);
}

private function onKey(event:KeyboardEvent):void{


if(Screen.screens.length > 1){
switch(event.keyCode){
case Keyboard.LEFT :
moveLeft();
break;
case Keyboard.RIGHT :
moveRight();
break;
case Keyboard.UP :
moveUp();
break;
case Keyboard.DOWN :
moveDown();
break;
}
}
}

private function moveLeft():void{


var currentScreen = getCurrentScreen();
var left:Array = Screen.screens;
left.sort(sortHorizontal);
for(var i:int = 0; i < left.length - 1; i++){
if(left[i].bounds.left < stage.nativeWindow.bounds.left){
stage.nativeWindow.x +=
left[i].bounds.left - currentScreen.bounds.left;
stage.nativeWindow.y += left[i].bounds.top - currentScreen.bounds.top;
}
}
}

private function moveRight():void{


var currentScreen:Screen = getCurrentScreen();
var left:Array = Screen.screens;
ADOBE PRODUCT X.0 86
User Guide

left.sort(sortHorizontal);
for(var i:int = left.length - 1; i > 0; i--){
if(left[i].bounds.left > stage.nativeWindow.bounds.left){
stage.nativeWindow.x +=
left[i].bounds.left - currentScreen.bounds.left;
stage.nativeWindow.y += left[i].bounds.top - currentScreen.bounds.top;
}
}
}

private function moveUp():void{


var currentScreen:Screen = getCurrentScreen();
var top:Array = Screen.screens;
top.sort(sortVertical);
for(var i:int = 0; i < top.length - 1; i++){
if(top[i].bounds.top < stage.nativeWindow.bounds.top){
stage.nativeWindow.x += top[i].bounds.left - currentScreen.bounds.left;
stage.nativeWindow.y += top[i].bounds.top - currentScreen.bounds.top;
break;
}
}
}

private function moveDown():void{


var currentScreen:Screen = getCurrentScreen();

var top:Array = Screen.screens;


top.sort(sortVertical);
for(var i:int = top.length - 1; i > 0; i--){
if(top[i].bounds.top > stage.nativeWindow.bounds.top){
stage.nativeWindow.x += top[i].bounds.left - currentScreen.bounds.left;
stage.nativeWindow.y += top[i].bounds.top - currentScreen.bounds.top;
break;
}
}
}

private function sortHorizontal(a:Screen,b:Screen):int{


if (a.bounds.left > b.bounds.left){
return 1;
} else if (a.bounds.left < b.bounds.left){
return -1;
} else {return 0;}
}

private function sortVertical(a:Screen,b:Screen):int{


if (a.bounds.top > b.bounds.top){
return 1;
} else if (a.bounds.top < b.bounds.top){
return -1;
} else {return 0;}
}

private function getCurrentScreen():Screen{


var current:Screen;
var screens:Array = Screen.getScreensForRectangle(stage.nativeWindow.bounds);
(screens.length > 0) ? current = screens[0] : current = Screen.mainScreen;
return current;
}
}
}
ADOBE PRODUCT X.0 87
User Guide
88

Chapter 14: Working with native menus


Use the classes in the Native Menu API to define application, window, context, and pop-up menus.

GETTING STARTED
• “AIR menu basics” on page 88
• “Adding native menus to an AIR application” on page 46

DEVELOPMENT TASKS
• “Creating native menus” on page 92

LANGUAGE REFERENCE
• NativeMenu
• NativeMenuItem

MORE INFORMATION
You may be able to find more information about working the AIR File API in the AIR Developer Center
(http://www.adobe.com/devnet/air/flex/). Search the site using the keywords ‘AIR menus’.

AIR menu basics


The native menu classes allow you to access the native menu features of the operating system on which your appli-
cation is running. NativeMenu objects can be used for application menus (available on OS X), window menus
(available on Windows), context menus, and pop-up menus.

Types of menus
AIR supports the following types of menus:
Application menus Application menus can be accessed through the Shell class on operating systems that support
them. On Mac OS X, an application menu is automatically provided by the operating system. Your application can
use the AIR menu API to add items and submenu s to the standard menus as well as add handlers for the existing
menu commands. You can also replace the standard menu entirely.
Window menus Menus can be added to a window by creating a new NativeMenu object and assigning it to the
NativeWindow.menu property on operating systems that support them. Window menus are supported on the
Windows operating system, but not Mac OS X. Native menus can only be used for windows that have system chrome.
Context menus Context menus can be added to InteractiveObjects and document elements in an HTMLControl.
You would typically use the Webkit API for context menus on an HTML element, but you could also use a context
menu directly on the HTMLControl and modify it based on the element which generated the open menu event.
Dock and system tray icon menus Menus can be added to the dock or system tray icons by creating a new
NativeMenu object and assign it to the Shell.shell.icon.menu property. On Mac OS X, the new menu will be added
above the standard items in the menu. On Windows, there is no default menu.
ADOBE PRODUCT X.0 89
User Guide

Flex menus The Flex framework provides a set of Flex menu classes. The Flex classes can be used for windows that
do not have system chrome and allow you to specify menus declaratively in MXML format. If you are using the Flex
Framework, you may wish to use the Flex menu classes for window menus instead of the native classes.
Custom menus Native menus are drawn by the operating system and as such exist outside the Flash and HTML
rendering models. You are, of course, free to draw your own non-native menus. However, the AIR menu classes do
not provide an “owner draw” facility.

Menu structure
A menu contains one or more menu items. A menu item can be a command, a submenu, or a separator. Normally, a
menu item is a command. A menu item becomes a submenu when you assign a NativeMenu object to the submenu
property of the item. A menu item becomes a separator when you set the isSeparator parameter of the item
constructor to true.
The root-level menu in the structure contains the submenu items that appear on the menu bar. Only submenu items
should be added to the root menu of application and window menus. (Although some operating systems will display
command items on the menu bar, putting commands directly on the menu bar is still unconventional.) Command
and separator items can be used at the root level of pop-up and context menus.
The following diagram illustrates the structure of a typical menu. The root menu contains submenu items for a “File”
menu and an “Edit” menu. The “File” menu contains two commands and a submenu item for opening recent
documents (which has a menu containing three items). The “Edit” menu contains three commands and a separator.
ADOBE PRODUCT X.0 90
User Guide

To view a code example that creates this menu see Example: Window and application menu.

Menu events
Menus and menu items both dispatch displaying and select events:
Displaying: Immediately before a menu is displayed, the menu and its menu items will dispatch a displaying event
to any registered listeners. The displaying event gives you an opportunity to update the menu contents or item
appearance before it is shown to the user. For example, in the handler for the displaying event of an “Open Recent”
menu, you could change the menu items to reflect the current list of recently viewed documents.
ADOBE PRODUCT X.0 91
User Guide

The target property of the event object will always be the menu that is about to be displayed. The currentTarget
will be the object on which the listener is registered, either the menu itself, or one of its items.
Select: When a command item is chosen by the user, the item will dispatch a select event to any registered listeners.
Submenu and separator items cannot be selected and so never dispatch a select event.
Select events bubble up from items to their containing menu, on up to the root menu. You can listen for select events
directly on an item and you can listen higher up in the menu structure. When you listen for the select event on a
menu, you can identify the selected item using the event target property. As the event bubbles up through the menu
hierarchy, the currentTarget property of the event object identifies the current menu object.

Mnemonics
Mnemonics are part of the operating system keyboard interface to menus. Both Mac OS X and Windows allow users
to open menus and select commands with the keyboard, but there are subtle differences. On Mac OS X, the user
types the first letter or two of the menu or command and then types return.
On Windows, only a single letter is significant. By default, the significant letter is the first character in the label, but
if you assign a mnemonic to the menu item, then the designated letter will become the significant character. If two
items in a menu have the same significant character (whether or not a mnemonic has been assigned), then the user’s
keyboard interaction with the menu changes slightly. Instead of pressing a single letter to select the menu or
command, the user must press the letter as many times as necessary to highlight the desired item and then press the
enter key to complete the selection. To maintain a consistent behavior, it is generally advisable to assign a unique
mnemonic to each item in a menu for window menus.
Specify the mnemonic character as an index into the label string. The index of the first character in a label is 0. Thus,
to use “r” as the mnemonic for a menu item labeled, “Format”, you would set the mnemonicIndex property equal to 2.
var item:NativeMenuItem = new NativeMenuItem("Format");
item.mnemonicIndex = 2;
Note: If you are localizing the menu labels, be sure to update the mnemonic indexes as well as the label strings. (AIR
does not provide built-in support for localization. However, localization support is provided by the Flex framework.)

Menu item state


Menu items have the two state properties, checked and enabled:
checked Set to true to display a check mark next to the item label.
var item:NativeMenuItem = new NativeMenuItem("Format");
item.checked = true;

enabled toggle the value between true and false to control whether the command is enabled. Disabled items are
visually “grayed-out” and do not dispatch select events.
var item:NativeMenuItem = new NativeMenuItem("Format");
item.enabled = false;

Attaching an object to a menu item


The data property of the NativeMenuItem class allows you to reference an arbitrary object in each item. For
example, in an “Open Recent” menu, you could assign the File object for each document to each menu item.
var file:File = new File("app-storage:/GreatGatsby.pdf");
var menuItem:NativeMenuItem = docMenu.addItem(new NativeMenuItem(file.name));
menuItem.data = file;
ADOBE PRODUCT X.0 92
User Guide

Creating native menus


• “Creating a new menu” on page 92
• “Defining menu items, submenus, and separators” on page 92
• “Creating a context menu” on page 93
• “Displaying a pop-up menu” on page 94
• “Handling menu events” on page 94
• “Defining menus declaratively” on page 94
• “Example: Window and application menu” on page 95

Creating a new menu


To create a menu, create a new NativeMenu object to serve as the root of the menu:
var root:NativeMenu = new NativeMenu();

In the root menu of a window or application, all the items must be submenus. (The root of a context menu can
contain all three types of menu items.) AIR provides two ways to create submenus. You can create the menu item and
assign the submenu in one step with the addSubmenu() method:
var editMenu:NativeMenuItem = root.addSubmenu(new NativeMenu(), "Edit");

You can also create the menu item and assign the submenu separately:
var editMenu:NativeMenuItem = root.addItem("Edit");
editMenu.submenu = new NativeMenu();

After the menu is created, you can assign it as an application, window, icon, or context menu:

Setting the application menu:


Shell.shell.menu = root;

Setting a window menu:


nativeWindowObject.menu = root;

Setting a context menu


interactiveObject.contextMenu = root;

Setting a dock icon menu


DockIcon(Shell.shell.icon).menu = root;

Setting a system tray icon menu


SystemTrayIcon(Shell.shell.icon).menu = root;

Defining menu items, submenus, and separators


A menu item can be a command, a submenu, or a separator. A menu item becomes a separator if you set the
isSeparator parameter to true in the item constructor. An item is a submenu if you assign a NativeMenu object
to it’s submenu property (or create the item using the addSubmenu() method). Otherwise, the item is a command.
Only command items will dispatch select events.
Items appear in the menu in the order in which they are added, unless you add the items at a specific index, for
example by using the addItemAt() method of a menu object.
ADOBE PRODUCT X.0 93
User Guide

Creating a command:
var copy:NativeMenuItem = new NativeMenuItem("Copy",false);
copy.addEventListener(Event.SELECT,onCopyCommand);
editMenu.addItem(copy);

Creating a submenu:
var editMenu:NativeMenuItem = new NativeMenuItem("Edit", false);
editMenu.submenu = new NativeMenu();

You can also create an item and its submenu in one step using the parent menu’s addSubmenu() method:
var editMenu:NativeMenuItem = root.addSubmenu(new NativeMenu(), "Edit");

Creating a separator:
var separatorA:NativeMenuItem = new NativeMenuItem("A", true);
editMenu.addItem(separatorA);

Creating a context menu


In AIR, the ContextMenu API classes inherit from the NativeMenu classes. The ContextMenu classes provide an
event property, ContextMenuEvent.mouseTarget, that identifies the object that was clicked to open the menu.
Context menus can be assigned to any Flash object that inherits from the InteractiveObject class. To assign a context
menu to an object, set the contextMenu property.
The root of a context menu can contain items for commands and separators in addition to submenus. There are no
built-in items in an AIR context menu.
The following example creates a simple edit context menu for an interactive object:
var editContextMenu:ContextMenu = new ContextMenu();
var item:NativeMenuItem;
item = editContextMenu.addItem(new ContextMenuItem("Cut"));
item.name="Cut";
editContextMenu.addItem(new ContextMenuItem("Copy"));
item.name="Copy";
editContextMenu.addItem(new ContextMenuItem("Paste"));
item.name="Paste";
editContextMenu.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
function(event:ContextMenuEvent):void{
var command:NativeMenuItem = event.target as NativeMenuItem;
switch (command.name){
case "Cut" :
doCutCommand(event.mouseTarget);
break;
case "Copy" :
doCopyCommand(event.mouseTarget);
break;
case "Paste" :
doPasteCommand(event.mouseTarget);
break;
}
});
this.contextMenu = editContextMenu; //Where "this" is an InteractiveObject
ADOBE PRODUCT X.0 94
User Guide

Displaying a pop-up menu


You can display a pop-up menu at an arbitrary time and location above a window, by calling the menu display()
method.
The following method displays a menu defined by a popupMenu object in response to a mouse click:
private function onMouseClick(event:MouseEvent):void{
popupMenu.display(event.target.stage, event.stageX, event.stageY);
}
Note: The menu does not need to be displayed in response to an event. Any method can call the display() function.

Handling menu events


When a menu item is clicked, an event is dispatched. An AIR application responds to a user’s selection by adding
listeners to NativeMenuItem instances.
The following code traces the name of the menu item that was activated:
var submenu:NativeMenu = new NativeMenu();
var red:NativeMenuItem = new NativeMenuItem("Red");
red.addEventListener(Event.SELECT,announceSelection)
var green:NativeMenuItem = new NativeMenuItem("Green");
green.addEventListener(Event.SELECT,announceSelection)
var blue:NativeMenuItem = new NativeMenuItem("Blue");
blue.addEventListener(Event.SELECT,announceSelection)
var menuItem:NativeMenuItem = new NativeMenuItem("Change Color");
submenu.addItem(red);
submenu.addItem(green);
submenu.addItem(blue);
menuItem.submenu = submenu;
Shell.shell.menu.addItem(menuItem);
function announceSelection(e:Event):void {
var menuItem:NativeMenuItem = e.target as NativeMenuItem;
trace(menuItem.label + “ has been selected”)
}
Note: As an alternative to listening for select events on each item in a menu, you can listen for select events on a menu
or submenu and use the target property of the event object to get the NativeMenuItem object for the selected menu
command.

Defining menus declaratively


Coding the properties of a menu and menu items can be a bit tedious. However, since menus have a natural hierar-
chical structure, it is straightforward to write a function that creates a menu using an XML-formatted definition.
The following class extends NativeMenu, taking an XML object in its constructor, to do just that:
package
{
import flash.display.NativeMenu;
import flash.display.NativeMenuItem;
import flash.events.Event;

public class DeclarativeMenu extends NativeMenu


{
public function DeclarativeMenu(XMLMenuDefinition:XML):void{
super();
addChildrenToMenu(this, XMLMenuDefinition.children());
}
ADOBE PRODUCT X.0 95
User Guide

private function addChildrenToMenu(menu:NativeMenu,


children:XMLList):NativeMenuItem{
var menuItem:NativeMenuItem;
var submenu:NativeMenu;

for each (var child:XML in children){


if(String(child.@label).length > 0){
menuItem = new NativeMenuItem(child.@label);
menuItem.name = child.name();
} else {
menuItem = new NativeMenuItem(child.name());
menuItem.name = child.name();
}
menu.addItem(menuItem);
if(child.children().length() > 0){
menuItem.submenu = new NativeMenu();
addChildrenToMenu(menuItem.submenu,child.children());
}
}
return menuItem;
}
}//End class
}//End package

To create a menu with this class, pass an XML menu definition as follows:
var menuDefinition:String =
"<root>" +
"<FileMenu label='File'>" +
"<NewMenu label='New'>" +
"<NewTextFile label='Text file'/>" +
"<NewFolder label='Folder'/>" +
"<NewProject label='Project'/>" +
"</NewMenu>" +
"<OpenCommand label='Open'/>" +
"<SaveCommand label='Save'/>" +
"</FileMenu>" +
"<EditMenu label='Edit'/>" +
"<CutCommand label='Cut'/>" +
"<CopyCommand label='Copy'/>" +
"<PasteCommand label='Paste'/>" +
"<EditMenu/>" +
"<FoodItems label='Food Items'>" +
"<Jellyfish/>" +
"<Tripe/>" +
"<Gizzard/>" +
"</FoodItems>" +
"</root>";
var test:DeclarativeMenu = new DeclarativeMenu(new XML(menuDefinition));

To listen for menu events, you could listen at the root menu level and use the event.target.name property to detect
which command was selected. You could also look up items in the menu by name and add individual event listeners.

Example: Window and application menu


The following example creates the menu shown in Menu structure.
ADOBE PRODUCT X.0 96
User Guide

The menu is designed to work both under Windows, for which only window menus are supported, and under Mac
OS X, for which only application menus are supported. To make the distinction, the MenuExample class constructor
checks the static supportsMenu properties of the NativeWindow and Shell classes. If
NativeWindow.supportsMenu is true, then the constructor creates a new NativeMenu object for the window and
then creates and adds the File and Edit submenus. If Shell.supportsMenu is true, then the constructor creates and
adds the File and Edit menus to the existing menu provided by the OS X operating system.
The example also illustrates menu event handling. The select event is handled at the item level and also at the menu
level. Each menu in the chain from the menu containing the selected item to the root menu will respond to the
selection event. The displaying event is used with the “Open Recent” menu. Just before the menu is opened, the items
in the menu are refreshed from the recent Documents array (which doesn’t actually change in this example).
Although not shown in this example, you can also listen for displaying events on individual items.
package {
import flash.display.NativeMenu;
import flash.display.NativeMenuItem;
import flash.display.NativeWindow;
import flash.display.Sprite;
import flash.events.Event;
import flash.filesystem.File;
import flash.system.Shell;

public class MenuExample extends Sprite


{
private var recentDocuments:Array = new Array(new File("c:/GreatGatsby.pdf"),
new File("c:/WarAndPeace.pdf"),
new File("c:/Iliad.pdf"));

public function MenuExample()


{
var fileMenu:NativeMenuItem;
var editMenu:NativeMenuItem;

if(NativeWindow.supportsMenu){
stage.nativeWindow.menu = new NativeMenu();
stage.nativeWindow.menu.addEventListener(Event.SELECT, selectCommandMenu);
fileMenu = stage.nativeWindow.menu.addItem(new NativeMenuItem("File"));
fileMenu.submenu = createFileMenu();
editMenu = stage.nativeWindow.menu.addItem(new NativeMenuItem("Edit"));
editMenu.submenu = createEditMenu();
}

if(Shell.supportsMenu){
Shell.shell.menu.addEventListener(Event.SELECT, selectCommandMenu);
fileMenu = Shell.shell.menu.addItem(new NativeMenuItem("File"));
fileMenu.submenu = createFileMenu();
editMenu = Shell.shell.menu.addItem(new NativeMenuItem("Edit"));
editMenu.submenu = createEditMenu();
}
}

public function createFileMenu():NativeMenu{


var fileMenu:NativeMenu = new NativeMenu();
fileMenu.addEventListener(Event.SELECT,selectCommandMenu);

var newCommand:NativeMenuItem = fileMenu.addItem(new NativeMenuItem("New"));


newCommand.addEventListener(Event.SELECT,selectCommand);
var saveCommand:NativeMenuItem = fileMenu.addItem(new NativeMenuItem("Save"));
saveCommand.addEventListener(Event.SELECT,selectCommand);
var openRecentMenu:NativeMenuItem =
ADOBE PRODUCT X.0 97
User Guide

fileMenu.addItem(new NativeMenuItem("Open Recent"));


openRecentMenu.submenu = new NativeMenu();
openRecentMenu.submenu.addEventListener(Event.DISPLAYING,
updateRecentDocumentMenu);
openRecentMenu.submenu.addEventListener(Event.SELECT,selectCommandMenu);

return fileMenu;
}

public function createEditMenu():NativeMenu{


var editMenu:NativeMenu = new NativeMenu();
editMenu.addEventListener(Event.SELECT,selectCommandMenu);

var copyCommand:NativeMenuItem = editMenu.addItem(new NativeMenuItem("Copy"));


copyCommand.addEventListener(Event.SELECT,selectCommand);
var pasteCommand:NativeMenuItem =
editMenu.addItem(new NativeMenuItem("Paste"));
pasteCommand.addEventListener(Event.SELECT,selectCommand);
editMenu.addItem(new NativeMenuItem("",true));
var preferencesCommand:NativeMenuItem =
editMenu.addItem(new NativeMenuItem("Preferences"));
preferencesCommand.addEventListener(Event.SELECT,selectCommand);

return editMenu;
}

private function updateRecentDocumentMenu(event:Event):void{


trace("Updating recent document menu.");
var docMenu:NativeMenu = NativeMenu(event.target);

for each(var item:NativeMenuItem in docMenu.items){


docMenu.removeItem(item);
}

for each(var file:File in recentDocuments){


var menuItem:NativeMenuItem =
docMenu.addItem(new NativeMenuItem(file.name));
menuItem.data = file;
menuItem.addEventListener(Event.SELECT, selectRecentDocument);
}
}

private function selectRecentDocument(event:Event):void{


trace("Selected recent document: " + event.target.data.name);
}

private function selectCommand(event:Event):void{


trace("Selected command: " + event.target.label);
}

private function selectCommandMenu(event:Event):void{


if(event.currentTarget.parent){
var menuItem:NativeMenuItem =
findItemForMenu(NativeMenu(event.currentTarget));
if(menuItem){
trace("Select event for \"" +
event.target.label +
"\" command handled by menu: " +
menuItem.label);
}
} else {
ADOBE PRODUCT X.0 98
User Guide

trace("Select event for \"" +


event.target.label +
"\" command handled by root menu.");
}
}

private function findItemForMenu(menu:NativeMenu):NativeMenuItem{


for each(var item:NativeMenuItem in menu.parent.items){
if(item){
if(item.submenu == menu){
return item;
}
}
}
return null;
}
}
}
99

Part 1: Working with files and data


Working with the file system. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .100
Drag and Drop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .123
Copy and Paste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
Working with local SQL databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
100

Chapter 16: Working with the file system


You use the classes provided by the Adobe AIR file system API to access the file system of the host computer. Using
these classes, you can access and manage directories and files, create directories and files, write data to files, and so
on. Information on understanding and using the File API classes is available in the following categories:

GETTING STARTED
• “AIR file basics” on page 100
• “Building a text-file editor” on page 9
• “Building a directory search application” on page 13
• “Reading and writing from an XML preferences file” on page 17
• “Compressing files and data” on page 80

DEVELOPMENT TASKS
• “Working with File objects” on page 101
• “Working with directories” on page 107
• “Working with files” on page 109
• “Using the encrypted local store” on page 122

LANGUAGE REFERENCE
• File
• FileStream
• FileMode

MORE INFORMATION
You may be able to find more information about working the AIR File API in the AIR Developer Center
(http://www.adobe.com/devnet/air/flex/). Search the site using the keywords ‘AIR filesystem’.

AIR file basics


Adobe AIR provides classes that you can use to access, create, and manage both files and folders. These classes,
contained in the flash.filesystem package, are used as follows:
File A File object represents a path to a file or directory. You use a file object to create a pointer to a file or folder,
initiating interaction with the file or folder.
FileMode The FileMode class defines string constants used in the fileMode parameter of the open() and
openAsync() methods of the FileStream class. The fileMode parameter of these methods determines the capabilities
available to the FileStream object once the file is opened, which include writing, reading, appending, and updating.
ADOBE PRODUCT X.0 101
User Guide

FileStream A FileStream object is used to open files for reading and writing. Once you’ve created a File object that
points to a new or existing file, you pass that pointer to the FileStream object so that you can open and then manip-
ulate data within the file.
Some methods in the File class have both synchronous and asynchronous versions:
• File.copyTo() and File.copyToAsync()

• File.deleteDirectory() and File.deleteDirectoryAsync()

• File.deleteFile() and File.deleteFileAsync()

• File.getDirectoryListing() and File.getDirectoryListingAsync()

• File.moveTo() and File.moveToAsync()

• File.moveToTrash() and File.moveToTrashAsync()

Also, FileStream operations work synchronously or asynchronously depending on how the FileStream object opens
the file: by calling the open() or by calling openAsync() method.
The asynchronous versions let you initiate processes that run in the background and dispatch events when complete
(or when error events occur). Other code can execute while these asynchronous background processes are taking
place. With asynchronous versions of the operations, you need to set up event listener functions, using the
addEventListener() method of the File or FileStream object that calls the function.

The synchronous versions let you write simpler code that does not rely on setting up event listeners. However, since
other code cannot execute while a synchronous method is executing, important processes, such as display object
rendering and animation, may be paused.

Working with File objects


The File object is a pointer to a file or directory in the file system.
The File class extends the FileReference class. The FileReference class, which is available in Flash Player as well as
AIR, represents a pointer to a file, but the File class adds properties and methods that are not exposed in Flash Player
(in a SWF running in a browser), due to security considerations.
You can use the File class for the following:
• Getting the path to special directories, including the user directory, the user's documents directory, the directory
from which the application was launched, and the application directory
• Coping files and directories
• Moving files and directories
• Deleting files and directories (or moving them to the trash)
• Listing files and directories contained in a directory
• Creating temporary files and folders
Once a File object points to a file path, you can use it to read and write files, using the FileStream class.
A File object can point to the path of a file or directory that does not yet exist. You can use such a File object in
creating a new file or directory.
ADOBE PRODUCT X.0 102
User Guide

Paths of File objects


Each File object has two properties that each define its path:
nativePath This specifies the platform-specific path to a file. For example, on Windows a path might be "c:\Sample
directory\test.txt" whereas on Mac OS it could be "/Sample directory/test.txt". Note that a nativePath property uses
the backslash (\) character as the directory separator character on Windows, and it uses the forward slash (/)
character on Mac OS.
url This may use the file URL scheme to point to a file. For example, on Windows a path might be
"file:///c:/Sample%20directory/test.txt" whereas on Mac OS it could be "file:///Sample%20directory/test.txt". The
runtime includes other special URL schemes besides file, and these are described in “Supported URL schemes”
on page 105.
The File class includes properties for pointing to standard directories on both Mac and Windows, and these are
described in the following section.

Pointing a File object to a directory


There are a number of ways to set a File object to point to a directory.

Pointing to an explicit directory


You can point a File object to the user's home directory. On Windows, the home directory is the parent of the "My
Documents" directory (for example, "C:\Documents and Settings\userName\My Documents"). On Mac OS, it is the
Users/userName directory. The following code sets a File object to point to an AIR Test subdirectory of the home
directory:
var file:File = File.userDirectory.resolvePath("AIR Test");

You can point a File object to the user's documents directory. On Windows, this is typically the "My Documents"
directory (for example, "C:\Documents and Settings\userName\My Documents"). On Mac OS, it is the
Users/userName/Documents directory. The following code sets a File object to point to an AIR Test subdirectory of
the documents directory:
var file:File = File.documentsDirectory.resolvePath("AIR Test");

You can point a File object to the desktop. The following code sets a File object to point to an AIR Test subdirectory
of the desktop:
var file:File = File.desktopDirectory.resolvePath("AIR Test");

You can point a File object to the application storage directory. For every AIR application, there is a unique associated
path that defines the application store directory. You may want to use this directory to store application-specific data
(such as user data or preferences files). For example, the following code points a File object to a preferences file,
prefs.xml, contained in the application storage directory:
var file:File = File.applicationStorageDirectory;
file = file.resolvePath("prefs.xml");

You can point a File object to the directory in which the application was installed, known as the application resource
directory. You can reference this directory using the File.applicationResourceDirectory property. You may
use this directory to examine the application descriptor file or other resources installed with the application. For
example, the following code points a File object to a directory named images in the application resource directory:
var file:File = File.applicationResourceDirectory;
file = file.resolvePath("images");
ADOBE PRODUCT X.0 103
User Guide

The File.getRootDirectories() method lists all root volumes, such as C: and mounted volumes, on a Windows
computer. On Mac, this method always returns the unique root directory for the machine (the "/" directory).
You can point the File object to an explicit directory by setting the nativePath property of the File object, as in the
following example (on Windows):
var file:File = new File();
file.nativePath = "C:\\AIR Test\\";

You can use the resolvePath() method to obtain a path relative to another given path. For example, the following
code sets a File object to point to an "AIR Test" subdirectory of the user's home directory:
var file:File = File.userDirectory;
file = file.resolvePath("AIR Test");

You can also use the url property of a File object to point it to a directory based on a URL string, as in the following:
var urlStr:String = "file:///C:/AIR Test/";
var file:File = new File()
file.url = urlStr;

You can also use the nativePath property of a File object to set an explicit path. For example, the following code,
when run on a Windows computer, sets a File object to the AIR Test subdirectory of the C: drive:
var file:File = new File();
file.nativePath = "C:\\AIR Test";

For more information, see “Modifying File paths” on page 105.


You can get the directory location from which an application is invoked, by checking the currentDirectory
property of the InvokeEvent object dispatched when the application is invoked. For details, see “Capturing command
line arguments” on page 187.

Letting the user browse to select a directory


The File class includes the browseForDirectory() method, which presents a system dialog box in which the user
can select a directory to assign to the object. The browseForDirectory() method is asynchronous; it dispatches a
select event, if the user selects a directory and clicks the Open button, or it dispatches a cancel event if the user
clicks the Cancel button.
For example, the following code lets the user select a directory and outputs the directory path upon selection:
var file:File = new File();
file.addEventListener(Event.SELECT, dirSelected);
file.browseForDirectory();
function dirSelected(e:Event):void {
trace(file.nativePath);
}

Pointing a File object to a file


There are a number of ways to set the file to which a File object points.

Pointing to an explicit file path


You can use the resolvePath() method to obtain a path relative to another given path. For example, the following
code sets a File object to point to a log.txt file within the application storage directory:
var file:File = File.applicationStorageDirectory;
ADOBE PRODUCT X.0 104
User Guide

file = file.resolvePath("log.txt");

You can use the url property of a File object to point it to a file or directory based on a URL string, as in the following:
var urlStr:String = "file:///C:/AIR Test/test.txt";
var file:File = new File()
file.url = urlStr;

You can also pass the URL to the File() constructor function, as in the following:
var urlStr:String = "file:///C:/AIR Test/test.txt";
var file:File = new File(urlStr);

Note that url property always returns the URI-encoded version of the URL (for example, blank spaces are replaced
with "%20):
file.url = "file:///c:/AIR Test";
trace(file.url); // file:///c:/AIR%20Test

You can also use the nativePath property of a File object to set an explicit path. For example, the following code,
when run on a Windows computer, sets a File object to the test.txt file in the AIR Test subdirectory of the C: drive:
var file:File = new File();
file.nativePath = "C:/AIR Test/test.txt";

You can also pass this path to the File() constructor function, as in the following:
var file:File = new File("C:/AIR Test/test.txt");

On Windows you can use the forward slash (/) or backslash (\) character as the path delimiter for the nativePath
property. On Mac OS, use the forward slash (/) character as the path delimiter for the nativePath :
var file:File = new File(/Users/dijkstra/AIR Test/test.txt");

For more information, see “Modifying File paths” on page 105.

Enumerating files in a directory


You can use the getDirectoryListing() method of a File object to get an array of File objects pointing to files and
subdirectories at the root level of a directory. For more information, see “Enumerating directories” on page 108.

Letting the user browse to select a file


The File class includes the following methods that present a system dialog box in which the user can select a file to
assign to the object:
• browseForOpen()
• browseForSave()
• browseForMultiple()
For example, the following code presents the user with an “Open” dialog box in which the user can select a file.
var fileToOpen:File = File.documentsDirectory;
selectTextFile(fileToOpen);

function selectTextFile(root:File):void
{
var txtFilter:FileFilter = new FileFilter("Text", "*.as;*.css;*.html;*.txt;*.xml");
root.browseForOpen("Open", [txtFilter]);
root.addEventListener(Event.SELECT, fileSelected);
ADOBE PRODUCT X.0 105
User Guide

function fileSelected(event:Event):void
{
trace(fileToOpen.nativePath);
}

If the application has another browser dialog box open when you call a browse method, the runtime throws an error.

Modifying File paths


You can also modify the path of an existing File object by calling the resolvePath() method or by modifying the
nativePath or url property of the object, as in the following examples (on Windows):

var file1:File = File.documentsDirectory;


file1 = file1.resolvePath("AIR Test");
trace(file1.nativePath); // C:\Documents and Settings\userName\My Documents\AIR Test
var file2:File = File.documentsDirectory;
file2 = file2.resolvePath("..");
trace(file2.nativePath); // C:\Documents and Settings\userName
var file3:File = File.documentsDirectory;
file3.nativePath += "/subdirectory";
trace(file3.nativePath); // C:\Documents and Settings\userName\My Documents\subdirectory
var file4:File = new File();
file.url = "file:///c:/AIR Test/test.txt"
trace(file3.nativePath); // C:\AIR Test\test.txt

When using the nativePath property, you use either the forward slash (/) or backslash (\) character as the directory
separator character on Windows; use the forward slash (/) character on Mac OS. On Windows, remember to type
the backslash character twice in a string literal.

Supported URL schemes


You can use any of the following URL schemes in defining the url property of a File object:
file Use this to specify a path relative to the root of the file system. For example:

file:///c:/AIR Test/test.txt

app-resource Use this to specify a path relative to the root directory of the installed application (the directory that
contains the application.xml file for the installed application). For example, the following path points to an images
subdirectory of the directory of the installed application:
app-resource:/images

app-storage Use this to specify a path relative to the application store directory. For each installed application, AIR
defines a unique application store directory, which is a useful place to store data specific to that application. For
example, the following path points to a prefs.xml file in a settings subdirectory of the application store directory:
app-storage:/settings/prefs.xml

Finding the relative path between two files


You can use the getRelativePath() method to find the relative path between two files:
var file1:File = File.documentsDirectory.resolvePath("AIR Test");
var file2:File = File.documentsDirectory
file2 = file2.resolvePath("AIR Test/bob/test.txt");
ADOBE PRODUCT X.0 106
User Guide

trace(file1.getRelativePath(file2)); // bob/test.txt

The second parameter of the getRelativePath() method, the useDotDot parameter, allows for .. syntax to be
returned in results, to indicate parent directories:
var file1:File = File.documentsDirectory;
file1 = file1.resolvePath("AIR Test");
var file2:File = File.documentsDirectory;
file2 = file2.resolvePath("AIR Test/bob/test.txt");
var file3:File = File.documentsDirectory;
file3 = file3.resolvePath("AIR Test/susan/test.txt");

trace(file2.getRelativePath(file1, true)); // ../..


trace(file3.getRelativePath(file2, true)); // ../../bob/test.txt

Obtaining canonical versions of file names


File and path names are usually not case sensitive. In the following, two File objects point to the same file:
File.documentsDirectory.resolvePath("test.txt");
File.documentsDirectory.resolvePath("TeSt.TxT");

However, documents and directory names do include capitalization. For example, the following assumes that there
is a folder named AIR Test in the documents directory, as in the following examples:
var file:File = File.documentsDirectory.resolvePath("AIR test");
trace(file.nativePath); // ... AIR test
file.canonicalize();
trace(file.nativePath); // ... AIR Test

The canonicalize method converts the nativePath object to use the correct capitalization for the file or directory
name.
You can also use the canonicalize() method to convert short file names ("8.3" names) to long file names on
Windows, as in the following examples:
var path:File = new File();
path.nativePath = "C:\\AIR~1";
path.canonicalize();
trace(path.nativePath); // C:\AIR Test

Getting file system information


The File class includes the following static properties that provide some useful information about the file system:
File.lineEnding The line-ending character sequence used by the host operating system. On Mac OS, this is the
line-feed character. On Windows, this is the carriage return character followed by the line-feed character.
File.separator The host operating system's path component separator character. On Mac OS, this is the forward
slash (/) character. On Windows, it is the backslash (\) character.
The Capabilities class also includes useful system information that may be useful when working with files:
File.systemCharSet The default encoding used for files by the host operating system. This pertains to the
character set used by the operating system, corresponding to its language.
ADOBE PRODUCT X.0 107
User Guide

Capabilities.hasIME Specifies whether the player is running on a system that does (true) or does not (false)
have an input method editor (IME) installed.
Capabilities.language Specifies the language code of the system on which the player is running.

Capabilities.os Specifies the current operating system.

Working with directories


The runtime provides you with capabilities to work with directories on the local file system.
For details on creating File objects that point to directories, see “Pointing a File object to a directory” on page 102.
Using the File API, you can accomplish the following tasks:
• “Creating directories” on page 107
• “Creating a temporary directory” on page 107
• “Enumerating directories” on page 108
• “Copying and moving directories” on page 108
• “Deleting directory contents” on page 108

Creating directories
The File.createDirectory() method lets you create a directory. For example, the following code creates a
directory named AIR Test as a subdirectory of the user's home directory:
var dir:File = File.userDirectory.resolvePath("AIR Test");
dir.createDirectory();

If the directory already exists, the createDirectory() method does nothing.


Also, in some modes, a FileStream object will create directories when opening files. Missing directories are created
when you instantiate a FileStream instance with the fileMode parameter of the FileStream() constructor set to
FileMode.APPEND or FileMode.WRITE. For more information, see “Workflow for reading and writing files”
on page 112.

Creating a temporary directory


The File class includes a createTempDirectory() method, which creates a new directory in the temporary
directory folder for the System, as in the following example:
var temp:File = File.createTempDirectory();

The createTempDirectory() method automatically creates a unique temporary directory (saving you the work of
determining a new unique location).
You may use a temporary directory to temporarily store temporary files used for a session of the application. Note
that there is a createTempFile() method, for creating new, unique temporary files in the System temporary
directory.
You may want to delete the temporary directory before closing the application, as it is not automatically deleted.
ADOBE PRODUCT X.0 108
User Guide

Enumerating directories
You can use the getDirectoryListing() method or the getDirectoryListingAsync() method of a File object
to get an array of File objects pointing to files and subfolders in a directory.
For example, the following code lists the contents of the user's documents directory (without examining subdirec-
tories):
var directory:File = File.documentsDirectory;
var contents:Array = directory.getDirectoryListing();
for (var i:uint = 0; i < contents.length; i++)
{
trace(contents[i].name, contents[i].size);
}

When using the asynchronous version of the method, the directoryListing event object has a files property that is the
array of File objects pertaining to the directories:
var directory:File = File.documentsDirectory;
directory.getDirectoryListingAsync();
directory.addEventListener(FileListEvent.DIRECTORY_LISTING, dirListHandler);

function dirListHandler(event:FileListEvent):void
{
var contents:Array = event.files;
for (var i:uint = 0; i < contents.length; i++)
{
trace(contents[i].name, contents[i].size);
}
}

Copying and moving directories


You can copy or move a directory, using the same methods as you would to copy or move a file. For example, the
following code copies a directory synchronously:
var sourceDir:File = File.documentsDirectory.resolvePath("AIR Test");
var resultDir:File = File.documentsDirectory.resolvePath("AIR Test Copy");
sourceDir.copyTo(resultDir);

Note that when you specify true for the overwrite parameter of the copyTo() method, all files and folders in an
existing target directory are deleted and replaced with the files and folders in the source directory (even if the target
file does not exist in the source directory).
For details, see “Copying and moving files” on page 110.

Deleting directory contents


The File class includes a deleteDirectory() method and a deleteDirectoryAsync() method. These both delete
directories, the first working synchronously, the second working asynchronously (see “AIR file basics” on page 100).
Both methods include a deleteDirectoryContents parameter (which takes a Boolean value); when this
parameter is set to true (the default value is false) the call to the method deletes non-empty directories.
For example, the following code synchronously deletes the AIR Test subdirectory of the user's documents directory:
var directory:File = File.documentsDirectory.resolvePath("AIR Test");
directory.deleteDirectory(true);
ADOBE PRODUCT X.0 109
User Guide

The following code asynchronously deletes the AIR Test subdirectory of the user's documents directory:
var directory:File = File.documentsDirectory.resolvePath("AIR Test");
directory.addEventListener(Event.COMPLETE, completeHandler)
directory.deleteDirectoryAsync(true);

function completeHandler(event:Event):void {
trace("Deleted.")
}

Also included are the moveToTrash() and moveToTrashAsync methods, which you can use to move a directory to
the System trash. For details, see “Moving a file to the trash” on page 111.

Working with files


Using the AIR file API, you can add basic file interaction capabilities to your applications. For example, you can read
and write files, copy and delete files, and so on. Since your applications can access the local file system, you should
refer to “Introducing Adobe AIR” on page 13, if you haven't already done so.
Note: You can associate a file type with an AIR application (so that double-clicking it opens the application). For details,
see “Registering file types” on page 188.
Using the File API, you can accomplish the following tasks:
• “Getting file information” on page 109
• “Copying and moving files” on page 110
• “Deleting a file” on page 111
• “Moving a file to the trash” on page 111
• “Creating a temporary file” on page 111

Getting file information


The File class includes the following properties that provide information about a file or directory to which a File
object points:

File property Description

creationDate The creation date of the file on the local disk.

creator Obsolete—use the extension property. (This property reports the Macintosh creator type of the file, which
is only used in Mac OS versions prior to Mac OS X.)

exists Whether the referenced file or directory exists.

extension The file extension, which is the part of the name following (and not including) the final dot ("."). If there is no
dot in the filename, the extension is null.

icon An Icon object containing to the icons defined for the file.

isDirectory Whether the File object reference is to a directory.

modificationDate The date that the file on the local disk was last modified.

name The name of the file on the local disk.


ADOBE PRODUCT X.0 110
User Guide

File property Description

nativePath The full path in the host operating system representation. See “Paths of File objects” on page 102.

parent The folder that contains the folder or file represented by the File object. This property is null if the File object
references a file or directory in the root of the filesystem.

size The size of the file on the local disk in bytes.

type Obsolete—use the extension property. (On the Macintosh, this property is the four-character file type,
which is only used in Mac OS versions prior to Mac OS X.)

url The URL for the file or directory. See “Paths of File objects” on page 102.

For details on these properties, see the File class entry in the ActionScript 3.0 Language Reference.

Copying and moving files


The File class includes two methods for copying files or directories: copyTo() and copyToAsync(). The File class
includes two methods for moving files or directories: moveTo() and moveToAsync(). The copyTo() and moveTo()
methods work synchronously, and the copyToAsync() and moveToAsync() methods work asynchronously (see
“AIR file basics” on page 100).
To copy or move a file, you set up two File objects. One points to the file to copy or move, and it is the object that
calls the copy or move method, the other points to the destination (result) path.
The following copies a test.txt file from the AIR Test subdirectory of the user's documents directory to a file named
copy.txt in the same directory:
var original:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var newFile:File = File.resolvePath("AIR Test/copy.txt");
original.copyTo(newFile, true);

Note that in this example, the value of overwrite parameter of the copyTo() method (the second parameter) is set
to true. By setting this to true, an existing target file is overwritten. This parameter is optional. If you set it to false
(the default value), the operation dispatches an IOErrorEvent event if the target file already exists (and the file is not
copied).
The Async versions of the copy and move methods work asynchronously. Use the addEventListener() method to
monitor completion of the task or error conditions, as in the following code:
var original = File.documentsDirectory;
original = original.resolvePath("AIR Test/test.txt");

var destination:File = File.documentsDirectory;


destination = destination.resolvePath("AIR Test 2/copy.txt");

original.addEventListener(Event.COMPLETE, fileMoveCompleteHandler);
original.addEventListener(IOErrorEvent.IO_ERROR, fileMoveIOErrorEventHandler);
original.moveToAsync(destination);

function fileMoveCompleteHandler(event:Event):void {
trace(event.target); // [object File]
}
function fileMoveIOErrorEventHandler(event:IOErrorEvent):void {
trace("I/O Error.");
}
ADOBE PRODUCT X.0 111
User Guide

The File class also includes the File.moveToTrash() and File.moveToTrashAsync() methods, which move a file
or directory to the system trash.

Deleting a file
The File class includes a deleteFile() method and a deleteFileAsync() method. These both delete files, the
first working synchronously, the second working asynchronously (see “AIR file basics” on page 100).
For example, the following code synchronously deletes the test.txt file in the user's documents directory:
var directory:File = File.documentsDirectory.resolvePath("test.txt");
directory.deleteFile();

The following code asynchronously deletes the test.txt subdirectory of the user's documents directory:
var file:File = File.documentsDirectory.resolvePath("test.txt");
file.addEventListener(Event.COMPLETE, completeHandler)
file.deleteFileAsync();

function completeHandler(event:Event):void {
trace("Deleted.")
}

Also included are the moveToTrash() and moveToTrashAsync methods, which you can use to move a file or
directory to the System trash. For details, see “Moving a file to the trash” on page 111.

Moving a file to the trash


The File class includes a moveToTrash() method and a moveToTrashAsync() method. These both send a file or
directory to the System trash, the first working synchronously, the second working asynchronously (see “AIR file
basics” on page 100).
For example, the following code synchronously moves the test.txt file in the user's documents directory to the System
trash:
var file:File = File.documentsDirectory.resolvePath("test.txt");
file.moveToTrash();

Creating a temporary file


The File class includes a createTempFile() method, which creates a new file in the temporary directory folder for
the System, as in the following example:
var temp:File = File.createTempFile();

The createTempFile() method automatically creates a unique temporary file (saving you the work of determining
a new unique location).
You may use a temporary file to temporarily store information used in a session of the application. Note that there
is also a createTempDirectory() method, for creating a new, unique temporary directory in the System temporary
directory.
You may want to delete the temporary file before closing the application, as it is not automatically deleted.
ADOBE PRODUCT X.0 112
User Guide

Workflow for reading and writing files


The FileStream class lets AIR applications read and write to the file system. The workflow for reading and writing
files is as follows.

1. Initialize a File object that points to the path.


This is the path of the file that you want to work with (or a file that you will later create).
var file:File = File.documentsDirectory;
file = file.resolvePath("AIR Test/testFile.txt");

This example uses the File.documentsDirectory property and the resolvePath() method of a File object to
initialize the File object. However, there are many other ways to point a File object to a file. For more information,
see “Pointing a File object to a file” on page 103.

2. Initialize a FileStream object.

3. Call the open() method or the openAsync() method.


The method you call depends on whether you want to open the file for synchronous or asynchronous operations.
Use the File object as the file parameter of the open method. For the fileMode parameter, specify a constant from
the FileMode class that specifies the way in which you will use the file.
For example, the following code initializes a FileStream object that will be used to create a file and overwrite any
existing data:
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);

For more information, see “Initializing a FileStream object, and opening and closing files” on page 113 and
“FileStream open modes” on page 113.

4. If you opened the file asynchronously (using the openAsync() method), add and set up event listeners for
the FileStream object.
These event listener methods will respond to events dispatched by the FileStream object in a variety of situations,
such as when data is read in from the file, when I/O errors are encountered, or when the complete amount of data
to be written has been written.
For details, see “Asynchronous programming and the events generated by a FileStream object opened asynchro-
nously” on page 117.

5. Include code for reading and writing data, as needed.


There are a number of methods of the FileStream class related to reading and writing. (They each begin with "read"
or "write".) The method you choose to use to read or write data depends on the format of the data in the target file.
For example, if the data in the target file is UTF-encoded text, you may use the readUTFBytes() and
writeUTFBytes() methods. If you want to deal with the data as byte arrays, you may use the readByte(),
readBytes(), writeByte(), and writeBytes() methods.

For details, see “Data formats, and choosing the read and write methods to use” on page 118.

6. Call the close() method of the FileStream object when you are done working with the file.
This makes the file available to other applications.
ADOBE PRODUCT X.0 113
User Guide

For details, see “Initializing a FileStream object, and opening and closing files” on page 113.
The sections that follow provide more details on using the AIR file APIs to read and write files.
For examples of using FileStream object to read and write files, see the following topics:
• “Building a text-file editor” on page 9
• “Reading and writing from an XML preferences file” on page 17
• “Building a directory search application” on page 13

Working with FileStream objects


This section contains the following topics:
• FileStream open modes
• FileStream open modes
• The position property of a FileStream object
• The read buffer and the bytesAvailable property of a FileStream object
• Asynchronous programming and the events generated by a FileStream object opened asynchronously
• Data formats, and choosing the read and write methods to use

FileStream open modes


The open() and openAsync() method of a FileStream object each include a fileMode parameter, which defines a
number of properties for a file stream, including the following:
• The ability to read from the file
• The ability to write to the file
• Whether data will always be appended past the end of the file (when writing)
• What to do when the file does not exist (and when its parent directories do not exist)
The following table summarizes the various file modes (which you can specify as the fileMode parameter of the
open() and openAsync() methods):

FileMode.READ Specifies that the file is open for reading only.

FileMode.WRITE Specifies that the file is open for writing. If the file does not exist, it is created when the FileStream
object is opened. If the file does exist, any existing data is deleted.
FileMode.APPEND Specifies that the file is open for appending. The file is created if it does not exist. If the file
already exists, existing data is not overwritten, and all writing begins at the end of the file.
FileMode.UPDATE Specifies that the file is open for reading and writing. If the file does not exist, it is created.
Specify this mode for random read/write access to the file. You can read from any position in the file, and when
writing to the file, only the bytes written overwrite existing bytes (all other bytes remain unchanged).

Initializing a FileStream object, and opening and closing files


When you open a FileStream object, you make it available to read and write data to a file. You open a FileStream
object by passing a File object to the open() or openAsync() method of the FileStream object:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
ADOBE PRODUCT X.0 114
User Guide

myFileStream.open(myFile, FileMode.READ);

The fileMode parameter (the second parameter of the open() and openAsync() methods), specifies the mode in
which to open the file: for read, write, append, or update. For details, see the previous section, “FileStream open
modes” on page 113.
If you use the openAsync() method to open the file for asynchronous file operations, set up event listeners to handle
the asynchronous events:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.addEventListener(Event.COMPLETE, completeHandler);
myFileStream.addEventListener(ProgressEvent.PROGRESS, progressHandler);
myFileStream.addEventListener(IOErrorEvent.IOError, errorHandler);
myFileStream.open(myFile, FileMode.READ);

function completeHandler(event:Event):void {
// ...
}

function progressHandler(event:ProgressEvent):void {
// ...
}

function errorHandler(event:IOErrorEvent):void {
// ...
}

The file is opened for synchronous or asynchronous operations, depending upon whether you use the open() or
openAsync() method. For details, see “AIR file basics” on page 100.

If you set the fileMode parameter to FileMode.READ or FileMode.UPDATE in the open method of the FileStream
object, data is read into the read buffer as soon as you open the FileStream object. For details, see “The read buffer
and the bytesAvailable property of a FileStream object” on page 116.
You can call the close() method of a FileStream object to close the associated file, making it available for use by
other applications.

The position property of a FileStream object


The position property of a FileStream object determines where data will be read or written on the next read or write
method.
Prior to a read or write operation, set the position property to any valid position in the file.
For example, the following code writes the string "hello" (in UTF encoding) at position 8 in the file:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.open(myFile, FileMode.UPDATE);
myFileStream.position = 8;
myFileStream.writeUTFBytes("hello");

When you first open a FileStream object, the position property is set to 0.
Prior to a read operation, the value of position must be at least 0 and less than the number of bytes in the file (which
are existing positions in the file).
The value of the position property is modified only in the following conditions:
ADOBE PRODUCT X.0 115
User Guide

• When you explicitly set the position property.


• When you call a read method.
• When you call a write method.
When you call a read or write method of a FileStream object, the position property is immediately incremented by
the number of bytes that you will read or write. Depending on the read method you use, this will mean that the
position property is either incremented by the number of bytes you specify to read, or by the number of bytes
available. When you call a read or write method subsequently, it will read or write starting at the new position.
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.open(myFile, FileMode.UPDATE);
myFileStream.position = 4000;
trace(myFileStream.position); // 4000
myFileStream.writeBytes(myByteArray, 0, 200);
trace(myFileStream.position); // 4200

There is, however, one exception: for a FileStream opened in append mode, the position property is not changed
after a call to a write method. (In append mode, data is always written to the end of the file, independent of the value
of the position property.)
Note that for a file opened for asynchronous operations, the write operation does not complete before the next line
of code is executed. However, you can call multiple asynchronous methods sequentially, and the runtime will execute
them in order:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.openAsync(myFile, FileMode.WRITE);
myFileStream.writeUTFBytes("hello");
myFileStream.writeUTFBytes("world");
myFileStream.addEventListener(Event.CLOSE, closeHandler);
myFileStream.close()
trace("started.");

closeHandler(event:Event):void
{
trace("finished.");
}

The trace output for this code is the following:


started.
finished.

You can specify the position value immediately after you call a read or write method (or at any time), and the next
read or write operation will take place starting at that position. For example, note that the following code sets the
position property right after a call to the writeBytes() operation, and the position is set to that value (300) even
after the write operation completes:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.openAsync(myFile, FileMode.UPDATE);
myFileStream.position = 4000;
trace(myFileStream.position); // 4000
myFileStream.writeBytes(myByteArray, 0, 200);
myFileStream.position = 300;
trace(myFileStream.position); // 300
ADOBE PRODUCT X.0 116
User Guide

The read buffer and the bytesAvailable property of a FileStream object


When a FileStream object with read capabilities (one in which the fileMode parameter of the open() or
openAsync() method was set to READ or UPDATE) is opened, the runtime stores the data in an internal buffer. The
FileStream object begins reading data into the buffer as soon as you open the file (by calling the open() or
openAsync() method of the FileStream object).

For a file opened for synchronous operations (using the open() method), you can always set the position pointer
to any valid position (within the bounds of the file) and begin reading any amount of data (within the bounds of the
file), as shown in the following code (which assumes that the file contains at least 100 bytes):
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.open(myFile, FileMode.READ);
myFileStream.position = 10;
myFileStream.readBytes(myByteArray, 0, 20);
myFileStream.position = 89;
myFileStream.readBytes(myByteArray, 0, 10);

Whether a file is opened for synchronous or asynchronous operations, the read methods always read from the
"available" bytes, represented by the bytesAvalable property. When reading synchronously, all of the bytes of the
file are available all of the time. When reading asynchronously, the bytes become available starting at the position
specified by the position property, in a series of asynchronous buffer fills signalled by progress events.
For files opened for synchronous operations, the bytesAvailable property is always set to represent the number of
bytes from the position property to the end of the file (all bytes in the file are always available for reading).
For files opened for asynchronous operations, you need to ensure that the read buffer has consumed enough data
prior to calling a read method. For a file opened asynchronously, as the read operation progresses, the data from the
file, starting at the position specified when the read operation started, is added to the buffer, and the
bytesAvailable property increments with each byte read. The bytesAvailable property indicates the number of
bytes available starting with the byte at the position specified by the position property to the end of the buffer.
Periodically, the FileStream object sends a progress event.
For a file opened asynchronously, as data becomes available in the read buffer, the FileStream object periodically
dispatches the progress event. For example, the following code reads data into a ByteArray object, bytes, as it is
read into the buffer:
var bytes:ByteArray = new ByteArray();
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.addEventListener(ProgressEvent.PROGRESS, progressHandler);
myFileStream.openAsync(myFile, FileMode.READ);

function progressHandler(event:ProgressEvent):void
{
myFileStream.readBytes(bytes, myFileStream.position, myFileStream.bytesAvailable);
}

For a file opened asynchronously, only the data in the read buffer can be read. Furthermore, as you read the data, it
is removed from the read buffer. For read operations, you will need to ensure that the data exists in the read buffer
prior to calling the read operation. You may do this by setting up a progress handler. For example, the following
code reads 8000 bytes of data starting from position 4000 in the file:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.addEventListener(ProgressEvent.PROGRESS, progressHandler);
myFileStream.addEventListener(Event.COMPLETE, completed);
ADOBE PRODUCT X.0 117
User Guide

myFileStream.openAsync(myFile, FileMode.READ);
myFileStream.position = 4000;

var str:String = "";

function progressHandler(event:Event):void
{
if (myFileStream.bytesAvailable > 8000 )
{
str += myFileStream.readMultiByte(8000, "iso-8859-1");
}
}

During a write operation, the FileStream object does not read data into the read buffer. When a write operation
completes (all data in the write buffer is written to the file), the FileStream object starts a new read buffer (assuming
that the associated FileStream object was opened with read capabilities), and starts reading data into the read buffer,
starting from the position specified by the position property. The position property may be the position of the
last byte written, or it may be a different position, if the user specifies a different value for the position object after
the write operation.

Asynchronous programming and the events generated by a FileStream object opened asynchronously
When a file is opened asynchronously (using the openAsync() method), reading and writing files are done
asynchronously. As data is read into the read buffer and as output data is being written, other ActionScript code can
execute.
This means that you will need to register for events generated by the FileStream object opened asynchronously.
By registering for the progress event, you can be notified as new data becomes available for reading, as in the
following code:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.addEventListener(ProgressEvent.PROGRESS, progressHandler);
myFileStream.openAsync(myFile, FileMode.READ);
var str:String = "";

function progressHandler(event:ProgressEvent):void
{
str += myFileStream.readMultiByte(myFileStream.bytesAvailable, "iso-8859-1");
}

You can read the entire data by registering for the complete event, as in the following code:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.addEventListener(Event.COMPLETE, completed);
myFileStream.openAsync(myFile, FileMode.READ);
var str:String = "";
function completeHandler(event:Event):void
{
str = myFileStream.readMultiByte(myFileStream.bytesAvailable, "iso-8859-1");
}
ADOBE PRODUCT X.0 118
User Guide

In much the same way that input data is buffered to enable asynchronous reading, data that you write on an
asynchronous stream is buffered and written to the file asynchronously. As data is written to a file, the FileStream
object periodically dispatched an OutputProgressEvent object. An OutputProgressEvent object includes a
bytesPending property that is set to the number of bytes remaining to be written. You can register for the
outputProgressEvent to be notified as this buffer is actually written to the file, perhaps in order to display a
progress dialog. However in general it is not necessary to do so. In particular, you may call the close() method
without concern for the unwritten bytes. The FileStream object will continue writing data and the close event will
be delivered after the final byte is written to the file and the underlying file is closed.

Data formats, and choosing the read and write methods to use
Every file is a set of bytes on a disk. In ActionScript, the data from a file can always be represented as a ByteArray.
For example, the following code reads the data from a file into a ByteArray object named bytes:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.addEventListener(Event.COMPLETE, completed);
myFileStream.openAsync(myFile, FileMode.READ);

function completeHandler(event:Event):void
{
myFileStream.readBytes(bytes, 0, myFileStream.bytesAvailable);
}

Similarly, the following code writes data from a ByteArray named bytes to a file:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.open(myFile, FileMode.WRITE);
myFileStream.writeBytes(bytes, 0, bytes.length);

However, often you do not want to store the data in an ActionScript ByteArray object. And often the data file will be
in a specified file format.
For example, the data in the file may be in a text file format, and you may want to represent such data in a String
object.
For this reason, the FileStream class includes read and write methods for reading and writing data to and from types
other than ByteArray objects. For example, the readMultiByte() method lets you read data from a file and store it
to a string, as in the following code:
var myFile:File = File.documentsDirectory.resolvePath("AIR Test/test.txt");
var myFileStream:FileStream = new FileStream();
myFileStream.addEventListener(Event.COMPLETE, completed);
myFileStream.openAsync(myFile, FileMode.READ);
var str:String = "";

function completeHandler(event:Event):void
{
str = myFileStream.readMultiByte(myFileStream.bytesAvailable, "iso-8859-1");
}

Note that the second parameter of the readMultiByte() method specifies the text format that ActionScript uses to
interpret the data ("iso-8859-1" in the example). ActionScript supports a number of common character set
encodings, and these are listed in the ActionScript Language Reference (see Supported character sets at
http://livedocs.macromedia.com/flex/2/langref/charset-codes.html).
ADOBE PRODUCT X.0 119
User Guide

The FileStream class also includes the readUTFBytes() method, which reads data from the read buffer into a string
using the UTF-8 character set. Since characters in the UTF-8 character set is are of a variable length, you should not
try to use readUTFBytes() in a method that responds to the progress event, since the data at the end of the read
buffer may represent an incomplete character. (This is also true when using the readMultiByte() method with a
variable-length character encoding.) For this reason, you should read the entire data when the FileStream object
dispatches the complete event.
There are also similar write methods, writeMultiByte() and writeUTFBytes(), for working with String objects
and text files.
Note that the readUTF() and the writeUTF() methods (not to be confused with readUTFBytes() and
writeUTFBytes()) also read and write the text data to a file, but they assume that the text data is preceded by data
specifying the length of the text data, which is not a common practice in standard text files.
Some UTF-encoded text files begin with a "UTF-BOM" (byte order mark) character that defines the endianess as
well as the encoding format (such as UTF-16 or UTF-32).
For an example of reading and writing to a text file, see “Example: Reading an XML file into an XML object”
on page 119.
The readObject() and writeObject() are convenient ways to store and retrieve data for complex ActionScript
objects. The data is encoded in AMF (ActionScript Message Format). This format is proprietary to ActionScript.
Applications other than AIR, Flash Player, Flash Media Server, and Flex Data Services do not have built-in APIs for
working with data in this format.
There are a number of other read and write methods (such as readDouble() and writeDouble()). However, if you
use these, make sure that the file format matches the formats of the data defined by these methods.
File formats are often more complex than simple text formats. For example, an MP3 file includes compressed data
that can only be interpreted with the decompression and decoding algorithms specific to MP3 files. MP3 files also
may include ID3 tags that contain metatag information about the file (such as the title and artist for a song). There
are multiple versions of the ID3 format, but the simplest (ID3 version 1) is discussed in the “Example: Reading and
writing data with random access” on page 120 section.
Other files formats (for images, databases, application documents, etc.) have quite different structures, and to work
with their data in ActionScript, you need to understand how the data is structured.

Example: Reading an XML file into an XML object


This section shows how to read and write to a text file that contains XML data.
Reading from the file is easy. Simply initialize the File and FileStream objects, and set up an event listener for the
complete event:

var file:File = File.documentsDirectory.resolvePath("AIR Test/preferences.xml");


var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.READ);
var prefsXML:XML = XML(fileStream.readUTFBytes(fileStream.bytesAvailable));
fileStream.close();

Similarly, writing the data to the file is as easy as setting up an appropriate File and FileStream objects, and then
calling a write method of the FileStream object, passing the string version of the XML data to the write method as in
the following code:
var file:File = File.documentsDirectory.resolvePath("AIR Test/preferences.xml");
fileStream = new FileStream();
ADOBE PRODUCT X.0 120
User Guide

fileStream.open(file, FileMode.WRITE);

var outputString:String = '<?xml version="1.0" encoding="utf-8"?>\n';


outputString += prefsXML.toXMLString();

fileStream.writeUTFBytes(outputString);
fileStream.close();

Note that these examples use the readUTFBytes() and writeUTFBytes() methods, because they assume that the
files are in UTF-8 format. If this is not the case, you may need to use a different method (see “Data formats, and
choosing the read and write methods to use” on page 118).
The previous examples use FileStream objects opened for synchronous operation. You can also open files for
asynchronous operations (which rely on event listener functions to respond to events). For example, the following
code shows how to read an XML file asynchronously:
var file:File = File.documentsDirectory.resolvePath("AIR Test/preferences.xml");
var fileStream:FileStream = new FileStream();
fileStream.addEventListener(Event.COMPLETE, processXMLData);
fileStream.open(file, FileMode.READ);
var prefsXML:XML;

function processXMLData(event:Event):void
{
prefsXML = XML(fileStream.readUTFBytes(fileStream.bytesAvailable));
fileStream.close();
}

The processXMLData() method is invoked when the entire file is read into the read buffer (when the FileStream
object dispatches the complete event). It calls the readUTFBytes() method to get a string version of the read data,
and it creates an XML object, prefsXML, based on that string.
To see a sample application that shows these capabilities, see “Reading and writing from an XML preferences file”
on page 17.

Example: Reading and writing data with random access


MP3 files can include ID3 tags, which are sections at the beginning or end of the file that contain metadata identi-
fying the recording. The ID3 tag format itself has gone through a number of revisions. This section describes how to
read and write from an MP3 file that contains the simplest ID3 format: ID3 version 1.0. This example will not be
reading and writing to the file sequentially from start to finish, and this is known as random access to file data.
An MP3 file that contains an ID3 version 1 tag includes the ID3 data at the end of the file, in the final 128 bytes.
When accessing a file for random read/write access, it is important to specify FileMode.UPDATE as the fileMode
parameter for the open() or openAsync() method:
var file:File = File.documentsDirectory.resolvePath("My Music/Sample ID3 v1.mp3");
var fileStr:FileStream = new FileStream();
fileStr.open(file, FileMode.UPDATE);

var file = air.File.documentsDirectory.resolvePath("My Music/Sample ID3 v1.mp3");


var fileStr = new air.FileStream();
fileStr.open(file, air.FileMode.UPDATE);

This lets you both read and write to the file.


Upon opening the file, you can set the position pointer to the position 128 bytes before the end of the file,
ADOBE PRODUCT X.0 121
User Guide

fileStr.position = file.size - 128;

We set the position to this location in the file because the ID3 v1.0 format specifies that the ID3 tag data is stored
in the last 128 bytes of the file. The specification also says the following:
• The first 3 bytes of the tag contain the string "TAG".
• The next 30 characters contain the title for the MP3 track, as a string.
• The next 30 characters contain the name of the artist, as a string.
• The next 30 characters contain the name of the album, as a string.
• The next 4 characters contain the year, as a string.
• The next 30 characters contain the comment, as a string.
• The next byte contains a code indicating the track's genre.
• All text data is in ISO 8859-1 format.
The id3TagRead() method checks the data after it is read in (upon the complete event):
function id3TagRead():void
{
if (fileStr.readMultiByte(3, "iso-8859-1").match(/tag/i))
{
var id3Title:String = fileStr.readMultiByte(30, "iso-8859-1");
var id3Artist:String = fileStr.readMultiByte(30, "iso-8859-1");
var id3Album:String = fileStr.readMultiByte(30, "iso-8859-1");
var id3Year:String = fileStr.readMultiByte(4, "iso-8859-1");
var id3Comment:String = fileStr.readMultiByte(30, "iso-8859-1");
var id3GenreCode:String = fileStr.readByte().toString(10);
}
}

function id3TagRead()
{
if (fileStr.readMultiByte(3, "iso-8859-1").match(/tag/i))
{
var id3Title = fileStr.readMultiByte(30, "iso-8859-1");
var id3Artist = fileStr.readMultiByte(30, "iso-8859-1");
var id3Album = fileStr.readMultiByte(30, "iso-8859-1");
var id3Year = fileStr.readMultiByte(4, "iso-8859-1");
var id3Comment = fileStr.readMultiByte(30, "iso-8859-1");
var id3GenreCode = fileStr.readByte().toString(10);
}
}

You can also perform a random-access write to the file. For example, you could parse the id3Title variable to
ensure that it is correctly capitalized (using methods of the String class), and then write a modified string, called
newTitle, to the file, as in the following:

fileStr.position = file.length - 125; // 128 - 3


fileStr.writeMultiByte(newTitle, "iso-8859-1");

To conform with the ID3 version 1 standard, the length of the newTitle string should be 30 characters, padded at
the end with the character with the code 0 (String.fromCharCode(0)).
ADOBE PRODUCT X.0 122
User Guide

Using the encrypted local store


A persistent encrypted local store is available for each AIR application installed on a user’s machine. This lets you
save and retrieve data that is stored on the user’s local hard drive in an encrypted format that cannot be deciphered
by other applications or users. A separate encrypted local store is used for each AIR application, and each AIR appli-
cation uses a separate encrypted local store for each user.

You may want to use the encrypted local store to store information that must be secured, such as login credentials
for web services.
AIR uses DPAPI on Windows and KeyChain on Mac OS to associate the encrypted local store to each application
and user. The encrypted local store uses AES-CBC 128-bit encryption.
Information in the encrypted local data store is only available to AIR application content in the application security
sandbox.
Use the setItem() and removeItem() static methods of the EncryptedLocalStore class to store and retrieve data
from the local store. The data is stored in a hash table, using strings as keys, with the data stored as byte arrays.
For example, the following code stores a string in the encrypted local store:
var str:String = "Bob";
var bytes:ByteArray = new ByteArray();
bytes.writeUTFBytes(str);
EncryptedLocalStore.setItem("firstName", bytes);

var storedValue:ByteArray = EncryptedLocalStore.getItem("firstName");


trace(storedValue.readUTFBytes(storedValue.length)); // "Bob"

You can delete a value from the encrypted local store by using the EncryptedLocalStore.removeItem() method,
as in the following example:
EncryptedLocalStore.removeItem("firstName");
123

Chapter 17: Drag and Drop


Use the classes in the drag-and-drop API to support user-interface drag-and-drop gestures. With the drag-and-drop
API, you can allow a user to drag data between your AIR application and other applications, as well as between
components within your application. Supported transfer formats include:
• Bitmaps
• Files
• Text
• URLs
• Serialized objects
• Object references (only valid within the originating application)

GETTING STARTED
• “Drag and drop basics” on page 123
• “Dragging, copying, and pasting data” on page 35

DEVELOPMENT TASKS
• “Supporting the drag-out gesture” on page 125
• “Supporting the drag-in gesture” on page 127
• “HTML Drag and drop” on page 129

LANGUAGE REFERENCE
• DragManager *
• DragOptions *
• Clipboard
• NativeDragEvent
* The Flex 3 ActionScript Language Reference incorrectly lists these classes as NativeDragManager and NativeDra-
gOptions.

MORE INFORMATION
You may be able to find more information about working the AIR File API in the AIR Developer Center
(http://www.stage.adobe.com/devnet/air/flex/). Search the site using the keywords ‘AIR drag and drop’.

Drag and drop basics


The drag-and-drop API contains the following classes.
ADOBE PRODUCT X.0 124
User Guide

Package Classes

flash.desktop DragManager, DragOptions, Clipboard

Constants used with the drag-and-drop API are defined in the following classes: DragActions, ClipboardFormat, Clip-
boardTransferModes

flash.events NativeDragEvent

The drag-and-drop gesture has three stages:


Initiation A user initiates a drag-and-drop operation by dragging from a component, or an item in a component,
while holding down the mouse button. If the drag is initiated from an AIR application, the component is typically
designated as the drag initiator and will dispatch nativeDragStart and nativeDragComplete events for the drag
operation. An AIR application starts a drag operation by calling the DragManager.doDrag() method in response to
a mouseDown or mouseMove event.
Dragging While holding down the mouse button, the user moves the mouse cursor to another component, appli-
cation, or to the desktop. AIR optionally displays a proxy image during the drag. When the user moves the mouse
over a possible drop target in an AIR application, the drop target dispatches a nativeDragEnter event. The event
handler can inspect the event object to determine whether the dragged data is available in a format that the target
accepts and, if so, let the user drop the data onto it by calling the DragManager.acceptDrop() method.
Drop The user releases the mouse over an eligible drop target. If the target is an AIR application or component, then
the component dispatches a nativeDragDrop event. The event handler can access the transferred data from the event
object. If the target is outside AIR, the operating system handles the drop. In both cases, a nativeDragComplete event
is dispatched by the initiating object.
The DragManager class controls both drag-in and drag-out gestures. All the members of the DragManager class are
static, so an instance of this class does not need to be created.

The Clipboard object


Data that is dragged into or out of an application or component is contained in a Clipboard object. (Copy-and-paste
operations also use Clipboard objects.) A single Clipboard object can make available different representations of the
same information to increase the likelihood that another application can understand and use the data. For example,
an image might be included as image data, a serialized Bitmap object, and as a file. Rendering of the data in a format
can be deferred so that the data is not actually created until it is read. A Clipboard object has a limited lifespan. Once
a drag gesture has started, the Clipboard object can only be accessed from within an event handler for the native-
DragEnter, nativeDragOver, and nativeDragDrop events.
An application object can be transferred as a reference and as a serialized object. References are only valid within the
originating application. Serialized object transfers are valid between AIR applications, but can only be used with
objects that remain valid when serialized and deserialized.

Working with the Flex framework


In most cases, it is better to use the Flex drag-and-drop API when building Flex applications. The Flex framework
provides an equivalent feature set when a Flex application is run in AIR (it uses the AIR DragManager internally),
while also maintaining a more limited feature set when an application or component is running within the more
restrictive browser environment. AIR classes cannot be used in components or applications that run outside the AIR
run-time environment.
ADOBE PRODUCT X.0 125
User Guide

Supporting the drag-out gesture


To support the drag-out gesture, your application (or, more typically, a component of your application) must create
a Clipboard object in response to a mouseDown event and send it to the DragManager.doDrag() method. Your appli-
cation should then listen for the nativeDragComplete event on the initiating object to determine what action to take
when the gesture is completed or abandoned by the user.

Preparing data for transfer


To prepare data or an object for transfer out of a component or application, create a Clipboard object and add the
information to be transferred in one or more formats. By supplying information in multiple formats, you increase
the ability of other applications to use that information. You can use the standard data formats to pass data that can
be translated automatically to native clipboard formats, and application-defined formats to pass objects. If it is
computationally expensive to convert the information to be transferred into a particular format, you can supply the
name of a handler function that will perform the conversion if and only if that format is read by the receiving
component or application. For more information see “Clipboard data formats” on page 132.
The following example illustrates how to create a Clipboard object containing a bitmap in several formats: a Bitmap
object, a native bitmap format, and a file list format containing the file from which the bitmap was originally loaded.
import flash.desktop.Clipboard;
import flash.display.Bitmap;
import flash.filesystem.File;
public function createClipboard(image:Bitmap, sourceFile:File):Clipboard{
var transfer:Clipboard = new Clipboard();
transfer.setData(image,"CUSTOM_BITMAP",true); //Flash object by value and by reference
transfer.setData(image.bitmapData,ClipboardFormats.BITMAP_FORMAT,false);
transfer.setData(new Array(sourceFile),ClipboardFormats.FILE_LIST_FORMAT,false);
return transfer;
}

Starting a drag-out operation


To start a drag operation, call the DragManager.doDrag() method in response to a mouse down event. doDrag() is
a static method that takes the following parameters:
initiator The object from which the drag originates, and which will receive the dragStart and dragComplete
events. The initiator must be a DisplayObject.
clipboard The Clipboard object containing the data to be transferred. The Clipboard object is referenced in the
NativeDragEvent objects.
dragImage (Optional) A BitmapData object to display during the drag. The image can specify an alpha value. (Note
that Microsoft Windows always applies a fixed alpha fade to drag images).
offset (Optional) A Point object specifying the offset of the drag image from the mouse hotspot. Use negative
coordinates to move the drag image up and left relative to the mouse cursor. If no offset is provided, the top, left
corner of the drag image will be positioned at the mouse hotspot.
actionsAllowed (Optional) A NativeDragOptions object specifying which actions (copy, move, or link) are valid
for drag operation. If no object is provided, all actions are permitted. The DragOptions object is referenced in Native-
DragEvent objects to enable a potential drag target to check that the allowed actions are compatible with the target
component’s purpose. For example, a “trash” component might only accept drag gestures that allow the move action.
ADOBE PRODUCT X.0 126
User Guide

The following example illustrates how to start a drag operation for a bitmap object loaded from a file. The example
loads an image and, on a mouseDown event, starts the drag operation.
import flash.desktop.DragManager;
import mx.core.UIComponent;
import flash.desktop.Clipboard;
import flash.display.Bitmap;
import flash.filesystem.File;

public class DragOutExample extends UIComponent {


protected var fileURL:String = "app-resource:/image.jpg";
protected var display:Bitmap;

private function init():void {


DragManager.init();
loadImage();
}
private function onMouseDown(event:MouseEvent):void {
var bitmapFile:File = new File(fileURL);
var transferObject:Clipboard =
createClipboard(display,bitmapFile);
DragManager.doDrag(this,transferObject,display.bitmapData,new Point(-mouseX,-
mouseY));
}
public function createClipboard(image:Bitmap, sourceFile:File):Clipboard {
var transfer:Clipboard = new Clipboard();
transfer.setData(image,Bitmap",false); //Flash object by value and by reference
transfer.setData(image.bitmapData,"bitmap",false); //Standard bitmap format
transfer.setData(new Array(sourceFile),"file list",false); //Standard file list
format
return transfer;
}
private function loadImage():void {
var url:URLRequest = new URLRequest(fileURL);
var loader:Loader = new Loader();
loader.load(url,new LoaderContext());
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
}
private function onLoadComplete(event:Event):void {
display = event.target.loader.content;
var flexWrapper:UIComponent = new UIComponent();
flexWrapper.addChild(event.target.loader.content);
addChild(flexWrapper);
flexWrapper.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
}
}

Completing a drag-out transfer


When a user drops the dragged item by releasing the mouse, a nativeDragComplete event is dispatched by the
initiator object. If your application should modify the source data according to the drop action, you can check the
dropAction property of the event object and then take the appropriate action. For example, if the action is “move,”
you might remove the source item from its original location. If a drag gesture is abandoned by the user, the action
will always be “none.”
Note: When passing DisplayObjects by reference within an application, adding the transferred object as a child of a new
parent will automatically remove the object from its original parent.
ADOBE PRODUCT X.0 127
User Guide

Dragging Sprites
Sprite objects have a separate drag API which allows drag gestures to be used to move sprites around on the stage of
a window. You can use both APIs at the same time to allow moving a sprite around its current stage as well as out of
the stage. However, the DragManager API will take precedence visually — that is, the drag image or mouse pointer
will be shown rather than the live, possibly animated, sprite.

Supporting the drag-in gesture


To support the drag-in gesture, your application (or, more typically, a visual component of your application) must
respond to nativeDragEnter or nativeDragOver events. The following sequence of events is typical for a drop
operation:
• The user drags a clipboard object over a component.
• The component dispatches a nativeDragEnter event.
• The nativeDragEnter event handler checks the formats available and, if relevant, the allowed actions. If the
component can handle the drop, it calls DragManager.acceptDragDrop().
• The DragManager changes the mouse cursor to indicate that the object can be dropped.
• The user drops the object over the component.
• A nativeDragDrop event is dispatched by the receiving component.
• The receiving component reads the data in the desired format from the Clipboard object within the event object.
• If the drag gesture originated within an AIR application, then a nativeDragComplete event is dispatched by the
initiating display object. If the gesture originated outside AIR, no feedback is sent.

Acknowledging a drag-in gesture


When a user drags a clipboard item within the bounds of a visual component, the component dispatches native-
DragEnter and nativeDragOver events. To determine whether the clipboard item can be dropped on the component,
the handlers for these events should check the available formats in the Clipboard object referenced by the event
clipboard property. If desired, the component can also check the allowed actions by examining the DragOptions
object referenced in the event. If the component can handle the drop, the event handler must call the
DragManager.acceptDragDrop() method, passing a reference to the receiving component and the drag actions that
the component supports. If more than one component responds to the drag-in gesture, the last component to
respond takes precedence. The acceptDragDrop() call is valid until the mouse leaves the bounds of the accepting
object, triggering the nativeDragExit event (or until the drop occurs).
The following example illustrates an event handler for a nativeDragEnter or nativeDragOver event:
import flash.desktop.DragManager;
import flash.desktop.DragActions;
import flash.events.NativeDragEvent;

public function onDragIn(event:NativeDragEvent):void{


if(event.clipboard.hasFormat("text")){
DragManager.setFeedback(DragActions.MOVE);
DragManager.acceptDragDrop(this); //this is the receiving component
}
}
ADOBE PRODUCT X.0 128
User Guide

If more than one drag action is specified, or no action is specified, the effective action reported back to the initiating
object will follow precedence: copy, move, link. If none of the actions set by the receiving component matches an
action allowed by the drag gesture, then the receiving component will not be eligible to take the drop.

Completing the drop


When the user drops a clipboard item on a component that has accepted the gesture, the component will dispatch a
nativeDragDrop event. The handler for this event can extract the data from the clipboard property of the event
object.
When the clipboard contains an application-defined format, the transfer mode parameter determines whether the
reference or the serialized version of the object within the data format is obtained.
The following example illustrates an event handler for the nativeDragDrop event:
import flash.desktop.Clipboard;
import flash.events.NativeDragEvent;

public function onDrop(event:NativeDragEvent):void{


if(event.clipboard.hasFormat(ClipboardFormats.TEXT_FORMAT)){
var text:String =
String(event.clipboard.getData(ClipboardFormats.TEXT_FORMAT));
}
Once the event handler exits, the Clipboard object is no longer valid. Any attempt to access the object or its data will
throw an exception.

Updating the visual appearance of a component


A component can update its visual appearance based on the NativeDragEvents:
nativeDragStart The initiating component can use the nativeDragStart event to provide visual feedback that the
drag gesture originated from that component.
nativeDragEnter A potential receiving component can use this event take the focus, or indicate visually that it can
or cannot accept the drop.
nativeDragOver A potential receiving component can use this event to respond to the movement of the mouse
within the component, such as when the mouse enters a “hot” region of a complex component like a map.
nativeDragExit A potential receiving component can use this event to restore its state when a drag gesture moves
outside its bounds.
nativeDragComplete The initiating component can use this event to update its associated data model, such as by
removing an item from a list, and to restore its visual state.

Tracking mouse position during a drag-in gesture


Once a drag gesture has entered a component, nativeDragOver events are dispatched by that component. These
events are dispatched whenever the mouse moves and also on a short interval. The nativeDragOver event object can
be used to determine the position of the mouse over the component. This can be helpful in situations where the
receiving component is complex, but is not made up of sub-components — such as a map.
ADOBE PRODUCT X.0 129
User Guide

HTML Drag and drop


To drag data into and out of an HTML-based application (or into and out of the HTML displayed in an
HTMLControl), you can use HTML drag and drop events. The HTML drag-and-drop API allows you to drag to and
from DOM elements in the HTML content.
The events dispatched by the initiator element from which a drag originates, are:
ondragstart dispatched when the user starts the drag gesture. The handler for this event can prevent the drag, if
necessary, by calling the event object’s cancelDefault() method. To control whether the dragged data may be
copied, linked, or moved, set the effectAllowed property. Selected text is put onto the clipboard automatically, but
you can put data onto the clipboard using the dataTransfer property of the event object.
ondrag dispatched continuously during the drag gesture.

ondragend dispatched when the user releases the mouse button to end the drag gesture.

The events dispatched by a drag target are:


ondragenter dispatched when the drag gesture enters the boundaries of the element. The handler for this event
should set the dataTransfer.dropEffect property to indicate whether the drop will result in a copy, move, or link
action.
ondragover dispatched continuously while the drag gesture remains within the element boundaries.

ondragleave dispatched when the drag gesture leaves the element boundaries.

ondragdrop dispatched when the user drops the data onto the element. The data being dragged can only be accessed
within the handler for this event.

Setting the dragged data


You can set the data to be dragged in the handler for the ondragstart event using the dataTransfer property:
function doDragStart(evt) {
evt.dataTransfer.setData("text/plain","A picture of George");
evt.dataTransfer.setData("image/x-vnd.adobe.air.bitmap",
document.getElementById("imageOfGeorge");
evt.dataTransfer.setData("application/x-vnd.adobe.air.file-list",
new Array(georgeJPG));
evt.dataTransfer.effectAllowed = "all";
}
Use the dataTransfer.setData() method to put data onto the clipboard, passing in the mime type and data to
transfer.
The valid data mime types are:
Text •"text/plain"
URL •"text/uri-list"
Bitmap •"image/x-vnd.adobe.air.bitmap"
File list •"application/x-vnd.adobe.air.file-list"

Getting the dropped data


You can access the dropped data in the handler for the ondragdrop event:
function doDrop(evt)
ADOBE PRODUCT X.0 130
User Guide

{
droppedText = evt.dataTransfer.getData("text/plain");
}
Use the dataTransfer.getData() method to read the data onto the clipboard, passing in the mime type of the data
format to read.

About drag effects


The initiator of the drag gesture can limit the allowed effects by setting the dataTransfer.effectAllowed
property in the handler for the ondragstart event. The following string values can be used:
none No drag operations are allowed.

copy The data should be copied to the destination, leaving the original in place.

link The data should be shared with the drop destination using a link back to the original.

move The data should be copied to the destination and removed from the original location.

copyLink The data can be copied or linked.

copyMove The data can be copied or moved. This is the default value.

linkMove The data can be linked or moved.

all The data can be copied, moved, or linked.

The target of the drag gesture should set the dataTransfer.dropEffect property to indicate the action taken. The
value should be one of the actions allowed by the initiator, but this is not enforced (it is up to the application logic to
take the appropriate action). The dropEffect is typically set in the handler for the ondragenter handler, but can
be set in any of the events dispatched by the drag target (except ondragleave).
function doDragStart(evt) {
evt.dataTransfer.setData("text/plain","Text to drag");
evt.dataTransfer.effectAllowed = "all";
}

function doDragEnter(evt) {
evt.dataTransfer.dropEffect = "move";
}
131

Chapter 18: Copy and Paste


Use the classes in the clipboard API to copy information to and from the system clipboard. The data formats that can
be transferred into or out of an AIR application include:
• Bitmaps
• Files
• Text
• URL strings
• Serialized objects
• Object references (only valid within the originating application)

GETTING STARTED
• “Copy-and-paste basics” on page 131
• “Dragging, copying, and pasting data” on page 35

DEVELOPMENT TASKS
• “Reading from and writing to the system clipboard” on page 132
• “Clipboard data formats” on page 132

LANGUAGE REFERENCE
• Clipboard
• ClipboardFormats
• ClipboardTransferMode

MORE INFORMATION
You may be able to find more information about working the AIR clipboard API in the AIR Developer Center
(http://www.stage.adobe.com/devnet/air/flex/). Search the site using the keywords ‘AIR copy and paste’.

Copy-and-paste basics
The copy-and-paste API contains the following classes.

Package Classes

flash.desktop Clipboard, Constants used with the copy-and-paste API are defined in the following classes: ClipboardFormats, Clip-
boardTransferMode

The static Clipboard.generalClipboard property represents the operating system clipboard. Access the system
clipboard through the static Clipboard.generalClipboard property. Clipboard objects are also used to transfer
data through the drag-and-drop API. The Clipboard class provides methods for reading and writing data to
clipboard objects.
ADOBE PRODUCT X.0 132
User Guide

The data on the clipboard is represented by an instance of a Clipboard object. Different representations of the same
information can be made available in a single Clipboard object to increase the ability of other applications to under-
stand and use the data. For example, an image might be included as image data, a serialized Bitmap object, and as a
file. Rendering of the data in a format can be deferred so that the format is not actually created until the data in that
format is read.
An application object can be transferred as a reference and as a serialized object. References are only valid within the
originating application. Serialized object transfers are valid between Adobe AIR applications, but can only be used
with objects that remain valid when serialized and deserialized.

See also
• “Clipboard data formats” on page 132

Reading from and writing to the system clipboard


To read the clipboard, call the getData() method of the Clipboard.generalClipbooard, passing in the name of
the format to read:
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;

if(Clipboard.generalClipboard.hasFormat(ClipboardFormats.TEXT_FORMAT)){
var text:String = Clipboard.generalClipboard.getData(ClipboardFormats.TEXT_FORMAT);
}
To write to the clipboard, add the data to Clipboard.generalClipboard in one or more formats. Any existing data
in the same format will be overwritten automatically. However, it is a good practice to also clear the system clipboard
before writing new data to it to make sure that unrelated data in any other formats is also deleted.
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;

Clipboard.generalClipboard.clear();
var textToCopy:String = "Copy to clipboard.";
Clipboard.generalClipboard.addData(ClipboardFormats.TEXT_FORMAT,textToCopy,false);

Clipboard data formats


Clipboard formats describe the data placed in a Clipboard object. AIR automatically translates the standard data
formats between ActionScript data types and system clipboard formats. In addition, application objects can be trans-
ferred within and between AIR applications using application-defined formats.
A Clipboard object can contain representations of the same information in different formats. For example, a
Clipboard object representing a Sprite could include a reference format for use within the same application, a
serialized format for use by another AIR application, a bitmap format for use by an image editor, and a file list format,
perhaps with deferred rendering to encode a PNG file, for dragging a representation of the Sprite to the file system.

Standard data formats


The constants defining the standard format names are provided in the ClipboardFormats class:
ADOBE PRODUCT X.0 133
User Guide

TEXT_FORMAT Text-format data is translated to and from the ActionScript String class.

BITMAP_FORMAT Bitmap-format data is translated to and from the ActionScript BitmapData class.

FILE_LIST_FORMAT File-list-format data is translated to and from an array of ActionScript File objects.

URL_FORMAT URL-format data is translated to and from the ActionScript String class.

When copying and pasting data in response to an oncopy, oncut, or onpaste event in HTML content, mime types
must be used instead of the ClipboardFormat strings. The valid data mime types are:
Text •"text/plain"
URL •"text/uri-list"
Bitmap •"image/x-vnd.adobe.air.bitmap"
File list •"application/x-vnd.adobe.air.file-list"

Custom data formats


You can use application-defined custom formats to transfer objects as references or in serialized form. References
are only valid within the originating application. Serialized objects may be valid in other AIR applications as well
(when the receiving application knows how to use the serialized data).
To add a serialized object to a Clipboard object, set the serializable parameter to true when calling the
Clipboard.setData() method. The format name can be one of the standard formats or an arbitrary string defined
by your application.
public function createClipboardObject(object:Object):Clipboard{
var transfer:Clipboard = new Clipboard();
transfer.setData("object", object, true);
}

To extract a serialized object from the clipboard object (after a drop or paste operation), use the same format name
and the cloneOnly or clonePreferred transfer modes.
var transfer:Object = clipboard.getData("object", ClipboardTransferMode.CLONE_ONLY);

A reference is always added to the Clipboard object. To extract the reference from the clipboard object (after a drop
or paste operation), instead of the copy, use the originalOnly or originalPreferred transfer modes:
var transferedObject:Object =
clipboard.getData("object", ClipboardTransferMode.ORIGINAL_ONLY);
References are only valid if the Clipboard object originates from the current AIR application. Use the originalPre-
ferred transfer mode to access the reference when it is available, and the serialized clone when the reference is not
available.

Deferred rendering
If creating a data format is computationally expensive, you can use deferred rendering by supplying a function that
supplies the data on demand. The function is only called if a receiver of the drop or paste operation requests data in
the deferred format.
The rendering function is added to a Clipboard object using the setDataHandler() method and must return the
data in the expected format.
If a data format of the same type is added to a Clipboard object with the setData() method, that data will take prece-
dence over the deferred version (the rendering function will never be called).
ADOBE PRODUCT X.0 134
User Guide

The following example illustrates implementing a function to provide deferred rendering.


When the Copy button in the example is pressed, the application clears the system clipboard to ensure that no data
is left over from previous clipboard operations, then puts the renderData() function onto the clipboard with the
clipboard setDataHandler() method.
When the Paste button is pressed, the application accesses the clipboard and sets the destination text. Since the text
data format on the clipboard has been set with a function rather than a string, the clipboard will call the
renderData() function. The renderData() function returns the text in the source TextArea, which is then
assigned to the destination TextArea. Notice that if you edit the source text before pressing the Paste button, the edit
will be reflected in the pasted text, even when the edit occurs after the copy button was pressed. This is because the
rendering function doesn’t copy the source text until the paste button is pressed. (When using deferred rendering in
a real application, you might want to copy or protect the source data in some way to prevent this problem.)
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
width="326" height="330">
<mx:Script>
<![CDATA[
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;

public function doCopy():void{


Clipboard.generalClipboard.clear();
Clipboard.generalClipboard.setDataHandler(ClipboardFormats.TEXT_FORMAT,
renderData);
}

public function doPaste():void{


destination.text =
Clipboard.generalClipboard.getData(ClipboardFormats.TEXT_FORMAT) as String;
}

public function renderData():String{


trace("Rendering data");
return source.text;
}
]]>
</mx:Script>
<mx:Label x="10" y="10" text="Source"/>
<mx:TextArea id="source" x="10" y="36" width="300" height="100">
<mx:text>Neque porro quisquam est qui dolorem ipsum
quia dolor sit amet, consectetur, adipisci velit.</mx:text>
</mx:TextArea>
<mx:Label x="10" y="181" text="Destination"/>
<mx:TextArea id="destination" x="12" y="207" width="300" height="100"/>
<mx:Button click="doPaste();" x="166" y="156" label="Paste"/>
<mx:Button click="doCopy();" x="91" y="156" label="Copy"/>
</mx:WindowedApplication>
135

Chapter 19: Working with local SQL


databases
Adobe AIR includes the capability of creating and working with local SQL databases. The runtime includes a SQL
database engine with support for many standard SQL features, using the open source SQLite database system. A local
SQL database can be used for storing local, persistent data, which can be application data, application user settings,
documents, or any other type of data that you might want your application to save locally.
This section contains the following topics:
• About local SQL databases
• Common SQL database tasks
• Creating and modifying a database
• Using synchronous and asynchronous database operations
• Strategies for working with SQL databases

About local SQL databases


Adobe AIR includes a SQL-based relational database engine that runs within the runtime, with data stored locally
in database files on the computer on which the AIR application runs (for example, on the computer’s hard drive).
Because the database runs and data files are stored locally, a database can be used by an AIR application regardless
of whether a network connection is available. Thus, if you have experience with SQL and relational databases, the
runtime’s local SQL database engine provides a convenient mechanism for storing persistent, local application data.
However, because the data is local to a single computer, data is not automatically shared among users on different
computers, and the local SQL database engine doesn’t provide any capability to execute SQL statements against a
remote or server-based database.
The AIR local SQL database functionality can be used for any purpose for which you might want to store application
data on a user’s local computer. Adobe AIR includes several mechanisms for storing data locally, each of which has
different advantages. The following are some possible uses for a local SQL database in your AIR application:
• For a data-oriented application (for example an address book), a database can be used to store the main appli-
cation data.
• For a document-oriented application, where users create documents to save and possibly share, each document
could be saved as a database file, in a user-designated location. (Note, however, that any AIR application would be
able to open the database file, so a separate encryption mechanism would be recommended for potentially sensitive
documents.)
• For a network-aware application, a database can be used to store a local cache of application data, or to store
data temporarily when a network connection isn’t available. You could create a mechanism for synchronizing the
local database with the network data store.
• For any application, a database can be used to store individual users’ application settings, such as user options
or application information like window size and position.
ADOBE PRODUCT X.0 136
User Guide

About AIR databases and database files


An individual Adobe AIR local SQL database is stored as a single file in the computer’s file system. The runtime
includes the SQL database engine that manages creation and structuring of database files and manipulation and
retrieval of data from a database file. The runtime does not specify how or where database data is stored on the file
system; rather, each database is stored completely within a single file. You can specify the location in the file system
where the database file is stored, and a single AIR application can access one or many separate databases (i.e.
separate database files). Because the runtime stores each database as a single file on the file system, a database file
can be accessed by all application users on a single computer, or each user can have a separate database file,
according to the design of the application and the constraints on file access specified by the operating system.

About relational databases


A relational database is a mechanism for storing (and retrieving) data on a computer. Data is organized into tables:
rows represent records or items, and columns (sometimes called “fields”) divide each record into individual values.
For example, an address book application might contain a “friends” table. Each row in the table could represent a
single friend stored in the database. The table’s columns might represent data such as first name, last name, birth
date, and so forth. For each friend row in the table, the database stores a separate value for each column.
Relational databases are designed to store complex data, where one item is associated with or related to items of
another type. In a relational database, any data that has a one-to-many relationship—where a single record can be
related to multiple records of a different type—should be divided among different tables. For example, suppose you
want your address book application to store multiple phone numbers for each friend; this is a one-to-many
relationship. The “friends” table could contain all the personal information for each friend, and a separate “phone
numbers” table could contain all the phone numbers for all the friends.
In addition to storing the data about friends and phone numbers, each table would need a piece of data to keep track
of the relationship between the two tables—to match individual friend records with their phone numbers. This data
is known as a primary key—a unique identifier that distinguishes each row in a table from other rows in that table.
The primary key can be a “natural key,” meaning it’s one of the items of data that naturally distinguishes each record
in a table. In the “friends” table, if you knew that none of your friends share a birth date, you could use the birth date
column as the primary key (a natural key) of the “friends” table. If there is no natural key, you would create a
separate primary key column such as a “friend id”—an artificial value that the application uses to distinguish
between rows.
Using a primary key, you can set up relationships between multiple tables. For instance, suppose the “friends” table
has a column “friend id” that contains a unique number for each row (each friend). The related “phone numbers”
table can be structured with a column containing the “friend id” of the friend to whom the phone number belongs,
and another column containing the actual phone number. That way, no matter how many phone numbers are a
single friend has, they can all be stored in the “phone numbers” table and can be linked to the related friend using
the “friend id” primary key. When a primary key from one table is used in a related table to specify the connection
between the records, the value in the related table is known as a foreign key. Unlike many databases, the AIR local
database engine does not allow you to create foreign key constraints, which are constraints that automatically check
that an inserted or updated foreign key value has a corresponding row in the primary key table. Nevertheless, foreign
key relationships are an important part of the structure of a relational database, and foreign keys should be used
when creating relationships between tables in your database.
ADOBE PRODUCT X.0 137
User Guide

About SQL
Structured Query Language (SQL) is used with relational databases to manipulate and retrieve data. SQL is a
descriptive language rather than a procedural language: instead of giving the computer instructions on how it
should retrieve data, a SQL statement describes the set of data you want, and the computer determines how to
retrieve that data.
The SQL language has been standardized by the American National Standards Institute (ANSI). The Adobe AIR
local SQL database supports most of the SQL-92 standard. For specific descriptions of the SQL language supported
in Adobe AIR, see the appendix “SQL support in local databases” in the Adobe AIR language reference.

About SQL database classes


To work with local SQL databases in ActionScript 3.0, you use instances of these classes in the flash.data package:
• flash.data.SQLConnection Provides the means to create and open databases (database files), as well as
methods for performing database-level operations and for controlling database transactions.
• flash.data.SQLStatement Represents a single SQL statement (a single query or command) that is executed
on a database, including defining the statement text and setting parameter values.
• flash.data.SQLResult Provides a way to get information about or results from executing a statement, such
as the result rows from a SELECT statement, the number of rows affected by an UPDATE or DELETE statement, and so
forth.
To obtain schema information describing the structure of a database, you use these classes in the flash.data package:
• flash.data.SQLSchemaResult Serves as a container for database schema results generated by calling the
SQLConnection.loadSchema() method.

• flash.data.SQLTableSchema Provides information describing a single table in a database.

• flash.data.SQLViewSchema Provides information describing a single view in a database.

• flash.data.SQLColumnSchema Provides information describing a single column of a table or view in a


database.
• flash.data.SQLIndexSchema Provides information describing a single index in a database.

• flash.data.SQLTriggerSchema Provides information describing a single trigger in a database.

Other classes in the flash.data package provide constants that are used with the SQLConnection class and the
SQLColumnSchema class:
• flash.data.SQLColumnNameStyle Defines a set of constants representing the possible values for the
SQLConnection.columnNameStyle property.

• flash.data.SQLTransactionLockType Defines a set of constants representing the possible values for the
option parameter of the SQLConnection.begin() method.

• flash.data.SQLCollationType Defines a set of constants representing the possible values for the
SQLColumnSchema.defaultCollationType property and the defaultCollationType parameter of the
SQLColumnSchema() constructor.

In addition, the following classes in the flash.events package represent the events (and supporting constants) that
you will use:
ADOBE PRODUCT X.0 138
User Guide

• flash.events.SQLEvent Defines the events that are dispatched when any of the SQLConnection or
SQLStatement operations execute successfully. Each operation has an associated event type, all of which are defined
by the SQLEvent class.
• flash.events.SQLErrorEvent Defines the event that is dispatched when any of the SQLConnection or
SQLStatement operations results in an error.
• flash.events.SQLUpdateEvent Defines the event that is dispatched by a SQLConnection instance when
table data in one of its connected databases changes as a result of an INSERT, UPDATE, or DELETE SQL statement being
executed.
Finally, the following classes in the flash.errors package provide information about database operation errors:
• flash.errors.SQLError Provides information about a database operation error, including the operation that
was being attempted and the cause of the failure.
• flash.errors.SQLErrorCode Defines a set of constants representing the possible values for the SQLError
class’s code property, which indicates the type of error that led to a failed operation.
• flash.errors.SQLErrorOperation Defines a set of constants representing the possible values for the SQLError
class’s operation property, which indicates the database operation that resulted in an error.

Common SQL database tasks


This section describes the most common tasks that you will perform when you’re working with local SQL databases.
This includes connecting to a database, adding data to and retrieving data from tables in a database, and issues to
keep in mind while performing these tasks, such as working with data types and handling errors.
Note that there are also several database tasks that are things you’ll deal with less frequently, but you’ll often need
to do before you can perform the tasks in this section. For example, before you can connect to a database and retrieve
data from a table, you’ll need to create the database and create the table structure in the database. Those less-frequent
initial setup tasks are discussed in the section “Creating and modifying a database” on page 151.
You can choose whether to perform database operations asynchronously, meaning the database engine runs in the
background and notifies you when the operation succeeds or fails by dispatching an event, or synchronously,
meaning the database operations are performed one after another, and the entire application (including updates to
the screen) waits for the operations to complete before executing other code. This section introduces common
database operations and demonstrates how to perform them asynchronously, which is generally the best approach.
For details on performing these operations synchronously, see the section “Using synchronous and asynchronous
database operations” on page 152.

Connecting to a database
Before you can perform any database operations, you need to first open a connection to the database file. A
SQLConnection instance is used to represent a connection to one or more databases. The first database that is
connected using a SQLConnection instance, known as the “main” database, is connected using the open() method.
Opening a database is an asynchronous operation, meaning you must register for the SQLConnection instance’s
open event in order to know when the open() operation completes, and the SQLConnection instance’s error event
to determine if the operation fails. The following example shows how to open an existing database file named
“DBSample.db,” located in the user’s application storage directory.
import flash.data.SQLConnection;
import flash.events.SQLErrorEvent;
ADOBE PRODUCT X.0 139
User Guide

import flash.events.SQLEvent;
import flash.filesystem.File;

var conn:SQLConnection = new SQLConnection();


conn.addEventListener(SQLEvent.OPEN, openHandler);
conn.addEventListener(SQLErrorEvent.ERROR, errorHandler);

var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db");

conn.open(dbFile, false);

function openHandler(event:SQLEvent):void
{
trace("the database was opened successfully");
}

function errorHandler(event:SQLErrorEvent):void
{
trace("Error code:", event.error.code);
trace("Details:", event.error.message);
}

Notice that in the open() method call, the second argument is false. Specifying false for the second parameter
(autoCreate) causes the runtime to dispatch an error if the specified file doesn’t exist. If you pass true for the
autoCreate parameter (or leave it off and only specify a file location as the first parameter), the runtime will attempt
to create a new database file if the specified file doesn’t exist.

Working with SQL statements


An individual SQL statement (a query or command) is represented in the runtime as a SQLStatement object. Follow
these steps to create and execute a SQL statement:

1. Create a SQLStatement instance


The SQLStatement object represents the SQL statement in your application.
var selectData:SQLStatement = new SQLStatement();

2. Specify which database the query will run against.


To do this, set the SQLStatement object’s sqlConnection property to the SQLConnection instance that’s connected
with the desired database.
// A SQLConnection named "conn" has been created previously
selectData.sqlConnection = conn;

3. Specify the actual SQL statement.


Create the statement text as a String and assign it to the SQLStatement instance’s text property.
selectData.text = "SELECT col1, col2 FROM my_table WHERE col1 = :param1";

4. (Optional) Define functions to handle the result of the execute operation.


Use the addEventListener() method to register functions as listeners for the SQLStatement instance’s result
and error events.
// using listener methods and addEventListener();
selectData.addEventListener(SQLEvent.RESULT, resultHandler);
selecteData.addEventListener(SQLErrorEvent.ERROR, errorHandler);
ADOBE PRODUCT X.0 140
User Guide

private function resultHandler(event:SQLEvent):void


{
// do something after the statement execution succeeds
}

private function errorHandler(event:SQLErrorEvent):void


{
// do something after the statement execution fails
}

Alternatively, you can specify listener functions by creating a Responder object that refers to the listener functions
and passing it to the execute() method call.
// using a Responder
var selectResponder = new Responder(onResult, onError);

private function onResult(result:SQLResult):void


{
// do something after the statement execution succeeds
}

private function onError(error:SQLError):void


{
// do something after the statement execution fails
}

5. If the statement text includes parameter definitions, assign values for those parameters.
To do this, use the SQLStatement instance’s parameters associative array property.
selectData.parameters["param1"] = 25;

6. Execute the SQL statement.


Call the SQLStatement instance’s execute() method.
// using listener methods
selectData.execute();

// using a Responder
selectData.execute(-1, selectResponder);

For specific examples that demonstrate these steps, see the following sections:
• Retrieving data from a database
• Inserting data
• Changing or deleting data

Using parameters in statements


Frequently you will use a single SQL statement multiple times in an application, with slight variation. For example,
consider an inventory-tracking application where a user can add new inventory items to the database. The appli-
cation code that adds an inventory item to the database executes a SQL INSERT statement that actually adds the data
to the database. However, each time the statement is executed there will be a slight variation—the actual values that
are inserted in the table will be specific to the inventory item being added.
ADOBE PRODUCT X.0 141
User Guide

In cases like the one described here, where you have a SQL statement that’s used multiple times except that it uses
different values in the statement, the best approach is to use a SQL statement that includes parameters rather than
literal values in the SQL text. A parameter is a placeholder in the statement text that is replaced with an actual value
each time the statement is executed. To use parameters in a SQL statement, you create the SQLStatement instance
and assign the SQL statement that uses parameter placeholders rather than literal values to its text property. You
then define the value for each parameter by setting the value of an element in the SQLStatement instance’s
parameters property. The parameters property is an associative array, so you set a particular value using the
following syntax:
statement.parameters[parameter_identifier] = value;

The parameter_identifier is a string if you’re using a named parameter, or an integer index if you’re using an
unnamed parameter.

Using named parameters


A parameter can be a named parameter, meaning it has a specific name that you use to match the parameter value
to its placeholder location in the statement text. You use the “:” or “@” character followed by the parameter name
to indicate a named parameter, as in the following examples:
:itemName
@firstName

The following code listing demonstrates the use of named parameters:


var sql:String =
"INSERT INTO inventoryItems (name, productCode)" +
"VALUES (:name, :productCode)";
var addItemStmt:SQLStatement = new SQLStatement();
addItemStmt.sqlConnection = conn;
addItemStmt.text = sql;

// set parameter values


addItemStmt.parameters[":name"] = "Item name";
addItemStmt.parameters[":productCode"] = "12345";

addItemStmt.execute();

Using unnamed parameters


As an alternative to using named parameters, you can also use unnamed parameters. In that case you denote a
parameter in a SQL statement using a “?” character. Each parameter is assigned a numeric index, according to the
order of the parameters in the statement, starting with index 1 for the first parameter. The following example
demonstrates a version of the previous example, using unnamed parameters.
var sql:String =
"INSERT INTO inventoryItems (name, productCode)" +
"VALUES (?, ?)";
var addItemStmt:SQLStatement = new SQLStatement();
addItemStmt.sqlConnection = conn;
addItemStmt.text = sql;

// set parameter values


addItemStmt.parameters[1] = "Item name";
addItemStmt.parameters[2] = "12345";

addItemStmt.execute();
ADOBE PRODUCT X.0 142
User Guide

Benefits of using parameters


Using parameters in a SQL statement provides several benefits:
Better performance A SQLStatement instance that uses parameters can execute more efficiently compared to one
that dynamically creates the SQL text each time it executes, because it is prepared a single time and can then be
executed multiple times using different parameter values, without needing to recompile the SQL statement.
Explicit data typing Parameters are used to allow for typed substitution of values that are unknown at the time the
SQL statement is constructed. The use of parameters is the only way to guarantee the storage class for a value passed
in to the database. When parameters are not used, all values are converted from their text representation to a storage
class based on the associated column's type affinity. For more information on storage classes and column affinity, see
the section “Data type support” in the appendix “SQL support in local databases” in the Adobe AIR language
reference.
Greater security The use of parameters helps prevent a malicious technique known as a SQL injection attack. In a
SQL injection attack, a user enters SQL code in a user-accessible location (for example, a data entry field). If appli-
cation code constructs a SQL statement by directly concatenating user input into the SQL text, the user-entered SQL
code will be executed against the database. The following listing shows an example of concatenating user input into
SQL text. Do not use this technique:
// assume the variables "username" and "password"
// contain user-entered data
var sql:String =
"SELECT userId " +
"FROM users " +
"WHERE username = '" + username + "' " +
" AND password = '" + password + "'";
var statement:SQLStatement = new SQLStatement();
statement.text = sql;

Using statement parameters instead of concatenating user-entered values into a statement's text prevents a SQL
injection attack, because the parameter values are treated explicitly as substituted values, rather than becoming part
of the literal statement text. The following is the recommended alternative to the previous listing:
// assume the variables "username" and "password"
// contain user-entered data
var sql:String =
"SELECT userId " +
"FROM users " +
"WHERE username = :username " +
" AND password = :password";
var statement:SQLStatement = new SQLStatement();
statement.text = sql;

// set parameter values


statement.parameters[":username"] = username;
statement.parameters[":password"] = password;

Retrieving data from a database


To retrieve existing data from a database, you create a SQLStatement instance, assign the desired SQL SELECT
statement to the instance’s text property, then call the execute() method. For details on the syntax of the SELECT
statement, see the appendix “SQL support in local databases” in the Adobe AIR language reference.
var selectStmt:SQLStatement = new SQLStatement();
selectStmt.sqlConnection = conn;
selectStmt.text = "SELECT itemId, itemName, price FROM products";
ADOBE PRODUCT X.0 143
User Guide

// resultHandler and errorHandler are listener methods described in


// a subsequent code listing
selectStmt.addEventListener(SQLEvent.RESULT, resultHandler);
selectStmt.addEventListener(SQLErrorEvent.ERROR, errorHandler);

selectStmt.execute();

When the statement finishes executing, the SQLStatement instance dispatches a result event (SQLEvent.RESULT)
indicating that the statement was run successfully. Alternatively, if a Responder object is passed as an argument in
the execute() call, the Responder object’s result handler function is called. Each row of data in the result becomes
an Object instance whose properties’ names match the result set’s column names and whose properties contain the
values from the result set’s columns. For example, if a SELECT statement specifies a result set with three columns
named “itemId,” “itemName,” and “price,” for each row in the result set an Object instance is created with properties
named itemId, itemName, and price, containing the values from their respective columns.
function resultHandler(event:SQLEvent):void
{
var result:SQLResult = selectStmt.getResult();
var numResults:int = result.data.length;
for (var i:int = 0; i < numResults; i++)
{
var row:Object = result.data[i];
var output:String = "itemId: " + row.itemId;
output += "; itemName: " + row.itemName;
output += "; price: " + row.price;
trace(output);
}
}

function errorHandler(event:SQLErrorEvent):void
{
// Information about the error is available in the
// event.error property, which is an instance of
// the SQLError class.
}

As the preceding code listing shows, the result objects are contained in an array that is available as the data property
of a SQLResult instance. If you’re using an event listener, to retrieve that SQLResult instance you call the
SQLStatement instance’s getResult() method. If you specify a Responder argument in the execute() call, the
SQLResult instance is passed to the result handler function as an argument. In either case, once you have the
SQLResult object you can access the result rows using the data array property.
The following code listing defines a SQLStatement instance whose text is a SELECT statement. The statement
retrieves rows containing the firstName and lastName column values of all the rows of a table named employees.
When the execution completes, the selectResult() method is called, and the resulting rows of data are retrieved
using SQLStatement.getResult() and displayed using the trace() method. Note that this listing assumes there
is a SQLConnection instance named conn that has already been instantiated and is already connected to the
database. It also assumes that the “employees” table has already been created and populated with data.
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLStatement;
import flash.events.SQLErrorEvent;
import flash.events.SQLEvent;

// ... create and open the SQLConnection instance named conn

// create the SQL statement


ADOBE PRODUCT X.0 144
User Guide

var selectStmt:SQLStatement = new SQLStatement();


selectStmt.sqlConnection = conn;

// define the SQL text


var sql:String =
"SELECT firstName, lastName " +
"FROM employees";
selectStmt.text = sql;

// register listeners for the result and error events


selectStmt.addEventListener(SQLEvent.RESULT, selectResult);
selectStmt.addEventListener(SQLErrorEvent.ERROR, selectError);

// execute the statement


selectStmt.execute();

function selectResult(event:SQLEvent):void
{
var result:SQLResult = selectStmt.getResult();
var numRows:int = result.data.length;
for (var i:int = 0; i < numRows; i++)
{
var output:String = "";
for (var columnName:String in result.data[i])
{
output += columnName + ": " + result.data[i][columnName] + "; ";
}
trace("row[" + i.toString() + "]\t", output);
}
}

function selectError(event:SQLErrorEvent):void
{
trace("Error code:", event.error.code);
trace("Details:", event.error.message);
}

Defining the data type of SELECT result data


By default, each row returned by a SELECT statement is created as an Object instance whose properties’ names match
the result set's column names, and with the value of each column used as the value of its associated property.
Before executing a SQL SELECT statement you can set the itemClass property of the SQLStatement instance to a
class, and each row returned by the SELECT statement is created as an instance of the designated class. The runtime
matches the column names in the SELECT result set to the names of the properties in the specified class in order to
know which value to assign to which property.
Any class assigned as an itemClass property value must have a constructor that does not require any parameters.
In addition, the class must have a single property for each column returned by the SELECT statement. It is considered
an error if a column in the SELECT list does not have a matching property name in the itemClass class.

Retrieving SELECT results in parts


By default, a SELECT statement execution retrieves all the rows of the result set at one time. Once the statement
completes, you will usually process the retrieved data in some way, such as creating objects or displaying the data
on the screen. If the statement returns a large number of rows, processing all the data at once can be demanding for
the computer, which in turn will cause the user interface to pause and not refresh itself.
ADOBE PRODUCT X.0 145
User Guide

You can improve the perceived performance of your application by instructing the runtime to return a specific
number of result rows at a time. Doing so will cause the initial result data to return more quickly. It also allows you
to divide the result rows into sets, so that the user interface is updated after each set of rows is processed. When you
call a SQLStatement instance’s execute() method, specify a value for the prefetch parameter (the execute()
method’s first parameter) to indicate the number of result rows to retrieve when the statement executes:
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = conn;
stmt.text = "SELECT ...";
stmt.addEventListener(SQLEvent.RESULT, selectResult);
stmt.execute(20); // only the first 20 rows (or fewer) will be returned

The statement dispatches the result event, indicating that the first set of result rows are available. The resulting
SQLResult instance’s data property contains the rows of data, and its complete property indicates whether there are
additional result rows to retrieve. To retrieve subsequent result rows, call the SQLStatement instance’s next()
method. Like the execute() method, the next() method’s first parameter is used to indicate how many rows to
retrieve the next time the result event is dispatched.
function selectResult(event:SQLEvent):void
{
var result:SQLResult = stmt.getResult();
if (result.data != null)
{
// ... loop through the rows or perform other processing
if (!result.complete)
{
stmt.next(20); // retrieve the next 20 rows
}
else
{
stmt.removeEventListener(SQLEvent.RESULT, selectResult);
}
}
}

The SQLStatement dispatches a result event each time the next() method returns a subsequent set of result rows,
so the same listener function can be used to continue processing results until all the rows are retrieved.
For more information, see the language reference descriptions for the SQLStatement.execute() method (the
prefetch parameter description) and the SQLStatement.next() method.

Inserting data
To add data to a table in a database, you create and execute a SQLStatement instance using a SQL INSERT statement.
The following example uses a SQLStatement instance to add a row of data to the already-existing employees table.
Note that this listing assumes that there is a SQLConnection instance named conn that has already been instantiated
and is already connected to a database. It also assumes that the “employees” table has already been created.
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLStatement;
import flash.events.SQLErrorEvent;
import flash.events.SQLEvent;

// ... create and open the SQLConnection instance named conn

// create the SQL statement


var insertStmt:SQLStatement = new SQLStatement();
ADOBE PRODUCT X.0 146
User Guide

insertStmt.sqlConnection = conn;

// define the SQL text


var sql:String =
"INSERT INTO employees (firstName, lastName, salary) " +
"VALUES ('Bob', 'Smith', 8000)";
insertStmt.text = sql;

// register listeners for the result and failure (status) events


insertStmt.addEventListener(SQLEvent.RESULT, insertResult);
insertStmt.addEventListener(SQLErrorEvent.ERROR, insertError);

// execute the statement


insertStmt.execute();

function insertResult(event:SQLEvent):void
{
trace("INSERT statement succeeded");
}

function insertError(event:SQLErrorEvent):void
{
trace("Error code:", event.error.code);
trace("Details:", event.error.message);
}

Retrieving a database-generated primary key of an inserted row


Often after inserting a row of data into a table, you will want to retrieve a database-generated primary key or row
identifier value for the newly-inserted row. For example, once you insert a row in one table, you might want to add
rows in a related table, inserting the primary key value as a foreign key in the related table. The primary key of a
newly-inserted row can be retrieved in a way that’s similar to the way result data is retrieved after a SELECT
statement is executed—using the SQLResult object associated with the statement execution.
Like with a SELECT or any other SQL statement, when an INSERT statement execution completes and the result
event is dispatched, the runtime creates a SQLResult instance that is accessed by calling the SQLStatement object’s
getResult() method (if you’re using an event listener) or as the argument passed to the result handler function (if
you specified a Responder in the execute() call). The SQLResult instance has a property, lastInsertRowID, that
contains the row identifier of the most-recently inserted row if the executed SQL statement is an INSERT statement.
insertStmt.text = "INSERT INTO ...";
insertStmt.addEventListener(SQLEvent.RESULT, resultHandler);
insertStmt.execute();

private function resultHandler(event:SQLEvent):void


{
var result:SQLResult = insertStmt.getResult();
var primaryKey:Number = result.lastInsertRowID;
// do something with the primary key
}

Note that the row identifier may or may not be the value of the column that is designated as the primary key column
in the table structure, according to the following rule:
• If the table is defined with a primary key column whose affinity (column data type) is INTEGER, the
lastInsertRowID property contains the value that was inserted into that row (or the value generated by the
runtime if it’s an AUTOINCREMENT column).
ADOBE PRODUCT X.0 147
User Guide

• If the table is defined with multiple primary key columns (a composite key) or with a single primary key column
whose affinity is not INTEGER , behind the scenes the database generates a row identifier value for the row, and that
generated value is the value of the lastInsertRowID property.
• The value is always the row identifier of the most-recently inserted row. If an INSERT statement causes a trigger
to fire, which in turn inserts a row, the lastInsertRowID property will contain the row identifier of the last row
inserted by the trigger, rather than the row created by the INSERT statement.
Consequently, if you want to have an explicitly defined primary key column whose value available after an INSERT
command through the SQLResult.lastInsertRowID property, the column must be defined as an INTEGER
PRIMARY KEY column. Note, however, that even if your table does not include an explicit INTEGER PRIMARY KEY
column, it is equally acceptable to use the database-generated row identifier as a primary key for your table in the
sense of defining relationships with related tables. The row identifier column value is available in any SQL statement
by using one of the special column names ROWID, _ROWID_, or OID. You can create a foreign key column in a related
table, and use the row identifier value as the foreign key column value, just as you would with an explicitly-declared
INTEGER PRIMARY KEY column. In that sense, if you are using an arbitrary primary key rather than a natural key,
and as long as you don’t mind the runtime generating the primary key value for you, it makes little difference
whether you use an INTEGER PRIMARY KEY column or the system-generated row identifier as a table’s primary key
for defining a foreign key relationship with between two tables.
For more information about primary keys and generated row identifiers, see the sections titled “CREATE TABLE”
and “Expressions” in the appendix “SQL support in local databases” in the Adobe AIR language reference.

Changing or deleting data


The process for executing other data manipulation operations is identical to the process used to execute a SQL
SELECT or INSERT statement; you simply substitute a different SQL statement in the SQLStatement instance’s text
property:
• To change existing data in a table, use an UPDATE statement.
• To delete one or more rows of data from a table, use a DELETE statement.
For descriptions of these statements, see the appendix “SQL support in local databases” in the Adobe AIR language
reference.

Handling database errors


In general, database error handling is similar to other runtime error handling—you should write code that is
prepared for errors that may occur, and respond to the errors rather than leave it up to the runtime to do so. The
SQLErrorCode class defines a set of constants that represent the set of errors that can occur during the various
database operations. In a general sense, the possible database errors can be divided into three categories: connection
errors, SQL syntax errors, and constraint errors.

Connection errors
Most database errors are connection errors, and they can occur during any operation. Although there are strategies
for preventing connection errors, there is rarely a simple way to gracefully recover from a connection error if the
database is a critical part of your application.
Most connection errors have to do with how the runtime interacts with the operating system, the file system, and
the database file. For example, a connection error will occur if the user doesn’t have permission to create a database
file in a particular location on the file system. The following strategies will help you to prevent connection errors:
ADOBE PRODUCT X.0 148
User Guide

Use user-specific database files Rather than using a single database file for all users who use the application on a
single computer, give each user their own database file, stored in a location that’s associated with the user’s account
such as the application’s storage directory, the user’s documents folder, the user’s desktop, and so forth.
Consider different user types Test your application with different types of user accounts, on different operating
systems. Don’t assume that the user will have administrator permission on the computer, or that the individual who
installed the application is the user who’s running the application.
Consider various file locations If you allow users to specify the location where a database file is saved or the location
of a database file to open, consider the possible file locations that users might specify. In addition, consider defining
limits on where users can store (or from where they can open) database files. For example, you might only allow
users to open files that are within their user account’s storage location.
If a connection error occurs, it most likely happens on the first attempt to create or open the database. This means
that the user will be unable to do any database-related operations in the application. For certain types of errors, such
as read-only or permission errors, one possible recovery technique is to implement application logic that copies the
database file to (or creates a new database file in) a different location where the user does have permission to create
and write to files.

Syntax errors
A syntax error occurs when a SQL statement is incorrectly formed, and the application attempts to prepare or
execute the statement. Because local database SQL statements are created as strings, compile-time SQL syntax
checking is not possible, and all SQL statements must be executed (or prepared) to check their syntax. Use the
following strategies to prevent SQL syntax errors:
Test all SQL statements thoroughly If possible, while developing your application test your SQL statements
separately before encoding them as statement text in the application code. In addition, use a code-testing approach
such as unit testing to create a set of tests that exercise every possible option and variation in the code.
Use statement parameters and avoid concatenating (dynamically generating) SQL Using parameters, and avoiding
dynamically-built SQL statements, means that the same SQL statement text is used each time a statement is
executed. Consequently, it’s much easier to test your statements and limit the possible variation. If you must dynam-
ically generate a SQL statement, keep the dynamic parts of the statement to a minimum, and carefully validate any
user input to make sure it won’t cause syntax errors.
Use prepared SQL statements In many ways this is simply a combination of the two previous strategies. Prepared
statements can’t be dynamically generated. Syntax checking happens when a statement is prepared, so if you use
prepared SQL statements in your application, and explicitly prepare all statements, each statement is tested. For
more on using prepared statements, see “Use prepared statements” on page 157.
To recover from a syntax error, an application would need complex logic to be able to examine a SQL statement and
correct its syntax. By following the previous guidelines for preventing syntax errors, you should also be able to
identify any potential run-time sources of SQL syntax errors (such as user input used in a statement). To recover
from a syntax error, you could provide the user with a specific error message guiding him or her to what needs to
be corrected to make the statement execute correctly.

Constraint errors
Constraint errors occur when an INSERT or UPDATE statement attempts to add data to a column that violates one of
the defined constraints for the table or column. The following constraints can be defined:
Unique constraint Indicates that across all the rows in a table, there cannot be duplicate values in one column (or,
when multiple columns are combined in a unique constraint, the combination of values in those columns must not
be duplicated). In other words, in terms of the specified unique column(s), each row must be distinct.
ADOBE PRODUCT X.0 149
User Guide

Primary key constraint In terms of the data that a constraint allows and doesn’t allow, a primary key constraint is
identical to a unique constraint.
Not null constraint Specifies that a single column cannot store a NULL value and consequently that in every row, that
column must have a value.
Check constraint Allows you to specify an arbitrary constraint on one or more tables. Common examples of check
constraints are a rule that define that a column’s value must be within certain bounds (for example, that a numeric
column’s value must be larger than 0), or one that specifies relationships between column values (for example, that
a column’s value must be different than the value of another column in the same row).
The runtime does not enforce constraints on foreign key values (foreign key values aren’t required to match an
existing primary key value). The runtime also does not enforce the data type of columns’ values (although under
some conditions the runtime converts an inserted values to another data type that matches its column’s specified
type). See the section “Data type support” in the appendix “SQL support in local databases” in the Adobe AIR
language reference for more information.
In addition to the predefined constraint types, the runtime SQL engine supports the use of triggers. A trigger is a
predefined set of instructions that are carried out when a certain action happens, such as when data is inserted into
or deleted from a particular table. One possible use of a trigger is to examine data changes and cause an error to
occur if specified conditions aren’t met. Consequently, a trigger can serve the same purpose as a constraint, and the
strategies for preventing and recovering from constraint errors also apply to trigger-generated errors. However, the
error code for trigger-generated errors (the constant SQLErrorCode.ABORT) is different from the error code for
constraint errors (the constant SQLErrorCode.CONSTRAINT).
Because constraints are specified when a table is created, triggers must be explicitly created, and Adobe AIR doesn’t
specify any additional constraints on tables or columns, the set of constraints that apply to a particular table can be
identified ahead of time and can be taken into account both in preventing and in recovering from constraint errors.
Because constraint errors are dependent on data that is to be added to a database, and often on the relationship
between the new data and other data that already exists in the database, constraint errors are difficult to systemati-
cally predict and prevent. The following strategies can help you avoid many constraint errors:
Carefully plan database structure and constraints The purpose of constraints is to enforce application rules and
help protect the integrity of the database’s data. When you’re planning your application, consider how to structure
your database to support your application. As part of that process, you will want to identify rules about your data,
such as whether certain values are required, whether a value has a default, whether duplicate values are allowed, and
so forth. Those rules will guide you in defining database constraints.
Explicitly specify column names An INSERT statement can be written without explicitly specifying the columns into
which values are to be inserted, but doing so is an unnecessary risk. By explicitly naming the columns into which
values are to be inserted, you can allow for automatically generated values, columns with default values, and
columns that allow NULL values. In addition, by doing so you can ensure that all NOT NULL columns have an explicit
value inserted.
Use default values Whenever you specify a NOT NULL constraint for a column, if at all possible you should also
specify a default value in the column definition. Application code can also provide default values, for example
checking if a String variable is null and assigning it a value before using it to set a statement parameter value.
Validate user-entered data Check user-entered data ahead of time to make sure that it obeys limits specified by
constraints, especially in the case of NOT NULL and CHECK constraints. Naturally, a UNIQUE constraint is more
difficult to check for because doing so would require executing a SELECT query to determine whether the data is
unique.
ADOBE PRODUCT X.0 150
User Guide

Use triggers You can write a trigger that validates (and possibly replaces) inserted data or takes other actions to
correct invalid data. This can prevent a constraint error from occurring.
Fortunately, while constraint errors are perhaps more difficult to prevent than other types of errors, there are several
strategies to recover from constraint errors in ways that don’t make the application unstable or unusable:
Use conflict algorithms When you define a constraint on a column, and when you create an INSERT or UPDATE
statement, you have the option of specifying a conflict algorithm, defining what action the database should take
when a constraint violation occurs. The possible actions can range from ending a single statement or a whole trans-
action, to ignoring the error or even replacing old data with the new data in order to conform to a constraint. For
more information see the section “ON CONFLICT (conflict algorithms)” in the appendix “SQL support in local
databases” in the Adobe AIR language reference.
Provide corrective feedback Since the set of constraints that can affect a particular SQL command can be identified
ahead of time, you can anticipate constraint errors that a statement could cause. With that knowledge, you can build
application logic to respond to a constraint error, such as by showing the user a helpful error message that allows
him or her to correct the error and attempt the operation again. For example, if an application includes a data entry
form for entering new products, and the product name column in the database is defined with a UNIQUE constraint,
the action of inserting a new product row in the database could cause a constraint error. Consequently, if a
constraint error occurs, the application could prompt the user to indicate that the specified product name is already
in use, and encourage the user to choose a different name (or perhaps allow the user to view information about the
other product with the same name).

Working with database data types


When a table is created in a database, part of the SQL statement for creating the table involves defining the affinity,
or preferred data type, for each column in the table. Although affinity declarations can be omitted, it’s a good idea
to explicitly declare column affinity in your CREATE TABLE SQL statements. This section describes concepts and
techniques that you’ll want to keep in mind for dealing with database data types from ActionScript or JavaScript.
For more information about the available column affinity types and using data types in SQL statements, see the
section “Data type support” in the appendix “SQL support in local databases” in the Adobe AIR language reference..

Data type conversion between a database and application code


As a general rule, any object that you store in a database using an INSERT statement should be returned as an
instance of the same data type when you execute a SELECT statement. However, one complicating factor that can
make this not happen is the affinity of the database column in which the value is stored. When a value is stored in a
column, the database attempts to convert the value to a particular data type in order to match the declared affinity
of the column. For example, if a database column is declared with NUMERIC affinity, the database will attempt to
convert inserted data into a numeric storage class (INTEGER or REAL) and will only store it in another form if the
data can’t be converted. This means that if the String “12345” is inserted into a NUMERIC column, the database will
automatically convert it to the integer value 12345 before storing it in the database—and when it’s retrieved with a
SELECT statement, it will be returned as an instance of a numeric data type (such as Number) rather than as a String
instance.
The best way to avoid undesirable data type conversion is to define each column with the affinity that matches the
type of data that it will store, and only insert values into that column whose data type matches the defined affinity.
That way, when you insert the data it won’t be converted unexpectedly (and possibly lose its intended meaning as a
result) and when you retrieve it, it will be returned as the original data type.
ADOBE PRODUCT X.0 151
User Guide

Creating and modifying a database


This section covers database tasks that are less-frequently used, but are generally necessary for most applications.
This section covers the following topics:
• Creating a new database
• Creating database tables
Like the previous section, this section demonstrates how to perform these operations asynchronously, which is
generally the best approach. For details on performing these operations synchronously, see the section “Using
synchronous and asynchronous database operations” on page 152.

Creating a new database


To create a new database file, you create a SQLConnection instance and call its open() method, as you would to
open a connection to an existing database file. By specifying a file parameter that indicates a file location that does
not exist, the open() method will create a new database file at that location, then open a connection to the newly-
created database. When using the open() method to create a new database file, you can use the autoClean and
pageSize parameters to define those characteristics of the database being created.

The following code listing shows the process of creating a new database file (a new database). In this case, the
database file is saved in the application’s storage directory, with the file name “DBSample.db”:
import flash.data.SQLConnection;
import flash.events.SQLErrorEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;

var conn:SQLConnection = new SQLConnection();


conn.addEventListener(SQLEvent.OPEN, openHandler);
conn.addEventListener(SQLErrorEvent.ERROR, errorHandler);

var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db");

conn.open(dbFile);

function openHandler(event:SQLEvent):void
{
trace("the database was created successfully");
}

function errorHandler(event:SQLErrorEvent):void
{
trace("Error code:", event.error.code);
trace("Details:", event.error.message);
}

A database’s file name can be any valid file name, with any file extension. If you call the open() method with null
for the file parameter, a new in-memory database is created rather than a database file on disk.

Creating database tables


Creating a table in a database involves executing a SQL statement on that database, using the same process that you
use to execute a SQL statement such as SELECT, INSERT, and so forth. To create a table, you use a CREATE TABLE
statement, which includes definitions of columns and constraints for the new table.
ADOBE PRODUCT X.0 152
User Guide

The following example demonstrates creating a new table named “employees” in an existing database file. Note that
this code assumes there is a SQLConnection instance named conn that is already instantiated and is already
connected to a database.
import flash.data.SQLConnection;
import flash.data.SQLStatement;
import flash.events.SQLErrorEvent;
import flash.events.SQLEvent;

// ... create and open the SQLConnection instance named conn

var createStmt:SQLStatement = new SQLStatement();


createStmt.sqlConnection = conn;

var sql:String =
"CREATE TABLE IF NOT EXISTS employees (" +
" empId INTEGER PRIMARY KEY AUTOINCREMENT, " +
" firstName TEXT, " +
" lastName TEXT, " +
" salary NUMERIC CHECK (salary > 0)" +
")";
createStmt.text = sql;

createStmt.addEventListener(SQLEvent.RESULT, createResult);
createStmt.addEventListener(SQLErrorEvent.ERROR, createError);

createStmt.execute();

function createResult(event:SQLEvent):void
{
trace("Table created");
}

function createError(event:SQLErrorEvent):void
{
trace("Error code:", event.error.code);
trace("Details:", event.error.message);
}

Using synchronous and asynchronous database


operations
Previous sections have described common database operations such as retrieving, inserting, updating, and deleting
data, as well as creating a database file and tables and other objects within a database. The examples have demon-
strated how to perform these operations asynchronously—you instruct the database to perform the operation, it
does its work in the background, and when the operation is completed (or when it fails) an event is dispatched,
which your code can listen for and use to carry out subsequent operations. This approach has a significant
advantage: because the runtime performs the database operations in the background while the main application
code continues executing. If the database operation takes a notable amount of time, the application continues to run
and most importantly the user can continue to interact with it without the screen freezing. Nevertheless,
asynchronous operation code can be more complex to write because multiple dependent operations must be divided
up among various event listener methods rather than being able to write a sequence of steps.
ADOBE PRODUCT X.0 153
User Guide

In addition to asynchronous database operations, the runtime also allows you to execute database operations
synchronously. The decision to execute operations asynchronously or synchronously isn’t made on an operation-
by-operation basis; instead it’s specified by passing a value for the SQLConnection constructor’s isSync parameter
when you create a SQLConnection instance. (The default value, which is used when no argument is passed, is false,
meaning the connection doesn’t use synchronous execution.) Once a SQLConnection instance is created, its
execution mode (synchronous or asynchronous) is fixed; all database operations that are performed on any database
that’s opened using that SQLConnection instance will execute using the specified mode. This is true regardless of
whether the operation is performed by calling a SQLConnection method (such as opening a connection using
SQLConnection.open() or beginning a transaction using SQLConnection.begin()), or if the operation is a SQL
statement such as a SELECT query that’s executed using a SQLStatement instance whose sqlConnection property
is set to the SQLConnection instance.

Using synchronous database operations


There is little difference in the actual code that you use to execute and respond to operations when using
synchronous execution, except that you write the code as a series of steps rather than registering event listeners. To
execute operations synchronously, when you create the SQLConnection instance, you must explicitly provide a
true value for the SQLConnection() constructor’s isSync parameter (the method’s only parameter). The code to
create a SQLConnection instance that executes its operations synchronously looks like this:
// performs operations synchronously
var conn:SQLConnection = new SQLConnection(true);

By creating it that way, all operations will be performed synchronously for that SQLConnection object. You can
execute a series of database operations in succession within a single code block, as in the following example:
var conn:SQLConnection = new SQLConnection(true);
var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db");

// open the database


conn.open(dbFile, false);

// start a transaction
conn.begin();

// add the customer record to the database


var insertCustomer:SQLStatement = new SQLStatement();
insertCustomer.sqlConnection = conn;
insertCustomer.text =
"INSERT INTO customers (firstName, lastName) " +
"VALUES ('Bob', 'Jones')";

insertCustomer.execute();
var customerId:Number = insertCustomer.getResult().lastInsertRowID;

// add a related phone number record for the customer


var insertPhoneNumber:SQLStatement = new SQLStatement();
insertPhoneNumber.sqlConnection = conn;
insertPhoneNumber.text =
"INSERT INTO customerPhoneNumbers (customerId, number) " +
"VALUES (:customerId, '800-555-1234')";
insertPhoneNumber.parameters[":customerId"] = customerId;

insertPhoneNumber.execute();

// commit the transaction


conn.commit();
ADOBE PRODUCT X.0 154
User Guide

As you can see, you call the same methods to perform database operations whether you’re using synchronous or
asynchronous execution. The key differences between the two approaches have to do with executing operations that
depend on other operations (such as SELECT result rows or the primary key of the row added by an INSERT
statement) and handling errors that can occur with any database operation.

Executing operations that depend on other operations


When you’re using synchronous execution mode, instead of writing code that listens for an event to determine when
an operation completes, you can assume that if an operation in one line of code completes successfully, execution
will continue with the next line of code. Consequently, if you need to perform an operation that depends on the
success of another operation or execute some code after a particular statement completes, you can simply write the
dependent code immediately following the operation on which it depends. For instance, if your application needs
to begin a transaction, execute an INSERT statement, retrieve the primary key of the inserted row, insert that primary
key into another row of a different table, and finally commit the transaction, the code can all be written as a series
of statements, as shown in the following example:
var conn:SQLConnection = new SQLConnection(true);
var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db");

// open the database


conn.open(dbFile, false);

// start a transaction
conn.begin();

// add the customer record to the database


var insertCustomer:SQLStatement = new SQLStatement();
insertCustomer.sqlConnection = conn;
insertCustomer.text =
"INSERT INTO customers (firstName, lastName) " +
"VALUES ('Bob', 'Jones')";

insertCustomer.execute();
var customerId:Number = insertCustomer.getResult().lastInsertRowID;

// add a related phone number record for the customer


var insertPhoneNumber:SQLStatement = new SQLStatement();
insertPhoneNumber.sqlConnection = conn;
insertPhoneNumber.text =
"INSERT INTO customerPhoneNumbers (customerId, number) " +
"VALUES (:customerId, '800-555-1234')";
insertPhoneNumber.parameters[":customerId"] = customerId;

insertPhoneNumber.execute();

// commit the transaction


conn.commit();

Handling errors with synchronous execution


Rather than writing code that listens for an error event to determine that an operation has failed, with synchronous
execution you surround any code that could trigger errors in a set of try..catch..finally code blocks. You wrap
the error-throwing code in the try block, and the actions to perform in response to each type of error in separate
catch blocks. Any code that should always be executed regardless of success or failure (for example, closing a
database connection that’s no longer needed) is placed in a finally block. The following example demonstrates
this, adding to the previous one by adding error handling code:
var conn:SQLConnection = new SQLConnection(true);
var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db");
ADOBE PRODUCT X.0 155
User Guide

// open the database


conn.open(dbFile, false);

// start a transaction
conn.begin();

try
{
// add the customer record to the database
var insertCustomer:SQLStatement = new SQLStatement();
insertCustomer.sqlConnection = conn;
insertCustomer.text =
"INSERT INTO customers (firstName, lastName)" +
"VALUES ('Bob', 'Jones')";

insertCustomer.execute();
var customerId:Number = insertCustomer.getResult().lastInsertRowID;

// add a related phone number record for the customer


var insertPhoneNumber:SQLStatement = new SQLStatement();
insertPhoneNumber.sqlConnection = conn;
insertPhoneNumber.text =
"INSERT INTO customerPhoneNumbers (customerId, number)" +
"VALUES (:customerId, '800-555-1234')";
insertPhoneNumber.parameters[":customerId"] = customerId;

insertPhoneNumber.execute();

// if we've gotten this far without errors, commit the transaction


conn.commit();
}
catch (error:SQLError)
{
// rollback the transaction
conn.rollback();
}

Understanding the asynchronous execution model


One common misconception about the AIR local database asynchronous execution model is that you can’t start
executing a SQLStatement instance if another SQLStatement is currently executing against the same database
connection. In fact this isn’t true. While a SQLStatement instance is executing you can’t change the text property
of the statement; however, if you follow the recommended practice of using separate SQLStatement instances for
each different SQL statement that you want to execute, you can call the execute() method of a SQLStatement while
another SQLStatement instance is still executing, without causing an error.
Internally, when you’re executing database operations using asynchronous execution mode, each database
connection (each SQLConnection instance) has its own queue or list of operations that it is instructed to perform.
The runtime executes each operation in sequence, in the order they are added to the queue. When you create a
SQLStatement instance and call its execute() method, that statement execution operation is added to the queue
for the connection. If no operation is currently executing on that SQLConnection instance, the statement begins
executing in the background. If within the same block of code you create another SQLStatement instance and also
call that method’s execute() method, that second statement execution operation is added to the queue immedi-
ately after the first statement. As soon as the first statement finishes executing, the runtime moves to the next
operation in the queue. This happens in the background, even while the result event for the first operation is being
dispatched in the main application code. The following code demonstrates this technique:
ADOBE PRODUCT X.0 156
User Guide

// Using asynchronous execution mode

var stmt1:SQLStatement = new SQLStatement();


stmt1.sqlConnection = conn;
// ... Set statement text and parameters, and register event listeners ...
stmt1.execute();
// At this point stmt1's execute() operation is added to conn's execution queue.

var stmt2:SQLStatement = new SQLStatement();


stmt2.sqlConnection = conn;
// ... Set statement text and parameters, and register event listeners ...
stmt2.execute();
// At this point stmt2's execute() operation is added to conn's execution queue.
// When stmt1 finishes executing, stmt2 will immediately begin executing
// in the background.

A side effect of the database automatically executing subsequent queued statements is that if one statement or
operation depends on the outcome of another operation, when you’re using asynchronous execution mode you won’t
be able to make changes to the second statement once its execute() method is called, so you must wait for the event
indicating the first operation completes before starting the next operation. For instance, if you want to execute a
statement in the context of a transaction, the statement execution depends on the operation of opening the trans-
action. In that case, after calling the SQLConnection.begin() method to open the transaction, you would need to
wait for the SQLConnection instance to dispatch its begin event before calling the SQLStatement instance’s
execute() method. The simplest way to organize your application to ensure that the operations are executed
properly is to create a method that’s registered as a listener for the begin event, and call the
SQLStatement.execute() method within that listener.

Strategies for working with SQL databases


There are various ways that an application can access and work with a local SQL database, in terms of how the appli-
cation code can be organized, the sequence and timing of how operations are performed, and so forth. The
techniques you choose can have an impact on how easy it is to develop your application, how easy it is to modify the
application in future updates, and how well the application performs from the users’ perspective.
This section includes the following topics:
• Improving database performance
• Working with multiple databases
• Distributing a pre-populated database
• Best practices for working with local SQL databases

Improving database performance


This section describes several techniques that are built into Adobe AIR that allow you to improve the performance
of database operations in your application:
• Use prepared statements
• Minimize runtime processing
ADOBE PRODUCT X.0 157
User Guide

In addition to the techniques described in this section, the way a SQL statement is written can also impact database
performance. Frequently, there are multiple ways to write a SQL SELECT statement to retrieve a particular result set,
and in some cases the different approaches will require more or less effort from the database engine. This aspect of
improving database performance—designing SQL statements for better performance—is not covered in the Adobe
AIR documentation.

Use prepared statements


Before any SQL statement is executed, the runtime prepares (compiles) it to determine the steps that will be
performed internally to carry out the statement. When you call SQLStatement.execute(), the statement is
automatically prepared before it is executed. You can also call the SQLStatement.prepare() method in advance,
which will cause the statement to be prepared ahead of time so that it can be executed immediately when execute()
is called (as long as the SQLStatement.text property hasn’t changed in the mean time).
In order to gain the maximum benefit from prepared statements, if values need to change between statement execu-
tions you should use statement parameters (specified using the SQLStatement.parameters associative array
property) to customize your statement,. Unlike changing the SQLStatement instance’s text property, changing the
values of statement parameters does not require the statement to be prepared again. For more information about
using parameters in statements, see “Using parameters in statements” on page 140.
Like executing a statement using the SQLStatement class’s execute() method, preparing a statement using the
prepare() method is an asynchronous operation. The statement preparation doesn’t complete immediately, and
you determine whether it succeeds or fails (and the reason for any failure) using event listeners. You can register
event listeners using the addEventListener() method (specifying the SQLEvent.PREPARE and
SQLErrorEvent.ERROR event types) or by passing a Responder object as an argument in the prepare() method
call.
Because preparing a statement is an operation that is potentially demanding, a good strategy is to wait until the
initial start-up operations of your application have completed, or for another “idle” time in the application, and call
the prepare() method at that point. For instance, if your application doesn’t access the database at all in order to
display its initial screen, you could wait until that screen display, then open the database connection using
SQLConnection.open(), and finally create the SQLStatement instances and call the prepare() method for each
one. Alternatively, your application might open and immediately display some data, such as the result of a particular
query. In that case, you’d want to just go ahead and execute the SQLStatement instance for that query, then (after
the initial data is loaded and displayed) create SQLStatement instances for other database operations and call the
prepare() method on them.

A key aspect of using a prepared SQLStatement instance is that your application needs to keep a reference to the
SQLStatement instance once it has been prepared, meaning it shouldn’t be declared as a function-scope variable. In
practice, the best way to do this is to structure your application so that a SQL statement (or a group of statements
that are executed in combination) is wrapped in a single class. The SQLStatement instance or instances can be
member variables of the class, meaning they will persist as long as the instance of the wrapper class exists in the
application.
In the most simple case, you can simply define a variable containing the SQLStatement instance outside of a
function, (for example, as a member variable in an ActionScript class, or as a non-function variable in a JavaScript
file) so that the instance persists in memory. You can then call the prepare() method of that instance when you
want to prepare the statement, and later set parameter values and call its execute() method when you want to
actually run the query.
ADOBE PRODUCT X.0 158
User Guide

Group multiple operations in a transaction


When you’re executing a large number of SQL statements that involve adding or changing data (INSERT or UPDATE
statements), you can get a significant increase in performance by executing all the statements within an explicit
transaction. If you don’t explicitly begin a transaction, each of the statements runs in its own implicit transaction,
meaning that after each statement finishes executing the runtime writes the resulting data to the database file on the
disk. On the other hand, if you explicitly create a transaction and execute the statements in the context of that trans-
action, the runtime will make all the changes in memory, and will write all the changes to the database file at one
time when the transaction is committed. Because writing the data to disk is usually the most time-intensive part of
the operation, writing to the disk one time rather than once per SQL statement can give you significantly better
performance.

Minimize runtime processing


Using the following techniques can prevent unneeded work on the part of the database engine and make applica-
tions perform better:
• Always explicitly specify database names in statements—use “main” if it’s the main database—to prevent the
runtime from having to check each database to find the matching table (and to avoid the possibility of having the
runtime choose the wrong database).
• Always explicitly specify column names in SELECT and INSERT statements.
• Break up SELECT statements that retrieve large numbers of rows: see “Retrieving SELECT results in parts”
on page 144.

Working with multiple databases


Use the SQLConnection.attach() method to open a connection to an additional database on a SQLConnection
instance that already has an open database. You give the attached database a name using the name parameter in the
attach() method call. When writing statements to manipulate that database, you can then use that name in a prefix
(using the form database-name.table-name) to qualify any table names in your SQL statements, indicating to the
runtime that the table can be found in the named database.
You can execute a single SQL statement that includes tables from multiple databases that are connected to the same
SQLConnection instance. If a transaction is created on the SQLConnection instance, that transaction applies to all
SQL statements that are executed using the SQLConnection instance, regardless of which attached database the
statement runs on.
Alternatively, you can also create multiple SQLConnection instances in an application, each of which is connected
to one or multiple databases. One important point to keep in mind if you use this approach is that database trans-
actions are not shared across SQLConnection instances. Consequently, if you connect to the same database file
using multiple SQLConnection instances, you cannot rely on both connections’ data changes being applied in the
expected manner. For example, if two operations are performed against the same database through different
SQLConnection instances, and the application has an error after one operation takes place, the database data could
be left in an intermediate state that would not be reversible and might affect the application.

Distributing a pre-populated database


When you use an AIR local SQL database in your application, it’s likely that the application will expect a certain
database structure. Some applications will also expect pre-populated data in their database file. One approach to
creating the initial structure of the database for the application is to have the application check for the existence of
its database file in a particular location. if the file doesn’t exist, the application can execute a set of commands to
create the database file, create the database structure, and populate the tables with the initial data.
ADOBE PRODUCT X.0 159
User Guide

As an alternative to creating the database, structure, and data programmatically, you can distribute a pre-populated
database with your application by including the database file in the application’s AIR package. This has the benefit
of removing the need for a complex set of program instructions (the code that creates the database and its tables)
that are only used once in the lifetime of the application, but still add to the size and complexity of the application.
One important issue to keep in mind if you’re using the approach of including a pre-created database in your AIR
file has to do with the initial location where the database file is placed when the application is installed. Like all files
that are included in an AIR package, a bundled database file will be installed in the application’s resource directory.
However, because the user who is running the application may not necessarily be the user who installed the appli-
cation, that user may not have the necessary permission from the operating system to write to the file in its default
location. Instead, it is recommended that you use the file that is included in the AIR package as a “template”
database, from which individual users’ databases can be created. The first time a user runs the application, the
original database file can be copied into the user’s application storage directory (or another location), and that
database can be the one that’s actually used by the application, thereby avoiding the issue.

Best practices for working with local SQL databases


The following list is a set of suggested techniques you can use to improve the performance, security, and ease of
maintenance of your applications when working with local SQL databases:
Pre-create database connections Even if your application doesn’t need to execute any statements when it first loads,
instantiate a SQLConnection object and call its open() method ahead of time (such as after the initial application
startup) to avoid delays when running statements.
Re-use database connections If you will be accessing a certain database throughout the execution time of your
application, keep a reference to a SQLConnection instance, and reuse it throughout the application.
Use asynchronous execution mode When writing data-access code it can be tempting to execute operations
synchronously rather than asynchronously, because using synchronous operations frequently requires shorter and
less complex code. However, as described in “Using synchronous and asynchronous database operations”
on page 152, synchronous operations can have a performance impact that is obvious to users and detrimental to
their experience with an application, because the application’s code (including the code that refreshes the display)
waits for the database operations to complete before running. The amount of time the operation takes will vary
according to the operation and particularly the amount of data it affects. For instance, A SQL INSERT statement that
only adds a single row to the database will take less time than a SELECT statement that retrieves thousands of rows
of data. However, when you’re using synchronous execution to perform multiple operations, while the time each
operation takes may be very short, the application will be frozen until all synchronous operations are completed, so
the cumulative time of multiple operations strung together may be enough to stall your application.
You should use asynchronous operations as a standard approach, especially with operations that involve large
numbers of rows. You should only use synchronous operations when you can’t achieve certain functionality using
asynchronous programming, when you’ve considered the performance trade-offs that your application’s users will
face, and when you’ve tested your application so that you know how your application’s performance is actually
affected. While it may involve more complex coding, remember that you only have to write the code once, but the
application’s users may have to use it repeatedly.
Note that in many cases, by using a separate SQLStatement instance for each SQL statement to be executed, multiple
SQL operations can be queued up at one time, which makes asynchronous code similar to synchronous code in
terms of how code is written. For more information, see
ADOBE PRODUCT X.0 160
User Guide

Use prepared SQL statements For any SQL statement that will be executed more than once in an application, create
the SQLStatement instance and call it’s prepare() method ahead of time to reduce processing time later. For
example, your application might load its initial screen or initial set of data to get the user started, then instantiate
the SQLStatement instances and call their prepare() methods. See “Use prepared statements” on page 157 for more
information.
Use statement parameters Use SQLStatement parameters—never concatenate user input into statement text.
Doing so makes your application more secure (it prevents the possibility of SQL injection attacks), makes it possible
to use objects in queries (rather than only SQL literal values), and makes statements run more efficiently (because
they can be reused without needing to be recompiled each time they’re executed).
Use constants for column and parameter names When you don’t specify an itemClass for a SQLStatement, to
avoid spelling errors, define constants for column names and use the constants in the statement text and for the
property names when retrieving values from result objects.

See also
• “Improving database performance” on page 156
161

Part 1: Adding content to AIR


applications
Adding HTML content to SWF-based applications. . . . . . . . . . . . . . . . . . . . . . . . . . .162
Rendering PDF content in AIR applications. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .182
162

Chapter 21: Adding HTML content to


SWF-based applications
Adobe AIR lets you render complex HTML content in a SWF file, using the HTMLControl class.
The AIR HTMLControl class is built on WebKit technology (http://webkit.org/), used by the Apple Safari web
browser, and has full support for all standard HTML tags, JavaScript, images, and CSS, allowing you to build complex
applications using Ajax techniques.
This chapter contains the following sections:
• “About the HTMLControl class” on page 162
• “Loading HTML content from a URL ” on page 163
• “Loading HTML content from a string ” on page 165
• “Display properties of HTMLControl objects ” on page 166
• “Scrolling HTML content ” on page 167
• “Events dispatched by an HTMLControl object ” on page 167
• “Accessing the HTML history list from ActionScript” on page 168
• “Cross-scripting between ActionScript and JavaScript” on page 168t
• “Accessing runtime classes and functions from JavaScript ” on page 174
• “The AIRAliases.js file” on page 175
• “Defining browser-like user interfaces for HTML content” on page 175
• “Creating subclasses of the HTMLControl class ” on page 180
• “Unsupported JavaScript functionality ” on page 181

About the HTMLControl class


The HTMLControl class of Adobe AIR defines a display object that can display HTML content in a SWF file.
For example, the following code loads a URL into an HTMLControl object and sets the object as a child of a Sprite
object:
var container:Sprite;
var html:HTMLControl = new HTMLControl;
html.width = 400;
html.height = 600;
var urlReq:URLRequest = new URLRequest("http://www.adobe.com/");
html.load(urlReq);
container.addChild(html);
ADOBE PRODUCT X.0 163
User Guide

Note: In the Flex framework, only classes that extend the UIComponent class can be added as children of a Flex
Container components. For this reason, you cannot directly add an HTMLControl as a child of a Flex Container
component; however you can use the Flex HTML control, you can build a custom class that extends UIComponent and
contains an HTMLControl as a child of the UIComponent, or you can add the HTMLControl as a child of a UICom-
ponent and add the UIComponent to the Flex container.
You can also render HTML text by using the TextField class, but its capabilities are limited. The Flash Player’s
TextField class supports a subset of HTML markup, but because size limitations, its capabilities are limited. (The
HTMLControl class included in Adobe AIR is not available in Flash Player.)

See also
• “Using the Flex AIR components” on page 40

Loading HTML content from a URL


The load() method of an HTMLControl object loads content into the HTMLControl object from a URL defined
by a URLRequest object:
var html:HTMLControl = new HTMLControl();
var urlReq:URLRequest = new URLRequest("http://www.adobe.com/");
html.load(urlReq);

URLRequest objects in AIR


The load() method of an HTMLControl takes a URLRequest object as its single parameter (the urlRequest-
ToLoad parameter). The following properties of the URLRequest are used:

• data
• requestHeaders
• method
• url
The corresponding static properties of the URLRequestDefaults class are also used (if the property is not set in the
URLRequest object).
The HTMLControl class includes the following properties for that pertain to all data loads in the HTMLControl
object:
• manageCookies
• userAgent
• userCache
These properties override any settings in the URLRequest or URLRequestDefaults class.
The username and password set with the setLoginCredentials() of the URLRequest object (and with the static
setLoginCredentialsForHost() method of the URLRequestDefaults class) are also used.

Certain headers defined in the requestHeader property, such as the Accept header, are not used.
In defining the url property of a URLRequest object, use any of the following URL schemes:
file A path relative to the root of the file system. For example:

file:///c:/AIR Test/test.txt
ADOBE PRODUCT X.0 164
User Guide

app-resource A path relative to the root directory of the installed application (the directory that contains the appli-
cation.xml file for the installed application). For example, the following path points to an images subdirectory of the
directory of the installed application:
app-resource:/images

You can access the location of the app-resource directory using the File.applicationResourceDirectory
property in ActionScript or the air.File.applicationResourceDirectory property in JavaScript.
app-storage A path relative to the application store directory. For each installed application, AIR defines a unique
application store directory, which is a useful place to store data specific to that application. For example, the following
path points to a prefs.xml file in a settings subdirectory of the application store directory:
app-storage:/settings/prefs.xml

You can access the location of the app-storage directory using the File.applicationStorageDirectory in
ActionScript or the air.File.applicationStorageDirectory property in JavaScript.
http A standard HTTP request:

http://www.adobe.com

https A standard HTTPS request:

https://secure.example.com

In HTML content running in AIR, you can also use any of these URL schemes in defining src attributes for img,
frame, iframe, and script tags, in the href attribute of a link tag, and anywhere you can provide a URL.

For content loaded from sandboxes other than the application security sandbox, as in SWF content running in Flash
Player (not in AIR), only the http: and https: URL schemes are supported.
You can use a URLRequest object that uses any of these URL schemes to define the URL request for a number of
different objects, such as an HTMLControl object, a File object, a Loader object, a URLStream object, or a Sound
object.

Loading a PDF document into an HTMLControl object


You can also use an HTMLControl object’s load() method to load a PDF file into the object. However, if the user
of an AIR application does not have the Acrobat Reader plug-in version 8.1 or later installed, PDF content will not
display. You can obtain information on the user’s Acrobat Reader version from the static HTMLControl.pdfCapa-
bility property. If the property evaluates to PDFCapability.STATUS_OK, then the application can load PDF
content; otherwise you may want to display an error message.
For more information on using PDF content in AIR, see “Rendering PDF content in AIR applications” on page 182.

Setting the user agent used when loading HTML content


The HTMLControl class has a userAgent property, which lets you set the user agent string used by the
HTMLControl. You must set the userAgent property of the HTMLControl object before calling the load()
method. If you set this property, then the userAgent property of the URLRequest passed to the load() method is
not used.
You can set the default user agent string used by all HTMLControl objects in an application domain by setting the
URLRequestDefaults.userAgent property. The static URLRequestDefaults properties apply as defaults for all
URLRequest objects, not only those used by the load() method of HTMLControl objects. Setting the userAgent
property of an HTMLControl overrides the default URLRequestDefaults.userAgent setting.
ADOBE PRODUCT X.0 165
User Guide

If a value is set for neither the userAgent property of the HTMLControl object nor for URLRequestDe-
faults.userAgent, a default value is used as the user agent string. This default value varies depending on the
runtime operating system (such as Mac OS or Windows), the runtime language, and the runtime version, as in the
following two examples:
• "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko)
Apollo/1.0"
• "Mozilla/5.0 (Windows; U; en) AppleWebKit/420+ (KHTML, like Gecko) Apollo/1.0"

Loading HTML content from a string


The loadString() method of an HTMLControl object loads a string of HTML content into the HTMLControl
object:
var html:HTMLControl = new HTMLControl();
var htmlStr:String = "<html><body>Hello <b>world</b>.</body></html>";
html.loadString(htmlStr);

Consider using the XML class to represent large amounts of HTML data, and then using the toXMLString()
method of the XML object to convert the data to a string:
var xHTML:XML =
<html>
<head>
<style>
tr { font-family: Verdana }
</style>
<script>
function init() {
alert("Document loaded.");
}
</script>
</head onLoad="init()">
<body>
<table>
<tr>
<th>HTMLControl feature</th>
<th>Description</th>
<tr>
td>Full HTML tag support</td>
<td>Including tables, etc.</td>
</tr>
<tr>
<td>CSS support</td>
<td>As shown here</td>
</tr>
<tr>
<td>JavaScript support</td>
<td>Including cross-script communication to ActionScript.</td>
</tr>
</table>
</body>
</html>;

var htmlStr:String = xHTML.toXMLString();


var html:HTMLControl = new HTMLControl();;
html.width = 400;
ADOBE PRODUCT X.0 166
User Guide

html.height = 200;
addChild(html);
html.loadString(htmlStr);

Content loaded via the loadString() method is put in the application security sandbox, giving it full access to
AIR APIs.

Display properties of HTMLControl objects


Because the HTMLControl class is a subclass of the Sprite class, you can set and get any of the properties in that class.
For example, you can set the height, width, and position. You can also set properties, such as the filters array, that
control more advanced visual effects:
var html:HTMLControl = new HTMLControl();
var urlReq:URLRequest = new URLRequest("http://www.adobe.com/");
html.load(urlReq);
html.width = 800;
html.height = 600;

var blur:BlurFilter = new BlurFilter(8);


var filters:Array = [blur];
html.filters = filters;

An HTMLControl object's width and height properties are both set to 0 by default. You will want to set these before
adding an HTMLControl object to the stage.

Transparency of HTMLControl content


The paintsDefaultBackground property of an HTMLControl object, which is true by default, determines
whether the HTMLControl object uses an opaque background. With this property set to false, the HTMLControl
object uses its display object container as a background for the HTML and it use the opacity (alpha value) of the
display object container as the HTML background.
However, if the body element or any other element of the HTML document has an opaque background color
(specified by style="background-color:gray", for instance), then that portion of the rendered HTML will use
the specified opaque background color.
Note: To you can use a transparent, png-format graphic to provide an alpha-blended background for an element in an
HTML document.

Scaling HTMLControl content


You should not scale content in an HTMLControl object beyond a scale factor of 1.0. Text in HTMLControl content
is rendered at a specific resolution, so it appears pixelated if scaled up. You may want to set the scaleMode property
of the Stage to StageScaleMode.NO_SCALE.

Considerations when loading PDF content


PDF content loaded into in an HTMLControl object disappears in the following conditions:
• If you scale the HTMLControl object to a factor other that 1.0
• If you set the alpha property of the HTMLControl object to a value other than 1.0.
ADOBE PRODUCT X.0 167
User Guide

• If you rotate the HTMLControl content.


The PDF content reappears if you remove the offending property setting.
The AIR runtime cannot display PDF content in transparent windows.

Scrolling HTML content


The HTMLControl class includes the following properties that let you control the scrolling of HTML content:
htmlHeight The height, in pixels, of the HTML content.

htmlWidth The width, in pixels, of the HTML content.

scrollH The horizontal scroll position of the HTML content within the HTMLControl object.

scrollV The vertical scroll position of the HTML content within the HTMLControl object.

You may want to check the htmlHeight and htmlWidth properties when the HTMLControl object dispatches an
htmlBoundsChanged event. For example, the following code set the scrollV property so that HTML content is
scrolled to the bottom of the content:

var html:HTMLControl = new HTMLControl();


html.addEventListener(Event.HTML_BOUNDS_CHANGE, scrollHTML);

const SIZE:Number = 600;


html.width = SIZE;
html.height = SIZE;

var urlReq:URLRequest = new URLRequest("http://www.adobe.com");


html.load(urlReq);
this.addChild(html);

function scrollHTML(event:Event):void
{
html.scrollV = html.htmlHeight - SIZE;
}

You could use code like this to control or implement UI controls such as scroll bars. The HTMLControl does not
include horizontal and vertical scroll bars. You can implement these in ActionScript or by using a Flex component.
The Flex HTML component automatically includes scroll bars for HTML content. You can also use the
HTMLControl.createRootWindow() method to create a window that contains an HTMLControl object with scroll
bars (see “Creating windows with scrolling HTML content” on page 180).

Events dispatched by an HTMLControl object


An HTML control dispatches the following events:
domInitialize Dispatched when the HTML document is created, but before any DOM nodes are actually created
in the page.
complete Dispatched when the HTML DOM has been created in response to a load operation, immediately after
the onload event in the HTML page.
ADOBE PRODUCT X.0 168
User Guide

htmlBoundsChanged Dispatched when one or both of the htmlWidth and htmlHeight properties have changed.

locationChange Dispatched when the location property of the HTMLControl has changed.

scroll Dispatched anytime the HTML engine changes the scroll position. This can be due to navigation to anchor
links (# links) in the page or to JavaScript calls to the window.scrollTo() method. Typing in a text input or text
area can also cause a scroll.
uncaughtJavaScriptException Dispatched when a JavaScript exception occurs in the HTMLControl and the
exception is not caught in JavaScript code.
You can also register an ActionScript function for a JavaScript event (such as onClick). For details, see “Registering
ActionScript functions to respond to JavaScript events ” on page 171.

Accessing the HTML history list from ActionScript


As new pages are loaded in an HTMLControl object, the runtime maintains a history list for the object. This corre-
sponds to the window.history object in the HTML page. The HTMLControl class includes the following
properties and methods that let you work with the HTML history list from ActionScript:
historyLength The overall length of the history list, including back and forward entries.

historyPosition The current position in the history list. History items before this position represent “back”
navigation, and items after this position represent “forward” navigation.
historyAt() Returns the URLRequest object corresponding to the history entry at the specified position in the
history list.
historyBack() Navigates back in the history list, if possible.

historyForward() Navigates back in the history list, if possible.

historyGo() Navigates the indicated number of steps in the browser history. Navigates forward if positive,
backward if negative. Navigation by zero forces a reload. Attempts to set position beyond the end will set it to the
end.
Items in the history list are stored as objects of type HistoryListItem. The HistoryListItem class has the following
properties:
isPost Set to true if the HTML page includes POST data

originalUrl The original URL of the HTML page, prior to any redirects

title The title of the HTML page

url The URL of the HTML page

Cross-scripting between ActionScript and JavaScript


SWF content in the application security sandbox can access the HTML DOM in content loaded in an HTMLControl
object. Similarly, JavaScript in a page loaded in an HTMLControl object can access the ActionScript in the loading
SWF. The following sections describe this sort of cross-scripting:
• “Accessing the HTML DOM and JavaScript in an HTML page ” on page 169
ADOBE PRODUCT X.0 169
User Guide

• “Converting JavaScriptObject objects to the appropriate ActionScript type ” on page 170


• “Manipulating an HTML stylesheet from ActionScript ” on page 170
• “Registering ActionScript functions to respond to JavaScript events ” on page 171
• “Responding to uncaught JavaScript exceptions in ActionScript ” on page 172

Accessing the HTML DOM and JavaScript in an HTML page


Once the HTML page is loaded, and the HTMLControl object dispatches the complete event, you can access the
objects in the HTML DOM (document object model) for the page. This includes display elements (such as div and
p objects in the page) as well as JavaScript variables and functions. If possible, wait for the complete event before
accessing the HTML DOM.
For example, consider the following HTML page:
<html>
<script>
foo = 333;
function test() {
return "OK.";
}
</script>
<body>
<p id="p1">Hi.</p>
</body>
</html>

This simple HTML page has a JavaScript variable named "foo" and a JavaScript function named "test()". Both of these
are properties of the window object of the page (the page's DOM). Also, the window.document object includes a
named P element (with the ID "p1"), which you can access using the getElementById() method. Once the page is
loaded (when the HTMLControl object dispatches the complete event), you can access each of these from Action-
Script, as shown in the following ActionScript code:
var html:HTMLControl = new HTMLControl();
html.width = 300;
html.height = 300;
html.addEventListener(Event.COMPLETE, completeHandler);
var xhtml:XML =
<html>
<script>
foo = 333;
function test() {
return "OK.";
}
</script>
<body>
<p id="p1">Hi.</p>
</body>
</html>;
html.loadString(xhtml.toString());

function completeHandler(e:Event):void {
trace(html.window.foo); // 333
trace(html.window.document.getElementById("p1").innerHTML); // Hi.
trace(html.window.test()); // OK.
}
ADOBE PRODUCT X.0 170
User Guide

To access the content of an HTML element, use the innerHTML property. For example, the previous code uses
html.window.document.getElementById("p1").innerHTML to get the contents of the HTML element named
"p1".
You can also set properties of the HTML page from ActionScript. For example, the following sets the contents of the
p1 element on the page and it sets the value of the foo JavaScript variable on the page:

html.window.document.getElementById("p1").innerHTML = "eeee";
html.window.foo = 66;

Converting JavaScriptObject objects to the appropriate ActionScript type


From ActionScript, all DOM objects are of the ActionScript type JavaScriptObject, JavaScriptArray (for array
objects), or JavaScriptFunction (for function objects). These classes are defined in the flash.html package. Both
objects declared in a JavaScript script and other DOM elements are of type JavaScriptObject.
The JavaScriptFunction class extends the ActionScript 3.0 Function class, so you can call JavaScript functions from
ActionScript without type conversion. Similarly, the JavaScriptArray class extends the ActionScript 3.0 Array class,
so you ActionScript code can access JavaScript arrays directly as arrays without type conversion.
However, in ActionScript, you will sometimes need to examine JavaScript objects which are of type JavaScriptObject
to
For example, consider the following HTML:
<html>
<script>
var date = new Date();
var primes = [2, 3, 5, 7];
function sayHi()
{
alert("Hi " + date);
</script>
<body>
<p id='p1'>Buenos días.</p>
</body>
</html>

When you load this content into an HTMLControl object, the date property (defined in the JavaScript script) and
the p element (of the document) are both of type JavaScriptObject (when accessed from ActionScript); however the
primes object is of type JavaScriptArray and the sayHi() function is of type JavaScriptFunction:

var html:HTMLControl = new HTMLControl();


var urlReq:URLRequest = new URLRequest("test.html");
html.load(urlReq);
html.addEventListener(Event.COMPLETE, htmlLoaded)
function htmlLoaded(e:Event):void {
trace(html.window.jsArray is JavaScriptObject); // true
trace(html.window.document.getElementById('p1') is JavaScriptObject); // true
trace(html.window.sayHi is JavaScriptFunction); // true
trace(html.window.primes is JavaScriptArray); // true
}

Manipulating an HTML stylesheet from ActionScript


Once the HTMLControl object has dispatched the complete event, you can examine and manipulate CSS styles in
a page.
ADOBE PRODUCT X.0 171
User Guide

For example, consider the following simple HTML document:


<html>
<style>
.style1A { font-family:Arial; font-size:12px }
.style1B { font-family:Arial; font-size:24px }
</style>
<style>
.style2 { font-family:Arial; font-size:12px }
</style>
<body>
<p class="style1A">
Style 1A
</p>
<p class="style1B">
Style 1B
</p>
<p class="style2">
Style 2
</p>
</body>
</html>

After an HTMLControl object loads this content, you can manipulate the CSS styles in the page via the cssRules
array of the window.document.styleSheets array, as shown here:
var html:HTMLControl = new HTMLControl( );
var urlReq:URLRequest = new URLRequest("test.html");
html.load(urlReq);
html.addEventListener(Event.COMPLETE, completeHandler);
function completeHandler(event:Event):void {
var styleSheet0:Object = html.window.document.styleSheets[0];
styleSheet0.cssRules[0].style.fontSize = "32px";
styleSheet0.cssRules[1].style.color = "#FF0000";
var styleSheet1:Object = html.window.document.styleSheets[1];
styleSheet1.cssRules[0].style.color = "blue";
styleSheet1.cssRules[0].style.font-family = "Monaco";
}

This code adjusts the CSS styles so that the resulting HTML document appears like the following:

Keep in mind that code can add styles to the page after the HTMLControl object dispatches the complete event.

Registering ActionScript functions to respond to JavaScript events


You can register ActionScript functions to respond to JavaScript events. For example, consider the following HTML
content:
<html>
<body>
<a href="#" id="testLink">Click me.</a>
ADOBE PRODUCT X.0 172
User Guide

</html>

You can register an ActionScript function as a handler for any event in the page. For example, the following code
adds the clickHandler() function as the listener for the onclick event of the testLink element in the HTML
page:
var html:HTMLControl = new HTMLControl( );
var urlReq:URLRequest = new URLRequest("test.html");
html.load(urlReq);
html.addEventListener(Event.COMPLETE, completeHandler);

function completeHandler(event:Event):void {
html.window.document.getElementById("testLink").onclick = clickHandler;
}

function clickHandler():void {
trace("You clicked it!");
}

You can also use the addEventListener() method to register for these events. For example, you could replace the
completeHandler() method in the previous example with the following code:

function completeHandler(event:Event):void {
var testLink:JavaScriptObject = html.window.document.getElementById("testLink");
testLink.addEventListener("click", clickHandler);
}

It is good practice to wait for the HTMLControl to dispatches the complete event before adding these event
listeners, as shown in the previous example. HTML pages often load multiple files and the HTML DOM is not fully
built until all the files are loaded and parsed, at which point the HTMLControl to dispatches the complete event.

Responding to uncaught JavaScript exceptions in ActionScript


Consider the following HTML:
<html>
<head>
<script>
function throwError() {
var x = 400 * melbaToast;
}
</script>
</head>
<body>
<a href="#" onclick="throwError()">Click me.</a>
</html>

It contains a JavaScript function, throwError(), that references an unknown variable, melbaToast:


var x = 400 * melbaToast;

When a JavaScript operation encounters an illegal operation that is not caught in the JavaScript code with a
try/catch structure, the HTMLControl object dispatches an HTMLUncaughtJavaScriptExceptionEvent event. You
can register an ActionScript method to listen for this event, as in the following code:
var html:HTMLControl = new HTMLControl();
var urlReq:URLRequest = new URLRequest("test.html");
html.load(urlReq);
ADOBE PRODUCT X.0 173
User Guide

html.width = container.width;
html.height = container.height;
container.addChild(html);
html.addEventListener(HTMLUncaughtJavaScriptExceptionEvent.UNCAUGHT_JAVASCRIPT_EXCEPTION,
htmlErrorHandler);
function htmlErrorHandler(event:HTMLUncaughtJavaScriptExceptionEvent):void
{
event.preventDefault();
trace("exceptionValue:", event.exceptionValue)
for (var i:uint = 0; i < event.stackTrace.length; i++)
{
trace("___________________");
trace("sourceURL:", event.stackTrace[i].sourceURL);
trace("line:", event.stackTrace[i].line);
trace("function:", event.stackTrace[i].functionName);
}
}

In this example, the htmlErrorHandler() event handler cancels the default behavior of the event (which is to send
the JavaScript error message to the trace output), and generates its own output message. It outputs the value of the
exceptionValue of the HTMLUncaughtJavaScriptExceptionEvent object. It outputs the properties of each object
in the stackTrace array:
exceptionValue: ReferenceError: Can't find variable: melbaToast
___________________
sourceURL: app-resource:/test.html
line: 5
function: throwError
___________________
sourceURL: app-resource:/test.html
line: 10
function: onclick

By calling the preventDefault() method of a HTMLUncaughtJavaScriptExceptionEvent object, you prevent the


exception dialog box from appearing in ADL.

Making objects in the container display object available to JavaScript


JavaScript in the HTML page loaded by an HTMLControl object can call the classes, objects, and functions of the
parent display object, via the window.htmlControl object in the HTML page.
Code in the container SWF file must set properties of the window property of an HTMLControl to make properties
and methods available to the loaded HTMLContent, as properties and methods of the JavaScript
window.htmlControl object. The following ActionScript code and the HTML code that follows illustrate this:

var html:HTMLControl = new HTMLControl();


html.useApplicationDomain = ApplicationDomain.currentDomain;
var foo:String = "Hello from container SWF."
function helloFromJS(message:String):void {
trace("JavaScript says:", message);
}
var urlReq:URLRequest = new URLRequest("test.html");
html.addEventListener(Event.COMPLETE, loaded);
html.load(urlReq);

function loaded(e:Event):void
{
html.window.foo = foo;
html.window.helloFromJS = helloFromJS;
ADOBE PRODUCT X.0 174
User Guide

By setting the useApplicationDomain property to ApplicationDomain.currentDomain, definitions in the content


loaded in the HTMLControl share the same application domain as that of the loading SWF content.
The HTML content (in a file named test.html) loaded into the HTMLControl in the previous example can access the
foo property and the helloFromJS() method in the parent SWF file:

<html>
<script>
function alertFoo() {
alert(foo);
}
</script>
<body>
<button onClick="alertFoo()">
What is foo?
</button>
<p><button onClick="helloFromJS('Hi.')">
Call helloFromJS() function.
</button></p>
</body>
</html>

To make ActionScript classes defined in a SWF file available in JavaScript, you need to make sure that the loaded
HTML content is in the same application domain as the display object container, by setting the useApplication-
Domain property of the HTMLControl object to ApplicationDomain.currentDomain (the application domain of
the SWF content), as shown in the following code:
html.useApplicationDomain = ApplicationDomain.currentDomain;

The HTML content must be from a compatible security domain.

Accessing runtime classes and functions from


JavaScript
JavaScript can call classes and methods in the AIR runtime. This is done using a special runtime property of the
window object of the HTML page, which is available to any JavaScript code loaded in the runtime. However, only
HTML pages in the application security domain (those HTML pages in the application resource directory, the
directory in which the application is installed) have unrestricted access to the AIR runtime classes. HTML pages
from other security domains will have access to the runtime classes, but using APIs that provide special functionality
to AIR applications (APIs would not be available to SWF content running outside of the browser) will cause the
application to dispatch a SecurityErrorEvent.
For example, JavaScript in an HTML page that is in the application security domain can use the AIR file system API
to enumerate the contents of a directory on the user’s computer:
var directory = window.runtime.filesystem.File.documentsDirectory;
var contents = directory.getDirectoryListing();;
ADOBE PRODUCT X.0 175
User Guide

Because the package structure of the runtime classes would require you to type long strings of JavaScript code strings
to access each class (as in window.runtime.filesystem.File), the AIR SDK for HTML includes an AIRAliases.js
file that lets you access runtime classes much more easily (for instance, by simply typing air.File), which is
described in the following section.

The AIRAliases.js file


The runtime classes are organized in a package structure, as in the following:
• window.runtime.flash.system.Shell
• window.runtime.flash.desktop.ClipboardManager
• window.runtime.flash.filesystem.FileStream
• window.runtime.flash.data.SQLDatabase
Included in the AIR SDK for HTML is an AIRAliases.js file that provide “alias” definitions that let you access the
runtime classes with less typing. For example, you can access the classes listed above by simply typing the following:
• air.Shell
• air.ClipboardManager
• air.FileStream
• air.SQLDatabase
This is just a short subset of the classes in the AIRAliases.js file. You can open the AIRAliases.js file to see the full list.
In addition to commonly used runtime classes, the AIRAliases.js file includes aliases for commonly used package-
level functions: window.runtime.trace(), window.runtime.flash.net.navigateToURL(), and
window.runtime.flash.net.sendToURL(), which are aliased as air.trace(), air.navigateToURL(), and
air.sendToURL().

To use the AIRAliases.js file, include the following script reference in your HTML page:
<script src="AIRAliases.js" />

The AIR SDK and the Flex SDK include thatAIRAliases.js file in a frameworks subdirectory. Copy the file to your
project source directory, and adjust the path in the src reference in the script tag, as needed.
Important: Except where noted, the JavaScript example code in this documentation assumes that you have included
the AIRAliases.js file in your HTML page.

Defining browser-like user interfaces for HTML content


The HTMLControl class defines no default user interface behavior for these settings, because you may not want
changes to the window.document.title property, for example, in an HTMLControl object to set the title in the
window that contains the application. For example, if the application presents multiple HTMLControl objects in a
tabbed interface, you may want the title settings to apply to each tab, but not to the main window title. Similarly, your
code could respond to a window.moveTo() call by repositioning the HTMLControl object in its parent display
object container, by moving the window that contains the HTMLControl object, by doing nothing at all, or by doing
something else entirely.
By default, an HTMLControl object does not make any changes based on JavaScript code that is intended to change
the user interface. For example, the following properties in JavaScript have no effect, by default:
ADOBE PRODUCT X.0 176
User Guide

• window.status
• window.document.title
• window.location
Similarly, calling the following methods in JavaScript has no effect, by default:
• window.blur()
• window.close()
• window.focus()
• window.moveBy()
• window.moveTo()
• window.open()
• window.resizeBy()
• window.resizeTo()
The HTMLHost class lets you determine how to handle JavaScript that modifies these properties or calls these
methods.The methods in the HTMLHost class correspond to each of these window settings.

Example: Extending the HTMLHost class


The following example shows how to customize the way that an HTMLControl object affects the user interface, by
extending the HTMLHost class.
1 Create a new class that extends the HTMLHost class (a subclass).
2 Override methods of the new class to handle changes in the user interface-related settings. For example, the
following class, CustomHost, defines behaviors for calls to window.open() and changes to
window.document.title. Calls to window.open() open the HTML page in a new window, and changes to
window.document.title (including the setting of the <title> element of an HTML page) set the title of that
window.
package
{
import flash.html.*;
import flash.display.StageScaleMode;
import flash.display.NativeWindow;
import flash.display.NativeWindowInitOptions;

public class CustomHost extends HTMLHost


{
import flash.html.*;
override public function
createWindow(windowCreateOptions:HTMLWindowCreateOptions):HTMLControl
{
var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
var window:NativeWindow = new NativeWindow(initOptions);
window.visible = true;
window.stage.scaleMode = StageScaleMode.NO_SCALE;
window.stage.addChild(htmlControl);
return htmlControl;
}
override public function updateTitle(title:String):void
{
htmlControl.stage.nativeWindow.title = title;
}
}
}
ADOBE PRODUCT X.0 177
User Guide

3 In the code that contains the HTMLControl (not the code of the new subclass of HTMLHost) instantiate an
object of the type defined by the new class, and assign that object to the htmlHost property of the HTMLControl.
The following Flex code uses the CustomHost class defined in the previous step:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
applicationComplete="init()">
<mx:Script>
<![CDATA[
import flash.html.HTMLControl;
import CustomHost;
private function init():void
{
var html:HTMLControl = new HTMLControl();
html.width = container.width;
html.height = container.height;
var urlReq:URLRequest = new URLRequest("Test.html");
html.htmlHost = new CustomHost();
html.load(urlReq);
container.addChild(html);
}
]]>
</mx:Script>
<mx:UIComponent id="container" width="100%" height="100%"/>
</mx:WindowedApplication>

To test the code described here, in the application resource directory include an HTML file with the following
content:
<html>
<head>
<title>Test</title>
</head>
<script>
function openWindow()
{
window.runtime.trace("in");
document.title = "foo"
window.open('Test.html');
window.runtime.trace("out");
}
</script>
<body>
<a href="#" onclick="openWindow()">window.open('Test.html')</a>
</body>
</html>

Handling changes to the window.location property


To handle changes of the URL of the HTML page, requested when JavaScript in a page changes the value of
window.location, override the locationChange() method of the class that extends the HTMLHost class, as
shown in the following example:
override public function updateLocation(locationURL:String):void
{
trace(locationURL);
}
ADOBE PRODUCT X.0 178
User Guide

Handling JavaScript calls to window.moveBy(), window.moveTo(), window.resizeTo(),


window.resizeBy()
To handle changes in the bounds of the HTML content, requested when JavaScript in a page calls these methods,
override the set windowRect() method of the class that extends the HTMLHost class, as shown in the following
example:
override public function set windowRect(value:Rectangle):void
{
htmlControl.stage.nativeWindow.bounds = value;
}

Handling JavaScript calls to window.open()


To handle JavaScript calls to window.open(), override the createWindow() method of the class that extends the
HTMLHost class, as shown in the following example:
override public function createWindow(windowCreateOptions:HTMLWindowCreateOptions):void
{
var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
var window:NativeWindow = new NativeWindow(initOptions);
window.visible = true;
htmlControl.width = window.width;
htmlControl.height = window.height;
window.stage.scaleMode = StageScaleMode.NO_SCALE;
window.stage.addChild(htmlControl);
return htmlControl;
}

The object passed as a parameter to the createWindow() method is an HTMLWindowCreateOptions object. The
HTMLWindowCreateOptions class includes properties that define values that are set in the features parameter
string in the call to window.open():

HTMLWindowCreateOptions Corresponding setting in the


property features string in the JavaScript call
to window.open()

fullscreen fullscreen

height height

locationBarVisible location

menuBarVisible menubar

resizeable resizable

scrollBarsVisible scrollbars

statusBarVisible status

toolBarVisible toolbar

width width

x left or screenX

y top or screenY
ADOBE PRODUCT X.0 179
User Guide

Handling JavaScript calls to window.close()


To handle JavaScript calls to window.close(), override the closeWindow() method of the class that extends the
HTMLHost class, as shown in the following example:
override public function closeWindow():void
{
htmlControl.stage.nativeWindow.close();
}

JavaScript calls to window.close() do not automatically cause the containing window to close. You may, for
example, want a call to window.close() to remove the HTMLControl from the display list, leaving the window
(which may have other content) open, as in the following code:
override public function closeWindow():void
{
htmlControl.parent.removeChild(htmlControl);
}

Handling changes of the window.status property


To handle JavaScript changes to the value of window.status, override the updateStatus() method of the class that
extends the HTMLHost class, as shown in the following example:
override public function updateStatus(status:String):void
{
trace(status);
}

The requested status is passed as a string to the updateStatus() method.

Handling changes of the window.document.title property


To handle JavaScript changes to the value of window.document.title, override the updateTitle() method of
the class that extends the HTMLHost class, as shown in the following example:
override public function updateTitle(title:String):void
{
htmlControl.stage.nativeWindow.title = title + " - Sample";
}

The requested title is passed as a string to the updateTitle() method.


You may not want the change to document.title to affect the title of the window containing the HTMLControl
object. You may, for example, want the change to show up in another interface element, such as a text field.

Handling JavaScript calls to window.blur() and window.focus()


To handle JavaScript calls to window.blur() and window.focus(), override the windowBlur() and window-
Focus() methods of the class that extends the HTMLHost class, as shown in the following example:

override public function windowBlur():void


{
htmlControl.alpha = 0.5;
}
override public function windowFocus():void
{
htmlControl.alpha = 1.0;
ADOBE PRODUCT X.0 180
User Guide

Creating windows with scrolling HTML content


The HTMLControl class includes a static method, HTMLControl.createRootWindow(), which lets you open a new
window (represented by a NativeWindow object) that contains an HTMLControl object and define some user
interface settings for that window. The method takes four parameters, which let you define the user interface:
visible A Boolean value that specifies whether the window is initially visible (true) or not (false).

windowInitOptions A NativeWindowInitOptions object. The NativeWindowInitOptions class defines initial-


ization options for a NativeWindow object, including the following: whether the window has a menu, whether the
window is minimizable, whether the window is maximizable, whether the window is resizable, whether the window
has system chrome (or custom chrome), whether the window is transparent (for windows that do not use system
chrome), and the type of window.
scrollBarsVisible Whether there are scroll bars (true) or not (false).

bounds A Rectangle object defining the position and size of the new window.

For example, the following code uses the HTMLControl.createRootWindow() method to create a new window
with HTMLControl content that uses scrollbars:

var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();


var bounds:Rectangle = new Rectangle(10, 10, 600, 400);
var html2:HTMLControl = HTMLControl.createRootWindow(true, initOptions, true, bounds);
var urlReq2:URLRequest = new URLRequest("http://www.example.com");
html2.load(urlReq2);
html2.stage.nativeWindow.orderToFront();

Creating subclasses of the HTMLControl class


You can create a subclass of the HTMLControl class, to create new behaviors. For example, you can create a subclass
that defines default event listeners for HTMLControl events (such as those dispatched when HTML is rendered or
when the user clicks a link).
For example, the following defines a subclass of the HTMLHost class, which defines normal behavior when the
JavaScript window.open() method is called in an HTMLControl that implements this class as its htmlHost
property:
package
{
import flash.html.HTMLControl;
public class MyHTMLHost extends HTMLHost
{
public function MyHTML()
{
super();
}
override public function createWindow(opts:HTMLWindowCreateOptions):void
{
var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
var bounds:Rectangle = new Rectangle(opts.x, opts.y, opts.width, opts.height);
var html:HTMLControl = HTMLControl.createRootWindow(true,
initOptions,
opts.scrollBarsVisible,
ADOBE PRODUCT X.0 181
User Guide

bounds);
html.stage.nativeWindow.orderToFront();
return html
}
}

Then the following defines a subclass of the HTMLControl class that uses the MyHTMLHost class as its htmlHost
property:
package
{
import flash.html.HTMLControl;
import MyHTMLHost;
import MyHTMLControl;
public class MyHTML extends HTMLControl
{
public function MyHTML()
{
super();
htmlHost = new MyHTMLHost();
}
}
}

For details on the HTMLHost class and the HTMLControl.createRootWindow() method used in this example, see
“Defining browser-like user interfaces for HTML content” on page 175.

Unsupported JavaScript functionality


AIR does not support the Window.print() method. Calls to window.print() fail silently.
182

Chapter 22: Rendering PDF content in


AIR applications
AIR applications can render not only SWF files and HTML content but also PDF files. PDF files, implemented using
HTMLControl objects and the WebKit engine, can either stretch across the full height and width of your application
or alternatively as a portion of the interface. The Acrobat Reader browser plug-in controls display of PDF files in an
Adobe AIR application, so modifications to Reader's toolbar interface (position, anchoring, visibility, etc.) will
persist in subsequent viewing of PDF files in both AIR applications and the browser.

Detecting PDF Capability


If the user does not have an installed version of Adobe Reader 8.1 plug-in or greater, PDF content will not display in
an AIR application; the HTMLControl instance will remain unaffected. To detect if a user can render PDF content,
first check the HTMLControl.pdfCapability property. This property will return one of the following constants of
the HTMLPDFCapability class:
HTMLPDFCapability.STATUS_OK A A sufficient version (8.1 or greater) of Acrobat Reader is detected and PDF
content can be loaded into an HTMLControl object
HTMLPDFCapability.ERROR_INSTALLED_READER_NOT_FOUND A No version of Acrobat Reader is detected. An
HTMLControl object cannot display PDF content.
HTMLPDFCapability.ERROR_INSTALLED_READER_TOO_OLD A Acrobat Reader has been detected, but the version is
too old. An HTMLConrol object cannot display PDF content.
HTMLPDFCapability.ERROR_PREFERRED_READER_TOO_OLD A A sufficient version (8.1 or later) of Acrobat Reader
is detected, but the version of Acrobat Reader that is setup to handle PDF content is older than Reader 8.1. An
HTMLConrol object cannot display PDF content.
Note: On Windows, if Adobe Acrobat or Acrobat Reader version 7.x or above is currently running on the user's system,
that version is used even if a later version that supports loading PDF loaded in an HTMLControl object is installed. In
this case, if the value of the pdfCampability property is HTMLPDFCapability.STATUS_OK, when an AIR application
attempts to load PDF content into an HTMLControl object, the older version of Acrobat or Reader displays an alert,
without an error message displayed the Apollo runtime. If this is a possibility situation for your end users, you may
consider providing them with instructions to close Acrobat while running your application. You may consider displaying
these instructions if the PDF content does not load within an acceptable time frame.
The following code detects whether a user can display PDF content in an AIR application, and if not traces the error
code that corresponds to the HTMLPDFCapability error object:

import flash.html.HTMLControl;
import flash.net.URLRequest;

if(HTMLControl.pdfCapability == HTMLPDFCapability.STATUS_OK) {
trace("PDF content can be displayed");
}
else {
ADOBE PRODUCT X.0 183
User Guide

trace("PDF cannot be displayed. Error code: " + HTMLControl.pdfCapability);


}

Adding a PDF to the display


You can add a PDF within the Adobe Reader interface to an AIR application by creating an HTMLControl instance,
setting its dimensions, and loading the path of a PDF.
The following example loads a PDF from an external site. Replace the URLRequest with the path to an available
external PDF.
var request:URLRequest = new URLRequest("http://www.exampledomain.com/test.pdf");
pdf = new HTMLControl();
pdf.height = 800;
pdf.width = 600;
pdf.load(request);
container.addChild(pdf);

Known limitations in the beta release


The beta release version of Adobe AIR includes the following limitations:
• PDF content does not display in a window (a NativeWindow object) that is transparent (where the transparent
property is set to true).
• The display order of a PDF file operates differently than other display objects in an Apollo application. Although
a PDF will correctly clip according to HTML display order, it will always sit on top of content in the AIR application's
display order.
• The visual properties of an HTMLControl object that contains a PDF file cannot be changed. Changes to an
HTMLControl object's filters, alpha, rotation, or scaling will render the PDF file invisible until the properties are
reset.
• Within a PDF file, links to content within the PDF file will update the scroll position of the PDF. Links to content
outside the PDF will redirect the HTMLControl object that contains the PDF. Links that target a new window will
be ignored.

Forms
PDF files that contain forms will only post correctly if form posting uses a supported link format and returns PDF
or HTML. Adobe discourages the use of forms that return FDF or rely heavily on JavaScript communication. Collab-
orative PDF forms will not function correctly in AIR 1.0.
184

Part 1: Interacting with the operating


system
Application invocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .185
Capturing command line arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .187
Registering file types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .188
Reading the application descriptor file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .188
Getting the runtime version and patch level . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .189
Detecting AIR capabilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .189
Tracking user presence. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .189
Taskbar icons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .190
Application termination. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .193
185

Chapter 24: Interacting with the


operating system
Though AIR applications are designed to run on multiple operating systems that support the AIR runtime, there are
ways in which you may want the AIR application to interact with the host operating system. For instance, you may
want to capturing command line arguments passed when the application was launched.
The Shell class defines properties, methods, and events useful in interacting with the operating system.
This section contains the following topics:
• “Application invocation” on page 185
• “Capturing command line arguments” on page 187
• “Registering file types” on page 188
• “Reading the application descriptor file” on page 188
• “Getting the runtime version and patch level” on page 189
• “Detecting AIR capabilities” on page 189
• “Tracking user presence” on page 189
• “Taskbar icons” on page 190
• “Application termination” on page 193

Application invocation
An AIR application is invoked when the user (or the operating system):
• launches the application from the desktop shell
• uses the application as a command on a command-line shell
• opens a type of file for which the application is the default opening application
• (Mac OS X) clicks the application icon in the dock taskbar (whether or not the application is currently running)
• chooses to launch the application from the installer (either at the end of a new installation process, or after
double-clicking the AIR file for an already installed application).
• begins an update of an AIR application when the installed version has signaled that it should handle application
updates itself by including a <handleUpdates/> flag in the application descriptor
Whenever an AIR application is invoked, AIR dispatches an InvokeEvent object through the singleton Shell object.
To allow an application time to initialize itself and register an event listener, invoke events are queued instead of
discarded when there are no registered listeners for the event. As soon as a listener is registered, all the queued events
are delivered.
ADOBE PRODUCT X.0 186
User Guide

When you are using the Flex framework, the WindowedApplication component will listen for the invocation event
and redispatch them. If your Flex application listens for invoke events on the Shell object, it will not receive any such
events dispatched before it has registered its listener, including the initial event, since the events will have already
been delivered to the WindowedApplication’s listener. Flex applications should listen for invoke events on the
WindowedApplication object rather than the Shell object. You add a listener for the invoke event either by setting
the invoke attribute in the <WindowedApplication> element tag:
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
invoke="onInvoke(event)" title="Invocation Event Log">

or by registering the handler with the WindowedApplication addEventListener() method:


<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
initialize="init()" title="Invocation Event Log">
<mx:Script>
<![CDATA[
import flash.events.InvokeEvent;
public function init():void{
this.addEventListener(InvokeEvent.INVOKE,onInvoke);
}
//...

When you add the listener using the addEventListener function, you must call the method in response to the
initialize event (or earlier) in order to get the first invoke event dispatched when an application is launched, which
will contain any command line arguments.
Only one instance of an AIR application will be started. When an already running application is invoked again, AIR
dispatches a new invocation event to the running instance. It is an AIR application’s responsibility to respond to an
invocation event and take the appropriate action (such as opening a new document window to display the file
contents).
An invocation event object contains any arguments passed to the application, as well as the directory from which the
application has been invoked. If the application was invoked because of a file-type association, then the full path to
the file is included in the command line arguments. Likewise, if the application was invoked because of an appli-
cation update, the full path to the update AIR file is provided. The invocation event object is defined by the AIR
InvokeEvent class.
Your application can handle invocation events by registering a listener with its Shell (or WindowedApplication)
object:
Shell.shell.addEventListener(InvokeEvent.INVOKE,onInvokeEvent);

And defining an event handler:


var arguments:Array;
var currentDir:File;
public function onInvokeEvent(invocation:InvokeEvent):void{
arguments = invocation.arguments;
currentDir = invocation.currentDirectory;
}

See also
• “Setting application properties” on page 62
• “Presenting a custom application update user interface” on page 215
ADOBE PRODUCT X.0 187
User Guide

Capturing command line arguments


The command line arguments associated with the invocation of an AIR application are delivered in the invoke event
dispatched by the Shell object. The InvokeEvent.arguments property contains an array of the arguments passed by
the operating system when an AIR application is invoked. If the arguments contain relative file paths, you can
typically resolve the paths using the currentDirectory property.
The arguments passed to an AIR program are treated as white-space delimited strings, unless enclosed in double
quotes:

Arguments Array
tick tock {tick,tock}
tick "tick tock" {tick,tick tock}
"tick" “tock” {tick,tock}
\"tick\" \"tock\" {"tick","tock"}

The InvokeEvent.currentDirectory property contains a File object representing the directory from which the
application was launched.
When an application is invoked because a file of a type registered by the application is opened, the native path to the
file is included in the command-line arguments as a string. (It is your application’s responsibility to open or perform
the intended operation on the file.) Likewise, when an application handles updates itself, the native path to the AIR
file will be included when a user double-clicks an AIR file containing an application with a matching application ID.
You can access the file using the resolve() method of the currentDirectory File object:
if((invokeEvent.currentDirectory != null)&&(invokeEvent.arguments.length > 0)){
dir = invokeEvent.currentDirectory;
fileToOpen = dir.resolvePath(invokeEvent.arguments[0]);
}

You should also validate that an argument is indeed a path to a file.

Example: Invocation event log


The following example demonstrates how to register listeners for and handle the InvokeEvent. The example logs all
the invocation events received and displays the current directory and command-line arguments.
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
invoke="onInvoke(event)" title="Invocation Event Log">
<mx:Script>
<![CDATA[
import flash.events.InvokeEvent;
import flash.system.Shell;

public function onInvoke(invokeEvent:InvokeEvent):void{


var now:String = new Date().toTimeString();
logEvent("Invoke event received: " + now);

if(invokeEvent.currentDirectory != null){
logEvent("Current directory=" + invokeEvent.currentDirectory.nativePath);
} else {
logEvent("--no directory information available--");
}

if(invokeEvent.arguments.length > 0){


logEvent("Arguments: " + invokeEvent.arguments.toString());
} else {
ADOBE PRODUCT X.0 188
User Guide

logEvent("--no arguments--");
}
}

public function logEvent(entry:String):void {


log.text += entry + "\n";
trace(entry);
}
]]>
</mx:Script>
<mx:TextArea id="log" width="100%" height="100%" editable="false"
valueCommit="log.verticalScrollPosition=log.textHeight;"/>
</mx:WindowedApplication>

Registering file types


You can register an application to be the default system application for handling specific file extensions. To do this,
add the file extension to the list of registered file types in the application descriptor file, as in the following:
<fileTypes>
<fileType>
<name>adobe.VideoFile</name>
<extension>avf</extension>
<description>Adobe Video File</description>
<contentType>application/vnd.adobe.video-file</contentType>
</fileType>
</fileTypes>

More than one <fileType> element can be placed within the <fileTypes> element to register more than one
extension.
For details, see “Registering file types” on page 66.

Reading the application descriptor file


You can read the application descriptor file of the currently running application, as an XML object, by getting the
applicationDescriptor property of the Shell object, as in the following:
var appXml:XML = Shell.shell.applicationDescriptor;

The Shell object also has an id property, that is the application ID for the file, as illustrated in the following code:
trace(Shell.shell.id);

For more information, see “The application descriptor file structure” on page 62.
ADOBE PRODUCT X.0 189
User Guide

Getting the runtime version and patch level


The Shell object also has a runtimeVersion property, which is the version of the runtime in which the application
is running (a string, such as "1.0.5"), and a runtimePatchLevel property, which is the patch level of the runtime
(a number, such as 2960), as illustrated in the following code:
trace(Shell.shell.runtimeVersion);
trace(Shell.shell.runtimePatchLevel);

Detecting AIR capabilities


For a file that is bundled with the Adobe AIR application, the Security.sandboxType property is set to the value
defined by the Security.APPLICATION constant. So, you can check to see if a file is in the Adobe AIR security
sandbox, and load other content (which may or may not contain APIs specific to AIR) accordingly, as in the
following:
if (Security.sandboxType == Security.APPLICATION)
{
// Load SWF that contains AIR APIs
}
else
{
// Load SWF that does not contain AIR APIs
}

All resources that are not installed with the AIR application are put in the same security sandboxes as they would be
placed in if they were running in Flash Player in a web browser. Remote resources are put in sandboxes according to
their source domains, and local resources are put in the local-with-networking, local-with-filesystem, or local-
trusted sandbox.
Although content outside of the AIR application security sandbox may access AIR runtime APIs, but some function-
ality (such as attempting to read files from the filesystem) will result in a runtime security exception.
You can check if the Capabilities.playerType static property is set to Security.APPLICATION to see if content
is in the runtime (and not running in Flash Player running in a browser).
For more information, see “AIR Security” on page 52.

Tracking user presence


The Shell object dispatches two events that help you detect when a user is actively using a computer. If no mouse or
keyboard activity is detected in the interval determined by the Shell.idleThreshold property, then the Shell will
dispatch a user idle event. When the next keyboard or mouse input occurs, the Shell object will dispatch a user
present event. The idleThreshold interval is measured in seconds and has a default value of 300 (5 minutes). You
can also get the number of seconds since the last user input from the Shell.shell.lastUserInput property.
The following lines of code, set the idle threshold to 2 minutes and listen for both the user idle and user present
events:
ADOBE PRODUCT X.0 190
User Guide

Shell.shell.idleThreshold = 120;
Shell.shell.addEventListener(Event.USER_IDLE,function(event:Event){
trace(“Idle”);
});
Shell.shell.addEventListener(Event.USER_PRESENT,function(event:Event){
trace(“Present”);
});

Note: Only a single idle event will be dispatched between present events.

Taskbar icons
Many operating systems provide a taskbar, such as the Mac OS X dock, that can contain an icon to represent an appli-
cation. AIR provides an interface for interacting with the application task bar icon through the Shell.shell.icon
property.
AIR creates the Shell.shell.icon object automatically (whether or not the icon is visible). The object type will
always be a subclass of InteractiveIcon, either DockIcon or SystemTrayIcon, depending on the operating system. You
can determine which subclass AIR supports on the current operating system using the Shell.supportsDockIcon
and Shell.supportsSystemTrayIcon properties. The InteractiveIcon base class provides the properties width,
height, and bitmaps, which you can use to change the image used for the icon, but accessing properties specific to
DockIcon or SystemTrayIcon on the wrong operating system will generate runtime exceptions.
To set or change the image used for an icon, create an array containing one or more images and assign it to the
Shell.shell.icon.bitmaps property. The size of taskbar icons is very different on different operating systems.
You can provide multiple sizes of images in the bitmaps array to avoid scaling. If more than one image is supplied in
the array, AIR will select the size closest to the current display size of the taskbar icon, scaling it if necessary. The
following example sets the image for a taskbar icon:
Shell.shell.icon.bitmaps = [bmp16x16.bitmapData, bmp128x128.bitmapData];

You can animate the icon by resetting the bitmaps property with an array containing a different image in response
to the stage enterFrame event or a timer event. (Only one image in a bitmaps array is used.)
To remove the icon from the task bar on Windows, or restore the default icon appearance on Mac OS X, set bitmaps
to an empty array:
Shell.shell.icon.bitmaps = [];

Dock icons
AIR supports dock icons when Shell.supportsDockIcon is true. The Shell.shell.icon property represents the
application icon on the dock (not a window dock icon).

Dock icon menus

You can add commands to the standard dock menu by creating a new NativeMenu object containing the commands
and assigning it to the Shell.shell.icon.menu property. The items in the menu will be displayed above the
standard dock icon menu items.
ADOBE PRODUCT X.0 191
User Guide

Bouncing the dock

You can bounce the dock icon by calling the Shell.shell.icon.bounce() method. If you set the bounce()
priority parameter to informational, then the icon will bounce once. If you set it to critical, then the icon will
bounce until the user activates the application. Constants for the priority parameter are defined in the Notifica-
tionType class.
Note: The icon will not bounce if the application is already active.

Dock icon events

When the dock icon is clicked, the Shell object will dispatch an InvokeEvent. If the application is not running, the
system will launch it; otherwise, the invocation event will be delivered to the running version.

System Tray icons


AIR supports system tray icons when Shell.supportsSystemTrayIcon is true. System tray icons are displayed in
the notification area of the taskbar. No icon is displayed by default, to show an icon, assign an array containing
BitmapData objects to the icon bitmaps property. To change the icon image, assign an array containing the new
images to bitmaps. To remove the icon, set bitmaps to null.
Note: In this Beta release, the system tray icon will disappear if you close all the windows in an application, even if the
application is still running in the background. To work around this problem, retain a hidden window (set
visible=false for the window).

System tray icon menus

You can add a menu to the system tray icon by creating a new NativeMenu object and assigning it to the
Shell.shell.icon.menu property (no default menu is provided by the operating system) The system tray icon is
accessed by right-clicking the icon.

System tray icon tooltips

Add a tooltip to an icon by setting the tooltip property:


Shell.shell.icon.tooltip = “Application name”;

System tray icon events

The system tray icon dispatches ScreenMouseEvents for click, mouseDown, mouseUp, rightClick, right-
MouseDown, and rightMouseUp events. You can use these events, along with a menu, to allow users to interact with
your application when it has no visible windows.

Example: Creating an application with no windows

The following example creates an AIR application which has a system tray icon, but no visible windows. The system
tray icon has a menu with a single command for exiting the application.
package {
import flash.display.Loader;
import flash.display.NativeMenu;
import flash.display.NativeMenuItem;
import flash.display.NativeWindow;
import flash.display.Sprite;
import flash.display.SystemTrayIcon;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.Shell;

public class SysTrayApp extends Sprite


ADOBE PRODUCT X.0 192
User Guide

{
Shell.shell.autoExit = false;
var iconLoader:Loader = new Loader();
var iconMenu:NativeMenu = new NativeMenu();
var exitCommand:NativeMenuItem =
iconMenu.addItem(new NativeMenuItem("Exit"));
exitCommand.addEventListener(Event.SELECT,function(event:Event):void{
Shell.shell.icon.bitmaps = [];
Shell.shell.exit();
});

if(Shell.supportsSystemTrayIcon){
Shell.shell.autoExit = false;
iconLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,
iconLoadComplete);
iconLoader.load(new URLRequest("icons/AIRApp_16.png"));

var systray:SystemTrayIcon = Shell.shell.icon as SystemTrayIcon;


systray.tooltip = "AIR application";
systray.menu = iconMenu;
}

if(Shell.supportsDockIcon){
iconLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,
iconLoadComplete);
iconLoader.load(new URLRequest("icons/AIRApp_128.png"));
var dock:DockIcon = Shell.shell.icon as DockIcon;
dock.menu = iconMenu;
}

//In this Beta release, you must keep a window open or the icon will be removed
//stage.nativeWindow.close();
stage.nativeWindow.visible = false;
}

private function iconLoadComplete(event:Event):void


{
Shell.shell.icon.bitmaps = [event.target.content.bitmapData];
}
}
}

When you set the dock icon image, the change will only apply while the application is running. When the user quits,
the icon image will revert to the icon specified in the application descriptor (or the operating system default icon if
no icon image has been provided).
This example assumes that there are image files named AIRApp_16.png and AIRApp_128.png in an icons subdi-
rectory of the application. (Sample icon files are included with the AIR SDK.)

Window taskbar icons


Iconified representations of windows are typically displayed in the window area of a taskbar or dock to allow users
to easily access background or minimized windows.
ADOBE PRODUCT X.0 193
User Guide

Highlighting the taskbar window icon

When a window is in the background, you can notify the user that an event of interest related to the window has
occurred. On Mac OS X, you can notify the user by bouncing the application dock icon (as described in “Bouncing
the dock” on page 191). On Windows, you can highlight the window taskbar icon by calling the window’s
notifyUser() method. The type parameter passed to the method determines the urgency of the notification:
• NotificationType.CRITICAL: the window icon will flash until the user brings the window to the foreground.
• NotificationType.INFORMATIONAL: the window icon will highlight by changing color.
var type:String;
if(critical){
type = NotificationType.CRITICAL;
} else{
type = NotificationType.INFORMATIONAL;
}
stage.nativeWindow.notifyUser(type);

Calling the window notifyUser() method on an operating system that does not support window-level notification
will have no affect. Use the NativeWindow.supportsNotification property to determine if window notification
is supported.

Creating windows without taskbar window icons

On the Windows operating system, you can prevent the window from appearing on the taskbar by creating windows
of type utility or lightweight. Invisible windows also do not appear on the taskbar.
Because the initial window is necessarily of type, normal, in order to create an application without any windows that
appear in the taskbar, you must leave the initial window created at startup invisible. To do this, add
<visible>false</visible> to the <initalWindow> element of the application descriptor file (and don’t set
visible=true or call activate() on the window). In the NativeWindowInitOption object used to create any new
windows, set type to NativeWindowType.UTILITY or NativeWindowType.LIGHTWEIGHT.

Changing the image of a window taskbar icon

On Windows, you can change the icon that is displayed on the window system chrome title bar and in the iconified
representation of the window in the task bar. To change the icon image, assign an array containing the BitmapData
objects for the new image to the window’s icon property. (You can use the Loader class to load image files.) Because
not all windows have an icon, you should check that the property is not null before attempting to assign a value to it.
if(nativeWindow.icon != null){
nativeWindow.icon.bitmaps = new Array(loadedImage.bitmapData);
}

Note: AIR does not support changing window icons on the dock under Mac OS X. (You can only change the dock icon
for a running application.)

Application termination
The quickest way to end it all is to call Shell.shell.exit() and this works fine when your application has no data
to save or resources to clean up. Calling exit() will automatically close all windows and then terminate the appli-
cation. However, to allow windows or other components of your application to interrupt the termination process,
perhaps to save vital data, you must dispatch the proper warning events before calling exit().
ADOBE PRODUCT X.0 194
User Guide

Another consideration in gracefully shutting down an application is providing a single execution path, no matter
how the shut-down process starts. Application termination can be triggered by the user (or OS) in the following
ways:
• Closing the last application window when Shell.shell.autoExit=true.
• Application exit command from the OS, for example, when the user chooses exit application command from the
default menu. This will only happen on Mac OS X; Windows does not provide an application exit command through
system chrome.
• Computer shut down.
When exit command is mediated through the operating system by one of these routes, the Shell object will dispatch
an exiting event. If no listeners cancel the exiting event, any open windows are closed. Each window will dispatch a
closing and then a close event. If any of the windows cancel the closing event, the shut-down process will halt.
If the order of window closure is an issue for your application, then you should listen for the exiting event from the
Shell and close the windows in the proper order yourself. This might be the case, for example, if you have a document
window with tool palettes. It might be inconvenient, or worse, if the system closed the palettes, but the user decided
to cancel the exit command to save some data. On Windows, the only time you will get the exiting event is after
closing the last window (when autoExit=true).
A good practice for exiting the application, which provides consistent behavior on all platforms and whether or not
the exit sequence is initiated via operating system chrome or menu commands, or initiated through application logic,
is to:
1 Always dispatch an exiting event through the Shell object before calling exit() in application code and check
that another component of your application doesn’t cancel the event.
public function applicationExit():void{
var exitingEvent:Event = new Event(Event.EXITING,false,true);
Shell.shell.dispatchEvent(exitingEvent);
if(!exitingEvent.isDefaultPrevented()){
Shell.shell.exit();
}
}

2 Listen for the exiting event and in the handler close any windows (dispatching a closing event first) and then
perform any needed clean up tasks such as saving application data, deleting temporary files, etc. Only synchronous
methods should be used to ensure that they will finish before the application quits.
If the order in which your windows are closed doesn’t matter, then you can loop through the
Shell.shell.openedWindows array and close each window in turn. If order does matter, then you will have to
provide a means of closing the windows in the correct sequence.
private function onExiting(exitingEvent:Event):void{
var winClosingEvent:Event;
for each(var win:NativeWindow in Shell.shell.openedWindows){
winClosingEvent = new Event(Event.CLOSING,false,true);
win.dispatchEvent(winClosingEvent);
if(!winClosingEvent.isDefaultPrevented()){
win.close();
} else{
exitingEvent.preventDefault();
}
}

if(!exitingEvent.isDefaultPrevented()){
//perform cleanup
}
ADOBE PRODUCT X.0 195
User Guide

3 Windows should always handle their own clean up by listening for the window closing event.
4 Only one exiting listener should be used in your application since handlers called earlier cannot know whether
subsequent handlers will cancel the exiting event (and it would be unwise to rely on the order of execution).
196

Part 1: Networking and communications


Monitoring network connectivity. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
URL requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
Scripting and communications between applications and content . . . . . . . . . . . . . .202
197

Chapter 26: Monitoring network


connectivity
The air.net package includes three classes that can be used to detect network connectivity: ServiceMonitor, Socket-
Monitor, and URLMonitor.
These classes are not available automatically in the runtime. To use these classes in ActionScript code (in SWF devel-
opment), include the ServiceMonitor.swc file in the compiler. To use these classes in JavaScript code (in HTML
development), load the ServiceMonitor.swf file (via a script tag), as in the following:
<script src="ServiceMonitor.swf" type="application/swf">

Service Monitoring
Your AIR application can run in environments with uncertain and changing network connectivity. To help an appli-
cation manage connections to online resources, Adobe AIR sends a network change event whenever a network
connection becomes available or unavailable. The network change event is dispatched by the application’s
flash.system.Shell object. To react to this event, add a listener:
Shell.shell.addEventListener(Event.NETWORK_CHANGE, onNetworkChange);

And define an event handler function:


function onNetworkChange(event:Event)
{
//Check resource availability
}

The Event.NETWORK_CHANGE event does not indicate a change in all network activity, only that a network
connection has changed. AIR does not attempt to interpret the meaning of the network change. A networked
computer may have many real and virtual connections, so losing a connection does not necessarily mean losing a
resource. On the other hand, new connections do not guarantee improved resource availability, either. Sometimes a
new connection can even block access to resources previously available (for example, when connecting to a VPN).
In general, the only way for an application to determine whether it can connect to a remote resource is to try it. To
this end, the service monitoring frameworks in the air.net package provide AIR applications with an event-based
means of responding to changes in network connectivity to a specified host.
Note: The service monitoring framework detects whether a server responds acceptably to a request. This does not
guarantee full connectivity. Scalable web services often use caching and load-balancing appliances to redirect traffic to
a cluster of web servers. In this situation, service providers only provide a partial diagnosis of network connectivity.

Service monitoring basics


The service monitor framework, separate from the AIR framework, resides in the file servicemonitor.swc. This file
must be included in your build process in order to use the framework. Flex Builder includes this automatically.
ADOBE PRODUCT X.0 198
User Guide

The ServiceMonitor class implements the framework for monitoring network services and provides a base function-
ality for service monitors. By default, an instance of the ServiceMonitor class will dispatch events regarding network
connectivity. These events will be dispatched, when the instance is created and whenever a network change is
detected by Adobe AIR. Additionally, you can set the pollInterval property of a ServiceMonitor instance to check
connectivity at a specified interval in milliseconds, regardless of general network connectivity events. An instance of
ServiceMonitor will not check network connectivity until the start() method is called.
The URLMonitor class, subclassing the ServiceMonitor class, detects changes in HTTP connectivity for a specified
URLRequest.
The SocketMonitor class, also subclassing the ServiceMonitor class, detects changes in connectivity to a specified
host at a specified port.

Detecting HTTP connectivity


The URLMonitor class determines if HTTP requests can be made to a specified address at port 80 (the typical port
for HTTP communication). The following code uses an instance of the URLMonitor class to detect connectivity
changes to the Adobe website:
import air.net.URLMonitor;
import flash.net.URLRequest;
import flash.events.StatusEvent;

var monitor:URLMonitor;
monitor = new URLMonitor(new URLRequest('http://www.adobe.com'));
monitor.addEventListener(StatusEvent.STATUS, announceStatus);
monitor.start();

function announceStatus(e:StatusEvent):void {
trace("Status change. Current status: " + monitor.available);
}

Detecting Socket connectivity


AIR applications can also use socket connections for push-model connectivity. Firewalls and network routers
typically restrict network communication on unauthorized ports for security reasons. For this reason, developers
must consider that users may not have the capability of making socket connections.
Similar to the URLMonitor example, the following code uses an instance of the SocketMonitor class to detect
connectivity changes to a socket connection at 6667, a common port for IRC.
import air.net.ServiceMonitor;
import flash.events.StatusEvent;

socketMonitor = new SocketMonitor('www.adobe.com',80);


socketMonitor.addEventListener(StatusEvent.STATUS, socketStatusChange);
socketMonitor.start();

function announceStatus(e:StatusEvent):void {
trace("Status change. Current status: " + socketMonitor.available);
}
199

Chapter 27: URL requests


The new Adobe AIR functionality related to networking and communications is not available to SWF content
running in the browser. This functionality is only available to content in the application security sandbox. For
content in other sandboxes (such as content in a network sandbox, like the sandbox for www.example.com), calling
any of these APIs will cause Adobe AIR to throw a SecurityError exception.
For other information on using ActionScript 3.0 networking and communications capabilities, see Programming
ActionScript 3.0, delivered with both Adobe Flash CS3 and Adobe Flex Builder 2.0.
This section contains the following topics:
• Using the URLRequest class
• Changes to the URLStream class
• Opening a URL in the default system web browser
This section describes AIR networking and communication API—functionality uniquely provided to applications
running in the AIR runtime. It does not describe networking and communications functionality inherent to HTML
and JavaScript that would function in a web browser (such as the capabilities provided by the XMLHttpRequest
class).

Using the URLRequest class


The URLRequest class lets you define more than simply the URL string.
AIR adds some new properties to the URLRequest class, which are only available to AIR content running in the
application security sandbox.

URLRequest properties
The URLRequest class includes the following properties which are available to content only in the AIR application
security sandbox:
followRedirects Specifies whether redirects are to be followed (true, the default value) or not (false). This is
only supported in the runtime.
manageCookies Specifies whether the HTTP protocol stack should manage cookies (true, the default value) or not
(false) for this request. This is only supported in the runtime.
shouldAuthenticate Specifies whether authentication requests should be handled (true) for this request. This is
only supported in the runtime. The default is to authenticate requests—this may cause an authentication dialog box
to be displayed if the server requires credentials to be shown. You can also set the user name and password—see
“Setting login credentials for a URLRequest object” on page 200.
shouldCacheResponse Specifies whether successful response data should be cached for this request. This is only
supported in the runtime. The default is to cache the response (true).
useCache Specifies whether the local cache should be consulted before this URLRequest fetches data. This is only
supported in the runtime. The default (true) is to use the local cached version, if available.
userAgent Specifies the user-agent string to be used in the HTTP request.
ADOBE PRODUCT X.0 200
User Guide

The following properties of a URLRequest object can be set by content in any sandbox (not just the AIR application
security sandbox):
contentType The MIME content type of any data sent with the URL request.

data An object containing data to be transmitted with the URL request.

digest A secure "digest" to a cached file to track Flash Player cache.

method Controls the HTTP request method, such as a GET or POST operation. (Content running in the AIR
application security domain can specify strings other than "GET" or "POST" as the method property. Any HTTP verb
is allowed and "GET" is the default method. See “AIR Security” on page 52.)
requestHeaders The array of HTTP request headers to be appended to the HTTP request.

url Specifies the URL to be requested.

Note: The HTMLControl class has related properties for settings pertaining to content loaded by an HTMLControl
object. For details, see “Loading HTML content from a URL ” on page 163.

Setting login credentials for a URLRequest object


The setLoginCredentials() method lets you set the user name and password to use for the URLRequest object,
as illustrated in the following code:
var urlReq:URLRequest = new URLRequest();
urlRequest.setLoginCredentials("www.example.com", "Babbage", "cs1791!!");

The URLRequestDefaults class lets you define default settings for URLRequest objects. For example, the following
code sets the default values for the manageCookies and useCache properties:
URLRequestDefaults.manageCookies = false;
URLRequestDefaults.useCache = false;

The URLRequestDefaults class includes a setLoginCredentialsForHost() method that lets you specify a default
user name and password to use for a specific host. The host, which is defined in the hostname parameter of the
method, can be a domain, such as "www.example.com", or a domain and a port number, such as
"www.example.com:80". Note that "example.com", "www.example.com", and "sales.example.com" are each
considered unique hosts.
These credentials are only used if the server requires them. If the user has already authenticated (for example, by
using the authentication dialog box) then you cannot change the authenticated user by calling the
setLoginCredentialsForHost() method.

For example, the following code sets the default user name and password to use at www.example.com:
URLRequestDefaults.setLoginCredentialsForHost("www.example.com", "Ada", "love1816$X");

Each property of URLRequestDefaults settings apply to only the application domain of the content setting the
property. However, the setLoginCredentialsForHost() method applies to content in all application domains
within an AIR application. This way, an application can log into a host and have all content within the application be
logged in with the specified credentials.
For more information, see the URLRequestDefaults class in the Flex Language Reference for Apollo.

Using AIR URL schemes in URLRequest object URLs


You can also use the following schemes when defining a URL for a URLRequest object:
ADOBE PRODUCT X.0 201
User Guide

http: and https: Use these as you would use them in a web browser.

file: Use this to specify a path relative to the root of the file system. For example:

file:///c:/AIR Test/test.txt

app-resource: Use this to specify a path relative to the root directory of the installed application (the directory
that contains the application descriptor file for the installed application). For example, the following path points to
a resources subdirectory of the directory of the installed application:
app-resource:/resources

When running in the ADL application, the application resource directory is set to the directory that contains the
application descriptor file.
app-storage:/ Use this to specify a path relative to the application store directory. For each installed application,
AIR defines a unique application store directory, which is a useful place to store data specific to that application. For
example, the following path points to a prefs.xml file in a settings subdirectory of the application store directory:
app-storage:/settings/prefs.xml

You can use a URLRequest object that uses any of these URL schemes to define the URL request for a number of
different objects, such as a FileStream or a Sound object. You can also use these schemes in HTML content running
in AIR; for example, you can use them in the src attribute of an img tag.
However, you can only use these AIR URL schemes in content in the application resource directory. For more infor-
mation, see “AIR Security” on page 52.

Changes to the URLStream class


The URLStream class provides low-level access to downloading data from URLs. In the runtime, the URLStream
class includes a new event: httpResponseStatus. Unlike the httpStatus event, the httpResponseStatus event
is delivered before any response data. The httpResponseStatus event (defined in the HTTPStatusEvent class)
includes a responseURL property, which is the URL that the response was returned from, and a responseHeaders
property, which is an array of URLRequestHeader objects representing the response headers that the response
returned.

Opening a URL in the default system web browser


You can use navigateToURL() function to open a URL in the default system web browser. For the URLRequest
object you pass as the request parameter of this function, only the url property is used.
202

Chapter 28: Scripting and


communications between applications
and content
This section covers the following topics:
• Using the LocalConnection class in AIR applications
• Scripting between content in different domains

Using the LocalConnection class in AIR applications


The LocalConnection class enables communications between AIR applications, as well as among AIR applications
and SWF content running in the browser.
The connect() method of the LocalConnection class uses a connectionName parameter to identify applications.
In content running in the AIR application security sandbox (content installed with the AIR application), AIR uses
the string app# followed by the application ID for the AIR application (defined in the application descriptor file) in
place of the domain used by SWF content running in the browser. For example a connectionName for an application
with the application ID com.example.air.MyApp, the connectionName resolves to
"app#com.example.air.MyApp:connectionName".

Scripting between content in different domains


AIR applications are granted special privileges when they are installed. It is crucial that the same privileges not be
leaked to other content, including remote files and local files that are not part of the application.
Normally, content from other domains cannot call scripts in other domains. To protect AIR applications from
accidental leakage of privileged information or control, the following restrictions are place on content in the
application security sandbox (content installed with the application):

• The Security.allowDomain() method cannot be called from the application security sandbox.
• Importing non-application content into the application sandbox by setting the
LoaderContext.securityDomain or the LoaderContext.applicationDomain property is prevented.

But there are still cases where the main AIR application wants content from a remote domain to have controlled
access to scripts in the main AIR application, or vice versa. To accomplish this, the AIR runtime provides a sandbox
bridge mechanism, which serves as a gateway between the two, providing explicit interaction between remote and
application security sandboxes.
Suppose an AIR music store application wants to allow remote SWF files to broadcast the price of albums, but does
not want the remote SWF file to disclose whether the price is a sale price. To do this, a StoreAPI class provides a
method to acquire the price, but obscures the sale price. An instance of this StoreAPI class is then assigned to the
parentSandboxBridge property of the LoaderInfo object of the Loader object that loads the remote SWF.
ADOBE PRODUCT X.0 203
User Guide

The following is the code for the AIR music store:


<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
title="Hello World" creationComplete="initApp()">
<mx:Script>
import flash.display.Loader;
import flash.net.URLRequest;
import flash.html.HTMLControl;

private var child:Loader;


private var isSale:Boolean = false;

private function initApp():void {


var request:URLRequest = new
URLRequest("http://[www.yourdomain.com]/PriceQuoter.swf")

child = new Loader();


child.contentLoaderInfo.parentSandboxBridge = new StoreAPI(this);
child.load(request);
container.addChild(child);
}
public function getRegularAlbumPrice():String {
return "$11.99";
}
public function getSaleAlbumPrice():String {
return "$9.99";
}
public function getAlbumPrice():String {
if(isSale) {
return getSaleAlbumPrice();
}
else {
return getRegularAlbumPrice();
}
}
</mx:Script>
<mx:UIComponent id="container" />
</mx:WindowedApplication>

The following code represents an example of a PriceQuoter SWF file that reports the store’s price, but cannot report
the sale price:
package
{
import flash.display.Sprite;
import flash.system.Security;
import flash.text.*;

public class PriceQuoter extends Sprite


{
private var storeRequester:Object;

public function PriceQuoter() {


trace("Initializing child SWF");
trace("Child sandbox: " + Security.sandboxType);
storeRequester = loaderInfo.parentSandboxBridge;

var tf:TextField = new TextField();


tf.autoSize = TextFieldAutoSize.LEFT;
addChild(tf);
ADOBE PRODUCT X.0 204
User Guide

tf.appendText("Store price of album is: " + storeRequester.getAlbumPrice());


tf.appendText("\n");
tf.appendText("Sale price of album is: " + storeRequester.getSaleAlbumPrice());
}
}
}

When exposing sandbox bridges, it's important to expose high-level APIs that limit the degree to which they can be
abused. Keep in mind that the content calling your bridge implementation may be compromised. So, for example,
exposing a "readFile(path:String)" method via a bridge is vulnerable to abuse. It would be better to expose a
"readApplicationSetting()" API that doesn't take a path. The more semantic approach limits the damage that
an application can do once part of it is compromised.
For more information on security, see “AIR Security” on page 52.
205

Part 1: Distributing and updating


applications
Distributing AIR applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .206
Updating AIR applications programmatically . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .214
206

Chapter 30: Distributing AIR applications


When you package an application into an AIR installation file, you must provide a provide a digital code-signing
certificate with which to sign the application. Digitally signing the file provides assurance that your application has
not been altered since it was signed. In addition, if the digital certificate was issued by the Verisign or Thawte certif-
icate authorities, your users can confirm your identity as the publisher and signer.
For information about how to package an application into an AIR file using Flex Builder see “Packaging AIR appli-
cations with Flex Builder” on page 26.

For information about how to package an application into an AIR file using the AIR SDK see “Exporting an AIR
installation file using the AIR Developer Tool” on page 32.
Once you package an AIR application there are a couple of ways to distribute it:
• You can install it by sending the AIR package to the end user just as you would distribute any file. For example,
you can send the AIR package as an e-mail attachment or as a link in a web page.
• Using a seamless install installation link in a web page.
AIR applications are easy to install and run. The seamless install feature lets users install the latest version of the AIR
runtime (if it is not installed) when clicking the link to install a specific AIR application. Once the AIR application
is installed, users simply double-click the application icon to run it, just like any other desktop application.
This section contains the following topics:
• “Digitally signing an AIR file” on page 206
• “Distributing and installing using the seamless install feature” on page 208
• “Distributing and installing an AIR file without using the seamless install feature” on page 212

Digitally signing an AIR file


Digitally signing your AIR installation files with a certificate issued by a recognized certificate authority (CA)
provides significant assurance to your users that the application they are installing has not been accidentally or
maliciously altered and identifies you as the signer (publisher). AIR recognizes code signing certificates issued by
the Verisign and Thawte certificate authorities. AIR will display the publisher name during installation when you
have signed the AIR file with a Verisign or Thawte certificate.
Important: A malicious entity could forge an AIR file with your identity if it somehow obtains your signing keystore file
or discovers your private key.
The security assurances, limitations and legal obligations involving the use of code- signing certificates are outlined
in the Certificate Practice Statements (CPS) and subscriber agreements published by the certificate authority. For
more information refer to:
Verisign CPS (http://www.verisign.com/repository/CPS/)
Verisign Subscriber’s Agreement (https://www.verisign.com/repository/subscriber/SUBAGR.html)
Thawte CPS (http://www.thawte.com/cps/index.html)
ADOBE PRODUCT X.0 207
User Guide

Thawte Code Signing Developer’s Agreement (http://www.thawte.com/ssl-digital- certificates/free-guides-white-


papers/pdf/develcertsign.pdf)

AIR code signing


When an AIR file is signed, a digital signature is included in the installation file. The signature includes a digest of
the package, which is used to verify that the AIR file has not been altered since it was signed, and it includes infor-
mation about the signing certificate, which is used to verify the publisher identity (if the certificate was issued by
Verisign or Thawte).
AIR uses the public key infrastructure (PKI) supported through the operating system is certificate store. The
computer on which an AIR application is installed must trust the root certificate of the Verisign and Thawte certif-
icate authorities for the publisher information to be verified.
If an AIR file is signed with a certificate that does not “chain” to the Verisign or Thawte root certificates, and this
includes all self-signed certificates, then the publisher information cannot be verified. While AIR can determine that
the AIR package has not been altered since it was signed, there is no way to know who actually created and signed
the file.

Certificate format
The AIR signing tools accept PKCS#12 keystore files (which typically use a .pfx or .p12 file extension). These files
can be generated from a certificate using a variety of third- party tools, including the Java Keytool utility included
with the JDK, and the Microsoft pvk2pfx utility included with Windows development tools. In addition, many
browsers, including Internet Explorer and Firefox, can export or backup keystore files in the proper format.
You can use an existing class-3, high assurance code signing certificate or you can obtain a new one. The certificate
issued by the certificate authority must be one of the following types:
• Verisign:
• Microsoft Authenticode Digital ID
• Sun Java Signing Digital ID
• Thawte:
• Apple Developer Certificate
• JavaSoft Developer Certificate
• Microsoft Authenticode Certificate
Adobe developer certificates will be offered by these CAs in the future. Although there will be Adobe developer
certificates, existing code-signing certificates issued by these CAs will continue to be supported.
Note: You cannot use an SSL certificate to sign AIR files.

Obtaining a certificate
To obtain a certificate, you must typically visit the Verisign or Thawte web site and complete the procurement
process. The tools used to produce the keystore file needed by the AIR tools will depend on the type of certificate
purchased, how the certificate is stored on the receiving computer, and, in some cases, the browser used to obtain
the certificate. For example, to obtain and export a Microsoft Authenticode certificate, Verisign or Thawte will
require you to use Microsoft Internet Explorer. The certificate can then be exported as a .pfx file directly from the
Internet Explorer user interface.
ADOBE PRODUCT X.0 208
User Guide

You can generate a self-signed certificate using the ADT tool used to package AIR installation files. A number of
third-party tools can also be used.
For instructions on how to generate a self-signed certificate, as well as instructions on signing an AIR file, see
“Exporting an AIR installation file using the AIR Developer Tool” on page 32. You can also export and sign AIR files
using Flex Builder, Dreamweaver, and the AIR update for Flash.

Terminology
This section provides a glossary of some of the key terminology you should understand when making decisions
about how to sign your application for public distribution.
Certificate Authority (CA) An entity in a public-key infrastructure network that serves as a trusted third party and
ultimately certifies the identity of the owner of a public key. A CA normally issues digital certificates, signed by its
own private key, to attest that it has verified the identity of the certificate holder.
Certificate Practice Statement (CPS) Sets forth the practices and policies of the certificate authority in issuing and
verifying certificates. The CPS is part of the contract between the CA and its subscribers and relying parties. It also
outlines the policies for identify verification and the level of assurances offered by the certificates they provide.
Certificate Revocation List (CRL) A list of issued certificates that have been revoked and should no longer be relied
upon.
Digital Certificate A digital document that contains information about the identity of the owner, the owner’s public
key, the identity of the certificate itself, and which is signed by the issuing certificate authority.
Digital Signature An encrypted message or digest that can only be decrypted with the public key half of a public-
private key pair. In a PKI, a digital signature will contain one or more digital certificates that are ultimately traceable
to the certificate authority. A digital signature can be used to validate that a message (or computer file) has not been
altered since it was signed (within the limits of assurance provided by the cryptographic algorithm used), and,
assuming one trusts the issuing certificate authority, the identity of the signer.
Private Key The private half of a two-part, public-private key asymmetric cryptography system. The private key
must be kept secret and should never be transmitted over a network. Digitally signed messages are encrypted with
the private key by the signer.
Public Key The public half of a two-part, public-private key asymmetric cryptography system. The public key is
openly available and is used to decrypt messages encrypted with the private key.
Public Key Infrastructure (PKI) A system of trust in which certificate authorities attest to the identity of the owners
of public keys. Clients of the network rely on the digital certificates issued by a trusted CA to verify the identity of
the signer of a digital message (or file).

Distributing and installing using the seamless install


feature
The seamless install feature lets you provide a link in a web page that lets the user install an AIR application by simply
clicking the link. If the AIR runtime is not installed, the user is given the option to install it. The seamless install
feature also lets users install the AIR application without downloading the AIR file to their machine.
The seamless install link is a link in a SWF file (badge.swf) that has special capabilities that allow it to download the
AIR runtime. The SWF file, and its source code, is provided to you for distribution on your website.
ADOBE PRODUCT X.0 209
User Guide

Note: The instructions in this topic provide information on setting parameters of the badge.swf file provided by Adobe.
We also provide the source code for the badge.swf file, which you can customize.

Add a seamless install link to a page


1 Add the badge.swf file to the web server.
This file is provided in the lib directory of the AIR SDK. To customize the SWF file, see “Modifying the badge.swf
file” on page 212.
2 Embed the SWF file in the HTML page, at the point that you want the link (contained in the badge.swf file) to
appear.
3 Adjust the FlashVars parameter definitions for the following:
• appurl (required)—The URL of the AIR file to be downloaded. This must be an absolute, not relative, URL.

• airversion (required)—For this beta release, set this to 1.0.M5.

• appname—The name of the application to display in the badge SWF file text when the runtime is not
installed.
• buttoncolor—The color of the download button (specified as a hex value, such as FFCC00).

• messagecolor—The color of the text message displayed below the button when the runtime is not installed
(specified as a hex value, such as FFCC00).
• imageurl—The URL of the image (optional) to display in the badge.

For details, see “Adjusting the code in the HTML page that contains the badge.swf file” on page 210.

Installing the AIR application from a seamless install link in a web page
Once you have added the seamless install link to a page, you (or any user) can install the AIR application by clicking
the link in the SWF file.
1 Navigate to the HTML page in a web browser that has Flash Player (version 6 or later) installed.
2 In the web page, click the link in the badge.swf file.
• If you have installed the AIR runtime, skip to the next step.
• If you have not installed the AIR runtime, a dialog box is displayed asking whether you would like to install
it. Install the AIR runtime (see “Install the runtime” on page 2), and then proceed with the next step.
3 In the Installation window, leave the default settings selected, and then click Continue.
On a Windows computer, AIR automatically does the following:
• Installs the application into c:\Program Files\
• Creates a desktop shortcut for application
• Creates a Start Menu shortcut
• Adds an entry for application in the Add/Remove Programs Control Panel
On Mac OS, the installer adds the application to the Applications subdirectory of the user directory (for example,
in the /Applications directory in Mac OS).
4 When the installation is complete, click Finish.
5 Select the options you want, and then click the Install button.
ADOBE PRODUCT X.0 210
User Guide

Adjusting the code in the HTML page that contains the badge.swf file
You need to add FlashVars parameter definitions for the URL variable, and modify the path to the badge.swf file (if
needed). SWF files are embedded in a page using the EMBED and OBJECT tags, as in the following:
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
id="HelloWorld" width="215" height="138"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
<param name="movie" value="badge.swf" />
<param name="FlashVars" value="appUrl=AIRFileURL.air&airversion=1.0.M5" />
<embed src="badge.swf" quality="high" bgcolor="#869ca7"
FlashVars="appUrl=AIRFileURL.air&airversion=1.0.M5"
width="215" height="138" name="HelloWorld" align="middle"
play="true"
type="application/x-shockwave-flash"
pluginspage="http://www.adobe.com/go/getflashplayer">
</embed>
</object>

The boldface lines represent code that you need to add and modify to reflect the URL of your AIR file and the
badge.swf file.
However, this is a simplified version of code embedding a SWF file. In most HTML pages, SWF files are embedded
with more complicated code. For example, Flex Builder and Flash CS3 both insert code as described in the Flash
Player Detection Kit (http://www.adobe.com/products/flashplayer/download/detection_kit/), as shown below:
<script language="JavaScript" type="text/javascript">
<!--
// Version check for the Flash Player that has the ability to start Player Product Install
(6.0r65)
var hasProductInstall = DetectFlashVer(6, 0, 65);

// Version check based upon the values defined in globals


var hasRequestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion,
requiredRevision);

// Check to see if a player with Flash Product Install is available and the version does
not meet the requirements for playback
if ( hasProductInstall && !hasRequestedVersion ) {
// MMdoctitle is the stored document.title value used by the installation process to
close the window that started the process
// This is necessary in order to close browser windows that are still utilizing the
older version of the player after installation has completed
// DO NOT MODIFY THE FOLLOWING FOUR LINES
// Location visited after installation is complete if installation is required
var MMPlayerType = (isIE == true) ? "ActiveX" : "PlugIn";
var MMredirectURL = window.location;
document.title = document.title.slice(0, 47) + " - Flash Player Installation";
var MMdoctitle = document.title;

AC_FL_RunContent(
"src", "playerProductInstall",
"FlashVars",
"MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"&a
ppUrl=AIRFileURL.air&airversion=1.0.M5",
"width", "215",
"height", "138",
"align", "middle",
"id", "badge",
"quality", "high",
"bgcolor", "#869ca7",
ADOBE PRODUCT X.0 211
User Guide

"name", "badge",
"allowScriptAccess","sameDomain",
"type", "application/x-shockwave-flash",
"pluginspage", "http://www.adobe.com/go/getflashplayer"
);
} else if (hasRequestedVersion) {
// if we've detected an acceptable version
// embed the Flash Content SWF when all tests are passed
AC_FL_RunContent(
"src", "badge",
"width", "215",
"height", "138",
"align", "middle",
"id", "badge",
"quality", "high",
"bgcolor", "#869ca7",
"name", "badge",
"flashvars",'historyUrl=history.htm%3F&lconid=' + lc_id + '
&appUrl=AIRFileURL.air&airversion=1.0.M5',
"allowScriptAccess","sameDomain",
"type", "application/x-shockwave-flash",
"pluginspage", "http://www.adobe.com/go/getflashplayer"
);
} else { // flash is too old or we can't detect the plugin
var alternateContent = 'Alternate HTML content should be placed here. '
+ 'This content requires the Adobe Flash Player. '
+ '<a href=http://www.adobe.com/go/getflash/>Get Flash</a>';
document.write(alternateContent); // insert non-flash content
}
// -->
</script>
<noscript>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
id="badge" width="100%" height="100%"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
<param name="movie" value="badge.swf" />
<param name="FlashVars" value="appInstallUrl=AIRFileURL.air&airversion=1.0.M5" />
<param name="quality" value="high" />
<param name="bgcolor" value="#869ca7" />
<param name="allowScriptAccess" value="sameDomain" />
<embed src="badge.swf" quality="high" bgcolor="#869ca7"
FlashVars="appUrl=AIRFileURL.air&airversion=1.0.M5"
width="215" height="138" name="badge" align="middle"
play="true"
loop="false"
quality="high"
allowScriptAccess="sameDomain"
type="application/x-shockwave-flash"
pluginspage="http://www.adobe.com/go/getflashplayer">
</embed>
</object>
</noscript>

In this code, there are many more lines to modify, as the code embeds the SWF file in a number of ways (including
via JavaScript calls). Be sure to modify AIRFileURL.air to point to the URL (or relative path) of your AIR file, and
modify it each place it is referenced in the code. If necessary, also modify the path to the badge.swf file, and modify
it each place it is referenced in the code.
ADOBE PRODUCT X.0 212
User Guide

Modifying the badge.swf file


The AIR SDK provides the source files for the badge.swf file. These files are included in the src folder of the SDK:
badge.fla The source Flash file used to compile the badge.swf file. The badge.fla file compiles into a SWF 6 file
(which can be loaded in Flash Player versions 6 and later).
install.as An ActionScript 2.0 script file referenced in the badge.fla file. This script is written with ActionScript
2.0 class (rather than ActionScript 3.0) for support in Flash Player versions 6 and later. Details are provided below.
You can use Flash to redesign the visual interface of the badge.fla file.
The install.as file includes code for installing the AIR runtime. It uses the flash.system.ProductManager class, which
is used to download and install extensions to Flash Player (in this case, the AIR runtime) that originate from
www.adobe.com. In the badge.swf file, the class is used to install the AIR Detection Add-In, a browser add-in that is
used to install the AIR runtime, check the runtime version, and launch the AIR application installer. The
Flash.system.ProductManager class is generally undocumented because it can only be used to install extensions that
originate from www.adobe.com. We recommend that you do not modify the script in the install.as file.
If the AIR Detection Add-In is not installed, the user is asked to grant permission to install it. Once the AIR
Detection Add-In is installed, it launches the AIR runtime. If the AIR runtime does not exist, the AIR Detection
Add-In asks the user to grant permission to install the AIR runtime. Once the AIR runtime is installed, the badge.swf
file proceeds to install the requested AIR file, by opening the AIR application installer (which also gives the user the
choice to proceed with installing the AIR application).
Both the AIR Detection Add-In and the AIR runtime are signed.
Neither the badge.swf file, the AIR Detection Add-In, nor the AIR application installer send any information about
the identity of the AIR file you are installing to adobe.com.

Distributing and installing an AIR file without using the


seamless install feature
If you choose not to use the seamless install feature, you can simply send the AIR file to the recipient. For example,
you can send the AIR file as an e-mail attachment or as a link in a web page. Once the user downloads the AIR appli-
cation, the user follows these instructions to install it.
1 Double-click the AIR file.
2 In the Installation window, leave the default settings selected, and then click Continue.
In Windows, AIR automatically does the following:
• Installs the application into the Program Files directory
• Creates a desktop shortcut for application
• Creates a Start Menu shortcut
• Adds an entry for application in the Add / Remove Programs Control Panel
In the Mac OS, by default the application is added to the Applications subdirectory of the user directory.
If the application is already installed, the installer gives the user the choice of opening the existing version of the
application or updating to the version in the downloaded AIR file. The installer identifies the application using
the application ID (appID) in the AIR file.
ADOBE PRODUCT X.0 213
User Guide

3 When the installation is complete, click Finish.


An application can also install a new version via ActionScript or JavaScript. For more information, see “Updating
AIR applications programmatically” on page 214.

Running an AIR application


Once you have installed the runtime and the AIR application, running the application is as simple as running any
other desktop application:
• On a Windows computer, double-click the application's icon (which may be installed on the desktop or in a
folder), or select the application from the Start menu.
• On Mac OS, double-click the application in the folder in which it was installed. The default installation directory
is the /Applications directory.
214

Chapter 31: Updating AIR applications


programmatically
Users can install or update an AIR application by double-clicking an AIR file on their computer, and the AIR installer
application will manage the installation, alerting the user if they are updating an already existing application.
However, you can also have an installed application update itself to a new version, using the Updater class. The
Updater class includes an update() method that lets you point to an AIR file on the user’s computer and update to
that version.

About updating applications


The Updater class (in the flash.system package) includes one method, update(), which you can use to update the
currently running application with a different version. For example, if the user has a version of the AIR file
("Sample_App_v2.air") located on the desktop, the following code updates the application:
var updater:Updater = new Updater();
var airFile:File = File.desktopDirectory.resolve("Sample_App_v2.air");
var version:String = "2.01";
updater.update(airFile, version);

Results of the method call


When an application in the runtime calls the update() method, the runtime closes the application, and it then
attempts to install the new version from the AIR file. The runtime checks that the application ID specified in the AIR
file matches the application ID for the application calling the update() method. It also checks that the version string
matches the version string passed to the update() method. If installation completes successfully, the runtime opens
the new version of the application. Otherwise (if the installation cannot complete), it re-opens the existing (pre-
install) version of the application.
When testing an application using ADL, calling the update() method installs and runs a new version of the appli-
cation only if the runtime is installed. If the runtime is not installed, the call to the update() results in a runtime
exception.

About the version string


The string that is specified as the version parameter of the update() method must match the string in the version
attribute of the main application element of the application descriptor file for the AIR file to be installed. Speci-
fying the version parameter is required for security reasons. By requiring the application to verify the version
number in the AIR file, the application will not inadvertently install an older version, which might contain a security
vulnerability that has been fixed in the currently installed application.
The version string can be of any format. For instance, it can be "2.01" or "version 2". The format of this string is
left for you, the application developer, to decide.
ADOBE PRODUCT X.0 215
User Guide

If an Adobe AIR application downloads an AIR file via the web, it is a good practice to have a mechanism by which
the web service can notify the Adobe AIR application of the version being downloaded. The application can then use
this string as the version parameter of the update() method. If the AIR file is obtained by some other means, in
which the version of the AIR file is unknown, the AIR application can examine the AIR file to determine the version
information. (An AIR file is a ZIP-compressed archive, and the application descriptor file is the second record in the
archive.)
For details on the application descriptor file, see “Setting application properties” on page 62.

Presenting a custom application update user interface


AIR includes a default update interface:

This interface is always used the first time a user installs a version of an application on a machine. However, you can
define your own interface to use for subsequent instances. To do this specify a handleUpdates element in the appli-
cation descriptor file for the application:
<handleUpdates/>

When the application is installed, when the user opens an AIR file with an application ID matching the installed
application, the runtime opens the application, rather than the default AIR application installer. For more infor-
mation, see “Signaling the inclusion of an update interface” on page 66.
The application can decide, when it is invoked (when the Shell.shell object dispatches an invoke event) whether
to update the application (using the Updater class). If it decides to update, it can present its own installation interface
(which differs from its standard running interface) to the user.
ADOBE PRODUCT X.0 216
User Guide

Downloading an AIR file to the user’s computer


To use the Updater class, you must first save an AIR file locally to the user's machine. For example, the following
code reads an AIR file from a URL (http://example.com/air/updates/Sample_App_v2.air) and saves the AIR file to
the application storage directory:
var urlString:String = "http://example.com/air/updates/Sample_App_v2.air";
var urlReq:URLRequest = new URLRequest(urlString);
var urlStream:URLStream = new URLStream();
var fileData:ByteArray = new ByteArray();
urlStream.addEventListener(Event.COMPLETE, loaded);
urlStream.load(urlReq);

function loaded(event:Event):void {
urlStream.readBytes(fileData, 0, urlStream.bytesAvailable);
writeAirFile();
}

function writeAirFile():void {
var file:File = File.applicationStorageDirectory.resolve("My App v2.air");
var fileStream:FileStream = new FileStream();
fileStream.addEventListener(Event.CLOSE, fileClosed);
fileStream.openAsync(file, FileMode.WRITE);
fileStream.writeBytes(fileData, 0, fileData.length);
fileStream.close();
}

function fileClosed(event:Event):void {
trace("The AIR file is written.");
}

See also
• “Workflow for reading and writing files” on page 112.

Checking to see if an application is running for the first


time
Once you have updated an application you may want to provide the user with "getting started" or "welcome" message.
To do this, you will want the application, upon launching, to check to see it is running for the first time, so that you
can determine whether to display the message upon.
One way to do this is to save a file to the application store directory upon initializing the application (for example,
when a top-level Flex component dispatches the applicationComplete event). Every time the application starts
up, it should check for the existence of that file. If the file does not exist, then the application is running for the first
time. If the file exists, the application has already run at least once. If the file exists and contains a version number
older than the current version number, then you know the user is running the new version for the first time.
Here is a Flex example:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
ADOBE PRODUCT X.0 217
User Guide

title="Sample Version Checker Application"


applicationComplete="init()">
<mx:Script>
<![CDATA[
import flash.filesystem.*;
public var file:File;
public var currentVersion:String = "1.2";
public function init():void {
file = File.applicationStorageDirectory.resolve("Preferences/version.txt");
trace(file.nativePath);
if(file.exists) {
checkVersion();
} else {
firstRun();
}
}
private function checkVersion():void {
var stream:FileStream = new FileStream();
stream.open(file, FileMode.READ);
var prevVersion:String = stream.readUTFBytes(stream.bytesAvailable);
stream.close();
if (prevVersion != currentVersion) {
log.text = "You have updated to version " + currentVersion + ".\n";
} else {
saveFile();
}
log.text += "Welcome to the application.";
}
private function firstRun():void {
log.text = "Thank you for installing the application. \n"
+ "This is the first time you have run it.";
saveFile();
}
private function saveFile():void {
var stream:FileStream = new FileStream();
stream.open(file, FileMode.WRITE);
stream.writeUTFBytes(currentVersion);
stream.close();
}
]]>
</mx:Script>
<mx:TextArea id="log" width="100%" height="100%" />
</mx:WindowedApplication>
If your application saves data locally (such as, in the application storage directory), you may want to check for any
previously saved data (from previous versions) upon first run.
1

Chapter 32: New functionality in Adobe


AIR
This appendix provides an overview of the new functionality in AIR that is not available to SWF content running in
Flash Player.
This appendix contains the following sections:
• New runtime classes
• Runtime classes with new functionality
• New Flex components
• Service monitoring framework classes

New runtime classes


The following runtime classes are new in Adobe AIR. They are not available to SWF content running in the browser:

Class Package

Clipboard flash.desktop

ClipboardFormats flash.desktop

CompressionAlgorithm flash.utils

ClipboardTransferMode flash.desktop

DockIcon flash.display

DragActions flash.desktop

DragManager flash.desktop

DragOptions flash.desktop

DRMAuthenticateEvent flash.events

DRMStatusEvent flash.events

EncryptedLocalStore flash.filesystem

File flash.filesystem

FileListEvent flash.events

FileMode flash.filesystem

FileStream flash.filesystem

HTMLControl flash.html

HTMLHistoryItem flash.html

HTMLHost flash.html
ADOBE PRODUCT X.0 2
User Guide

Class Package

HTMLPDFCapability flash.html

HTMLUncaughtJavaScriptExceptionEvent flash.html

HTMLWindowCreateOptions flash.html

Icon flash.desktop

InteractiveIcon flash.display

InvokeEvent flash.events

JavaScriptFunction flash.html

JavaScriptObject flash.html

NativeDragEvent flash.events

NativeMenu flash.display

NativeMenuItem flash.display

NativeWindow flash.display

NativeWindowBoundsEvent flash.events

NativeWindowDisplayState flash.display

NativeWindowDisplayStateEvent flash.events

NativeWindowErrorEvent flash.events

NativeWindowIcon flash.display

NativeWindowInitOptions flash.display

NativeWindowResize flash.display

NativeWindowSystemChrome flash.display

NativeWindowType flash.display

NotificationType flash.display

OutputProgressEvent flash.events

Screen flash.display

Shell flash.system

SQLCollationType flash.data

SQLColumnNameStyle flash.data

SQLColumnSchema flash.data

SQLConnection flash.data

SQLError flash.errors

SQLErrorCode flash.errors

SQLErrorEvent flash.events

SQLErrorOperation flash.errors
ADOBE PRODUCT X.0 3
User Guide

Class Package

SQLEvent flash.events

SQLIndexSchema flash.data

SQLResult flash.data

SQLSchema flash.data

SQLSchemaResult flash.data

SQLStatement flash.data

SQLTableSchema flash.data

SQLTransactionLockType flash.data

SQLTriggerSchema flash.data

SQLUpdateEvent flash.events

SQLViewSchema flash.data

SystemTrayIcon flash.display

Updater flash.system

URLRequestDefaults flash.net

XMLSignatureValidator flash.utils

TransferableTransferMode flash.desktop

URLRequestDefaults flash.net

Updater flash.system

Most of these classes are available only to content in the AIR application security sandbox (see “Introducing Adobe
AIR” on page 13). However, the following classes are also available to content running in other sandboxes:
• Door
• URLRequest.

Runtime classes with new functionality


The following classes are available to SWF content running in the browser, but AIR provides additional properties
or methods:
ADOBE PRODUCT X.0 4
User Guide

Class Property or Method

HTTPStatusEvent HTTP_RESPONSE_STATUS

responseURL

responseHeaders

URLRequest followRedirects

manageCookies

shouldAuthenticate

shouldCacheResponse

userAgent

userCache

setLoginCredentials()

URLStream httpResponseStatus event

Stage nativeWindow

Security APPLICATION

Most of these new properties and methods are available only to content in the AIR application security sandbox.
However, the new members in the URLRequest classes are also available to content running in other sandboxes.
The ByteArray.compress() and ByteArray.uncompress() methods each include a new algorithm parameter,
allowing you to choose between deflate and zlib compression.

New Flex components


The following Flex components are available when developing content for the AIR runtime:
• FileEvent
• FileSystemComboBox
• FileSystemDataGrid
• FileSystemEnumerationMode
• FileSystemHistoryButton
• FileSystemList
• FileSystemSizeDisplayMode
• FileSystemTree
• HTML
• WindowedApplication
ADOBE PRODUCT X.0 5
User Guide

Service monitoring framework classes


The air.net package contains classes for network detection. This package is only available to content running in
Adobe AIR. It is included in the ServiceMonitor.swc file.
The package includes the following classes:
• ServiceMonitor
• SocketMonitor
• URLMonitor
6

Part 1: AIR Quick Starts


Building the quick-start sample applications with Flex. . . . . . . . . . . . . . . . . . . . . . . . . .7
Building a text-file editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9
Building a directory search application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13
Reading and writing from an XML preferences file. . . . . . . . . . . . . . . . . . . . . . . . . . . .17
Adding native menus to an AIR application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46
Interacting with a window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21
Creating a transparent window application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .26
Launching native windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .31
Creating toast-style windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .51
Controlling the display order of windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .56
Dragging, copying, and pasting data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .35
Creating resizable, non-rectangular windows. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .60
Building a JPEG file uploader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42
Measuring the virtual desktop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
Using the system tray and dock icons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
Creating and working with a local SQL database. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .75
Compressing files and data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .80
7

Chapter 34: Building the quick-start


sample applications with Flex
The sample applications contained in these quick-start topics are some of the most important or popular aspects of
specific Adobe® AIR™ features.

Set up, download, and install


1 Set up your development environment so that you can work with the sample code and compile and test the appli-
cations.
2 Download the sample application source files from the online location indicated in each of the quick-start topics.
Note: This is a sample application provided as is, for instructional purposes.
3 Install the sample applications using either of the following techniques (depending on which development
environment you're using).

See also
• “Setting up for Flex Builder” on page 4
• “Setting up for Flex SDK” on page 13

Use the Flex SDK and the command-line tools to install sample applications
1 Unzip the sample application files into a folder on your computer. Create a new folder for each, if you like.
2 With the SDK properly installed and configured (the SDK added to your class path), you're ready to test the
samples.
CLOSE PROCEDURE

Use Flex Builder 3 to install sample applications


1 Start Flex Builder and create a new AIR project (see “Creating AIR projects with Flex Builder” on page 25).
2 Name the project the same name as the sample application's main MXML file (for example, for the sample that
contains TextEditor.mxml, name the project TextEditor).
3 Close Flex Builder.
4 Copy the sample application zip file to the folder containing the new AIR project and then unzip the file,
overwriting the contents of the project.
5 Open Flex Builder and select the project from the Project Navigator.
CLOSE PROCEDURE
ADOBE PRODUCT X.0 8
User Guide

Next steps
Follow the instructions in the quick-start topics to edit, run, and debug the applications.

See also
Flex Builder users:
• Developing AIR applications with Flex Builder
Flex developers using the Flex SDK:
• Compiling an AIR application with the amxmlc compiler
• Compiling an AIR component or library with the acompc compiler
• Debugging using the AIR Debug Launcher
• Exporting an AIR installation file using the AIR Developer Tool
9

Chapter 35: Building a text-file editor


The Text Editor sample application shows a number of features of working with files in Adobe AIR, including the
following:
• Setting up a File object to point to a file path.
• Getting the system-specific path (nativePath) for the file.
• Using the FileMode and FileStream classes to read data from the file.
• Using the FileMode and FileStream classes to write data from the file.
• Using events to handle asynchronous processes.
Note: This is a sample application provided, as is, for instructional purposes.

Testing the application


The TextEditor application is intentionally simple. The intent is to show you the basics of how to work with files in
Adobe AIR.

The installer (AIR) file for this sample application is available in the Samples directory included with the documen-
tation at the AIR pre-release site.
The application is a straightforward, but simple, editor of plain text files. The application uses UTF-8 encoding for
reading and writing all text files.
ADOBE PRODUCT X.0 10
User Guide

Files used to build the application


The application is built from the following source files:

File Description

TextEditor.mxml The main application file in MXML for Flex. Details of the code are discussed in “Understanding the
code” on page 10.

TextEditor-app.xml The AIR application descriptor file

icons/ApolloApp_16.png Sample AIR icon files.

icons/ApolloApp_32.png

icons/ApolloApp_48.png

icons/ApolloApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/TextEditor.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7

Understanding the code


This article does not describe all of the Flex components used in the MXML code for the file. For information on
these, see the Flex 3 Language Reference.

Pointing a File object to a file


The appCompleteHandler() method sets the defaultFile File object to point to a pre-defined path:
defaultFile = File.documentsDirectory.resolvePath("test.txt");

This code sets the File object to point to the test.txt file in the user's documents directory. In Windows, this is the My
Documents directory. In Mac OS, it is the /Users/userName/Documents directory.
The defaultFile file is later passed to Open and Save As pop-up windows, if the user has not yet selected a file path.

Reading a file
The openFile() method contains code that opens a file chooser dialog box. When the user selects a file and clicks
the OK button. The fileOpenSelected() method is called. The method first initiates the file File object to point
to the path specified by the user:
currentFile = event.file;

Next the stream FileStream object is closed (in case it currently has a file open) and the stream FileStream object
are initialized:
if (stream != null)
{
ADOBE PRODUCT X.0 11
User Guide

stream.close();
}
stream = new FileStream();
stream.openAsync(currentFile, FileMode.READ);

Note: The fileMode parameter of the openAsync() method is set to FileMode.READ. This lets you read the file.
Event handlers are set up to respond to the complete and ioError events:
stream.addEventListener(Event.COMPLETE, fileReadHandler);
stream.addEventListener(IOErrorEvent.IO_ERROR, readIOErrorHandler);

AIR begins reading the file asynchronously, and the runtime automatically starts reading the file in and firing events.
Note that you can add these event listeners after calling the openAsync() method, because the runtime will complete
executing this ActionScript code (the block of code that includes the calls to addEventListener() methods) before
responding to any events.
Note: This sample application shows how to read a file asynchronously. You can also read the file synchronously by
calling the open() method when opening the file, rather than calling the openAsync() method. For more information,
see “Working with the file system” on page 100.
If the stream object dispatches an ioError event, the readIOErrorHandler() displays an error message for the
end user.
When the file is read in fully, the stream object dispatches the complete event, and the fileReadHandler())
method reads and processes the file.
The readUTFBytes() method of the stream object returns a string by reading UTF-8 characters from specified
number of bytes. Since the stream object just dispatched complete event, the bytesAvailable property represents
the total length of the file, and it is passed as the length property of the call to the readUTFBytes() method:
var str:String = stream.readUTFBytes(stream.bytesAvailable);
The following code replaces the line ending characters from the file with the "\n" newline character, which is used in
a TextField object in a SWF file. It then assigns the string to the text property of the Text control:
var lineEndPattern:RegExp = new RegExp(File.lineEnding, "g");
str = str.replace(lineEndPattern, "\n");
mainTextField.text = str;

Writing data to a file


The saveFile() contains code that writes the text to the file.
First, the method checks to see if the main File object is set:
if (currentFile)

The currentFile object is undefined when the user first opens the application, and when the user clicks the New
button. If there is no currentFile defined, then the saveAs() method is called (in the else block), which lets the
user select a file path for saving the file.
Next the stream FileStream object is closed (if it is currently open) and then it is initialized:
if (stream != null)
{
stream.close();
}
stream = new FileStream();
stream.openAsync(currentFile, FileMode.WRITE);
ADOBE PRODUCT X.0 12
User Guide

Note that the mode parameter of the openAsync() method is set to FileMode.WRITE. This lets you write to the file.
An event handlers is set up to respond to the any ioError events:
stream.addEventListener(IOErrorEvent.IO_ERROR, writeIOErrorHandler);

The following code replaces the "\n" newline character, which is used in a TextField object in a SWF file, with the
line ending character used in text files in the file system (File.lineEnding) It then assigns the string to the text
property of the Text control:
var str:String = mainTextField.text;
str = str.replace(/\r/g, "\n");
str = str.replace(/\n/g, File.lineEnding);

The writeUTFBytes() method of the stream object writes a string to the file in UTF-8 format and then closes the
file:
stream.writeUTFBytes(str);
stream.close();

If the stream object dispatches an ioError event, the writeIOErrorHandler() displays an error message to the
end user.
13

Chapter 36: Building a directory search


application
The File Search sample application shows a number of features of working with files in Adobe AIR, including the
following:
• Reading files asynchronously so that other ActionScript processes can take place as file data is read
• Getting the file extension from filenames
• Using the platform-specific nativePath property of a File object
Note: This is a sample application provided, as is, for instructional purposes.

Testing the application


The File Search application lets you search a directory—and its subdirectories—for files that have names matching
the search pattern you specify:
ADOBE PRODUCT X.0 14
User Guide

Files used to build the application


The application includes the following source files:

File Description

FileSearch.mxml The main application file in MXML for Flex. Details of the code are discussed in “Understanding the code”
on page 14.

FileSearch-app.xml The Apollo application descriptor file

icons/ApolloApp_16.png Sample Apollo icon files.

icons/ApolloApp_32.png

icons/ApolloApp_48.png

icons/ApolloApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/FileSearch.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7

Understanding the code


This topic does not describe all of the Flex components used in the MXML code for the file. For information on
these, see the Flex documentation.

Setting the root directory to search


The init() method sets the text value of the folderPath TextInput component to a pre-defined path (the user's
documents directory):
folderPath.text=File.documentsDirectory.nativePath;

The File.documentsDirectory is a File object pointing to the user's documents directory (such as My Documents
on Windows), and the nativePath property is a string value of the platform-specific path to the directory. For
example, on Windows, this path could be:
C:\Documents and Settings\userName\My Documents

Whereas on Mac OS, it could be:


/Users/userName/Documents

The user can edit this value (in the TextArea component), and when the user clicks the Search button, the search()
method checks to see if the path is valid, and displays an error message if it is not:
var dir:File = new File(folderPath.text);
if (!dir.isDirectory)
{
Alert.show("Invalid directory path.", "Error");
}
ADOBE PRODUCT X.0 15
User Guide

Notice the difference between the nativePath property and the url property of a File, listed here for the same
directory File object:

trace(directory.nativePath); // C:\Documents and Settings\swartz\My Documents


trace(directory.url); // file:///C:/Documents%20and%20Settings/swartz/My%20Documents

Searching the directory for matching files


The main search process lists directory contents asynchronously, one at a time. By doing this, other ActionScript-
based processes (such as the progress bar animation and result listing) can take place as Apollo gets directory listing
information (asynchronously) from the file system.
The search() method sets up event listeners for the dirListing event, which is dispatched when the getDirec-
toryListingAsync() process has completed:

dir.addEventListener(FileListEvent.DIRECTORY_LISTING, dirListed);
dir.getDirectoryListingAsync();

The dirListed() method processes the list of files in the current directory. This list is represented as the files
property of the dirListing event. The method sets a currentNodes variable to this array:
currentNodes = event.files;

The dirListed() method iterates through each member of this array, to see if it is a directory. If it is a directory, the
object is added to a list of current subdirectories (of the current directory being examined).
node = currentNodes[i];
if (node.isDirectory)
{
currentSubdirectories.push(currentNodes[i]);
}

After the iteration is completed, the members of this array are added to a master array of directories to be searched,
named directoryStack:
for (i = currentSubdirectories.length - 1; i > -1; i--)
{
directoryStack.push(currentSubdirectories[i]);
}

At this point, now that processing of the current directory is complete, the application can call another asynchronous
listing of the next directory in the stack (if there is one):
var dir:File = directoryStack.pop();
if (dir == null) {
progress.visible = false;
// There are no further directories to search. The search is completed.
} else {
dir.addEventListener(FileListEvent.DIRECTORY_LISTING, dirListed);
dir.listDirectoryAsync();
}

Displaying search results


As the dirListed() method iterates through each member of this array of contents of the current directory, it
checks if the its name matches the Search pattern, and if so, it creates an object that is added to the ArrayCollection
that is a data provider for the resultsGrid DataGrid component:
ADOBE PRODUCT X.0 16
User Guide

if (node.name.search(pattern) > -1)


{
if (node.isDirectory)
{
fileExtension= "";
}
else
{
fileExtension = node.name.substr(node.name.lastIndexOf(".") + 1);
}
var newData:Object = {name:node.name,
path:node.nativePath,
type:fileExtension}
resultData.addItem(newData);
}

Note: The file extension, represented by the fileExtension variable, is extracted from the filename by using the
substr() and lastIndexOf() methods of the name property of the File object. (The file extension is the string portion
of the filename that follows the last occurence of the "." character in the filename.)
17

Chapter 37: Reading and writing from an


XML preferences file
This topic demonstrates the following Adobe AIR features:
• Reading and writing to a text file
• Specifying a file in the Adobe AIR application storage directory
• Modifying the position, size, and visibility of an AIR application window
• Responding to the closing event dispatched by a Window object
Note: This is a sample application provided, as is, for instructional purposes.

Testing the application


The application is intentionally simple. It reads and writes data related to the position and size of the application, as
well as the date saved. It also positions the window according to that data when the application opens:

The preferences are stored in a text file contains XML data. The application converts that data to an XML object,
upon reading, and converts the XML data to text upon writing.
1 Open the application.
2 Resize and reposition the window.
3 Click the Save Preferences button.
The application writes the new data to the XML file.
4 Restart the application to see your preferences take effect.
ADOBE PRODUCT X.0 18
User Guide

Files used to build the application


The application is built from the following source files:

File Description

PrefsXMLDemo.mxml The main application file in MXML for Flex. Details of the code are discussed in “Understanding the code”
on page 18.

PrefsXMLDemo-app.xml The AIR application descriptor file

icons/ApolloApp_16.png Sample AIR icon files.

icons/ApolloApp_32.png

icons/ApolloApp_48.png

icons/ApolloApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/PrefsXMLDemo.zip.
For help on building this quick start sample application,

See also
• “Building the quick-start sample applications with Flex” on page 7.

Understanding the code


The following sections discuss how the code related to AIR works in the file.
This section does not describe all of the Flex components used in the MXML code for the file. For information on
these, see the Flex 3 Language Reference.

Reading data from the XML file


The appCompleteHandler() method initializes the prefsFile File object to point to a pre-defined path and then
call the readXML() method, which reads the data:
prefsFile = File.applicationStorageDirectory;
prefsFile = prefsFile.resolvePath("preferences.xml");
readXML();

File.applicationStorageDirectory points to the AIR application store directory, which is uniquely defined for
each AIR application.
The appCompleteHandler() method also sets up an event handler to respond to the window closing (which saves
the preferences data to the file):
stage.nativeWindow.addEventListener(Event.CLOSING, windowClosingHandler);

The readXML() method sets up a File object and a FileStream object. The fileMode parameter of the call to the
open() method is set to FileMode.READ, so that the FileStream object can read data from the file.

stream = new FileStream();


ADOBE PRODUCT X.0 19
User Guide

stream.open(prefsFile, FileMode.READ);

The open() method of the stream object opens the file synchronously and begins reading data into the read buffer.
The processXMLData() event method processes the XML data and closes the file. The bytesAvailable property
of the FileStream object is the number of bytes in the read buffer, which is all of the bytes from the file (since the file
is read synchronously):
prefsXML = XML(stream.readUTFBytes(stream.bytesAvailable));
stream.close();

Repositioning and resizing the application window


In the application.xml file, which defines properties of the application, the visible attribute of the initialWindow
property is set to false. The window is resized and repositioned before the window is made visible.
The window property of the Stage object contains properties of the AIR window.
The processXMLData() method resizes and repositions the window based on data in the XML preferences object
(which was just read in from the preferences file):
stage.nativeWindow.x = prefsXML.windowState.@x;
stage.nativeWindow.y = prefsXML.windowState.@y;
stage.nativeWindow.width = prefsXML.windowState.@width;
stage.nativeWindow.height = prefsXML.windowState.@height;

Note: This code sample uses E4X notation, which was introduced in ActionScript 3.0. For example,
prefsXML.windowState.@x is the value of the x attribute of the windowState property of the prefsXML XML object.
For more information, see the Working with XML (http://livedocs.adobe.com/flex/2/docs/00001910.html#118717)
chapter of the Programming ActionScript 3.0 book.
The readXML() method makes the window visible after the processXMLData() method returns:
stage.nativeWindow.visible = true;

Writing XML data to the file


The writeXMLData() converts the XML data to a string, adds the XML declaration to the beginning of the string,
and replaces line ending characters with the platform-specific line ending character (represented by the AIR
File.lineEnding constant):

var outputString:String = '<?xml version="1.0" encoding="utf-8"?>\n';


outputString += prefsXML.toXMLString();
outputString = outputString.replace(/\n/g, File.lineEnding);

It then sets up and uses a FileStream object for writing the data. Note that FileMode.WRITE is specified as the
fileMode parameter in the open() method. This specifies that the FileStream object will be able to write to the file
(and will truncate any existing data before writing):
stream = new FileStream();
stream.open(prefsFile, FileMode.WRITE);

Next, the writeUTFBytes() method is called, which writes the string version of the XML data to the file (as UTF-
8 data):
stream.writeUTFBytes(outputString);
ADOBE PRODUCT X.0 20
User Guide

Since this file was opened for synchronous operations (using the open() method) and the write method is included
within the event handler for the Window object's closing event, the file writing will complete before the window
(and application) actually close. This application uses synchronous read and write operations because the XML
preferences file is relatively small. If you were to want to write a file asynchronously, you would want to cancel the
closing event, and explicitly close the application by calling the Shell.shell.exit()) method in an event
handler for the outputProgress event dispatched by the FileStream object.
21

Chapter 38: Interacting with a window


The PixelPerfect sample application performs basic tasks on window objects. These tasks include creating, resizing,
moving, and closing windows.

Using the PixelPerfect AIR application


The PixelPerfect sample application creates a transparent pixel ruler on the operating system desktop. The pixel ruler
is represented by a window that can be moved, resized, and changed with respect to display state.

Note: This is a sample application provided, as is, for instructional purposes.


The PixelPerfect application demonstrates how you can use the AIR window API to:
• Open multiple windows
• Obtain a reference to a window.
• Use mouse events to trigger operations on a window.
• Use the startResize() method of the NativeWindow class to resize a window.
• Use the constants of the NativeWindowResize class to specify how a window resize operation is completed.
• Use the startMove() method of the NativeWindow class to move a window.
• Use the close() method of the NativeWindow class to close a window.
• Use full screen mode
You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/PixelPerfect.zip

See also
• “Understanding the code” on page 22
• “Building the quick-start sample applications with Flex” on page 7
ADOBE PRODUCT X.0 22
User Guide

Create the PixelPerfect project as a Flex application


The application includes the following source files:
PixelPerfect.as An ActionScript class that launches a new ruler window and then exits.
Ruler.as The ruler window class. Extends the NativeWindow class. Located in package: com.adobe.pixelperfect.
PixelPerfect-app.xml The AIR application descriptor file.
icons/AIRApp_128.png Sample AIR icon file.

Install the source files in Flex Builder


1 Open Flex Builder.
2 Select File > New, and then select AIR Project.
3 Enter PixelPerfect as the Project Name. Click Next.
4 Leave the Use Default Location and Use Default SDK options selected. Click Next.
5 Change the Main application file to PixelPerfect.as on the Set The Build Paths For The New AIR Project
page,. Leave the other settings as is. Click next to advance to the Application XML Properties page.
6 Fill in the Name, Description, and Copyright text boxes, as desired and click Finish. Your project is created.
7 Copy the source files into the project folder (maintaining the package folder structure).
CLOSE PROCEDURE

Install the application


1 Double-click the PixelPerfect.air file.
2 Click Continue. AIR installs the application.
CLOSE PROCEDURE

Run the application


• In Windows, double-click the desktop shortcut for the application or select the file from the Start menu.
• In Mac OS, double-click the PixelPerfect application icon, which is installed in the Applications subdirectory of
the user directory by default.
CLOSE PROCEDURE

Understanding the code


PixelPerfect presents a simple user interface that lets users access the NativeWindow API to modify application
windows based on user input. PixelPerfect supports the following user interactions:
Context menu Right-click on the window to open a menu of window commands.
Drag corners or sides to resize The mouseDown event triggers application logic to run that calculates the mouse position and, if
appropriate, makes a call to the NativeWindow.startResize() method.

Drag the middle to move The mouseDown event triggers application logic to run that calculates the mouse position and, if appro-
priate, makes a call to the NativeWindow.startMove() method.
ADOBE PRODUCT X.0 23
User Guide

Arrow keys move one pixel at a time The keyDown event triggers application logic that detects if an arrow key was pressed. If it was,
the NativeWindow.x or NativeWindow.y property is either increased or decreased by 1, depending on whether the arrow that was
pressed was the DOWN arrow key, the UP arrow key, the LEFT arrow key, or the RIGHT arrow key. This process occurs each time the user presses
an arrow key.

Shift + arrow keys resize one pixel at a time The keyDown event triggers application logic that detects if the SHIFT key was
pressed in combination with an arrow key. If such a key combination was pressed, the NativeWindow.width or Window.height
property is either increased or decreased by 1, depending on whether the arrow that was pressed was the DOWN arrow key, the UP arrow key,
the LEFT arrow key, or the RIGHT arrow key. This process occurs each time the user presses the SHIFT key in combination with an arrow key.

Mouse wheel changes opacity The event listener that was created to detect activity from the mouse wheel makes a call to an event
handler that determines the extent and direction in which the mouse wheel is turned. These values are used to modify the alpha property,
which affects the level of transparency that is applied to the background color.

Double-click to quit The doubleClick event triggers application logic that makes an explicit call to the Window.close()
method.

Press n to create a new window The keyDown event triggers application logic that detects if the N key was pressed. If so, a new
instance of the Ruler class (which extends NativeWindow) is created, displaying a new ruler window.

Creating a Window
To create a window, PixelPerfect instantiates a new Ruler object. The Ruler class extends the NativeWindow class.
In the Ruler constructor, the NativeWindowInitOptions object is created and passed to the NativeWindow super
class constructor:
public function Ruler(width:uint = 300,
height:uint = 300,
x:uint = 50,
y:uint = 50,
alpha:Number = .4){
var winArgs:NativeWindowInitOptions = new NativeWindowInitOptions();
winArgs.systemChrome = NativeWindowSystemChrome.NONE;
winArgs.transparent = true;
super(false, winArgs);

//...

The rest of the constructor initializes the properties of the new window and its members.

Resizing the application window


The startResize() method receives a constant value defined by the NativeWindowResize class that indicates the
edge or corner of the window from which the user is dragging the window to resize it. The startResize() method
triggers a system-controlled resizing of the window.
The application also explicitly sets the window bounds in response to keyboard events. In this case, no resize events
are involved. The following code snippet sets the window bounds inside a switch statement:
switch (e.keyCode)
{
case Keyboard.DOWN:
if (e.shiftKey)
height += 1;
else
y += 1;
drawTicks();
break;
case Keyboard.UP:
if (e.shiftKey)
height -= 1;
ADOBE PRODUCT X.0 24
User Guide

else
y -= 1;
drawTicks();
break;
case Keyboard.RIGHT:
if (e.shiftKey)
width += 1;
else
x += 1;
drawTicks();
break;
case Keyboard.LEFT:
if (e.shiftKey)
width -= 1;
else
x -= 1;
drawTicks();
break;
case 78:
createNewRuler();
break;
}

This code snippet also shows the key events used to move the window and to create new windows.

Moving the application window


The startMove() method is used to begin a system-controlled move of the window. The PixelPerfect sample appli-
cation uses this method when the mouse device controls the move.
When the arrow keys control the parameters of the move, PixelPerfect changes the window bounds, one pixel at a
time, by incrementing or decrementing the x or y property of the window. (The code is included in the switch
statement in the preceding code snippet.)

Changing the transparency of the application window


The visible background of the window is a Sprite object that was added to the ruler stage. The application changes
the transparency of the background by adjusting the alpha property of the background sprite in response to mouse
wheel events:
private function onMouseWheel(e:MouseEvent):void
{
var delta:int = (e.delta < 0) ? -1 : 1;
if (sprite.alpha >= .1 || e.delta > 0)
sprite.alpha += (delta / 50);
}

For a window to be transparent against the desktop, the window must be created with transparency enabled by
setting transparent property to true in the NativeWindowsInitOptions object passed to the window
constructor. The transparency state of a window cannot be changed once the window is created.

Closing the application window


When a doubleclick event is dispatched, PixelPerfect calls the close() method of the window.
private function onDoubleClick(e:Event):void
{
close();
ADOBE PRODUCT X.0 25
User Guide

When a windows's close() method is called, the NativeWindow object does not dispatch a closing event. To allow
cancellation of the close operation, dispatch the closing event in your event handler:
private function onDoubleClick(e:Event):void
var closing:Event = new Event(Event.CLOSING,true,true);
dispatchEvent(closing);
if(!closing.isDefaultPrevented()){
close();
}
}

A close event is dispatched after the window is closed, signaling the completion of the close process. After a window
is closed, it cannot be reopened. Closing the window frees the resources associated with the window (unless they are
referenced elsewhere) so that they can be garbage collected. The application is responsible for closing its windows so
that the associated resources can be freed. When the last window of an application is closed, the application also exits.
This default behavior can be changed by setting the Shell object autoExit property to false.
For more information about the methods and properties of the AIR windowing API, see the AIR ActionScript 3.0
Reference.

See also
• “Working with windows” on page 69
26

Chapter 39: Creating a transparent


window application
The WeatherStation sample application illustrates the following Adobe AIR features:
• A window with a transparent background and a non-rectangular border
• Window control buttons for minimizing and closing the window
• Moving the window by responding to the "move" event
The WeatherStation sample also shows how some powerful Flash Player features can be used within an Adobe AIR
application, including:
• Calling an HTTP service to retrieve weather forecast data
• Storing and retrieving application settings using a SharedObject
Note: This is a sample application provided, as is, for instructional purposes.

About transparent window applications


Adobe AIR supports both opaque and transparent application windows. Transparent windows let the operating
system's desktop show through, while opaque windows obscure the desktop area behind them.
An AIR window can use the borders, title bar, menu bar, and window control buttons (known collectively as system
chrome) that are standard for the operating system. Your application uses the standard system chrome elements when
the <systemChrome> element in the application descriptor file is set to standard (the default setting). A window
that uses system chrome is always opaque.
The Flex WindowedApplication component provides an alternate set of window chrome elements and a rectangular
window frame. The WindowedApplication component's window is always opaque as well.
If you want your application to be transparent, it should not use system chrome or the WindowedApplication
component. This means that the application must provide its own mechanisms for controlling the window and its
background.

The WeatherStation example application


The WeatherStation application window is partially transparent and it does not use system chrome, so it displays
rounded and irregular borders instead of the usual rectangular frame. It also displays images that extend beyond the
visual border of the application.
The application queries weather forecast data from the Yahoo! Weather service based on a U.S. ZIP code. Data is
returned from the service as XML and the application then parses, formats and displays the data. The following
graphic shows the application in action:
ADOBE PRODUCT X.0 27
User Guide

Files used to build the application


The application includes the following source files:

File Description

WeatherStation.mxml The main application file in MXML for Flex 2. Details of the code are discussed in the Under-
standing the code section.

WeatherServices.mxml Defines the HTTPService call and its parameters.

WeatherStation-app.xml The application descriptor file.

assets/close_icon.png Images used for the custom window chrome buttons.

assets/minimize_icon.png

icons/AIRApp_16.png Sample icon files.

icons/AIRApp_32.png

icons/AIRApp_48.png

icons/AIRApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/WeatherStation.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7

Understanding the code


The WeatherStation application performs other interesting functions such as communicating with a remote HTTP
service, parsing an XML result using the new ActionScript 3.0 E4X syntax, and storing and retrieving settings data
using a SharedObject. For more information about these functions, see Programming ActionScript 3.0 and the
ActionScript 3.0 Language Reference.
This topic does not describe all of the Flex components used in the MXML code for this application. For more infor-
mation on the Flex components, see the Flex 2.0 Language Reference.
ADOBE PRODUCT X.0 28
User Guide

Setting the window transparency


The transparency of the application window is controlled in two places.
First, the application descriptor file for the application contains two elements that affect window transparency: the
<systemChrome> element and the <transparent> element. The <transparent> element must be set to true. The
<systemChrome> element should be set to none or else the standard system window title bar and buttons will be
used, and the application background will be opaque.
The following example shows these elements within the <initialWindow> element of the WeatherStation appli-
cation descriptor file:
<initialWindow>
<title>WeatherStation</title>
<content>WeatherStation.swf</content>

<systemChrome>none</systemChrome>
<transparent>true</transparent>

<!-- additional settings... -->


</initialWindow>

Second, the CSS styles that are applied to the Flex 2 Application component can affect how that component's
background is displayed and whether it is opaque or transparent. To make sure the background is transparent, the
WeatherStation.mxml file includes the following CSS style declaration:
<mx:Style>
Application
{
background-color:"";
background-image:"";
padding: 0px;
}

/* additional style declarations... */


</mx:Style>

Moving the application window


A user can move the WeatherStation window around on the desktop by clicking anywhere in the background of the
window, holding the mouse down, and dragging the window to another location. To trigger the window-moving
process, the application monitors and responds to the MouseEvent.MOUSE_DOWN event.
First, the Application declaration specifies that the initApp() method will handle the creationComplete event.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
usePreloader="false"
creationComplete="initApp()"
layout="absolute"
width="250"
paddingRight="0"
paddingLeft="0">

After the application is loaded, the initApp() method sets up mouseDown event listeners on the two visible VBox
components. When the user presses the mouse button while the cursor is over one of these VBox components, the
onMouseDown() method is loaded.

// adds mouseDown listeners so a click and drag on the background or the


ADOBE PRODUCT X.0 29
User Guide

// display area lets you move the window

this.bgBox.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
this.tempBox.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);

The onMouseDown() method shows how simple it can be to start the window moving sequence:
private function onMouseDown(evt:MouseEvent):void
{
stage.nativeWindow.startMove();
}

Calling the NativeWindow.startMove() method results in the window moving around the desktop in response to
mouse movements until the mouse button is released. You don't need to write additional code to control the
window moving and stopping sequence; it is handled automatically.

Minimizing and closing the application window


The WeatherStation application uses small Button components to let the user minimize or close the application.
Each button's click event is set to trigger an appropriate method, as follows:
<mx:Button id="minimizeBtn"
icon="@Embed('assets/minimize_icon.png')"
width="16"
height="16"
click="onMinimize(event)" />

<mx:Button id="closeBtn"
icon="@Embed('assets/close_icon.png')"
width="16"
height="16"
click="onClose(event)" />

When the minimize button is clicked, the onMinimize() method calls the NativeWindow.minimize() method:
private function onMinimize(evt:MouseEvent):void
{
stage.nativeWindow.minimize();
}

When the close button is clicked, the onClose() method calls the NativeWindow.close() method:
private function onClose(evt:MouseEvent):void
{
stage.nativeWindow.close();
}

The NativeWindow.close() method terminates the application. It does so asynchronously, and a close event is
started when the application is about to stop. The application can listen for the close event and perform various
clean-up or housekeeping functions before the application stops.

Casting a shadow on the desktop


The WeatherStation window appears to cast a shadow on the desktop as if it were floating above it. In fact, the drop
shadow is not applied to the window itself. It is applied to the bgBox VBox component that's used as a background
element, and it casts a shadow on the applications window's transparent background. This makes it seem like the
shadow is falling on the desktop instead.
ADOBE PRODUCT X.0 30
User Guide

To achieve this effect, the application first defines an instance of the flash.filters.DropShadowFilter class.
public var shadowFilter:DropShadowFilter;

The initApp() method then sets the parameters of the filter and applies the filter to the bgBox component and a
number of other components, as follows:
// creates a generic drop shadow to use on components that don't accept CSS shadow styles
shadowFilter = new DropShadowFilter();
shadowFilter.color = 0x000000;
shadowFilter.alpha = 0.4;
shadowFilter.blurX = 5;
shadowFilter.blurY = 5;
shadowFilter.distance = 5;

// 'external' shadows
addShadow(this.bgBox);
addShadow(this.largeImage);

// 'internal' shadows
addShadow(this.locationTxt);
addShadow(this.tempTxt);
addShadow(this.conditionTxt);
addShadow(this.additionalTxt);

Because the largeImage Image object extends beyond the boundaries of the bgBox component's background, it
also needs a shadow to make the illusion complete. The other display components are only given shadows because
they seem to stand out better that way.
The same DropShadowFilter instance is applied to each of the components in the addShadow() method:
/**
* Adds a standard drop shadow to a display object.
*/

public function addShadow(comp:DisplayObject):void


{
comp.filters = [this.shadowFilter];
}

Each component is passed as an argument to this method and treated as a DisplayObject instance (so the same
method could be used for DisplayObject instances that are not Flex UIComponent instances too). Each object's
filters array is then set to an array containing the one shadowFilter object.
31

Chapter 40: Launching native windows


The Window Launcher sample application shows a number of features for opening native windows in Adobe AIR,
including the following:
• Setting the window type.
• Setting the window chrome
• Turning on transparency
• Setting the window title
• Setting the window position and size
• Setting the whether a window is resizable, minimizable, and maximizable
Note: This is a sample application provided, as is, for instructional purposes.

Installing and testing the application


The WindowLauncher application is intentionally simple. The intent is to show you the basics of how to open new
windows in AIR.

The Window Launcher sample application on Microsoft Windows, flanked by four different kinds of native windows.
ADOBE PRODUCT X.0 32
User Guide

Files used to build the application


The application is built from the following source files:

File Description

WindowLauncher.mxml The main application file in MXML for Flex. Details of the code are discussed in “Understanding the
code” on page 32.

WindowLauncher-app.xml The AIR application descriptor file.

icons/AIRApp_16.png Sample AIR icon files.

icons/AIRApp_32.png

icons/AIRApp_48.png

icons/AIRApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/WindowLauncher.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7

Understanding the code


This article does not describe the Flex components used in the MXML code for the file. For information on these,
see the Flex 2.0 Language Reference.

Setting window initialization options


Before a new native window can be created, you must first create a NativeWindowInitOptions object and set its
properties. These properties cannot be changed after the window is created. The following lines in the
createNewWindow() method of the sample application create the NativeWindowInitOptions object and set its
properties based on the MXML controls of the launcher window:
var options:NativeWindowInitOptions =
new NativeWindowInitOptions();

options.maximizable = maximizableOption.selected;
options.minimizable = minimizableOption.selected;
options.resizable = resizableOption.selected;
options.transparent = transparentOption.selected;
options.systemChrome = Chrome.selectedItem.optionString;
options.type = windowType.selectedItem.optionString;

Note: Transparency is not supported for windows that use system chrome. Likewise, system chrome can not be used for
lightweight windows. These rules are enforced in Window Launcher by validating the settings as they are made.
ADOBE PRODUCT X.0 33
User Guide

Creating the window


Create a native window with the NativeWindow class constructor. Pass the NativeWindowInitOptions object created
earlier to the window constructor. The following lines from the createNewWindow() method create the new
window and set the properties based on the MXML controls of the launcher window:
var newWindow:NativeWindow = new NativeWindow(options);

newWindow.title = titleString.text;
newWindow.alwaysInFront = alwaysInFrontOption.selected;
newWindow.x = Number(xPosition.text);
newWindow.y = Number(yPosition.text);
newWindow.width = Number(widthValue.text);
newWindow.height = Number(heightValue.text);

These properties of the window can be changed at any time before a window is closed.
The width and height properties set the outer dimensions of the window, including the size of any system chrome.
Thus a window without system chrome has a smaller client area than a window with chrome.

Adding content and setting stage properties


A native window is created with a stage that represents the client (drawable) area of the window. This stage is the root
container of the display tree. Add content to the client area of a native window by adding a DisplayList object (or an
object that inherits from DisplayList) to the stage or another DisplayObject already on the stage with the
addChild() method. The stagescaleMode and align properties determine how the stage (and any objects on it)
behave when the window is resized.
The stageScaleMode property sets how the stage scales and clips when a window is resized:
noScale The stage is not scaled. The size of the stage changes directly with the bounds of the window. Objects may
be clipped if the window is resized smaller.
showAll The stage scales while maintaining the original aspect ratio. Objects in the original view area are never
clipped. Padding is added to the stage area when the window is resized to a different aspect ratio.
noBorder The stage scales while maintaining the original aspect ratio. Objects in the original view are clipped if the
window is resized to a different aspect ratio.
exactFit The stage scales without regard to the original aspect ratio. Objects are distorted when the window is
resized to a different aspect ratio.
The stage align property sets the edge or corner to which the stage origin is anchored when the window is resized.
For example, if you set the stage align property to bottomRight, when the window is resized larger from the bottom,
right corner, the stage origin also moves down and to the right in relation to the window. To then place an object in
the top, left corner, you use negative coordinates. The stage origin always starts in the top, left-hand corner of the
window no matter which alignment value is chosen.
The following lines from the createNewWindow() method of the sample application set the stage properties based
on the MXML control settings and draws some example content into the window by creating a Sprite and calling its
graphics methods to draw a grid of rectangles.
newWindow.stage.align = stageAlignment.selectedItem.optionString;
newWindow.stage.scaleMode = ScaleMode.selectedItem.optionString;
var client:Sprite = new Sprite();
var rectSize:int = 40;
var rectSpace:int = 4;
with(client.graphics){
ADOBE PRODUCT X.0 34
User Guide

lineStyle(1,0,1);
beginFill(0x234578,.5);
for(var i:int = 0; i <= Math.floor(newWindow.stage.stageWidth/rectSize); i++){
for (var j:int = 0; j <= Math.floor(newWindow.stage.stageHeight/rectSize); j++){
drawRoundRect(i*rectSize,j*rectSize,
rectSize-rectSpace,rectSize-rectSpace,10,10);
}//j loop
}//i loop
endFill();
}
newWindow.stage.addChild(client);

Adding interactivity and making the window visible


The final lines of the createNewWindow() method add event handlers to the new window. Two event handlers are
added to windows with no chrome to move and close the window.
//Add handlers to move and close the window if there is no chrome.
if(options.systemChrome == NativeWindowSystemChrome.NONE){
newWindow.stage.doubleClickEnabled = true;
newWindow.stage.addEventListener(KeyboardEvent.KEY_DOWN,
function(e:Event):void{e.target.stage.window.close();});
newWindow.stage.addEventListener(MouseEvent.MOUSE_DOWN,
function(e:Event):void{e.target.stage.window.startMove();})
}

After the window has been created, content added, and event handlers attached, the final step is to make the window
visible:
newWindow.visible = true;
35

Chapter 41: Dragging, copying, and


pasting data
The Scrappy sample application shows a number of features of the drag-and-drop and copy-and-paste APIs in Adobe
AIR, including the following:
• Creating a Clipboard object to contain the information or objects to be transferred through drag and drop or the
system clipboard.
• Starting a drag operation.
• Receiving dropped information or objects.
• Rendering the standard data formats using Adobe AIR and Flash objects.
• Writing to the system clipboard.
• Reading from the system clipboard.
Note: This is a sample application provided, as is, for instructional purposes.

Installing and testing the application


The Scrappy application is intentionally simple. The intent is to show you the basics of how to transfer data using
drag and drop and copy and paste in Adobe AIR.

To use Scrappy, drag files, bitmaps, URLs, and text to the window. The application accepts dragged items in any of
the standard formats supported by AIR. You can also paste (v) items into the window, as well as cut (x), copy (c) and
delete (Del) existing items.
ADOBE PRODUCT X.0 36
User Guide

Note: Not all applications post bitmap data to the clipboard in a format supported by AIR.

Files used to build the application


The application is built from the following source files:

File Description

Scrappy.as The main application file in ActionScript. Details of the code are discussed in “Understanding the
code” on page 36.

scraps/Scrap.as The base class for displaying the dragged or pasted information.

scraps/TextScrap.as Extends Scrap to display text.

scraps/BitmapScrap.as Extends Scrap to display images.

scraps/HTMLScrap.as Extends Scrap to display HTML and XML documents.

scraps/FileScrap.as Extends Scrap to load and display files.

AboutWindow.as Opens a window to display the about.html file.

about.html Describes this sample application and provides some items to drag into the main window.

Scrappy-app.xml The AIR application descriptor file (see “The application descriptor file structure” on page 62)

icons/AIRApp_16.png Sample AIR icon files.

icons/AIRApp_32.png

icons/AIRApp_48.png

icons/AIRApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/Scrappy.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7.

Understanding the code


The following sections discuss how the code related to AIR works in this sample application.
This article does not describe all of the ActionScript classes used in the application. For information on these, see
the Flex 3 Language Reference.
Note: This article demonstrates the use of the flash.desktop.DragManager class in an ActionScript project. If you are
building a Flex application, you should use the Flex mx.managers.DragManager class.
ADOBE PRODUCT X.0 37
User Guide

Creating the canvas


Scrappy uses a full-window sprite that acts as the window backdrop and the target for drag-and-drop operations. To
allow a Sprite, or other display object to act as a receiver for a drag gesture, you must listen for the NativeDragEvents
dispatched from that object.
The Scrappy class defines the following members for creating the window, the drop target sprite and the event
listeners:
public var dragTarget:Sprite = new Sprite();
public var about:NativeWindow;

public function Scrappy():void{


super();
addEventListener(Event.ADDED_TO_STAGE, onStaged);
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.stageFocusRect = false;

dragTarget.focusRect = false;
addChild(dragTarget);
}

public function onStaged(event:Event):void{


dragTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER,onDragIn);
dragTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP,onDrop);
dragTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_EXIT,onDragExit);

stage.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);

stage.window.addEventListener(NativeWindowBoundsEvent.RESIZE, onResize);
stage.window.addEventListener(Event.CLOSE,onClose);
}
The important events for the drag target are: nativeDragEnter, which tells you when a drag gesture enters within the
display object area; nativeDragExit, which tells you when a drag gesture leaves the display object area; and native-
DragDrop, which tells you that the drop has occurred on the display object. You can also listen for the native-
DragOver event instead of the nativeDragEnter event if you need to track the position of the mouse over the target.
The target will dispatch nativeDragOver events whenever the mouse moves (as well as on a short interval)
The keyDown event on the stage is used to check for the paste command. The event listener is attached to the stage
because a sprite will only receive keyboard events when it has focus.

Dropping data onto the canvas


To allow a user to drop data onto a target, you must:
1 Check the format of the data.
2 Accept the drag gesture with DragManager.acceptDrop().
3 Handle the nativeDragDrop event.

Checking the dragged data


To check the data being dragged into the target, access the clipboard property of the NativeDragEvent object:
public function onDragIn(event:NativeDragEvent):void{
var transferable:Clipboard = event.clipboard;
if(transferable.hasFormat(ClipboardFormats.BITMAP_FORMAT) ||
ADOBE PRODUCT X.0 38
User Guide

transferable.hasFormat(ClipboardFormats.FILE_LIST_FORMAT) ||
transferable.hasFormat(ClipboardFormats.TEXT_FORMAT) ||
transferable.hasFormat(ClipboardFormats.URL_FORMAT) ||
transferable.hasFormat("SCRAP")){
DragManager.acceptDragDrop(dragTarget);
}
}
The Scrappy application can accept each of the four standard formats. It also defines a custom format, “SCRAP,”
which contains a reference to a Scrap object created within the Scrappy application itself.
In some situations, you might also want to check the allowedActions property of the event object before accepting
the drop.

Accepting the gesture


The call to DragManager.acceptDrop() allows the passed in display object to receive the drop. The DragManager will
change the display cursor to indicate that the drop is possible. If the user releases the mouse button while over the
same display object (and before a nativeDragExit event occurs), then the designated display object will dispatch a
nativeDragDrop event.

Taking the drop


After the drop occurs on an eligible display object, you can access the data through the clipboard property of the
nativeDragDrop event object. You may also set a drag-and-drop action to indicate which of the three actions, copy,
move, or link should be taken. The chosen action is communicated back to the drag initiator object in the native-
DragComplete event.
Scrappy passes the Clipboard object from the nativeDragDrop event to the Scrap class factory method, which creates
new Scrap objects based on the data format and adds the returned display object to the stage.
public function onDrop(event:NativeDragEvent):void{
DragManager.dropAction = "copy";
var scrap:Scrap =
Scrap.createScrap(event.clipboard,false,event.stageX,event.stageY);
addChild(scrap);
scrap.grabFocus();
}

Displaying information with the Scrap class


The scrap package classes act as containers for information dropped or pasted into the Scrappy window. The base
Scrap class extends Sprite to add functions for handling user interaction with scrap objects, including dragging out
of the window, deleting, and copying. The Scrap class also implements a static factory method for creating new scrap
objects.
The scrap subclasses extend Scrap to handle the standard formats of data that can be transferred. To render the data,
these classes add child display objects to the scrap, such as a TextField or an HTMLControl. They also handle adding
data in the correct format to the Clipboard object when a scrap is dragged out of the application. The Scrap
subclasses are:
TextScrap Uses a TextField to display text data.

BitmapScrap Uses a Bitmap object to display bitmap data.

HTMLScrap Uses an HTMLControl to load and display URLs (including file URLs).

FileScrap Loads and displays the file contents, or displays the file name and icon. For the handful of file types
Scrappy understands, the FileScrap constructor loads the data and creates one of other types of Scrap object to render
it.
ADOBE PRODUCT X.0 39
User Guide

Scrap Factory
The scrap factory class, Scrap.createScrap(), takes a Clipboard object, checks the data formats available, and
then creates the appropriate type of scrap object to display the data.
public static function createScrap(data:Clipboard,
makeCopy:Boolean,
placeX:int,
placeY:int):Scrap{

var scrap:Scrap;
if(data.hasFormat("SCRAP")){
scrap = Scrap(data.dataForFormat("SCRAP",ClipboardTransferMode.ORIGINAL_ONLY));
} else if(data.hasFormat(ClipboardFormats.BITMAP_FORMAT)){
scrap = new BitmapScrap(BitmapData(data.getData(
ClipboardFormats.BITMAP_FORMAT)));

} else if(data.hasFormat(ClipboardFormats.TEXT_FORMAT)){
scrap = new TextScrap(String(data.getData(
ClipboardFormats.TEXT_FORMAT)));

} else if(data.hasFormat(ClipboardFormats.FILE_LIST_FORMAT)){
var dropfiles:Array =
data.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
for each (var file:File in dropfiles)
{
scrap = new FileScrap(file);
}

} else if(data.hasFormat(ClipboardFormats.URL_FORMAT)){
scrap = new HTMLScrap(String(data.getData(
ClipboardFormats.URL_FORMAT)));
}

scrap.x = placeX + offset.x;


scrap.y = placeY + offset.y;
return scrap;
}
If the Clipboard object contains the format, “SCRAP”, then the drag must have originated from the Scrappy appli-
cation itself (or from another AIR application that uses the “SCRAP” format name). This can happen if, for example,
the user drags a scrap out of the application window and then back into the window. By setting the transfer mode to
originalPreferred, a reference to the original scrap will be returned when available. This avoids duplicating the scrap
object when it originates in the current application. The originalOnly transfer mode should not be used in this case,
because there is a chance the original object will be deleted in the middle of a cut-and-paste operation.
Note: Copying serialized data is not implemented in this version of Scrappy. Because Scraps are complex objects, they
can not be simply deserialized and used as is. An additional step is required to rebuild a valid Scrap object from the
properties of the anonymous object returned by the deserializer.

Dragging data out of the canvas


To allow a user to drag an object out of an application, you must:
1 Respond to a mouseDown or mouseMove event.
2 Create a Clipboard object containing the data or object to drag.
3 Start the drag operation from the mouseDown or mouseMove event handler by calling
DragManager.doDrag().
ADOBE PRODUCT X.0 40
User Guide

Scrappy uses two separate drag APIs. As long as a drag gesture stays within the bounds of the main window, Scrappy
uses the Sprite drag API, which provides a somewhat smoother operation for positioning sprites. When the drag
gesture leaves the window, Scrappy transitions the drag gesture to the DragManager API, which allows data to be
dragged to other applications.

Preparing to drag
When the Scrap object being moved leaves the window, it dispatches a rollOut event. The handler for this event stops
the Sprite-controlled drag and gets a Clipboard object by calling the function, addTransferableData(). The addTrans-
ferableData() function creates a new Clipboard object and adds a reference to the current Scrap object by using a
custom format name. The addTransferableData function is overridden in the Scrap subclasses to add the data appro-
priate to that subclass in addition to the reference format.
protected function onDragOut(event:Event):void{
parent.removeEventListener(MouseEvent.ROLL_OUT,onDragOut);
this.stopDrag();
offset.x = -mouseX;
offset.y = -mouseY;
var transferObject:Clipboard = addTransferableData();
DragManager.doDrag(this,transferObject,getImage(),offset);
}
protected function addTransferableData():Clipboard{
var transfer:Clipboard = new Clipboard();
transfer.setData("SCRAP",this,true);
return transfer;
}

Starting the drag operation


The call to DragManager.doDrag(), starts the DragManager-controlled part of the drag gesture. The initiator
parameter of the method designates the display object that begun the drag gesture, and, more importantly, the object
that will dispatch the nativeDragComplete event when the user releases the mouse button. The clipboard
parameter is the Clipboard object. Once the doDrag() is called, the object can only be accessed in the event handlers
of the NativeDragEvents.
You can supply an image to display while the drag gesture is in progress. Scrappy creates an image by drawing the
sprite into a BitmapData object. The offset point lets you offset the image from the mouse hotspot.

Completing the drop


When the drop occurs, the object passed to DragManager.doDrag() as the initiator will dispatch a nativeDragCom-
plete event. The action selected by the drop target is reported in the dropAction property of the event. Scrappy only
supports the copy action for external transfers, so no action needs to be taken.

Copying and pasting data


Compared to drag and drop, copy and paste are very simple operations. The static property
Clipboard.generalClipboard represents the operating system clipboard. You can write data directly to the
clipboard with Clipboard.generalClipboard.setData() and you can read data with
Clipboard.generalClipboard.getData() (normally you should check whether the data format is appropriate
before reading the data).
Note: In this Beta release, the traditional key combinations used for copy, cut, and paste commands, Ctrl-c, Ctrl-x, and
Ctrl-v, are handled internally by the runtime and do not generate keyboard events.
ADOBE PRODUCT X.0 41
User Guide

Paste
Scrappy implements a paste command on the main window. In response to a paste command (v), the application
passes the clipboard data to the factory method Scrap.createScrap(), which is also used to create scraps for drop
operations.
public function doPaste():void{
var scrap:Scrap = Scrap.createScrap(Clipboard.generalClipboard,
stage.stageWidth/4,stage.stageHeight/4);
addChild(scrap);
scrap.grabFocus();
}
The pasted scrap is always placed in the same spot on the stage.

Copy
Copy is implemented by the Scrap base class. The function calls the addTransferable() function to get the data from
the Scrap object (this is the same function used for drag-and-drop). You cannot assign a Clipboard object directly to
the Clipboard.generalClipboard property, so you must copy each of the formats individually:
public function doCopy():void{
var transferObject:Clipboard = addTransferableData();
for each( var format:String in Clipboard.generalClipboard.formats){
Clipboard.generalClipboard.setData(format, transferObject.getData(format),true);
}
}

Cut
Cut just copies the scrap object to the clipboard, then deletes it.
42

Chapter 42: Building a JPEG file uploader


The Photo Uploader sample application shows a number of features of working with files in AIR, including the
following:
• Setting up a File object to point to a file path.
• Converting an image to JPEG binary data and writing the data to a file.
• Uploading a files to a server.
Note: This is a sample application provided, as is, for instructional purposes.
This chapter contains the following sections:
“Testing the application” on page 42
“Files used to build the application” on page 43
“Understanding the code” on page 43

Testing the application


The Photo Uploader uses the JPEGEncoder class, which is published at the ActionScript 3.0 Corelib project (located
at http://code.google.com/p/as3corelib).
It also requires you to have a webcam installed, and you must have access to a PHP-enabled web server.
The application lets you capture images from a web cam and upload the captured images (stored as JPEG files) to a
web server.

The installer (AIR) file for this sample application is available in the Samples directory included with the documen-
tation at the AIR pre-release site.
ADOBE PRODUCT X.0 43
User Guide

Before running the application, install the index.php file (included with the source files in the PHP directory) in the
http://localhost/PhotoSyncUpload/ folder of your PHP-enabled web server. Also, make sure that a webcam is
installed on your machine.
To test the application:
1 Install the application (by double-clicking the provided AIR file), and run it.
2 Point your webcam to something that you want to take a picture of.
3 Click the Preview button. Then click the Save button to save the image.
4 Repeat this step to save multiple images.
5 Click the Upload button to upload the files to your web server.

Files used to build the application


The application is built from the following source files:

File Description

PhotoUpload.mxml The main application file in MXML for Flex. Details of the code are discussed in “Understanding the
code” on page 43.

PhotoUpdate-app.xml The AIR application descriptor file

com/adobe/images/JPEGEncoder.as A utility class used by the JPEGEncoder class. This class is also available at the ActionScript 3.0
Corelib project.

com/adobe/images/BitString.as A utility class used by the JPEGEncoder class. This class is also available at the ActionScript 3.0
Corelib project.

com/adobe/net/Uploader.as A class used to upload files to a web server.

php/index.php A file to include in the http://localhost/PhotoSyncUpload/ folder of your PHP-enabled web server.

icons/ApolloApp_16.png Sample AIR icon files.

icons/ApolloApp_32.png

icons/ApolloApp_48.png

icons/ApolloApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/PhotoUpload.zip.

See also
“Building the quick-start sample applications with Flex” on page 7

Understanding the code


This section contains the following topics:
“Previewing the webcam image” on page 44
ADOBE PRODUCT X.0 44
User Guide

“Saving a file” on page 44


“Uploading files to a web server” on page 45
This article does not describe all of the MXML and ActionScript code for the file. For information on these, see the
Flex 3 Language Reference.

Previewing the webcam image


The shoot() method calls the flasher() method which simulates a camera flash by quickly rendering an opaque
white NativeWindow to fill the screen:
private function flasher():void
{
var windowInitOpts:NativeWindowInitOptions = new NativeWindowInitOptions();
windowInitOpts.systemChrome = NativeWindowSystemChrome.NONE;
windowInitOpts.type = NativeWindowType.LIGHTWEIGHT;
var flashCube:NativeWindow = new NativeWindow(windowInitOpts);
flashCube.x = 0;
flashCube.y = 0;
flashCube.width = Capabilities.screenResolutionX;
flashCube.height = Capabilities.screenResolutionY;
flashCube.visible = true;
setTimeout(closeFlash, 100, flashCube);
}
private function closeFlash(flashCube:NativeWindow):void {
flashCube.close()
}

The shoot() method then captures the image in the video VideoDisplay component to a BitmapData object and
writes that BitmapData to the shot Image component (by setting the source property of the Image component):
bmd = new BitmapData(video.width, video.height);
bmd.draw(video);
var bmp:Bitmap = new Bitmap(bmd);
bmp.x = (stage.stageWidth - bmp.width)/2
shot.source = bmp;

Saving a file
The save() method sets up a FileStream object to write the image data to a file. The image data is converted to a
ByteArray containing JPEG-encoded data by calling the encode() method of a JPEGEncoder object:
private function save():void {
file = File.applicationStorageDirectory;
file = file.resolvePath("images/" + new Date().time.toString() + ".jpg");
stream = new FileStream;
stream.open(file, FileMode.WRITE);
var data:ByteArray = getJPEG(bmd);
stream.writeBytes(data, 0, data.length);
stream.close();
}
private function getJPEG(bmd:BitmapData):ByteArray {
var jpg:JPEGEncoder = new JPEGEncoder();
return jpg.encode(bmd);
}

Note that the file object points to a file in the images subdirectory of the application resource directory, and the
name of the file is based on a timestamp provided by a Date object’s time property:
ADOBE PRODUCT X.0 45
User Guide

file = File.applicationStorageDirectory;
file = file.resolvePath("images/" + new Date().time.toString() + ".jpg");

Uploading files to a web server


The Uploader class contains code that uploads files to a web server. You add a file to the list of files to upload by
calling the addFile() method of an Uploader object, passing a File object to the method:
for (var i:uint = 0; i < files.length; i++)
{
file = files[i];
if (!file.isDirectory)
{
uploader.addFile(file);
}
}

You register event listeners for the complete and progress events of the Uploader object and then call its start()
method, which uploads the files to the URL passed as a parameter:
uploader.addEventListener(Event.COMPLETE, uploadCompleteHandler);
uploader.addEventListener(ProgressEvent.PROGRESS, progressHandler);
uploader.start(UPLOAD_URL);

The Uploader object uploads the files one at a time by calling its upload() method, passing the File object as a
parameter:
private function uploadFile(file:File):void
{
var urlRequest:URLRequest = new URLRequest(url);
urlRequest.method = URLRequestMethod.POST;
file.addEventListener(ProgressEvent.PROGRESS, uploadProgress);
file.addEventListener(Event.COMPLETE, uploadComplete);
file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA , uploadServerData);
file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, uploadError);
file.addEventListener(HTTPStatusEvent.HTTP_STATUS, uploadError);
file.addEventListener(IOErrorEvent.IO_ERROR, uploadError);
file.upload(urlRequest, "uploadfile");
}

The upload() method of the File object uploads the file to the given URL (specified as a URLRequest object):
The PHP page you posted on your web server recognizes the string as its cue to accept the file provided as POST
data as a file to be uploaded (and saved on the web server, in an “uploaded” subdirectory.
46

Chapter 43: Adding native menus to an


AIR application
Operating systems provide built-in (or native) facilities for creating menus. The AIR NativeMenu classes provide an
interface for creating and modifying native operating system menus as well as for adding event listeners to handle
menu events.
The AIRMenus sample application illustrates how to create the various kinds of native menus supported by AIR.
Note: This is a sample application provided, as is, for instructional purposes.

About AIR menus


Adobe AIR supports the following types of menus:
Application menus Application menus, only supported on Mac OS X, appear in a global menu bar provided by the
operating system. OS X provides a default menu for each application. Your AIR application can either use the default
menu by adding event listeners to the relevant menu commands, modify the default menu by adding and removing
commands and submenus, or replace the default menu with a new one. (The AIR Menus sample replaces the default
menu.)
Access the application menu through the Shell.shell.menu property. Use the Shell.supportsMenu property to
test whether your application is running in an environment with application menu support.
Window menus Window menus, only supported natively on the Microsoft Windows operating system, appear
below the title bar of a window. No default menu is supplied so you must create a new NativeMenu object to assign
as the window menu.
Access a window menu through the menu property of the window. Use the NativeWindow.supportsMenu property
to test whether your application is running in an environment with window menu support. Window menus can only
be used with windows that have system chrome.
Context menus Context menus can be attached to any InteractiveObject by setting the contextMenu property
of the object to a NativeMenu. No default context menu is provided in AIR.
Dock icon menus On Mac OS X, you can add a menu to the application dock icon. The menu items in the new menu
will appear in addition to the default operating system menu items.
System tray icon menus On Windows, you can add a menu to the system tray icon. No default menu is provided.

Pop-up menus A native menu can be displayed anywhere in an application by calling the menu display() method.

The AIR Menus sample application


The AIR Menus sample application creates a native menu containing File, Edit, and Help submenus and uses it in
each of the supported menu locations. When a menu command is chosen, its label is printed to the window by a
menu event handler.
ADOBE PRODUCT X.0 47
User Guide

The following screenshot shows the application in action on Windows:

Testing the application


Launch the AIR Menus sample. Select a menu item from the application menu bar or the window menu bar. Right-
click or command-click in the window to open a context menu. Click or left-click in the window to open a pop-up
menu. Click and hold the dock icon to open the dock menu. Right-click on the system tray icon to open the system
tray menu. Any time you select a menu command it will be listed on the window.

Files used to build the application


The application includes the following source files:

File Description

AIRMenus.as The main application file. Details of the code are discussed in the Understanding the code section.

AIRMenuIcon.as Extends the Icon class to embed the images used for the dock and system tray icons (which are
also used for the application icon).

AIRMenus-app.xml The application descriptor file. See “Setting application properties” on page 62.

icons/ApolloApp_16.png Sample icon files.

icons/ApolloApp_32.png

icons/ApolloApp_48.png

icons/ApolloApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/AIRMenus.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7
ADOBE PRODUCT X.0 48
User Guide

Understanding the code


For more information about using Flash classes, such as the TextField used by AIRMenus, see Programming Action-
Script 3.0 and the ActionScript 3.0 Language Reference.

Creating a menu
To create a menu, you start with a new NativeMenu object and add commands, submenus and separators to it. The
top, or root, level menu of application and window menus should only contain submenus. Command and separator
items in the root level menu will not be displayed by all operating systems, and even on those that do display them,
such items would be non-standard and likely to confuse users.
The AIR Menus example uses a function to create the root menu, which is called for each type of menu supported
by AIR. The function creates three example submenus, labeled File, Edit, and Help. The NativeMenu objects for these
submenus are, in turn, created by other functions:
private function createRootMenu():NativeMenu{
var menu:NativeMenu = new NativeMenu();
menu.addSubmenu(createFileMenu(),"File");
menu.addSubmenu(createEditMenu(),"Edit");
menu.addSubmenu(createHelpMenu(),"Help");
return menu;
}

The functions which create the submenus, use the addItem() method to add commands and separators. Each menu
object contains an items array. Since all of the events dispatched when a menu is selected are handled by the same
function, the items array is used to attach the listener to all the items.
private function createFileMenu():NativeMenu{
var menu:NativeMenu = new NativeMenu();
menu.addItem(new NativeMenuItem("New"));
menu.addItem(new NativeMenuItem("Open"));
menu.addItem(new NativeMenuItem("Save"));
menu.addItem(new NativeMenuItem("",true));//separator
menu.addItem(new NativeMenuItem("Exit"));

for each (var item:NativeMenuItem in menu.items){


item.addEventListener(Event.SELECT,itemSelected);
}
return menu;
}

Setting application and dock menus


When AIR supports application menus on an operating system, the static Shell.supportsMenu property will be
true. The Mac OS X operating system provides a default application menu. You have the option of using the
provided menu (most of the commands will do nothing unless you add event listeners), perhaps adding new items
and submenus, or replacing the menu entirely. The AIR Menus example takes the second approach.
if(Shell.supportsMenu){
Shell.shell.menu = createRootMenu();
}
ADOBE PRODUCT X.0 49
User Guide

When AIR supports dock icons on an operating system, the static Shell.supportsDockIcon will be true. The
dock icon is represented by the Shell.shell.icon property. The icon object is created automatically. You can
change the appearance of the dock icon by setting the icon.bitmaps property.
Mac OS X provides a default menu for the dock icon. You can add additional items to the dock menu by adding the
items to a NativeMenu object and assigning it to the icon.menu property.
if(Shell.supportsDockIcon){
DockIcon(Shell.shell.icon).menu = createRootMenu();
Shell.shell.icon.bitmaps = new AIRMenuIcon().bitmaps;
}

Setting window and system tray menus


When AIR supports window menus on an operating system, the static NativeWindow.supportsMenu property will
be true. No default window menu is provided by the Windows operating system, so you must assign a new menu
object to the window:
if(NativeWindow.supportsMenu){
stage.nativeWindow.menu = createRootMenu();
}

When AIR supports system tray icons on an operating system, the static Shell.supportsSystemTrayIcon will be
true. The system tray icon is represented by the Shell.shell.icon property. The icon object is created automat-
ically. To show the icon in the notification area of the taskbar, you only need to assign an array containing the icon
image to the icon bitmaps property. To remove the icon from the taskbar, set bitmaps to an empty array.
Add a menu to the system tray icon by assigning a NativeMenu object to the icon.menu property. You must cast the
object to the SystemTrayIcon class to access the menu property.
if(Shell.supportsSystemTrayIcon){
Shell.shell.icon.bitmaps = new AIRMenuIcon().bitmaps;
SystemTrayIcon(Shell.shell.icon).tooltip = "AIR Menus";
SystemTrayIcon(Shell.shell.icon).menu = createRootMenu();
}

Be careful about using SystemTrayIcon properties on the wrong operating system. An Mac OS X, for example, the
Shell.shell.icon object is of type, DockIcon. Attempting to set the tooltip would generate a runtime error.

Setting a context menu


Every InteractiveObject has a contextMenu property. When set, a command-click or right-mouse click on the object
will open the context menu.
textDisplay.contextMenu = createRootMenu();

A context menu can have commands, submenus, and separators in the root menu.

Setting a pop-up menu


Menus can be displayed anywhere over a window by calling the display() method of the NativeMenu object.
private function popUpMenu(event:MouseEvent):void{
popUp.display(stage, event.stageX, event.stageY);
}

A pop-up menu can have commands, submenus, and separators in the root menu.
ADOBE PRODUCT X.0 50
User Guide

Responding to menu events


Menu items dispatch a select event when they are selected by the user. The AIR Menus example uses the same event
handler for every item. To add a event listener, call the addEventListener() method of the item:
item.addEventListener(Event.SELECT,itemSelected);

Select events also bubble up through the menu hierarchy. Each parent menu in the chain will also dispatch a select
event. The target property of the event object will be the NativeMenuItem object of the selected command; the
currentTarget property will be the NativeMenu object of the current menu.

This example responds to each select event by adding a line describing the event to the TextField in the window.
private function itemSelected(event:Event):void{
textDisplay.appendText(selectCount++ + ". Selected item: "
+ event.target.label + "\n");
}

Also available, but not used in this example are displaying events. Displaying events are dispatched by a menu just
before it is displayed. You can use displaying events to update the menu or items within it to reflect the current state
of the application. For example, if your application used a menu to let users open recently viewed documents, you
could update the menu to reflect the current list inside the handler for the displaying event.
51

Chapter 44: Creating toast-style windows


When an application needs to display informational messages without disturbing the user’s workflow, toast-style
windows can be the right solution. The WordUp sample application creates toast windows with the following
attributes:
• Chromeless
• Displayed above other windows
• Does not steal the keyboard or mouse focus
• Expires so the user doesn’t have to close them
• Persists when the user is away from the computer
The WordUp sample illustrates how to use the following AIR APIs:
• NativeWindow — To create and display the toast windows
• Shell — To detect user presence and to use the dock or system tray icons
• Screen — To layout multiple toast windows on the desktop without overlapping
Note: This is a sample application provided, as is, for instructional purposes.

The WordUp sample application


The WordUp sample application runs in the background with no main window. When a timer with a randomized
interval fires, the application displays a toast window with a random message. After about five seconds, the message
expires and the toast window will close automatically.
To detect when the user is present, the application listens for userIdle and userPresent events from the Shell object.
When the userIdle event is dispatched, message expiration is suspended until the next userPresent event. This keeps
the messages on the screen until the user has a chance to read them. A userIdle event is dispatched when no keyboard
or mouse input has been detected within an interval determined by the Shell.idleThreshold property. The
default interval is a reasonable five minutes, but WordUp resets the value to be very short so the change in application
behavior can be more easily observed.
The following screenshot shows the application in action (on Windows):
ADOBE PRODUCT X.0 52
User Guide

Testing the application


Launch the WordUp application. On Windows, a system tray icon will be displayed in the notification area of the
taskbar. On Mac OS X, the normal triangle under the dock icon will show that the application is running. After about
five seconds, the first message will pop-up from the bottom of the screen. It will then disappear after a few moments.
If you leave your computer idle for several seconds, the messages will stay on screen until you make a mouse or
keyboard input, and then will remain for a few seconds longer to give you time to read the messages.
To exit the application, on Windows, right-click on the system tray icon and select “Exit WordUp” from the menu.
On Mac OS X, click the dock icon to activate the application and select “Quit WordUp” from the application menu.

Files used to build the application


The application includes the following source files:

File Description

WordUp.as The main application file. Listens for message events from the MessageCenter and passes the
message to the DisplayManager for display.

WordUpIcon.as Extends the Icon class to embed the icon assets for this application.

WordUp-app.xml The application descriptor file. See “Setting application properties” on page 62.

display/DisplayManager.as Manages the creation, placement, and expiration of message windows.

display/MessageWindow.as Extends the NativeWindow class to create a toast-style window displaying a text message.

messages/MessageCenter.as Dispatches message events. (In WordUp the messages are random text strings and dispatched on
a timer interval.)

messages/MessageEvent.as Extends the Event class to add a message property.

messages/RandomMessageGenerator.as Generates a random text string.

icons/WordUpIcon16.png Sample icon files used both for the desktop application icon and the system tray or dock icon.

icons/WordUpIcon32.png

icons/WordUpIcon48.png

icons/WordUpIcon128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/WordUp.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7

Understanding the code


For more information about non-AIR specific functions and APIs, see Programming ActionScript 3.0 and the Action-
Script 3.0 Language Reference.
ADOBE PRODUCT X.0 53
User Guide

Initializing a windowless application


The first step to creating a windowless application is to make sure that the initial window that is created automatically
by AIR never becomes visible. The visibility of this window is controlled in two places. The first place is within the
<initialWindow> element of the application descriptor file. If <visible>true</visible> is placed within this
element, then the window will be visible on application startup. The second place window visibility is controlled is
the visible property of the window itself. The visible property can be set directly. It is also set to true when the
window activate() method is called.
Note: If you are using the mx:WindowedApplication component from the Flex Framework to define your initial window,
you must also set the component’s visible property to false.
It is often a good idea to give the user some indication that an application is running, even when it has no visible
windows. On Mac OS X, the application dock icon serves this purpose. On Windows, the system tray icon can be
used. The dock icon automatically appears in the dock when the application is running and provides a default menu
for exiting the application so WordUp doesn’t need to change the dock icon properties. The system tray icon will only
be shown if you assign an image. You can also provide a tooltip and a menu.
WordUp detects whether it is running on an operating system that supports system tray icons using the static
Shell.supportsSystemTrayIcon property. If so, it adds a tooltip and a menu containing an exit command:
Shell.shell.icon.bitmaps = icon.bitmaps;
if(Shell.supportsSystemTrayIcon){
var sysTray:SystemTrayIcon = Shell.shell.icon as SystemTrayIcon;
sysTray.tooltip = "WordUp";
sysTray.menu = new NativeMenu();
var exitCommand:NativeMenuItem =
sysTray.menu.addItem(new NativeMenuItem("Exit WordUp"));
exitCommand.addEventListener(Event.SELECT,function(event:Event):void{
Shell.shell.exit();
});
}

To initialize the application logic, WordUp creates a new DisplayManager object, which manages the pop-up toast
windows, and a new MessageCenter object, which periodically generates an event containing a random message:
displayManager = new DisplayManager();
messageCenter = new MessageCenter();
messageCenter.addEventListener(MessageCenter.NEW_MESSAGE, onMessage);

The onMessage() event handler function passes the message text to the display manager, which creates a new
message window.
Since the initial window created by AIR is not needed, ideally it could be closed. However, there is a defect in this
Beta release of AIR that results in the system tray icon disappearing if the initial window is closed.
Shell.shell.autoExit = false;
//stage.nativeWindow.close();
}

Creating a toast-style window


The MessageWindow class extends NativeWindow. The constructor sets the appropriate NativeWindowInitOptions
for the window. The important options are:
options.type = NativeWindowType.LIGHTWEIGHT;
options.systemChrome = NativeWindowSystemChrome.NONE;
options.transparent = true;
ADOBE PRODUCT X.0 54
User Guide

These options define a window with no system chrome and which won’t appear on the Windows taskbar or OS X
windows menu. In addition, the window alwaysInFront property is set to true so that the message will appear
above other windows.
To regulate the life span of a message window, the window listens to custom lifeTick events from the DisplayManager
object:
manager.addEventListener(DisplayManager.LIFE_TICK,lifeTick,false,0,true);

When this event is dispatched, the window decrements its time-to-live counter. When the counter reaches zero, the
window closes itself (removing the event listener to aid garbage collection):
public function lifeTick(event:Event):void{
timeToLive--;
if(timeToLive < 1){
close();
}
}

public override function close():void{


manager.removeEventListener(DisplayManager.LIFE_TICK,lifeTick);
super.close();
}

The MessageWindow class overrides the close() method so that if the message window is closed in response to a
mouse event, the event listener will also be removed.

Placing the message window


WordUp uses the Screen API to find a spot to display the new message. The findSpotForMessage() function searches
for an open area starting with the lower-right-hand corner of the first screen in the Screen.screens array. When
it finds an open spot, it launches the window from the bottom of the screen to its appointed destination.
To check whether a message window is already displayed in a given spot, WordUp loops through the
Shell.shell.openedWindows array. It tests whether the rectangle defined by the area being considered intersects
the rectangle defined by the window bounds:
private function isOccupied(x:int,y:int):Boolean{
var occupied:Boolean = false;
var testRect:Rectangle =
new Rectangle(x,y,MessageWindow.stockWidth,MessageWindow.stockHeight);
for each (var window:NativeWindow in Shell.shell.openedWindows){
occupied = occupied || window.bounds.intersects(testRect);
}
return occupied;
}

Detecting user presence


WordUp uses the Shell userIdle and userPresent events to determine whether the user is actively using the computer.
Shell.shell.idleThreshold = idleTime;
Shell.shell.addEventListener(Event.USER_IDLE,onIdle);
Shell.shell.addEventListener(Event.USER_PRESENT,onPresence);

In response to these events, WordUp suspends or restarts the DisplayManager timer that dispatches lifeTick events
to the message windows:
//When the computer is idle, don't remove the messages
private function onIdle(event:Event):void{
displayManager.pauseExpiration();
trace("Idling.");
ADOBE PRODUCT X.0 55
User Guide

//On return, let windows expire again


private function onPresence(event:Event):void{
displayManager.resumeExpiration();
trace("Resuming.");
}
56

Chapter 45: Controlling the display order


of windows
Adobe AIR provides several methods and properties for controlling the display order of application windows. The
Z sample application illustrates the following AIR features:
• Activating a window
• Pulling a window to the top of the display order
• Pushing a window to the bottom of the display order
• Ordering a window relative to another application window
• Keeping a window in front of other windows
Note: This is a sample application provided, as is, for instructional purposes.

About window depth sorting


AIR allows you to sort windows into two display order groups based on the value of the alwaysInFront property.
Any windows with alwaysInFront = true will be displayed in front of any windows with alwaysInFront=false.
(They will also be displayed in front of other windows of other applications, so some caution should be exercised
before setting this property.)
AIR provides four methods for ordering the display depth of a window relative to other windows:
• orderToFront()
• orderToBack()
• orderInFrontOf(NativeWindow)
• orderInBackOf(NativeWindow)

In addition, the activate() method will also order the window to the front.
Changing the value of the alwaysInFront property may also change the ordering of a window. For example,
changing the value from true to false will order the window behind any other “alwaysInFront” windows, but still
in front of other windows. When you use the orderInFrontOf() or orderInBackOf() methods, the window will
take on the alwaysInFront setting of the target window.
The window ordering methods return a boolean value that indicates whether the ordering operation succeeded. The
only reason an ordering operation may fail is if the window being ordered is not visible.

The Z sample application


The Z application draws three example windows that allow you to experiment with the depth ordering APIs and how
they interact with user manipulation of the windows. On Windows, a fourth window supplies a menu bar which
contains menu commands for the depth ordering methods and properties.
The following screenshot shows the application in action (on Windows):
ADOBE PRODUCT X.0 57
User Guide

Testing the application


Launch the Z application. Use the application or window menu to exercise the native window depth ordering
methods and properties on the three geometric windows.

Files used to build the application


The application includes the following source files:

File Description

Z.as The main application file. Details of the code are discussed in the Understanding the code section.

Z-app.xml The application descriptor file. See “Setting application properties” on page 62.

window/ZWindow.as Extends the NativeWindow class to customize the behavior of the example windows in the Z
application.

window/CircularZWindow.as Extends ZWindow to draw a circular window.

window/RectangularZWindow.as Extends ZWindow to draw a rectangular window.

window/TriangularZWindow.as Extends ZWindow to draw a triangular window.

icons/ZIcon16.png Sample icon files.

icons/ZIcon32.png

icons/ZIcon48.png

icons/ZIcon128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/Z.zip.
ADOBE PRODUCT X.0 58
User Guide

See also
• “Building the quick-start sample applications with Flex” on page 7

Understanding the code


The Z application provides three example windows that can be ordered through menu commands. A base class,
ZWindow, is defined to establish the window behaviors. The subclasses, CircularZWindow, TriangularZWindow,
and RectangularZWindow, extend this base class to draw a distinctive geometric shape.
The ordering commands are dispatched through a native menu. Each window defines a menu through the
makeZOrderMenu() method. The method is called by the main Z application class for each window to add the
window’s menu as a submenu to either the application menu or the main Z window menu (depending on which type
of menu the operating system supports):
private function createWindowMenu():NativeMenu{
var menu:NativeMenu = new NativeMenu();
for each(var zWindow:ZWindow in ZWindow.zWindows){
menu.addSubmenu(zWindow.makeZOrderMenu(), zWindow.title);
}
return menu;
}

The makeZOrderMenu() method returns a NativeMenu object containing the menu commands and submenus.
The first two items in the menu show the window alwaysInFront and visible settings. To make sure the state of
the menu matches the state of the window, the menu item checked property is updated in response to the menu
displaying event. The displaying event is dispatched immediately before the menu is made visible.
var stateAlwaysInFront:NativeMenuItem =
menu.addItem(new NativeMenuItem("Always in front"));
stateAlwaysInFront.addEventListener(Event.SELECT, toggleAlwaysInFront);
var stateVisible:NativeMenuItem = menu.addItem(new NativeMenuItem("Visible"));
stateVisible.addEventListener(Event.SELECT, toggleVisibility);

menu.addEventListener(Event.DISPLAYING, function(event:Event):void{
stateAlwaysInFront.checked = alwaysInFront;
stateVisible.checked = visible;
});

Separator items are created by setting the isSeparator parameter to true in the item constructor.
menu.addItem(new NativeMenuItem("",true));//separator

Command items are added by adding an item with the appropriate label and attaching an event listener that carries
out the command. For example, the following statement adds the activate command:
var commandActivate:NativeMenuItem = menu.addItem(new NativeMenuItem("Activate"));
commandActivate.addEventListener(Event.SELECT, activateCommand);

The event handler simply calls the window activate() method.


private function activateCommand(event:Event):void{
activate();
}
ADOBE PRODUCT X.0 59
User Guide

Since the menu is defined in the window itself, there is no need to determine which window the command should
be applied to. However, for the “Order in front of ” and “Order behind” commands, the second window needed for
the command must be identified. To do this, the ZWindow object is assigned to the menu item data property when
a window submenu is populated.
The Z application creates a submenu containing an item for each of the orderable windows:
var submenuOrderInFront:NativeMenuItem =
menu.addSubmenu(createWindowSubmenu(),"Order in front of");
submenuOrderInFront.submenu.addEventListener(Event.SELECT, orderInFrontCommand);

The items in the submenu are added by the displayWindows() method by looping through the static
ZWindow.zWindows array, which contains every instance of a ZWindow.

private function displayWindows(event:Event):void{


for each(var existing:NativeMenuItem in event.target.items){
event.target.removeItem(existing);
}
for each(var zWindow:ZWindow in ZWindow.zWindows){
var item:NativeMenuItem = event.target.addItem(new NativeMenuItem(zWindow.title));
item.data = zWindow;
item.addEventListener(Event.SELECT,function(event:Event):void{
});
}
}

To make it easier to access the ZWindow object for the windows listed in the menu, the window object is assigned
to the menu item data property. The data property is of type object, so you can assign any object to it. This allows
the assigned object to be accessed through the event object target property when handling the menu item select
event:
private function orderInFrontCommand(event:Event):void{
orderInFrontOf(event.target.data as ZWindow);
}
60

Chapter 46: Creating resizable, non-


rectangular windows
Rectangular windows are fine, and appropriate for most applications. However, just because they are the easiest to
draw doesn’t mean that all your windows have to be curve impoverished. The !Square sample application creates a
window based on the ellipse, rather than the rectangle. The window uses vector graphics for its chrome, so there are
no issues with bitmap scaling to limit the window size or aspect ratio.
The !Square sample illustrates how to extend the NativeWindow class to create windows with alternative visuals and
behavior using the AIR APIs and the rich Flash graphics capabilities.
Note: This is a sample application provided, as is, for instructional purposes.

The !Square sample application


The !Square sample application.
The following graphic shows the application in action:

Testing the application


Launch the !Square application. Resize the window using any of the eight grippers along the window drag bar. Move
the window using the drag bar. Drag the white disk to the edge of the window to observe that any part of the disk
which is outside the window client area is properly clipped.

Files used to build the application


The application includes the following source files:

File Description

Launcher.as A stub class that creates the main application window and closes.

Launcher-app.xml The application descriptor file. See “Setting application properties” on page 62.

window/RoundWindow.as Extends the NativeWindow class.


ADOBE PRODUCT X.0 61
User Guide

window/ClippedStage.as Extends the Sprite class to create a content container that clips anything outside the window
client area.

window/EllipticalGripper.as Implements the gripper controls for resizing the window.

window/GeometryUtils.as A utility class providing some functions for calculating geometric relationships.

window/texture.png An embedded texture graphic for the gripper bitmap fill.

content/BouncingEye.as A class providing some sample content for the window.

content/EyeBall.as A visual object.

content/Spring.as Simulates spring forces.

icons/!SquareIcon16.png Graphics files used for the desktop application icon.

icons/!SquareIcon32.png

icons/!SquareIcon48.png

icons/!SquareIcon128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/!Square.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7

Understanding the code


The !Square application uses several graphics functions not specific to AIR. For more information about these
functions, see Programming ActionScript 3.0 and the ActionScript 3.0 Language Reference.

Extending the NativeWindow class


The RoundWindow class extends the AIR NativeWindow class to specialize the constructor and to define the
methods and properties to draw its window chrome.

Specializing the constructor


public function RoundWindow(title:String=""){
var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
initOptions.systemChrome = NativeWindowSystemChrome.NONE;
initOptions.transparent = true;
super(initOptions);
bounds = new Rectangle(0,0,viewWidth,viewHeight);
this.title = title;

stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.addChild(clippedStage);
addWindowDressing();

addEventListener(NativeWindowBoundsEvent.RESIZE,onBoundsChange);
draw(viewWidth,viewHeight);
activate();
}
ADOBE PRODUCT X.0 62
User Guide

Setting required listeners


addEventListener(NativeWindowBoundsEvent.RESIZE,onBoundsChange);

Drawing elliptical borders


with(border.graphics){
clear();
beginFill(bevelColor,1);
drawEllipse(0,0,viewWidth,viewHeight);
drawEllipse(4,4,viewWidth-8,viewHeight-8);
endFill();
beginFill(borderColor,1);
drawEllipse(4,4,viewWidth-8,viewHeight-8);
drawEllipse(16,16,viewWidth-32,viewHeight-32);
endFill();
beginFill(bevelColor,1);
drawEllipse(16,16,viewWidth-32,viewHeight-32);
drawEllipse(20,20,viewWidth-40,viewHeight-40);
endFill();
}

Drawing a section of an ellipse


Because the Graphics class does not have a function for drawing just a piece of an elliptical arc, drawing the resizing
gripper onto the border is actually more challenging than drawing the border itself. You could use the curveTo()
method, but calculating the proper control point locations to match the elliptical border has its own mathematical
challenges. For this task, !Square uses a simpler polyline technique based on the parametric equation of the ellipse.
A point on the border is calculated based on the height and width and angle from the midpoint of the ellipse. Then
the next point is calculated by increasing the angle a small amount and a line is drawn between them. This process
is repeated until the desired arc is drawn.
The following formula can be used to calculate a point along the ellipse, given the angle, and the width and the height
of the ellipse:
ADOBE PRODUCT X.0 63
User Guide

The grippers are arranged around the border at preset angles, so the angle is known. The width and height of the
ellipse is the width and height of the window for the outer edge of the window border (viewWidth and viewHeight),
and the width and height minus the thickness of the border for the inner edge of the window border.
The drawing routine uses this formula to calculate the x and y coordinates of the four corners of the gripper, points
A, B, C, and D:
var A:Point = new Point();
A.x = Math.cos(startAngle) * viewWidth/2;
A.y = Math.sin(startAngle) * viewHeight/2;
var B:Point = new Point();
B.x = Math.cos(startAngle) * (viewWidth-40)/2;
B.y = Math.sin(startAngle) * (viewHeight-40)/2;
var C:Point = new Point();
C.x = Math.cos(stopAngle) * (viewWidth-40)/2;
C.y = Math.sin(stopAngle) * (viewHeight-40)/2;
var D:Point = new Point();
D.x = Math.cos(stopAngle) * viewWidth/2;
D.y = Math.sin(stopAngle) * viewHeight/2;

The start and stop angles are calculated from the preset gripper angle, plus and minus the angular width of the
gripper. Defining these quantities as angles rather than lengths makes the math a bit easier and also provides a more
pleasing effect when the window is resized since the gripper size stays proportional to the border size.
var startAngle:Number = (angleRadians - spreadRadians);
var stopAngle:Number = (angleRadians + spreadRadians);

The routine next draws the shape of the gripper between the four points:

First, a straight line is drawn from A to B:


moveTo(A.x,A.y);
lineTo(B.x,B.y);

Next, a series of line segments is drawn between B and C. If enough segments are used, then the visual effect is indis-
tinguishable from an actual curve. !Square uses ten segments, which seems sufficient.
for(var i:int = 1; i < 10; i++){
lineTo(Math.cos(startAngle + i * incAngle) * (viewWidth-40)/2,
Math.sin(startAngle + i * incAngle) * (viewHeight-40)/2);
}

Another straight line is drawn from C to D and the shape is closed by drawing a polyline segment from D back to A.
The shape is started with the beginBitmapFill() method so when endFill() is called the area defined by the
drawing commands is filled with a bitmap texture.
ADOBE PRODUCT X.0 64
User Guide

Clipping the client area


The RoundWindow class uses a sprite with a clipping mask applied as the container for its contents. Any content
objects that are drawn outside the border of the window will be clipped. Window chrome elements are added directly
to the stage so that they will not be clipped.
Clipping can be implemented in Flash by setting the mask property of a Sprite object with another Sprite object. The
masking sprite is not drawn, but only the parts in the first sprite that fall under the area defined by the mask’s graphics
commands will be visible.
In !Square, the clipped sprite is defined by the ClippedStage class. This class creates a clipping mask using the familiar
commands for drawing an ellipse based on the width and height of the window. Any part of an object added as a
child of the ClippedStage object that falls outside the ellipse will be clipped.
private function setClipMask(bounds:Rectangle):void{
ellipticalMask.graphics.clear();
ellipticalMask.graphics.beginFill(0xffffff,1);
ellipticalMask.graphics.drawEllipse(0,0,bounds.width,bounds.height);
ellipticalMask.graphics.endFill();
}

The class also listens for resize events from the parent window and responds by redrawing the clipping mask based
on the new window dimensions.
private function onResize(event:NativeWindowBoundsEvent):void{
setClipMask(event.afterBounds);
}

This type of clipping is not limited to simple shapes, so the technique can be used for any window that has areas
which should be masked.

Resizing an elliptical window


To resize the window, the border and grippers must be redrawn based on the new width and height of the window.
The resize event object includes an afterBounds property that reports the new dimensions. The width and height
from this property are passed to the window draw() method.
private function onBoundsChange(boundsEvent:NativeWindowBoundsEvent):void{
draw(boundsEvent.afterBounds.width,boundsEvent.afterBounds.height);
}

Don’t forget to clear the graphics before drawing the new border.

Handling content in an elliptical window


Because the content in the !Square window is aware of its container (the springs are attached to the border), it must
react to changes in window size and shape. It does this by listening for the window resize event and recalculating the
dependent variables.
private function onResize(event:NativeWindowBoundsEvent):void{
center.x = event.afterBounds.width/2;
center.y = event.afterBounds.height/2;
eye.x -= event.afterBounds.x - event.beforeBounds.x;
eye.y -= event.afterBounds.y - event.beforeBounds.y;
for each (var spring:Spring in springs){
spring.outerEnd = calculateSpringAnchorPoint(spring.anchorAngle);
}
}
ADOBE PRODUCT X.0 65
User Guide

Since the content is always animated, it does not need to be redrawn by the resize listener. Redrawing will occur on
the next frame event anyway. For non-animated content, you may need to resize or reposition the window content
objects directly.
66

Chapter 47: Measuring the virtual desktop


The Screens sample application illustrates how to get information about the screens available to an application using
the AIR Screen API.
Note: This is a sample application provided, as is, for instructional purposes.

About the virtual desktop


The virtual desktop is composed of one or more screens. A main screen is designated and the origin of the virtual
desktop is placed at the top-left-hand corner of this screen. (Positive values are to the right and down, as you would
expect on a computer display.) Thus you should be prepared for negative coordinates when dealing with screens.
The usable area of a screen excludes any task bars, menu bars, sidebars, or other bars that are always drawn in front
of any windows. AIR provides dimensions for both the entire screen and the usable area.

The Screens sample application


The Screens application draws a representation of the screens on the virtual desktop, including a scaled down version
of itself. The usable area of each screen is shown in blue. The area that is within the screen, but outside the usable
area is shown in grey. A lighter blue highlight is displayed on the screen the application window is on.
The following screenshot shows the application in action:

Testing the application


Launch the Screen application. You can move the application window around, change monitor resolution and color
depth, and change the size and location of task bars to observe the effects on the screen information reported by the
AIR Screen API. Use the arrow keys to jump the window from screen to screen.

Files used to build the application


The application includes the following source files:
ADOBE PRODUCT X.0 67
User Guide

File Description

Screens.as The main application file. Details of the code are discussed in the Understanding the code section.

Screens-app.xml The application descriptor file. See “Setting application properties” on page 62.

icons/ApolloApp_16.png Sample icon files.

icons/ApolloApp_32.png

icons/ApolloApp_48.png

icons/ApolloApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/.

See also
• “Building the quick-start sample applications with Flex” on page 7

Understanding the code


The Screen application uses several graphics functions not specific to AIR. For more information about these
functions, see Programming ActionScript 3.0 and the ActionScript 3.0 Language Reference.

Computing the virtual bounds


A bounds is a rectangle that describes both location and dimension. The virtual desktop bounds is the bounding box
that encloses all of the screens. To compute this bounding box, the Screens application loops through each screen in
the array of Screen objects provided by the static Screen.screens property and compares each side of the screen
to the current side of the bounding box. If the screen value is further out than the current bounding box value, the
bound box value is pushed out to match the screen. The result is a bounds rectangle that encloses all the screens.
private function computeVirtualBounds(screens:Array):Rectangle{
var bounds:Rectangle = new Rectangle();
for each (var screen:Screen in screens){
if(bounds.left > screen.bounds.left){bounds.left = screen.bounds.left;}
if(bounds.right < screen.bounds.right){bounds.right = screen.bounds.right;}
if(bounds.top > screen.bounds.top){bounds.top = screen.bounds.top;}
if(bounds.bottom < screen.bounds.bottom){bounds.bottom = screen.bounds.bottom;}
}
return bounds;
}

Sizing the application window proportionally to the screen


Once the size of the virtual desktop is computed, the size of the application window can be determined. The width
of the window is constrained to be 60% of the width of the main screen (plus a little extra for a border). The height
of the window is scaled proportionally to the virtual desktop so that the window will be just tall enough to display
the entire scaled down representation:
var scale:Number = (Screen.mainScreen.bounds.width * .6)/virtualScreenBounds.width;
stage.stageWidth = Screen.mainScreen.bounds.width * .6 + border * 2;
stage.stageHeight = virtualScreenBounds.height * scale + border * 2;
ADOBE PRODUCT X.0 68
User Guide

The Screen object for the operating-system designated main screen can be accessed using the static mainScreen
property of the Screen class.

Drawing a representation of the screens


Since the Screens application class extends Sprite, the sprite graphics routines are used to draw the representation of
the screens. The screens in the desktop are drawn using the screen coordinates and a transformation matrix is set on
the main sprite to translate and scale the screen coordinates into the display area of the window. The transformation
matrix is created with the Matrix class createBox() method. This method take the horizontal and vertical scale
factors as parameters. The same scale is used for both and is computed by dividing the display area of the window
by the total width of the virtual desktop. The createBox() method also takes parameters for how much to shift (or
translate) the representation so that it lines up properly on the window.
The redraw() method computes the transformation matrix, assigns it to the main sprite’s transform.matrix property
and then calls the functions that draw the elements of the screen representation. The method is called in response to
the enterFrame event.
private function redraw(event:Event):void{
virtualScreenBounds = computeVirtualBounds(Screen.screens);
var scale:Number = (stage.nativeWindow.width - border * 2)/virtualScreenBounds.width;
virtualScreenToWindowTransform.createBox(scale,scale,0,
border - (virtualScreenBounds.x * scale),
border - (virtualScreenBounds.y * scale));
transform.matrix = virtualScreenToWindowTransform;
graphics.clear();
drawScreenRepresentation();
highlightScreens();
drawWindowModel();
}

To draw the screens, the drawScreenRepresentation() method loops through the screens and draws a rectangle
for the entire screen and another for the usable area:
with(graphics){
//Draw screen rectangle
beginFill(screenColor,.5);
drawRect(screen.bounds.left,
screen.bounds.top,
screen.bounds.width,
screen.bounds.height);
endFill();

//Draw rectangle for usable screen area


beginFill(usableScreenColor,.5);
drawRect(screen.visibleBounds.left,
screen.visibleBounds.top,
screen.visibleBounds.width,
screen.visibleBounds.height);
endFill();
//...

The method also draws several labels. To avoid having to add and remove TextField objects on each animation frame,
The labels are drawn into a bitmap with the BitmapData.draw() method. The resulting label bitmaps are drawn to
the main sprite using a bitmap fill.
ADOBE PRODUCT X.0 69
User Guide

Using bitmap fills


A non-intuitive aspect of using a bitmap fill is that the fill is registered to the origin of the sprite, not the top-left
corner of the area to be filled. For repeating textures this may not be an issue, but it can leave the words drawn in the
label quite far outside the filled area. The solution is to use another transformation matrix to move the label bitmap
to the filled area. The following lines draw a bitmap containing the words “Main Screen” to the center of the main
screen:
//Draw label to show which screen is the "main" screen
var mainLabelBitmap:BitmapData = drawLabel("Main Screen");
labelTransform = new Matrix(1,0,0,1,(Screen.mainScreen.visibleBounds.width -
mainLabelBitmap.width)/2,
(Screen.mainScreen.visibleBounds.height -
mainLabelBitmap.height)/2);
with(graphics){
beginBitmapFill(mainLabelBitmap,labelTransform,false);
drawRect((Screen.mainScreen.visibleBounds.width - mainLabelBitmap.width)/2,
(Screen.mainScreen.visibleBounds.height - mainLabelBitmap.height)/2,
mainLabelBitmap.width, mainLabelBitmap.height);
endFill();
mainLabelBitmap = null;
}

Moving a window between screens


The Screens application lets you jump the window from screen to screen using the arrow keys. To figure out where
to move the window, the application must determine which screen it is currently on and which, if any, screens lie in
the chosen direction.
The current screen can be determined using the static Screen.getScreensForRectangle() method. This method
returns an array of all screens with which the rectangle intersects. The following function passes the window bounds
into the function and returns the first screen in the resulting array:
private function getCurrentScreen():Screen{
var current:Screen;
var screens:Array = Screen.getScreensForRectangle(stage.nativeWindow.bounds);
(screens.length > 0) ? current = screens[0] : current = Screen.mainScreen;
return current;
}

If the window is somehow off any screen, the main screen is returned.
To find the proper screen to which to move the window, the array of screens is vertically or horizontally sorted and
then the relevant coordinates are compared by walking through the sorted array. The following method moves the
window to the left:
/*Moves the window to the next screen to the left*/
private function moveLeft():void{
var currentScreen:Screen = getCurrentScreen();
var left:Array = Screen.screens;
left.sort(sortHorizontal);
for(var i:int = 0; i < left.length - 1; i++){
if(left[i].bounds.left < stage.nativeWindow.bounds.left){
stage.nativeWindow.x += left[i].bounds.left - currentScreen.bounds.left;
stage.nativeWindow.y += left[i].bounds.top - currentScreen.bounds.top;
}
}
}

The sort method called above uses the following sorting function to compare screen coordinates:
ADOBE PRODUCT X.0 70
User Guide

private function sortHorizontal(a:Screen, b:Screen):int{


if (a.bounds.left > b.bounds.left){
return 1;
} else if (a.bounds.left < b.bounds.left){
return -1;
} else {return 0;}
}
71

Chapter 48: Using the system tray and


dock icons
Dock and system tray icons provide a convenient means to let users access an application that has no visible
windows.
The Stopwatch sample application illustrates the following Adobe AIR features:
• Setting and animating the icon image
• Using mouse events on the icon
• Bouncing the dock icon
Note: This is a sample application provided, as is, for instructional purposes.

About application icons


Applications are provided on both the Mac OS X and Windows operating systems, although the conventions for
using these icons on each system are a bit different. The application icon object in AIR, which provides an interface
for interacting with the application icon, is created automatically. The object type will be a subclass of the Interac-
tiveIcon class, but the exact type depends on the operating system on which an application is running. On Mac OS
X, the icon will be a DockIcon object. On Windows, the icon will be a SystemTrayIcon object. The InteractiveIcon
class defines the bitmaps property of the icon, so you can always set the image data. The other useful properties like
menu and tooltip are defined by the subclass. You must make sure that you do not attempt to access the members of
the wrong icon class or you will get a run-time exception. You can check which icon type is available using the static
Shell properties: supportsDockIcon and supportsSystemTrayIcon.

Dock icons
Dock icons provide a means for users to easily access applications. An application icon is shown in the dock
whenever the application is running. At the user’s discretion the icon can be kept in the dock even when the appli-
cation is not running. The image used for the icon in both cases is the icon specified in the application descriptor
file. When your application is running, you can change the icon image by setting the Shell.shell.icon.bitmaps
property. When the application shuts down, the icon, if left in the dock, will revert back to the original image.
In addition to setting the icon image, you can add a menu to the dock icon. When you add a menu, the items are
displayed above the standard, operating-system provided menu.
Although you cannot listen for mouse events on the dock icon directly, an invoke event is dispatched by the Shell
object whenever the user clicks on the dock icon.

System tray icons


On Windows, the official purpose of the “system tray” is to notify the user that important application events have
occurred, such as the arrival of an e-mail — hence the official name of the system tray is the notification area. In
practice the system tray has become a place where users can access applications that do not ordinarily display
windows.
ADOBE PRODUCT X.0 72
User Guide

System tray icons support a menu, a tooltip, and will dispatch events for mouse events.

The Stopwatch sample application


The Stopwatch application creates a 60-second timer utility that animates the system tray or dock icon as it counts
down. Stopwatch illustrates how to use and animate system tray and dock icons.
The following screenshot shows the application window (in non-minimized state):

Testing the application


Launch the Stopwatch application. Click the triangular start button to start the watch. Note that the system tray or
dock icon changes as the time ticks down. Click the small yellow button to minimize the application to the system
tray or dock. The stopwatch window will automatically reappear when the countdown reaches zero. You can also
click on the icon to restore the window.

Files used to build the application


The application includes the following source files:

File Description

Stopwatch.as The main application file. Details of the code are discussed in the Understanding the code section.

Stopwatch-app.xml The application descriptor file. See “Setting application properties” on page 62.

Clock.as Defines the clock control used to set and display the timer.

HandControl.as Defines the second hand, which is a subcomponent of the clock.

GoButton.as Defines the start/stop button.


ADOBE PRODUCT X.0 73
User Guide

MinimizeButton.as Defines the minimize button.

CloseButton.as Defines the close button.

icons/StopWatchIcon16.png Sample icon files.

icons/StopWatchIcon32.png

icons/StopWatchIcon48.png

icons/StopWatchIcon128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/Stopwatch.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7

Understanding the code


Stopwatch uses three functions related to the application icon:
• dock() “minimizes” the application to the dock or system tray.
• undock() restores the application.

• changeIcon() updates the icon to reflect the current visual state of the stopwatch.

The application defines two sets of functions for these operations, one for Windows and one for Mac OS X. When
the application is initialized at run time, the dock, undock, and changeIcon function objects are set to the version
designed for the current operating system.
On Mac OS X, the function references are set and a listener is added to detect when the user clicks on the dock icon:
dock = OSXdock;
undock = OSXundock;
changeIcon = OSXchangeIcon;
Shell.shell.addEventListener(InvokeEvent.INVOKE,onIconClick);

Clicks on the dock icon can be detected by listening to Shell invoke events.
On Windows, the function references are set, a tooltip is added and then a click event listener is added directly to the
system tray icon object:
dock = DOSdock;
undock = DOSundock;
changeIcon = DOSchangeIcon;
SystemTrayIcon(Shell.shell.icon).tooltip = "Stopwatch";
SystemTrayIcon(Shell.shell.icon).addEventListener(MouseEvent.CLICK, onIconClick);

Animating the icon


The icon is animated by updating the Shell.shell.icon.bitmaps array on every one second tick of the stopwatch
timer. A function is defined by the Clock class, which defines the stopwatch visuals and logic, to get a BitmapData
object containing a snapshot of the clock display:
public function get bitmapData():BitmapData{
var clockImage:BitmapData = new BitmapData(this.width, this.height,true,0x00ffffff);
clockImage.draw(this);
return clockImage;
ADOBE PRODUCT X.0 74
User Guide

To create a transparent icon image, the new BitmapData object must be created with the transparent parameter of
its constructor set to true. In addition, the fill color should be set using the 32-bit ARGB color value, setting the
alpha component to 0, for example, in hexadecimal format, you could use: 0x00ffffff.
On Windows, the system tray icon is so small that the animation effect is too subtle. To compensate, a color
transform is used to invert the colors of the icon for every other frame.
public function DOSchangeIcon():void{
var matrix:Matrix = new Matrix(16/clock.width,0,0,16/clock.height);
var icon:BitmapData = new BitmapData(16,16,true,0x00ffffff);
if(alternate){
icon.draw(clock.bitmapData,matrix,null,null,null,true);
} else {
icon.draw(clock.bitmapData,matrix,invert,null,null,true);
}
Shell.shell.icon.bitmaps = [icon];
alternate = !alternate;
}

The color transform is defined as:


private var invert:ColorTransform = new ColorTransform(-1,-1,-1,1,255,255,255,0);

which subtracts the value of each color component from 255.


In addition, rather than let the operating system scale the icon image down to 16x16 pixels, the clock bitmapData is
scaled down by drawing it into a new BitmapData object using a transformation matrix.

Notifying the user


On Mac OS X, the dock icon can be used to notify the user that an event of interest has occurred. In Stopwatch, the
dock icon is bounced when the timer reaches zero by calling icon.bounce(). If the critical notification type is used,
the dock icon will bounce until the user activates the application. If the informational type is used, the dock icon will
bounce once. (If the application already has focus, the dock icon will not bounce at all.)
Windows does not use the same notification scheme. You can change the system tray icon image or pop-up a light-
weight window containing a message, but the system tray icon cannot be bounced. A window-level notification
function is provided on Windows. This function, notifyUser(), called on the NativeWindow object, will highlight
or flash the window’s taskbar entry.
Stopwatch checks which type of icon the current operating system supports and calls either the DockIcon.bounce()
or the NativeWindow.notifyUser() function.
public function notify():void{
if(Shell.supportsDockIcon){
var dock:DockIcon = Shell.shell.icon as DockIcon;
dock.bounce(NotificationType.CRITICAL);
} else if (Shell.supportsSystemTrayIcon){
stage.nativeWindow.notifyUser(NotificationType.CRITICAL);
}

}
75

Chapter 49: Creating and working with a


local SQL database
This topic demonstrates the following Adobe AIR features:
• Connecting to a local SQL database
• Creating and executing SQL statements:
• Creating a table in the database
• Inserting data into the database table
• Retrieving data from the database table and display that data in a Flex DataGrid component
Note: This is a sample application provided, as is, for instructional purposes.

Testing the application


The application is intentionally simple. It creates a new database in the computer’s memory, creates a table in that
database, adds some data to the database, then retrieves the data and displays it on the screen:

Once the application’s data displays on the screen, there is no additional user interaction available. This is inten-
tional, in order to focus the application entirely on the database operations.
ADOBE PRODUCT X.0 76
User Guide

Files used to build the application


The application is built from the following source files:

File Description

SimpleDBExampleFlex.mxml The main application file in MXML for Flex. Details of the code are discussed in “Understanding the code”
on page 76.

SimpleDBExampleFlex-app.xml The AIR application descriptor file

icons/AIRApp_16.png Sample AIR icon files.

icons/AIRApp_32.png

icons/AIRApp_48.png

icons/AIRApp_128.png

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/SimpleDBExampleFlex.zip.

See also
• “Building the quick-start sample applications with Flex” on page 7.

Understanding the code


The following sections discuss how the code related to AIR works in the file.
This section does not describe all of the Flex components used in the MXML code for the file. For information on
these, see the Flex 3 Language Reference.

Connecting to a local SQL database


The init() method is called when the application finishes loading. Within this method, a SQLConnection instance
name conn is created. (The variable conn is declared outside of the method so that it is available to all the code in
the application.) This SQLConnection object establishes the connection to a database, and is used by other objects
to perform operations on that specific database. Once the SQLConnection instance is created, event listeners are
registered with it to be called when the open() operation succeeds or fails, and the open() method is called to open
the connection to the database.
conn = new SQLConnection();
conn.addEventListener(SQLEvent.OPEN, openSuccess);
conn.addEventListener(SQLErrorEvent.ERROR, openFailure);
conn.open(null);

In this case null is passed as an argument to the open() method, indicating that the runtime will create a new
database in the computer’s memory rather than in a disk location. Alternatively, you could specify a file location
(using a File instance). The runtime would then open the database file at that location (creating it first if it doesn’t
exist). The code to do that would look like this:
var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db");
conn.open(dbFile);
ADOBE PRODUCT X.0 77
User Guide

File.applicationStorageDirectory points to the AIR application store directory, which is uniquely defined for
each AIR application.
Assuming the open() operation succeeds and the database connection opens, the openSuccess() method is called.
That method simply performs the clean-up operation of removing the event listener registrations, and calls the
createTable() method that does the work of creating a table in the database.

Creating a table in the database


The createTable() method uses a SQLStatement instance to execute a SQL command against the database that
was opened in the init() method. The specific SQL command creates a table in the database named “employees,”
with four columns. Here is a breakdown of the code and what it does:
1 Creates a SQLStatement instance named createStmt:
createStmt = new SQLStatement();

2 Specifies that the statement will execute on the database that’s connected through the SQLConnection instance
conn:

createStmt.sqlConnection = conn;

3 Defines the SQL statement text to create a database table. The table is named “employees.” It has four columns:
“empId,” “firstName,” “lastName,” and “salary.”
var sql:String = "";
sql += "CREATE TABLE IF NOT EXISTS employees (";
sql += "empIdINTEGER PRIMARY KEY AUTOINCREMENT,";
sql += "firstNameTEXT,";
sql += "lastNameTEXT,";
sql += "salaryNUMERIC CHECK (salary >= 0) DEFAULT 0";
sql += ")";
createStmt.text = sql;

4 Registers event listeners to specify the methods that are called when the statement finishes executing
(createResult) or fails (createError):
createStmt.addEventListener(SQLEvent.RESULT, createResult);
createStmt.addEventListener(SQLErrorEvent.ERROR, createError);

5 Executes the statement:


createStmt.execute();

Assuming the statement runs successfully, the “employees” table is created and the createResult() method is
called. That method removes the registered listeners and calls the addData() method to perform the next step in
the process, adding data into the newly-created table.

Inserting data into the database table


Like the createTable() method, the addData() method creates a SQLStatement, in this case to insert a row of
data into the “employees” table in the database. The application inserts two rows of data, using two different
SQLStatement instances (insertStmt and insertStmt2):
insertStmt = new SQLStatement();
insertStmt.sqlConnection = conn;
var sql:String = "";
sql += "INSERT INTO employees (firstName, lastName, salary) ";
sql += "VALUES ('Bob', 'Smith', 8000)";
ADOBE PRODUCT X.0 78
User Guide

insertStmt.text = sql;

insertStmt.addEventListener(SQLEvent.RESULT, insertResult);
insertStmt.addEventListener(SQLErrorEvent.ERROR, insertError);

insertStmt.execute();

insertStmt2 = new SQLStatement();


insertStmt2.sqlConnection = conn;
var sql2:String = "";
sql2 += "INSERT INTO employees (firstName, lastName, salary) ";
sql2 += "VALUES ('John', 'Jones', 8200)";
insertStmt2.text = sql2;

insertStmt2.addEventListener(SQLEvent.RESULT, insertResult);
insertStmt2.addEventListener(SQLErrorEvent.ERROR, insertError);

insertStmt2.execute();

Note that because the second statement execution doesn’t depend on the result of the first one, the second
SQLStatement instance is created and its execute() method is called immediately after the first instance’s
execute() method is called. (As opposed to waiting for the result event of the first INSERT statement before
executing the second one.) The runtime queues up these two statements, executing the second one immediately
after the first one completes.
The only complicating factor is that the code needs to determine that both statements have completed before it
moves on to retrieve data from the database. In order to do this, in the insertResult() method (which is called
when either SQLStatement’s result method is triggered) the application determines which statement finished
executing, then checks whether both statements have finished executing. If they have, the getData() method is
called to retrieve the data from the database and display it on the screen:
private function insertResult(event:SQLEvent):void
{
var stmt:SQLStatement = event.target as SQLStatement;

stmt.removeEventListener(SQLEvent.RESULT, insertResult);
stmt.removeEventListener(SQLErrorEvent.ERROR, insertError);

if (stmt == insertStmt)
{
insert1Complete = true;
}
else
{
insert2Complete = true;
}

if (insert1Complete && insert2Complete)


{
getData();
}
}
ADOBE PRODUCT X.0 79
User Guide

Retrieving data from the database table


Like creating a table and inserting data into the table, retrieving data from a table is carried out by creating a
SQLStatement instance with a SQL SELECT statement as the SQLStatement instance’s text property. The following
code creates and executes the SELECT statement that retrieves all the rows from the “employees” table:
selectStmt = new SQLStatement();
selectStmt.sqlConnection = conn;
var sql:String = "SELECT empId, firstName, lastName, salary FROM employees";
selectStmt.text = sql;

selectStmt.addEventListener(SQLEvent.RESULT, selectResult);
selectStmt.addEventListener(SQLErrorEvent.ERROR, selectError);

selectStmt.execute();

As specified in the code, when the SELECT statement finishes executing the selectResult() method is called. In
selectResult(), the result data that is retrieved by the SELECT statement is accessed by calling the SQLStatement
instance’s getResult() method. Calling getResult() returns a SQLResult instance that is stored in the variable
result; the actual result rows are contained in an array in its data property. The results are displayed in the Flex
DataGrid control named resultsGrid by setting it (that is, the result.data property) as the resultsGrid data
grid’s dataProvider property:
private function selectResult(event:SQLEvent):void
{
// ... clean up ...

var result:SQLResult = selectStmt.getResult();


resultsGrid.dataProvider = result.data;
}
80

Chapter 50: Compressing files and data


This topic demonstrates the following Adobe AIR features:
• Compressing a file using the DEFLATE compression algorithm
• Decompressing a file that was compressed with the DEFLATE compression algorithm
Note: This is a sample application provided, as is, for instructional purposes.

Testing the application


The application is intentionally simple. It allows the user to select a file from the computer’s disk, which is
compressed using the gzip compressed file format. Alternatively, the user can select a gzip-compressed file from the
disk, decompress it and save the contents to the computer:

This application uses a specific compressed file format known as the gzip compressed file format. The specification
describing this format is available in IETF RFC 1952 (http://www.ietf.org/rfc/rfc1952).
ADOBE PRODUCT X.0 81
User Guide

Files used to build the application


The application is built from the following source files:

File Description

User interface files (in folder “FileCompressionTool”)

FileCompressionTool.mxml The main application file in MXML for Flex. Details of the code are discussed in “Understanding the code”
on page 81.

controls/CompressScreen.mxml MXML file defining the layout and functionality of the “compress a file” screen

controls/DecompressS- MXML file defining the layout and functionality of the “decompress a file” screen
creen.mxml

FileCompressionTool.-app.xml The AIR application descriptor file

icons/AIRApp_16.png Sample AIR icon files.

icons/AIRApp_32.png

icons/AIRApp_48.png

icons/AIRApp_128.png

Files that perform compression and decompression operations (in folder “GZIPEncoder/com/probertson/utils/”)

GZIPEncoder.as A GZIPEncoder instance can be used to compress a file into a gzip compressed format file or decompress
a gzip compressed format file into a ByteArray or File object.

GZIPFile.as A GZIPFile instance represents a single gzip compressed format file, with properties for the various meta-
data as well as the actual source data in the compressed file.

CRC32Generator.as A CRC32Generator instance is used to generate a CRC-32 value, which is used to verify that the uncom-
pressed data extracted from a gzip compressed file is not modified or damaged from its original, pre-
compression content. The CRC-32 check is used in the gzip compression format.

You can download the source files for this example at http://download.macro-
media.com/pub/labs/air/quickstart_apps/b2/FileCompressionTool.zip.
Note: The files in the “GZIPEncoder/com/probertson/utils/” folder are from the open-source “ActionScript GZIP
compression library” written by H. Paul Robertson and are available at http://probertson.com/projects/gzipencoder/ in
addition to being included as part of the downloadable source file described in the preceding paragraph.
For help on building this quick start sample application,

See also
• “Building the quick-start sample applications with Flex” on page 7.

Understanding the code


The following sections discuss how the code related to AIR works in the file.
This section does not describe all of the Flex components used in the MXML code for the file. For information on
these, see the Flex 3 Language Reference.
ADOBE PRODUCT X.0 82
User Guide

Understanding the gzip compression format


When you’re working with compressed-data files such as zip files or similar files (including gzip files used in this
application), the technique of compressing file data and storing it in a compressed file involves two parts.
First, the data itself must be compressed using a compression algorithm—a set of rules that define a way to decrease
the number of bytes needed to represent a file or item of data. (This is most commonly done by searching for and
removing redundant patterns in the data.) The DEFLATE algorithm is one such algorithm for compressing
computer data. In AIR, you can compress data in a ByteArray instance using the DEFLATE algorithm by calling the
ByteArray instance’s compress() method, passing the constant CompressionAlgorithm.DEFLATE for the method’s
algorithm parameter, as shown here:

import flash.utils.ByteArray;
import flash.utils.CompressionAlgorithm;

var dataToCompress:ByteArray = new ByteArray();


// ... add data to the ByteArray using writeBytes() or other methods ...

// Compress the data


dataToCompress.compress(CompressionAlgorithm.DEFLATE);
// The ByteArray now contains a compressed version of its previous data

Although the data is now compressed, only half the process of creating a compressed-format file is complete. In
addition to containing raw compressed data, each compression format (such as zip, gzip, etc.) stores extra infor-
mation about the compressed data within the compressed data file. What makes each compressed file format unique
is the definition of the specific way that a file is structured, including what extra information is stored along with the
compressed data and how the bundle of data and extra information is organized. For instance, the gzip compressed
format specifies that gzip-formatted data (for example, a gzip file) contains certain identifiers indicating the
compression format and compression algorithm used, information about the date and time that the compression
took place, the operating system on which the compression took place, and so forth, in addition to containing the
actual compressed data.
If you are writing code to create or parse files that are structured using a particular compressed file format, you need
to understand the distinction between the actual compressed data (which the runtime can create or extract for you
using ByteArray.compress() and ByteArray.uncompress()) and the structure of the compressed data, which
your code will need to create or read. For examples of how this is done in the File Compression Tool sample, see the
GZIPEncoder.compressToFile() method for creating a file in gzip compression format, and the
GZIPEncoder.parseGZIPFile() method for parsing a gzip compression format file into its parts.

Compressing a file using the DEFLATE algorithm


You use the ByteArray.compress() method to compress the data contained in a ByteArray instance. By default,
the compress() method compresses data using the zlib compression format. Like zip and gzip, zlib is a compression
format that includes compressed data as well as extra information about the data. If you want to use a different
compression format, such as zip or gzip, you can specify that the ByteArray should only be compressed using the
DEFLATE algorithm without the addition of any extra information by passing the
CompressionAlgorithm.DEFLATE constant as an argument to the compress() method. This is what the GZIPEn-
coder class does to compress a file in gzip compression format, in its compressToFile() method:
public function compressToFile(src:Object, output:File):void
{
// ... perform error checking ...

// This ByteArray will contain the data to compress


var srcBytes:ByteArray;
ADOBE PRODUCT X.0 83
User Guide

var target:File = new File(output.nativePath);

// ... populate srcBytes with data, depending on whether src is


// a File instance or a ByteArray instance ...

// Open the FileStream instance for creating the resulting file


var outStream:FileStream = new FileStream();
outStream.open(target, FileMode.WRITE);

// ... calculate the extra header and footer information for


// the gzip format and append it to the output FileStream ...

// Add the actual compressed data


srcBytes.compress(CompressionAlgorithm.DEFLATE);
outStream.writeBytes(srcBytes, 0, srcBytes.length);

// ... append the footer information to the output FileStream ...

outStream.close();
}

Although the compressToFile() method includes some additional complexity for dealing with creating the output
file and writing the extra information that’s part of the gzip compression format, the core function of compressing
the data and adding it to the gzip file is performed by these two lines:
srcBytes.compress(CompressionAlgorithm.DEFLATE);
outStream.writeBytes(srcBytes, 0, srcBytes.length);

The ByteArray instance named srcBytes contains the data to be compressed (loaded from the user-selected source
file). The compress() method is called with the CompressionAlgorithm.DEFLATE argument, so all the bytes in
srcBytes are compressed using the DEFLATE compression algorithm. At that point srcBytes contains the
compressed version of the data (the original content is replaced). That data is then written to the destination file (the
gzip file that’s being created) in the appropriate part of the file as specified in the gzip compression format. The
entire srcBytes ByteArray is written to the output file stream using the outStream variable’s writeBytes()
method—the three arguments indicate that the data to write comes from srcBytes, starting at position 0 in
srcBytes, and going to the final position of srcBytes (indicated by srcBytes.length).

Decompressing a DEFLATE-compressed file


When a user selects a gzip format file to decompress, that file is passed to the GZIPEncoder.uncompressToFile()
method, which actually decompresses the file, extracts the original source data, and writes it to a file in the specified
location. Like the compressToFile() method, the uncompressToFile() method performs several steps, most of
which involve error checking, separating the extra information in the gzip file from the actual compressed data, and
determining the file name of the file to which the newly uncompressed data is saved:
public function uncompressToFile(src:File, output:File):void
{
// ... error checking ...

// call the parseGZIPFile method, which extracts the gzip file into a
// GZIPFile instance with properties representing the parts of the gzip file
var gzipData:GZIPFile = parseGZIPFile(src);

var outFile:File = new File(output.nativePath);


// ... determine the destination file path, depending on whether
// output is a directory, and whether the source gzip file contained
// file name information ...
ADOBE PRODUCT X.0 84
User Guide

// get the actual compressed bytes from the gzip file


var data:ByteArray = gzipData.getCompressedData();

// Perform the uncompress operation


try
{
data.uncompress(CompressionAlgorithm.DEFLATE);
}
catch (error:Error)
{
throw new IllegalOperationError("The specified file is not a GZIP file.");
}

// ... write the uncompressed data to the destination file ...


}

The step of converting the compressed data to uncompressed data takes place in this line:
data.uncompress(CompressionAlgorithm.DEFLATE);

Once the uncompress() method is called, the compressed content of the data ByteArray instance is uncompressed
and the data variable then contains the uncompressed data, ready to be written to the destination file.
1

Index
A D browseForDirectory()
adobe-app URL scheme 105 deleteDirectory() method (File method 103
application storage director 105 class) 108 browseForOpen() method 104
application store directory 102 deleteDirectoryAsync() method browseForSave() method 104
(File class) 108 copyTo() method 110
app-storage URL scheme 105
deleteFile() method (File class) 111 copyToAsync() method 110
appStoreDirectory property (File
class) 102 deleteFileAsync() method (File createDirectory() method 107
class) 111
asynchronous methods 101 createTempDirectory()
deleting directories 108, 111 method 107, 111
deleting files 111 createTempFile() method 111
B
desktop directory 102 creationDate property 109
browseForDirectory() method
(File class) 103 desktopDirectory property (File creator property 109
class) 102
browseForOpen() method (File deleteDirectory() method 108
class) 104 directories 102, 107
deleteDirectoryAsync()
browseForSave() method (File copying 108 method 108
class) 104 creating 107 deleteFile() method 111
browsing to select a directory 103 deleting 108, 111 deleteFileAsync() method 111
browsing to select a file 104 enumerating 108 desktopDirectory property 101
moving 108 documentsDirectory
C referencing 102 property 101
clearing directories 108 directory chooser dialog boxes 103 encoding property 106
copying directories 108 documents directory 102 exists property 109
copying files 110 documentsDirectory property isDirectory property 109
copyTo() method (File class) 110 (File class) 102 lineEnding property 106
copyToAsync() method (File listDirectory() method 108
class) 110 E listDirectoryAsync()
createDirectory() method (File encoding property (File class) 106 method 108
class) 107 enumerating directories 108 listRootDirectories method 101
createTempDirectory() method exists property (File class) 109 modificationDate property 109
(File class) 107, 111
moveTo() method 110
createTempFile() method (File
class) 111 F moveToAsync() method 110
creating directories 107 file API 100 moveToTrash() method 111
creationDate property (File file chooser dialog boxes 104 moveToTrashAsync()
class) 109 File class 100, 101 method 111
creator property (File class) 109 appStoreDirectory property 101 name property 109
2 INDEX

nativePath property 101, 109 M T


parent property 109 modificationDate property (File temporary directories 107
relativize() method 105 class) 109 temporary files 111
resolve() method 101 moveTo() method (File class) 110 trash (deleting a file) 111
root volumes 101 moveToAsync() method (File type property (File class) 109
class) 110
separator property 106
moveToTrash() method (File
size property 109 class) 111 U
type property 109 moveToTrashAsync() method url property (File class) 102, 109
url property 101, 109 (File class) 111 URL schemes 105
userDirectory property 101 moving directories 108 userDirectory property (File
file URL scheme 105 moving files 110 class) 102

FileMode class 100 My Documents directory


files (Windows) 102 W
copying 110 writing files 112

deleting 111 N
moving 110 name property (File class) 109
reading 112 nativePath property (File
class) 102, 109
referencing 103
writing 112
P
FileStream class 100
parent property (File class) 109
path delimiter (file system) 103
H
paths (file and directory) 105
home directory 102
paths, relative 105

I
R
isDirectory property (File
class) 109 reading files 112
relative paths (between files) 105

L relativize() method (File class) 105


lineEnding property (File resolve() method (File class) 102
class) 106 root volumes 102
listDirectory() method (File
class) 108 S
listDirectoryAsync() method (File separator property (File class) 106
class) 108
size property (File class) 109
listRootDirectories method (File
class) 102 start-up directory of an
application 102
synchronous methods 101

You might also like