Now that we have learned how to collect the various directories and file types from the user lets start looking at the next step;  Directory Searching.  The user may have selected a folder that contains their entire media library or just a few files.  Either way, we want the next step to run as fast as possible while still allowing the user to use their computer.  Another thing to consider is that there may be subdirectories.  The next step will need to recursively search through all the directories and files and return all the media files.  The obvious solution is to create a function that takes the directory path as a parameter and examines each file in the directory.  If a media file is encountered then add it to an ArrayCollection.  If a directory is found then pass the new directory's path to the same function to process its entries.  Once all the files have been processed the result will be an ArrayCollection of File objects that reference all the media files found.  This will work but can take a long time when processing large directories resulting in our users sitting and waiting and staring at a spinning hour glass.  I knew there had to be a better approach.  I needed a function that would process even the largest of directories but still allow the user access to their computer at the same time.  The solution I was looking for is to call the library building function asynchronously.  Each time a file is found a new LibraryProgressEvent is raised.  Read on to get an understanding of the code used to implement the asynchronous event driven directory search.

Lets look at the code to search the directory.

private function processDir(dir:DirectoryDataItem):Boolean{
      var bReturn:Boolean = true;
      try{
            var viddir:File = new File(dir.DirectoryPath);
            for each(var file:File in viddir.getDirectoryListing()){
                  if(file.isDirectory){
                        var di:DirectoryDataItem = new DirectoryDataItem();
                        di.DirectoryPath = file.nativePath;
                        di.DirectoryType = dir.DirectoryType;
                        processing.addItem(di);
                  } else {
                        for each(var ext:String in SettingsManager.Instance.GetVideoExtensions()){
                              if(file.extension == ext){
                                    addFileToLibrary(file, "Video");
                              }
                        }
                  }
            }
      } catch(er:Error){
            bReturn = false;
      }
      return bReturn;
}


Each directory is passed to this function.  This function looks at each entry in the directory.  If a sub directory is found it is added to the processing ArrayCollection.  If a file is found then it is passed to a function to be added to the library.

As you can see, if this function was called and blocked the application the user could experience long lag times.  The way to get this to run Asynchronously is to create a public function that will act as a wrapper and get the search process started.  EventListeners will be set up to report the status of the search but the wrapper function will return control to the calling function.  This way the user can continue to interact with the GUI and still be updated as files are found.

Lets take a closer look at how to get all this asynchronous event driven stuff to work.  I always like to start by breaking my goals down into the components that are going to accomplish each step.  We will need the following:

1) Function to recursively search each directory.
2) Mechanism for calling the search function asynchronously.
3) Custom Event object to pass messages from the search function back to the GUI.
4) GUI code that knows how to listen for the custom Event.

For my application I created a class called LibraryManager.  It contains all the methods used to do the asynchronous directory search and raise progress events.  A complete listing of the code can be found in the file src\radshag\medialibrary\LibraryManager.as inside the source zip file at the top of this tutorial.  I will now discuss the key functions and events used for the asynchronous search.

There is one function that is used to start the directory searching process.

   1. public function BuildLibrary():void

This function initializes two ArrayCollections.  The library ArrayCollection is used to store all the files that are found during the search.  The processing ArrayCollection is used to store directories that are to be searched.  As subdirectories are found they are also added to the processing ArrayCollection.  The code for the BuildLibrary function is presented below:

public function BuildLibrary():void{
      library = new ArrayCollection;
      processing = new ArrayCollection;
      // Get a the collection of media directories to search
      var dirs:ArrayCollection = SettingsManager.Instance.GetMediaDirectories();              
      for each(var dir:DirectoryDataItem in dirs){
            // Add directory to collection processing to perform the actual search on each directory
            processing.addItem(dir);
      }
      // each time the ENTER_FRAME event is raised call onEnterFrame to process the next directory
      addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
}


The onEnterFrame function is responsible for determining if there are any directories to search.  Each time the function is called the ArrayCollection processing is checked to see if it contains any item to search.  If it does then the next item is removed from the ArrayCollection and passed to processDir.  The processDir function determines what kind of files are being searched for and calls the correct function.  If a directory is found it is added to the processing ArrayCollection.  If a media file is found then the addFileToLibrary function is called.

The new file is added to the public member library ArrayCollection and a new LibraryProgressEvent called progress is raised so any listening object can handle it.  Once the processing array is empty signaling that all directories have been searched,  the same LibraryProgressEvent is raised but is named complete so that any listening object can access the library once it is completed

At the top of the LibraryManager class file I defined two events as follows:

/**
*     A progress event is dispatched when library item is added.
*     @eventType radshag.medialibrary.events.LibraryProgressEvent
*/

[Event(name="progress",type="radshag.medialibrary.events.LibraryProgressEvent")]
/**
*     A complete event is dispatched when library processing is complete.
*     @eventType radshag.medialibrary.events.LibraryProgressEvent
*/

[Event(name="complete",type="radshag.medialibrary.events.LibraryProgressEvent")]


Each time the search function finds a media file a LibraryProgressEvent named progress is raised.  Once all the directories and files have been searched the same event is raised but is called complete to signal that directory searching has completed.  A complete listing of the LibraryProgressEvent is listed below:

package radshag.medialibrary.events
{
      import flash.events.Event;
      import flash.filesystem.File;
      public class LibraryProgressEvent extends Event
      {
            public static var PROGRESS:String = "progress";
            public static var COMPLETE:String = "complete";
            public var completed:int;
            public var total:int;
            public var file:String;
            public function LibraryProgressEvent(type:String, completed:int, total:int, file:String)
            {
                  super(type);
                  this.completed = completed;
                  this.total = total;
                  this.file = file;
            }
            override public function clone():Event{
                  var event:LibraryProgressEvent = new LibraryProgressEvent(type, completed, total, file);
                  return event;
            }
      }
}