Creating a File Manager in Adobe Air

In my previous post I taught you how to create a simple Adobe Air application. This time we are going to be a bit more ambitious. I want to show how easy is to create an Adobe Air application. So let’s create a simple file manager with drag & drop capabilities using out of the box Adobe Air components.

Before we go into the code lets just briefly look at the out of the box components that are available to build a file manager:

  • FileSystemDataGrid – The file system data grid is an out of the box Air component that extends DataGrid and allows you to view the contents of a directory providing information such as file name, file type, size and creation date. This will be one of the main components that we will use in this file manager.
  • FileSystemHistoryButton – The file system history button is a PopupButton that allows to go back or forward in the history of selected directories for a file system. The button click event allows to navigate to the immediate ancestor or successor directory in the history of selections. While the combo box enables jumping to any previous selected directory.
  • FileSystemTree – The file system tree is a component that displays the contents of the file system as a tree. The FileSystemTree extends the out of the box Tree component in flex and inherits all the associated behavior.

Using the components above let’s create a very simple file system navigator:

<mx:Panel layout="vertical" height="100%" title="Copy From">
	<mx:ControlBar >
			 <mx:FileSystemHistoryButton label="Back"  click="fs1.navigateBack()" enabled="{fs1.canNavigateBack}" itemClick="fs1.navigateBack(event.index)" dataProvider="{fs1.backHistory}"/>
			 <mx:FileSystemHistoryButton label="Forward" enabled="{fs1.canNavigateForward}" dataProvider="{fs1.forwardHistory}" itemClick="fs1.navigateForward(event.index)" dragStart="mx.controls.Alert.show('Drag Start')"/>
			 <mx:Button label="Up" enabled="{fs1.canNavigateUp}" click="{fs1.navigateUp()}"/>
			 <mx:Button label="Delete" enabled="{fs1.selectedItem!= null}" click="handleDelete(fs1)"/>

	</mx:ControlBar>
	<mx:FileSystemDataGrid id="fs1" directory="{File.documentsDirectory}" >

	</mx:FileSystemDataGrid>
 </mx:Panel>

We use the FileSystemHistoryButton to go back or forward in the history of previous selected directories.

Notice that there are two event handlers that we need to register. The first event handler handles the click event and is used if the user simply clicks on the button. In that case FileSystemDataGrid.navigateBack() or FileSystemDataGrid.navigateForward() is called. This causes the file system data grid to go back to the previously selected directory.
The second event handler handles itemClick event. This is the event that is raised if the user selects one of the directories in the popup menu. So in this case the event handler calls FileSystemDataGrid.navigateBack(event.index) or FileSystemDataGrid.navigateForward(event.index) where event.index is the index of the menu item that the user selected.

The FileSystemDataGrid.canNavigateBack() and FileSystemDataGrid.canNavigateForward() functions indicate whether we have reached a dead-end in the history of directory selections. In that case the Forward and Back buttons will be disabled.

The Up button when clicked navigates to the parent directory, by calling FileSystemDataGrid.navigateUp(). Similarly FileSystemDataGrid.canNavigateUp() indicates whether we have reached the root directory. In that case the Up button will be disabled.

Any decent file manager should allow you to copy, move or delete a file. Let’s add these operations to our file manager:

Deleting files

 /**
    * Checks that there is at least one item selected and asks the user whether
    * he really wants to delete the file. 
    * 
    */
    private function handleDelete(fs:FileSystemDataGrid):void {
 		mx.controls.Alert.show("Do you really want to delete this file? Please be aware that the delete actually deletes... not part of the demo.","Delete confirmation",Alert.YES|Alert.CANCEL, 
 		this, function(event:CloseEvent):void {
 			 deleteConfirmation(event, fs );
 		});
     }
 		
    /**
       * If the user really wants to delete the file we do what they ask
       * 
       */
       private function deleteConfirmation(event:CloseEvent, fs:FileSystemDataGrid):void {
 	     if (event.detail==Alert.YES) {
 			if (fs.selectedItem != null) {
 			var file:File= fs.selectedItem as File;
 			if (file.isDirectory) {
 				file.deleteDirectory(false);
 			}
 			else { 						
 				file.deleteFile();	
 			}
 			fs.refresh();
 		}
 	}	
 }

 

Deleting a file is irreversible, therefore it is always a good idea to double check that the user really wants to delete the file by using an Alert dialog box.
If the user confirms the deletion of the file then we get the current selected file by calling FileSystemDataGrid.selectedItem. A File object represents a path to a directory or to a file. By calling File.isDirectory() we know whether the file is a directory or not.

  • If the File object points to a directory we call File.deleteDirectory(false), where false indicates that we do not want to delete the directory if it contains any files. Note that this delete is done synchronously meaning that the user interface will block until the deletion is complete. If you don’t want this to happen call File.deleteDirectoryAsync() instead and register event handlers for Event.COMPLETE and IOErrorEvent.IO_ERROR
  • If the File object points to a file, in that case we call File.deleteFile() and that deletes the file.

Copying Files

  /**
    * Copies the file to a new location
    * 
    */
    private function copyFile(fileOrig:File, fileDest:File):void {
 	// once copying completes we want to refresh both file system data grids to reflect
       // any changes
       fileOrig.addEventListener(Event.COMPLETE, refreshFileSystems);

      if (!fileDest.exists) {
 		fileOrig.copyToAsync(fileDest);
 	}
 	else {
 		mx.controls.Alert.show("Are you sure you want to replace the existing file?","Replace with new file?", Alert.YES|Alert.CANCEL, this, 
 				function(event:CloseEvent):void {
 					// overwrite file
 					if (event.detail== Alert.YES) {
 						fileOrig.copyToAsync(fileDest, true);
 					}
 					else {
 						fs1.refresh();
 						fs2.refresh();
 					}
 				});
 			}
 		}

Copying a file can be done using File.copyTo() or File.copyToAsync(). File.copyTo() copies a file from one location to another but blocks until the copying finishes. On the other hand the File.copyToAsync() method returns immediately and performs the copy in the background. But in this case one should register event handlers for Event.COMPLETE and Event.PROGRESS so that we know when the copy operation is finished.

The first argument of File.copyTo() or File.copyToAsync() is the destination directory/file to copy to. There is an optional overwrite argument that you can use to specify whether you want to overwrite the destination file if one exists. If you set it to false and a file is present an IOError exception or IOErrorEvent is thrown.

Moving Files

 	/**
 	 * Moves the file to a new location
 	 * 
 	 */
 	private function moveFile(fileOrig:File, fileDest:File):void {
  		fileOrig.addEventListener(Event.COMPLETE, refreshFileSystems);
                if (!fileDest.exists) {
 			fileOrig.moveToAsync(fileDest);
 		}
 		else {
 			mx.controls.Alert.show("Are you sure you want to replace the existing file?","Replace with new file?", Alert.YES|Alert.CANCEL, this, 
 			function(event:CloseEvent):void {
 				// overwrite file
 				if (event.detail== Alert.YES) {
 					fileOrig.moveToAsync(fileDest, true);
 				}
 				else {
 					fs1.refresh();
 					fs2.refresh();
 				}
 			});
 		}
 
 	}

Moving a file can be done using File.moveTo() or File.moveToAsync(). File.moveTo() moves a file from one location to another but blocks until the move operation is finished. On the other hand the File.moveToAsync() method returns immediately and performs the move operation in the background. But in this case one should register event handlers for Event.COMPLETE and Event.PROGRESS so that we know when the move operation is finished.

The first argument of File.moveTo() or File.moveToAsync() is the destination directory/file to move to. There is an optional overwrite argument that you can use to specify whether you want to overwrite the destination file if one exists. If you set it to false and a file is present an IOError exception or IOErrorEvent is thrown.




Click here to download the example AirFileManager

Next we want to add drag & drop support to this file system manager. So that we don’t always have to click the buttons. If you are interested in how you can add that kind of functionality then read my tutorial on Adding Drag and Drag functionality in Adobe Air

1 thought on “Creating a File Manager in Adobe Air”

Leave a Reply

Your email address will not be published. Required fields are marked *