Last night I wanted to take a look at file/directory traversing in a mobile AIR application. I had assumed it would "just work" but I wanted to be sure of that myself and see it in action. I was also curious as to how the various 'helper aliases' work. By that I mean the aliases AIR provides for the user's desktop and documents directory. These are nice, cross platform ways to point to common folders across different operating systems. I wasn't quite sure how they would work on the Android so I figured it was a good time to find out. The application I built is quite short so I'll post the code and then explain how the parts work.
<?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="Browse File Demo" viewActivate="init()">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable] private var fileList:ArrayCollection;
private var dir:File;
private var initialDir:String;
[Bindable] private var enableUpDir:Boolean;
private function init():void {
// dir = File.documentsDirectory;
// dir = File.desktopDirectory;
//get root, but always default to first one
var roots:Array = File.getRootDirectories();
dir = roots[0];
initialDir = dir.nativePath;
enableUpDir = false;
listFiles();
trace(dir.nativePath);
}
private function listFiles():void {
trace("Listing "+dir.nativePath);
currentDirLabel.text = "Browsing: "+dir.nativePath;
fileList = new ArrayCollection(dir.getDirectoryListing());
trace("compare "+dir.nativePath+" to "+initialDir);
if(dir.nativePath != initialDir) enableUpDir = true;
else enableUpDir = false;
}
private function changeDir(evt:Event):void {
var sel:File = fileListing.selectedItem;
if(sel.isDirectory) {
dir = sel;
listFiles();
fileListing.selectedIndex=-1;
}
}
private function goUpDir(evt:Event):void {
var parent:File = dir.parent;
dir = parent;
listFiles();
}
private function displayFile(selectedFile:File):String {
if(selectedFile.isDirectory) return selectedFile.name+"/";
else return selectedFile.name;
}
]]>
</fx:Script>
<s:actionContent>
<s:Button height="100%" label="Go Up" enabled="{enableUpDir}" click="goUpDir(event)" />
</s:actionContent>
<s:layout>
<s:VerticalLayout paddingTop="10" paddingLeft="5" paddingRight="5" />
</s:layout>
<s:Label id="currentDirLabel" width="100%" height="75" />
<s:List id="fileListing" dataProvider="{fileList}" width="100%" height="100%" click="changeDir(event)" labelFunction="displayFile">
</s:List>
</s:View>
Before getting into the code, let me share a screen shot so you have an idea of what this thing does when run:
In the screen shot you can see that the application has a directory name printed on top and the contents in a list below. A simple button "Go Up" allows you to return to the higher level directory. Selecting a folder will move you into the folder but selecting a file will currently do nothing. The code begins with the init method. You can see that I started off testing the documents and desktop directory aliases. On the Android, these pointed to /mnt/sdcard - in other words, my SD Card. This seems to make perfect sense and is probably the closest analogy to the desktop or documents directory you are going to get on the phone. For the "real" application though I switched to using the root file system. On Windows this could be multiple drives. To keep it simple I simply grab the first value. I know that Android is Unix based so this would give me / as a root path. I could run this both on my desktop and my phone and it would work the same in either place.
The listFiles function handles getting the directory from the file object and deciding if the "Go Up" button should be enabled. I didn't check to see if any parent existed, but rather if I was at my original starting directory. I imagined a scenario where I'd want to keep my user within a particular area and not allow them to leave that.
Everything else is rather simple as well so I'll skip explaining them unless someone has a specific question. I've attached a zip of the entire project (which includes the APK if you are bored enough to install it) to this blog entry. Nothing too exciting here I guess but hopefully this will be useful to somebody out there.