WHAT YOU WILL LEARN IN THIS CHAPTER:
This chapter takes a look at the AIR File System API in depth, again using Flash Builder to take you through related examples. These will help you to build applications that can create or utilize existing data on a user's mobile device, whether that data is an MP3 file found in the device's native media library, or an image file referenced from the photo gallery.
The key aspect of the API is getting to understand the filesystem, learning how to resolve paths to files, and containing folders on the device. This chapter looks at all this in depth.
WARNING For security reasons the AIR File System API is restricted for use in non-browser Flash applications. Bear this in mind if you intend to create browser-based Flash mobile applications. In addition, Google Android and BlackBerry Tablet OS devices require users to grant usage of certain security levels when using the API.
Over the course of this chapter you'll construct a simple example running the majority of features.
To utilize the filesystem within your Flash mobile applications on a mobile device using AIR, you first need to familiarize yourself with the core classes involved in the API, and pay particular attention to how one points to files and directories in the filesystem.
The File and FileStream classes are the key classes that you can use to gain access to the filesystem data on the mobile device using AIR. Both files are located in the flash.filesystem package.
To use the File class in an ActionScript Mobile project, you need to import the class using the following statement:
import flash.filesystem.File;
Similarly, to use the FileStream class in an AS project, you need to import the class using the following statement:
import flash.filesystem.FileStream;
Using Flash Builder with the Flex framework and AIR doesn't require you to import the class in this way. The Files Explorer project therefore doesn't have either of these statements. Bear this in mind when you create your other projects.
The File class provides reference points to information about files and file directories, also giving you the methods to create, modify, and delete files or file directories. The FileStream class provides you with the methods to open, read, write, and modify files on the filesystem.
A File object has a number of properties that should uniquely distinguish it from another file object on the filesystem. These properties include:
You should be familiar with the majority of these properties as entities on your home computer. The modification date is a property of a file used frequently to see when a file was last saved. Looking at the properties, you can easily identify a file object by its name, whether it is a file or a directory, its size, the creation or modification dates, and URL paths.
Next take a look at the different ways in which you can create file objects using AIR.
The nativePath and url properties of the file object are references that point to a file object's location on the mobile device.
There are three URL schemes that are supported which can be used to create file objects via the File class constructor: app:/; app-storage:/; and file://.
In the following code snippet the file:// URL scheme is used to create a File object fileObj that attempts to point to Notes, a folder contained in the Documents directory on the filesystem:
var fileObj:File = new File("file:///documents/notes");
The way this file object is created could potentially pose a few problems for cross-platform compatibility and running the app on devices with different mobile operating systems. How does the mobile application know that the Documents or Notes directories exist on a device? And is the file path URL format recognized by the device?
If you don't know whether the file object created is present on the device, you can use the File.exists property of the File object to determine whether a particular file or directory exists — but this is only once the URL path of the file object has been set, again pointing to a potential issue with the URL format. To address differences in URL formats, you can use static properties of the file class to retrieve generic locations on devices, and as you'll see it provides an alternative way to create a file object by referencing a specific location on the device.
On a laptop or PC you may be familiar with commonly used file spaces such as Documents and Applications for Mac OS, or My Documents and Programs on a Windows machine. Simply put, these are quick access references to file directories that have certain document types. In essence these are familiar short names given to potentially complex filesystem references.
On mobile devices, users are less familiar with locations such as these, and generally come across physical file directory paths only when using applications designed for this. Applications such as Finder for Mac OS and File Explorer on Windows are designed for large screens, allowing a user to explore whole filesystems, which on mobile devices would be harder to navigate.
The File class has five static properties that you can use to reference commonly used file locations:
The file objects returned by these properties can be used to avoid potential issues like the ones encountered when specifying a hard-coded file-path URL. Each of the file references is pretty much static and can be referenced universally across different platforms using AIR.
Table 7-1 lists example url values returned by each static property on an Android mobile device running Gingerbread 2.3.4
PROPERTY | VALUE |
File.applicationDirectory.url | app:/ |
File.applicationStorageDirectory.url | app-storage:/ |
File.desktopDirectory.url | file:///mnt/sdcard |
File.documentsDirectory.url | file:///mnt/sdcard |
File.userDirectory.url | file:///mnt/sdcard |
From the table you'll see that the Android device returns three distinct file object url values.
WARNING Note that you cannot write to files or directories that have paths that use the app: URL scheme. Nor can you delete or create files or folders that have paths that use the scheme, as modifying content in the application directory is considered a bad practice, and for security reasons, it is usually blocked by the OS.
On Google Nexus One, an Android device running Gingerbread 2.3.4, the File.desktopDirectory, File.documentsDirectory, and File.userDirectory each returns a file object that points to the file:///mnt/sdcard location. To ensure that a file object points to a particular location, you must use the resolvePath() method to refine the target path.
In the following snippet the file object is pointing to the file:///mnt/sdcard/notes directory using the File.documentsDirectory as the initial reference point:
var fileObj:File = File.documentsDirectory.resolvePath("notes");
For the file object created, fileObj, if the Notes directory existed, then fileObj.exists would be set to true. Using the resolvePath() method essentially sets the target path for the file object, whether it exists or not. This is important for creating new files and folders, as you'll see later.
While the url property of a file object gives a precise value to a location, the nativePath property gives the full path to the file object as represented in the host operating system.
Next take a look at using the nativePath property of the file object in the example project.
You will need to set up a new Flex Mobile Project in Flash Builder.
The following lists a few of the familiar settings you will need to ensure are defined for the project:
This example project can run on each of the mobile platforms supporting AIR, including Apple iOS, Google Android, and BlackBerry Tablet OS. For Google Android and BlackBerry Tablet OS, a number of permissions need to be set to allow the application to utilize the device's filesystem, whereas for Apple iOS, no permissions need to be defined specifically.
For the AIR Application Descriptor file generated with the project in Flash Builder, FilesExplorerApp-app.xml, ensure the android.permission.WRITE_EXTERNAL_STORAGE permission is included as a manifest addition for the Google Android platform, as shown in the following code snippet:
<android> <manifestAdditions> <![CDATA[
<manifest> <uses-permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE”/> </manifest> ]]> </manifestAdditions> </android>
For BlackBerry Tablet OS applications, you need to specify the access_shared permission, to allow the application to write to the mobile device. Ensure this is set in the blackberry-tablet.xml file, as shown in the following code snippet:
<?xml version="1.0" encoding="UTF-8"?>
<qnx>
<author>jganderson</author>
<authorId>gYAAgFbt6rihu</authorId>
<category>core.media</category>
<buildId>1</buildId>
<platformVersion>1.0.0.0</platformVersion>
<permission>access_shared</permission>
</qnx>
There are no permissions that need to be defined for the Apple iOS platform.
You can elect to run this project on the desktop or directly on your mobile device. For consistency, this chapter uses a Google Nexus One as the connected device.
In this section, you begin building the Files Explorer App project in Flash Builder, first taking a look at the nativePath property of the File object.
Displaying the Native Path of a File Object
For the Files Explorer App project, follow the next steps to add a label to the main view that shows the current filesystem directory.
LISTING 7.2: Setting the styles via the <fx:Style> declaration in FilesExplorerApp.mxml
<?xml version="1.0" encoding="utf-8"?> <s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" firstView="views.FilesExplorerAppHome"> <fx:Style> @namespace s “library://ns.adobe.com/flex/spark”; s|View { backgroundColor:#999999; color:#393839; } s|Label { fontSize:22; } s|List { alternatingItemColors: #CCCCCC, #EEEEEE; selectionColor:yellow; fontSize:22; color:#393839; } </fx:Style> </s:ViewNavigatorApplication>
LISTING 7.3: The FilesExplorerAppHome.mxml view for the Files Explorer project
<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" creationComplete=“readDir()” title=“Files Explorer”> <fx:Script> <![CDATA[ protected function readDir():void {} private function exit():void { NativeApplication.nativeApplication.exit(); } ]]> </fx:Script> <s:layout> <s:VerticalLayout/> </s:layout> </s:View>
LISTING 7.4: Adding <s:Button> and <s:Label> components to the view in FilesExplorerAppHome.mxml
<fx:Script>
<![CDATA[
protected function readDir():void {}
private function exit():void
{
NativeApplication.nativeApplication.exit();
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:navigationContent>
LISTING 7.6: Setting the text to the native path in FilesExplorerAppHome.mxml
<fx:Script> <![CDATA[ private var selectedDirectory:File; protected function readDir():void { selectedDirectory = File.documentsDirectory; currentDirectory.text = selectedDirectory.nativePath; } private function exit():void { NativeApplication.nativeApplication.exit(); } ]]> </fx:Script>
Listing the Files of a Directory
Next take a look at how to display the contents of a directory.
LISTING 7.9: Populating the <s:List> component with the file object name property in FilesExplorerAppHome.mxml
protected function readDir():void { selectedDirectory = File.documentsDirectory; currentDirectory.text = selectedDir.nativePath; var docsDirectory:Array = selectedDirectory.getDirectoryListing(); var fileObj:File; dirList.dataProvider = new ArrayCollection(); for(var i:int = 0; i < docsDirectory.length; i++) { fileObj = docsDirectory[i]; dirList.dataProvider.addItem({ label: fileObj.name }); } }
Next modify the list so that only folders are displayed.
Within readDir() the getDirectoryListing() method is what provides the array of file objects from the selectedDirectory file object to the docsDirectory array. This object points to the reference Documents Directory on the mobile device. The length property on docsDirectory returns the number of file objects in the array. This is used in the for loop to iterate through each file object and display its name property in the List component dirList.
The dirData is used to hold only file objects that are known to be directories; using the isDirectory property all the files are filtered out of the array.
Next take a look at navigating between each directory.
LISTING 7.12: Storing data in the Array object in FilesExplorerAppHome.mxml
protected function readDir():void { selectedDirectory = File.documentsDirectory; currentDirectory.text = selectedDirectory.nativePath; var docsDirectory:Array = selectedDirectory.getDirectoryListing(); var fileObj:File; dirData = []; dirList.dataProvider = new ArrayCollection(); for(var i:int = 0; i < docsDirectory.length; i++) { fileObj = docsDirectory[i]; if(fileObj.isDirectory) { dirData.push(fileObj); dirList.dataProvider.addItem({ label: fileObj.name }); } } }
LISTING 7.13: Setting a selection color, the selected index, and click event handler for the <s:List> component in FilesExplorerAppHome.mxml
<s:List id="dirList" width="100%" height="85%" fontFamily="Arial" contentBackgroundColor="#B6B3B3" selectionColor=“#00A2FF” selectedIndex=“0” click=“readDir()”/>
LISTING 7.14: Setting the selected directory from the data stored in the data array in FilesExplorerAppHome.mxml
protected function readDir():void { if(dirData) { selectedDirectory = dirData[dirList.selectedIndex];
} else { selectedDirectory = File.documentsDirectory; } currentDirectory.text = selectedDirectory.nativePath; var docsDirectory:Array = selectedDirectory.getDirectoryListing(); var fileObj:File; dirData = []; dirList.dataProvider = new ArrayCollection(); for(var i:int = 0; i < docsDirectory.length; i++) { fileObj = docsDirectory[i]; if(fileObj.isDirectory) { dirData.push(fileObj); dirList.dataProvider.addItem({ label: fileObj.name }); } } }
Figure 7-5 shows the screen on the device when the folder has been selected. You will also notice that the native path is updated in the display.
RUNNING THE FILES EXPLORER APP ON APPLE IOS AND BLACKBERRY TABLET OS DEVICES
For the Files Explorer App running on an Apple iPhone 4, the initial file directory opened by the application will consist of the following URL path:
/var/mobile/Applications/<ID>/Documents
Here the <ID> value represents a unique value generated for the application by the device and could vary from iPhone to iPhone.
For the Files Explorer App running on a BlackBerry PlayBook, the initial file directory will consist of the following path:
/accounts/1000/appdata/com.wrox.ch7.FilesExplorerApp.debug.test<ID>/ shared/documents
Similarly, the <ID> value here also represents a value generated for the application by the device and could vary from PlayBook to PlayBook.
Next take a look at navigating back to the previous directory. For this you need to use the parent property of the file object.
LISTING 7.15: Adding a method to call the parent directory in FilesExplorerAppHome.mxml
protected function readDir():void { if(dirData) { selectedDirectory = dirData[dirList.selectedIndex]; } else { selectedDirectory = File.documentsDirectory;
} currentDirectory.text = selectedDirectory.nativePath; var docsDirectory:Array = selectedDirectory.getDirectoryListing(); var fileObj:File; dirData = []; dirList.dataProvider = new ArrayCollection(); setParentDir(); for(var i:int = 0; i < docsDirectory.length; i++) { fileObj = docsDirectory[i]; if(fileObj.isDirectory) { dirData.push(fileObj); dirList.dataProvider.addItem({ label: fileObj.name }); } } } protected function setParentDir():void {}
So far you've learned how to read the filesystem of a mobile device using AIR. In this section you take a look at modifying the filesystem objects.
To create files and folders on the mobile device, you need to use a combination of the File, FileStream, and FileMode classes.
The FileMode class is found in the flash.filesystem package. When creating ActionScript Mobile projects, you need to import the class through the following statement:
import flash.filesystem.FileMode;
When creating a Flex Mobile project in Flash Builder, you don't need to import the class.
The FileMode class provides four static constants. These are flags to define what a FileStream object should do with a File object it receives via the FileStream.open() method. At least one of these properties needs to be supplied as the second parameter in the open() method:
The following sections demonstrate how each of the FileMode properties can be used to read and write strings to a text file using the FileStream.readUTFBytes() and FileStream.writeUTFBytes() methods.
To write, update, and append a file, you use the writeUTFBytes() method on a FileStream object, supplying the text you want to add to the file as an argument.
In the following code snippet the FileStream object fs opens a text File object called story.txt, resolving a path located in the documents directory. The file stream opens the file and then writes the string “A long time ago,” which is 15 characters, and then closes the file stream:
var fileObj:File = File.documentsDirectory.resolvePath("story.txt"); var fs:FileStream = new FileStream(); fs.open(fileObj, FileMode.WRITE); fs.writeUTFBytes("A long time ago"); fs.close();
In the following code snippet the story.txt file is updated:
var fileObj:File = File.documentsDirectory.resolvePath("story.txt"); var fs:FileStream = new FileStream(); fs.open(fileObj, FileMode.UPDATE); fs.position = 15; fs.writeUTFBytes(" in a galaxy far, far away.... "); fs.close();
Notice that the FileStream.position property on the FileStream object is set to 15. This property represents the current position in the file stream, and has been set so that the existing text in the file is kept and isn't overridden when new text is supplied to the FileStream.writeUTFBytes() method. Following on from the previous code snippet the file should read “A long time ago.” When the update is applied the file should now read “A long time ago in a galaxy far, far away....”
Similarly, appending to a file using the FileMode.APPEND flag in FileStream.open() updates the file, but adds whatever is supplied to the FileStream.writeUTFBytes() method to the end of the file. The following code snippet appends the string “STAR WARS” to the story.txt file:
var fileObj:File = File.documentsDirectory.resolvePath("story.txt"); var fs:FileStream = new FileStream();
To read the contents of an existing text file, you need to use the FileStream.readUTFBtyes() method by supplying a reference to the bytesAvailable property on the file stream object.
In the following snippet the story.txt file is read:
var fileObj:File = File.documentsDirectory.resolvePath("story.txt"); var fs:FileStream = new FileStream(); fs.open(fileObj, FileMode.READ); fs.readUTFBytes(fs.bytesAvailable); fs.close();
Here the FileStream object fs is again passed a reference to the File object fileObj, which points to the story.txt file. The FileMode.READ flag is also passed to the FileStream.open() method.
If you've followed each of the previous code snippets, the story.txt file should read “A long time ago, in a galaxy far, far away.... STAR WARS.”
Creating a new file directory simply requires calling the createDirectory() method on the file object. The path to the new folder needs to be resolved using the resolvePath() method, as shown in the following snippet:
var fileDir:File = File.desktopDirectory.resolvePath("untitled folder"); fileDir.createDirectory();
In this snippet the “untitled folder” is created in the Desktop directory.
To move a file from one location on the device to another, you need to utilize two file objects. The first file object should point to the originating location, and the second should point to where you want to move the file. You call the moveTo() method on the first file object, supplying the second file object as the parameter as shown in the following snippet:
var originalFile:File = File.documentsDirectory.resolvePath("story.txt"); var newDir:File = File.applicationStorageDirectory.resolvePath("story.txt"); originalFile.moveTo(newDir);
Here the story.txt file is moved from the documents directory to the application's storage directory on the device.
The text supplied to resolvePath() for the second file object is what defines either the new filename if you are moving a file, or the directory.
To move a file directory also requires the use of the moveTo() method on the file object. In the following snippet the originalDir file object, which points to the “untitled folder” on the desktop, is moved to the destinationDir file object, which points to a folder called “shapes” in the documents directory.
var originalDir:File = File.desktopDirectory.resolvePath("untitled folder"); var destinationDir:File = File.documentsDirectory.resolvePath("shapes"); originalDir.moveTo(destinationDir);
Here the moveTo() method is called on originalDir, which is the file object representing the directory that you want to move. The destinationDir file object is supplied as the parameter for the moveTo() method.
Copying a file or a directory also requires two file objects. To copy a file, you need to call the copyTo() method, as shown in the following snippet:
var file:File = File.applicationStorageDirectory.resolvePath("story.txt"); var newFile:File = File.documentsDirectory.resolvePath("story copy.txt"); file.copyTo(newFile);
In the following snippet the originalDir file object, which now points to the “shapes” folder in the documents directory, is copied and a new file directory newDir is created called “shapes copy” via the copyTo() method.
var originalDir:File = File.documentsDirectory.resolvePath("shapes"); var newDir:File = File.documentsDirectory.resolvePath("shapes copy"); originalDir.copyTo(newDir);
The copyTo() method is called on originalDir, which is the file object representing the directory that you want to copy. The newDir file object is supplied as the parameter for copyTo().
Removing a file from the filesystem on the mobile device first requires that a file exists. In the following snippet the story copy.txt file is removed from the documents directory via the deleteFile() method:
var fileObj:File = File.documentsDirectory.resolvePath("story copy.txt"); if(fileObj.exists) fileObj.deleteFile();
To remove a directory from the filesystem, you call the File.deleteDirectory() method on a File object. Again, you need to ensure that the resolvePath() returns the correct file directory location.
var fileDir:File = File.documentsDirectory.resolvePath("stories"); if(fileDir.exists) fileDir.deleteDirectory();
Creating New Files and Folders
Returning to the Files Explorer project, add two new options to the main view, new folder and new file. These options will be created to demonstrate exactly how the functions perform.
LISTING 7.17: Adding a horizontal group component to the view in FilesExplorerAppHome.mxml
<s:List id="dirList" width="100%" height="85%" fontFamily="Arial" contentBackgroundColor="#B6B3B3" selectionColor="#00A2FF" selectedIndex="0" click="readDir()"/> <s:HGroup id=“buttonContainer” width=“100%” horizontalAlign=“center” paddingTop=“10” paddingBottom=“10”> </s:HGroup>
LISTING 7.18: Adding two new <s:Button> components to the horizontal group component in FilesExplorerAppHome.mxml
<s:HGroup id="buttonContainer" width="100%" horizontalAlign="center" paddingTop="10" paddingBottom="10"> <s:Button id=“folderBtn” label=“New Folder” height=“55” fontSize=“24”/> <s:Button id=“fileBtn” label=“New File” height=“55” fontSize=“24”/> </s:HGroup>
LISTING 7.19: Creating the FolderView.mxml view for the Files Explorer App project
<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" creationComplete=“onCreationComplete()” title=“Create a new Folder...” > <fx:Script> <![CDATA[ protected function onCreationComplete():void {} ]]> </fx:Script> <s:layout> <s:VerticalLayout paddingLeft=“10” paddingTop=“10” /> </s:layout> <s:Label id=“currentDirectory” text=“Current Directory” width=“100%” height=“58” verticalAlign=“middle”/> <s:Label width=“152” height=“55” text=“Folder name:” textAlign=“left” verticalAlign=“middle”/> <s:TextInput id=“directoryName” width=“450” contentBackgroundColor=“#605E5E”/> <s:Button label=“Create Folder” height=“55” fontSize=“24”/> </s:View>
LISTING 7.20: Creating the FileView.mxml file for the Files Explorer App project
<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" creationComplete=“onCreationComplete()” title=“Create a new File...” > <fx:Script> <![CDATA[ protected function onCreationComplete():void {} ]]> </fx:Script> <s:layout> <s:VerticalLayout paddingLeft=“10” paddingTop=“10” /> </s:layout> <s:Label id=“currentDirectory” text=“Current Directory” width=“100%” height=“58” verticalAlign=“middle”/> <s:Label width=“152” height=“55” text=“File name:” textAlign=“left”
verticalAlign=“middle”/> <s:TextInput id=“fileName” width=“450” contentBackgroundColor=“#605E5E”/> <s:Label width=“203” height=“55” text=“File Content:” textAlign=“left” verticalAlign=“middle”/> <s:TextArea id=“fileContent” width=“450” height=“209” contentBackgroundColor=“#605E5E” verticalAlign=“top”/> <s:Button label=“Create File” height=“55” fontSize=“24”/> </s:View>
LISTING 7.21: Displaying the nativePath and adding a back button in FolderView.mxml and FileView.mxml
<fx:Script> <![CDATA[ private var selectedDirectory:File; protected function onCreationComplete():void { selectedDirectory = data as File; currentDirectory.text = selectedDirectory.nativePath; } private function back():void { navigator.pushView( views.FilesExplorerAppHome, selectedDirectory );
LISTING 7.22: Adding a method to create a new directory in the FolderView.mxml file
protected function onCreationComplete():void { selectedDirectory = data as File; currentDirectory.text = selectedDirectory.nativePath; } protected function createFolder():void { var directoryName:String = directoryName.text; var newDir:File; if(!directoryName || directoryName == “”) { newDir = selectedDirectory.resolvePath(“untitled folder”); } else { newDir = selectedDirectory.resolvePath(directoryName); } newDir.createDirectory(); }
LISTING 7.24: Adding the createFile() method to create a new file in FileView.mxml
protected function onCreationComplete():void { selectedDirectory = data as File; currentDirectory.text = selectedDirectory.nativePath; } protected function createFile():void { var nameStr:String = fileName.text + “.txt”; var fileObj:File = selectedDirectory.resolvePath(nameStr); }
LISTING 7.25: Creating the file stream in FileView.mxml
protected function createFile():void { var nameStr:String = fileName.text + ".txt"; var fileObj:File = selectedDirectory.resolvePath(nameStr); var fs:FileStream = new FileStream(); fs.open(fileObj, FileMode.WRITE); fs.writeUTFBytes(fileContent.text); fs.close(); }
LISTING 7.27: Navigating to the new views in FilesExplorerAppHome.mxml
private function fileView():void { navigator.pushView(views.FileView, selectedDirectory); } private function folderView():void { navigator.pushView(views.FolderView, selectedDirectory); } private function exit():void { NativeApplication.nativeApplication.exit(); }
LISTING 7.28: Assigning folderView() and fileView() methods to click events in FilesExplorerAppHome.mxml
<s:HGroup id="buttonContainer" width="100%" horizontalAlign="center"> <s:Button id="folderBtn" label="New Folder" click=“folderView()” height="55" fontSize="24"/> <s:Button id="fileBtn" label="New File" click=“fileView()” height="55" fontSize="24"/> </s:HGroup>
For AIR on Android, three browse methods on the File object allow you to reference image, video, and audio files using the mobile device's native window dialog:
On an Android mobile device, the browse dialog allows the user to select only from audio, image, and video files, as shown in Figure 7-12.
Using the browseForOpen() method on a File object, you can present the user with a browse for open file dialog on the mobile device, which will allow you to reference a file in the application.
The browseForOpen() method takes two parameters. The first parameter is a title to be displayed in the dialog, and the second is an optional file filter that can be used to filter the types of files a user can select for opening.
The following snippet shows how a FileFilter object called audioFilter is defined to display all MP3 file types, before the browseForOpen() method is called on the File object fileDir:
var audioFilter:FileFilter; audioFilter = new FileFilter("audio", "*.mp3"); var fileDir:File = File.applicationStorageDirectory; fileDir.addEventListener(Event.SELECT, onSelect); fileDir.browseForOpen("Select a file...", [audioFilter]);
In the constructor of the file filter the string audio is supplied as a description along with *.mp3, a string representing the MP3 file extension. The browseForOpen() method is given two parameters. The first is the string Select a file..., and the second is an array of FileFilter objects, though here only the mediaFilter object is supplied.
In the example, addEventListener() is called on fileDir to register Event.SELECT, an event that is fired when a user selects an item in the browse dialog. The handler for the event defined as onSelect() returns a file object reference to the file selected in the target property of the Event object e.
Using the browseForOpenMultiple() method on a File object, you can present the user with a browse file dialog to open and save files. Instead of Event.SELECT being fired when a user selects a media file from the browse dialog, the FileListEvent.SELECT_MULTIPLE event is triggered. The handler for the event returns an array of File objects in the target property instead of just a single file.
The following code snippet demonstrates how to use a browse dialog to select multiple files:
var fileDir:File = File.documentsDirectory; fileDir.addEventListener(FileListEvent.SELECT_MULTIPLE, onSelect); fileDir.addEventListener(Event.CANCEL, onCancel); fileDir.browseForOpenMultiple("Select files...");
In this example addEventListener() is called on the File object fileDir to handle the SELECT_MULTIPLE event. In addition, the Event.CANCEL event is also handled by an onCancel() when the user clicks Cancel.
Take a look at browse dialogs in more detail.
Over the next few steps you'll try utilizing browseForOpenMultiple() by loading multiple images selected from a browse dialog directly into a mobile application.
LISTING 7.30: Adding the SELECT_MULTIPLE event to the File object and calling the browseForOpenMultiple() method in FilesExplorerAppHome.mxml
private function exit():void { NativeApplication.nativeApplication.exit(); } protected function selectMedia(fileObj:File):void { var jpgFilter:FileFilter; jpgFilter = new FileFilter("JPEG Files", "*.jpg"); fileObj.addEventListener(FileListEvent.SELECT_MULTIPLE, onSelect); fileObj.browseForOpenMultiple(“Select 2 image files...”, [jpgFilter]); } private function onSelect(e:Event):void {}
LISTING 7.32: Adding a horizontal group component containing the Open multiple media button in FilesExplorerAppHome.mxml
<s:HGroup id=“buttonContainer” width=“100%” horizontalAlign=“center”> <s:Button height=“55” label=“Open multiple media” click=“selectMedia(selectedDirectory)” fontSize=“24”/> </s:HGroup> <s:HGroup id="buttonContainer" width="100%" horizontalAlign="center"> <s:Button id="folderBtn" label="New Folder" height="55" fontSize="24"/> <s:Button id="fileBtn" label="New File" height="55" fontSize="24"/> </s:HGroup>
LISTING 7.33: Assigning values to the title and creationComplete in ImagesView.mxml
<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" title=“Selected files...” creationComplete=“onCreationComplete()”> <fx:Script> <![CDATA[ protected function onCreationComplete():void {} ]]> </fx:Script> </s:View>
LISTING 7.34: Adding a <s:Button> to navigate back in ImagesView.mxml
<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" title="Selected files..." creationComplete="onCreationComplete()"> <fx:Script> <![CDATA[ protected function onCreationComplete():void {} private function back():void { navigator.pushView(views.FilesExplorerAppHome); } ]]> </fx:Script> <s:navigationContent> <s:Button label=“Back” click=“back()”/> </s:navigationContent> </s:View>
NOTE The text supplied as the title parameter for the browseForOpenMultiple() and browseForOpen() methods does not appear in the browse dialog on a Google Nexus One running Android 2.3.4. In Figure 7-13 and Figure 7-14 you will see Upload appear as the title.
The browseForSave() method presents a dialog containing a list of files, and allows a user to save a file to a location on the mobile device.
The following snippet shows how to call browseForSave():
var fileDir:File = File.applicationStorageDirectory; fileDir.addEventListener(Event.SELECT, onSelect); fileDir.browseForSave("Save file...");
As with browseForOpen(), Event.SELECT needs to be registered with the file object to handle when the user selects the OK button to confirm saving the file (Figure 7-16).
NOTE The text supplied as the title parameter for the browseForSave() method doesn't appear in the browse dialog on a Google Nexus One running Android 2.3.4. In Figure 7-16 you will see Download appear as the title, and not Save File as highlighted in the snippet.
Over the course of this chapter you have learned how to utilize the AIR File System API using a combination of Flash, Flex, and AIR to build the Files Explorer project.
The File and FileStream classes are the key aspects of the AIR File System API, and can be used in a number of ways to read, write, and modify aspects of any existing filesystem via a Flash application, including: listing the files and folders of a directory; creating text files; and selecting media files to open on the device.
In the next chapter you'll use aspects of the AIR File System API to work with app data, focusing more on the application storage directory.
Before moving on to the next chapter, you can integrate a number of functions covered in this chapter into the Files Explorer project. The following set of exercises should allow you to explore these event types and appreciate gestures in more detail.
WHAT YOU LEARNED IN THIS CHAPTER
TOPIC | KEY CONCEPT |
Creating file objects | Use one of three URL schemes to create a file object: app:/;
app-storage:/; and file://. |
Creating file objects from static locations | Use one of five static properties to reference a file object, including:
File.applicationDirectory File.applicationStorageDirectory File.documentsDirectory File.desktopDirectory, and File.userDirectory |
Resolving file object paths | Use resolvePath() on a file object to refine a target path. |
Writing to files | Use the FileStream and FileMode objects to write to a file.
Set the file mode to FileMode.WRITE when opening a file stream to write to a file. Use writeUTFBytes() to write content to a file. |
Modifying files and directories | Use moveTo() on the file object to move to a file path.
Use copyTo() on the file object to make a duplicate of the file object. Use deleteFile() to remove a file. Use deleteDirectory() to remove a folder. |
Using browse dialogs | Use browseForOpen() to open a single file and use browseForOpenMultiple() to open multiple files.
Use browseForSave() to save a file object to the mobile device. |
13.59.154.143