Adobe AIR Xdrive Picture Syncing: Part 3

Although we've made great strides the last couple of posts developing the Xdrive picture syncing utility with Ext JS and Adobe AIR, today we get going with the really good stuff. In this third part of a five part series, I'll show you how to retrieve and display pictures from both your local file system and Xdrive.

Before we actually start querying for picture data from the local file system and Xdrive, we need to provide a way of authenticating with Xdrive. As with virtually all other method calls you'll use with Xdrive, it's a straightforward process to authenticate. To invoke the gateway, an HTTP request is created to invoke http(s)://plus.xdrive.com/json/version/method where version is the Xdrive version you are targeting and method is the method call to invoke. Most method calls are flexible to accept either GET or POST requests. For the picture syncing AIR application, the doLogin() function which is in the ui.js script looks like this:

function doLogin() {
	
	// capture the username and password
	var username = txtFld_username.getValue();
	var password = txtFld_password.getValue();
	
	// create an Xdrive Member object
	var obj_member = { "user":
		{ 
			"type":     "MemberObject",
			"username": username,
			"password": password
		}
	};		
	
	Ext.Ajax.request(
					 
		{
					 
			url: XDRIVE_URL + "member.login",
			method: "POST",
			params: { data: Ext.util.JSON.encode( obj_member ) },
			success: function( r, o ){
				
				// decode the response
				var obj_response = Ext.util.JSON.decode( r.responseText );
				
				// set the global variables
				rootFolderId  = obj_response.results.user.rootFolderId;
				jsessionid    = obj_response.jsessionid;
				recoveryToken = obj_response.recoveryToken;
				
				// hide the login window
				win_login.hide();
				
				// get the local pictures, format using templates
				// defined in the DataView
				loadPictures( "Local Pictures", pnl_west.body, parentSandboxBridge.getLocalPictureData() );	
				
				// do the same for Xdrive pictures
				initXdrivePictures();
				
			},
			failure: function( r, o ) {
				
				alert( "fail: " + r.responseText );
				
			}
		
		}
					 
	);
	
}

First, the username and password values are captured from their input controls defined using Ext JS. The values are used to construct an Xdrive MemberObject which is encoded and passed as a parameter to the member.login Xdrive method. The Xdrive method is invoked through an AJAX request using the Ext.Ajax singleton class. On success, the response is parsed and a few of the key pieces of information that we want to keep around such as the Xdrive root folder id, jsessionid, and recovery token are stored in global variables. The jsessionid and recovery token are particularly important as they will be used to send along with subsequent requests to maintain session state. The final actions after successful authentication are to remove the login window, and firing off the two functions used to populate the UI with picture data. Let's first explore how to get the actual picture information from both environments.

Getting Local Pictures

In part 1 of this series, we focused on talking about the two sandboxes which make up an AJAX style Adobe AIR application. Remember with this type of application we're given two environments, the application sandbox, and the browser sandbox. Most of the development we've been using so far has been the browser sandbox. However to use AIR specific features such as local file system access, it must be defined and executed in the application sandbox and exposed over a bridge to the browser sandbox. To get file information on a directory of pictures on my machine, I've defined the following function in root.js, which is part of the AirApp object exposed to the browser sandbox.

getLocalPictureData: function() {

    // create a File object
    var dir_localPictures = new air.File( "C:/data/pictures" );
    
    // get the files in the directory
    var arr_localPictures = dir_localPictures.getDirectoryListing();
    
    // create an array of picture objects
    var obj_localPictures = new Object();
    obj_localPictures.pictures = new Array();
    
    for( var i = 0; i < arr_localPictures.length; i++ ) {
        var obj_tmp = new Object();
        obj_tmp.name = arr_localPictures[i].name;
        obj_tmp.path = arr_localPictures[i].nativePath;
        obj_localPictures.pictures.push( obj_tmp );
    }
    
    return obj_localPictures;

}

The path to my pictures directory is hardcoded to create a new AIR File object. From this, a directory listing is obtained as an array of more File objects which in this case will be image files. The array is processed to capture the filename and absolute path to the file. This information is used to populate a custom object which will be used to feed to an Ext JS data store later on. Now that we have a local data source, let's get pictures from Xdrive.

Getting Xdrive Pictures

To retrieve file listings from Xdrive, the process is the same that we had used to login. The following code is defined in the browser sandbox side in ui.js:

function initXdrivePictures() {
    
    // tack on session state info
    var URL = XDRIVE_URL + "file.getlisting" + ";jsessionid=" + jsessionid + "?recoveryToken=" + recoveryToken;
    
    // create a FileObject, using the id for
    // the My Photos directory.
    var obj_file = { "srcFile":
        {
            "type": "FileObject",
            "id":   "xdr:XFS-1706469105"
        }
    };		
    
    Ext.Ajax.request(
                     
        {
            url: URL,
            method: "GET",
            params: { data: Ext.util.JSON.encode( obj_file ) },
            success: function( r, o ) {
                
                obj_response = Ext.util.JSON.decode( r.responseText );
                
                var obj_xdrivePictures = new Object();
                obj_xdrivePictures.pictures = new Array();
                
                for( var i = 0; i < obj_response.results.children.length; i++ ) {
                    var obj_tmp = new Object();
                    obj_tmp.name = obj_response.results.children[i].filename;
                    
                    // if the file is published, we have a live link to the file.
                    // otherwise, just display a generic icon.
                    if( typeof( obj_response.results.children[i].permalink ) != "undefined" ){
                        obj_tmp.path = obj_response.results.children[i].permalink;	
                    }
                    else {
                        obj_tmp.path = "C:/file_32.gif";	
                    }
                    
                    obj_xdrivePictures.pictures.push( obj_tmp );
                }
                
                loadPictures( "Xdrive Pictures", pnl_center.body, obj_xdrivePictures );
                
            },
            failure: function( r, o ) {
                alert( "failure!" );
            }
        }
                     
    );
    
}

Most of this will look the same as the login method. This time, an Xdrive FileObject is defined (hardcoded with my My Photos fileId), encoded and passed to the file.getlisting method. On success, the same type of custom object that we created for the local picture data is created for the Xdrive side. Using Xdrive however this is slightly different. While processing each file in the My Photos directory, a check is performed for the permalink property. This indicates that the file is published in Xdrive and publicly accessible. When a file is published on Xdrive, the AIR application can display the actual image. However if the file is not published, a generic icon is displayed in our application. Now that we have the data sources in place for both local and remote pictures, we can display them in the application.

Displaying Pictures

To display the pictures from the two data sources we just covered, a generic function is used. With a little help from some powerful Ext JS features, very little code is used to produce an eye catching interface. The following function processes the picture paths:

function loadPictures( groupTitle, el, pictureData ) {
    
    var ds_pictures = new Ext.data.JsonStore(
        {
            root:   "pictures",
            fields: [ "name", "path" ],
            data:   pictureData
        }
    );
    
    var tpl = new Ext.XTemplate(
        '<tpl for=".">',
            '<div class="thumb-wrap" id="{name}">',
            '<div class="thumb"><img src="{path}" title="{name}"></div>',
            '<span class="x-editable">{name}</span></div>',
        '</tpl>',
        '<div class="x-clear"></div>'
    );
    
    var pnl_pictures = new Ext.Panel(
        {
            id:          "images-view",
            frame:       true,
            width:       400,
            autoHeight:  true,
            collapsible: false,
            layout:      "fit",
            title:       groupTitle,
    
            items: new Ext.DataView(
                {
                    store:        ds_pictures,
                    tpl:          tpl,
                    autoHeight:   true,
                    multiSelect:  false,
                    overClass:    "x-view-over",
                    itemSelector: "div.thumb-wrap",
                    emptyText:    "No images to display"
                }
            )
        }
    );
    pnl_pictures.render( el );		
    
}

Much of this function is actually taken from one of the many examples found on the Ext JS website. This function accepts three arguments; a groupTitle which is just a string identifying the group of pictures, an element object used to attach the resulting panel to, and finally an object containing the paths to pictures. The power behind this function is the DataView attached as an item to the panel that's created. The DataView in Ext combines the power of templating with a data store to produce consistent output that responds if the store is manipulated. The addition of some CSS classes for the DataView yields the following output:

My local picture bank currently has a few pictures of my son which I will want to eventually upload to Xdrive and share with family and friends. Check back on Tuesday when I do just that.

Part 1 | Part 2 | Part 3 | Part 4 | Part 5