Thursday, November 5, 2009

Build Your Own Tweets Widget for the Symbian Platform

If you are a Web developer interested in developing a social networking application such as a Twitter app that can retrieve tweets on a Series 60 Symbian phone, then here are code snippets to demonstrate how easy it is to implement it using Nokia Web Runtime using Javascript, and HTML.  If you are new to mobile Web development on Symbian, take a look at the Web Runtime Quickstart tutorial.

Now to retrieve tweets on a Series 60 Symbian phone, you can implement the index.html (see slide 11 in the presentation attached below), which will invoke init() function within main.js.  The init() function will simply instantiate the UIManager and set up the views (see slides 12 and 13). Then, if the user selects the 'Tweetz' icon, then Twitter.Update() will be invoked as listed below.  The function Twitter.Update() will simply initialize the twitter buttons including adding the separator, and will display "Loading twitter feed" as it waits for the twitter feed to download.

Twitter.prototype.Update= function(numToShow){
    this.numToShow= numToShow;
    if ( this.buttons== null ) {

       // add the separator
       var separator = new NavigationButton(null, 
                           "tweetz-icon.png","");
       separator.setEnabled(false);
       this.parentView.addControl(separator);
       // create buttons
       this.buttons= new Array();
       for( var i = 0 ; i < this.numToShow; i++ ) {
           var button = 

           new NavigationButton("twitter_button_"+i, 
                                 null ,"");
           this.parentView.addControl(button);

           this.buttons.push(button);
       }
       this.buttons[0].setText(

           "Loading twitter feed...");
    }


Next, initialize the twitter URL for getting a user's RSS feed. In this case, it is initialized to the TwitMyMobile user account. Then, you can get the RSS feed by instantiating the AJAX request. The http.open() API is passed, "GET" for retrieving the data, the second parameter is the twitter URL, and the third parameter is set to true to indicate that this is an asynchronous request. The http.onreadstatechange is set when the request state changes, then the Twitter.DoUpdate() function is invoked for parsing the returned data. The code fragment for getting the user's tweets will look as follows:



 // Twitter API for getting a user’s RSS feed 
var twitterurl= "http://twitter.com/statuses/user_timeline/TwitMyMobile.rss"; 


// Get the rss feed 
// Prepare for asynchronous download 
this.http= new Ajax(); // true means asynchronous request this.http.open("GET", twitterurl, true); 
// When the AJAX request is done, it will call self.DoUpdate() this.http.onreadystatechange= function() { self.DoUpdate(); }; 


// send the AJAX request 
this.http.send(null);


Now, the Twitter.DoUpdate() method can easily parse the data. Use the DOMParser API to parse through the response XML. If the content type is not set correctly, we get the response as text. Next, we loop through traversing the elements, creating buttons and gathering tweets. The while loop traverses through the child nodes, and retrieves the title, and publishing date for each of the tweets. The tweet image is set to tweet.png for simplicity. If we get any exceptions and get no data, then we display "Tweetz not tweeting right now" message and intialize the title and date fields accordingly.


Twitter.prototype.DoUpdate= function() {
    if (this.http.readyState== 4) {
        try {
            // Get parsed Doc 
            var xmlDoc= this.http.responseXML;
            if (xmlDoc== null)
            { // if the content type is not set  
              // correctly, we get the response 
              // as text 
              var xmlparser= new DOMParser(); 
              xmlDoc = xmlparser.parseFromString(
                       this.http.responseText, 
                       "text/xml");
              var itemElements =
                  xmlDoc.getElementsByTagName("item");
              var loopEnd =
                     Math.min(this.numToShow,
                              itemElements.length);
              // traverse elements & create buttons 
              for (var i = 0; i < loopEnd; i++) { 
                 // iterate through child nodes of 
                 // this item and gather tweets
                 var title = null; 
                 var date = null; 
                 node = itemElements[i].firstChild; 
                 while (node != null) { 
                    if (node.nodeType == 
                         Node.ELEMENT_NODE) {
                       if (node.nodeName == "title") { 
                          // item title 
                          title = getTextOfNode(node); 
                       }else if 
                         (node.nodeName== "pubDate" ||
                          node.nodeName== "dc:date") { 
                          // item publishing date 
                          date = getTextOfNode(node); 
                       } 
                 } end while  


                 node = node.nextSibling; 
             } // end for  
             this.buttons[i].setText("
                 + date + " " + title + "");
             this.buttons[i].setImage("tweet.png");
          } // end if (xmldoc== null
      } // end try
      catch (x) {
         this.buttons[0].setText(
           "Uh-Oh! Tweetz not tweeting right now.");
         for (var i = 0; i < this.numToShow; i++) {

            this.buttons[i].setText("");
            this.buttons[i].setImage(null); 
         } // end for  
      } // end catch
  } // if (this.http.readyState== 4) {} 


 For a complete listing of the code, take a look at the slides I had presented at SEE 2009 on Improving the Mobile Web Developer Experience illustrated below.  If you find this article useful, please feel free to retweet and forward me any comments.