Use jQuery Mobile to Build a Native Android News Reader App: Part 2

In Part 1 of this tutorial series, we introduced our sample application, described the page flow, and discussed how to construct the pages in the application via jQuery Mobile. In Part 2, we will complete the implementation of our web application.


Application Startup

Having reviewed the basic page structure in Part 1, let us now focus on what happens when index.html is loaded in the browser. When that event takes place, the Categories page will be displayed. Instead of displaying an empty Categories page, we want to restore the news categories from the user's previous visit (during the same or previous browser session). For this reason, every time the user adds a news category we store it in HTML5 localStorage using the DST.js plugin, as will be shown in the "Storing Current News Categories" section. If the user removes a news category, it is removed from the browser's local storage. When the page is loaded in the browser, in jQuery the $(document).ready() function, we get the stored news categories and process them one after another to create a list item for each news category. This is shown below.

Each category is represented by value of the corresponding option in the <select id="category" ...> element in Category Selection page. The currently browsed categories are stored in localStorage as a string of comma separated category values under a key named news. For example, if the user is currently browsing the categories "Top Stories", and "Politics", the localStorage will contain the string topstories,politics that can be retrieved under the key named news. In $(document).ready(), we parse the stored categories and create a string array where each element corresponds to a category. Then, we call the restore() function:

In restore(), if there are no news categories to restore, we simply display the empty Categories page. If there are news categories to restore, we call the getNews() function by passing to it the last news category in the list. Note that the stored news categories constitute an ordered list where the latest added category is the last item. We therefore start restoring from the last category to create the same ordered list stored previously. Another argument to getNews() is the function pointer restoreNews. We will look at both getNews() and restoreNews() below.

The getNews() function constructs the URL for the news category and performs a jQuery ajax() call to that URL passing a function pointer to handle the response. The URL always starts with bridge.php?fwd=http://rss.news.yahoo.com/rss/ and has the news category as its last segment (e.g. bridge.php?fwd=http://rss.news.yahoo.com/rss/topstories) for the "Top Stories" news category. The response is in XML format which we will review soon.

The function restoreNews() is the handler to the AJAX call. It simply forwards the XML response to another function named populateSingleNews(). Then, it calls the previously discussed restore() function. Observe that there is a sequence of recursive function calls as follows:

The variable numNewsToRestore is decremented once in each call to numNewsToRestore. The recursion will continue until the variable numNewsToRestore reaches 0.

The file bridge.php is in the same web server folder as index.html and its main function is to perform an HTTP GET request to the URL in the value of the fwd parameter and then relay the response back without any manipulation. This is a work around for the same-origin restriction of AJAX requests. More discussion on this topic is given in "Forwarding AJAX Requests".


Adding A News Category

Let us now look at how to add news categories. This is done in the populateSingleNews() function. The argument to that function is XML text sent from Yahoo! News as a response to the AJAX query for news items regarding a particular news category. To create the content in our application, we need only a portion of the XML response. Below, we show an example of the XML response for the business category with two news items under that category. That sample XML will be the basis as we review the code for populateSingleNews().

Each title element above corresponds to a headline. In populateSingleNews(), we will parse the XML and add to the list currentNews, a single list item corresponding to the selected news category. The news headlines in the news category will be animated by fading in and out one headline at a time.

A review of populateSingleNews() is done in multiple steps, starting below.

Obtain News Category And Category Description

We cascade the jQuery find(), first(), and text() functions to parse the category and description elements from the XML. After executing the lines above, we have tmpTxt='business' and desc='Business News'.

Create The List Item For The News Category

At this point, we have various variables set as follows:

Next, look at the HTML fragment definitions below. Since these are repeatedly used, we are defining them as constants in our code.

We combine the previously defined variables with the HTML fragments to dynamically define a list item and add it to the list currentNews:

The expression

translates to

This gives us a complete definition of a list item associated with the 'Business News' category. Observe that we use the jQuery DOM manipulation method prependTo() to add this list item to the currentNews list.

This list item above gives insight into formatting of li elements and split list construction in jQuery Mobile. The figure below shows the dynamically constructed list currentNews with the just constructed list item in it. The split list item has two sections. The one on the left has text and the one on the right has a delete button.

Split list item construction

Figure 7. Split list item construction.
  • The description of the news category is enclosed within h3 tags, making it a header.
  • Notice the empty p tags with id="cat_business". Via an animation technique below, we will display the news headlines within those tags.
  • Observe the <a ... id="cat_business_d"> tag. This helps establish the split list. We will use this tag for event handling of the delete button to the right of the list.
  • The icon for the delete button is automatically constructed by jQuery Mobile by setting data-split-icon="delete".
  • The data-split-theme attribute defines a jQuery Mobile specific visual theme for the delete button. You can try one of a, b, c, d, e, as the value of that attribute. If left empty, jQuery Mobile will assign a default theme. We chose d as the theme.

At this point, we have parsed the response XML and dynamically constructed an li element to represent the news category. In the remainder of populateSingleNews(), we need to take care of the following:

  • handle the delete event for the news category
  • handle the event for going to the News page when the user clicks on the news category
  • animate the news headlines for the news category

We discuss these issues below.

Event Handling For Category Deletion

The following lines in our code define an event handler for the a tag with id="cat_business_d". This will be called when user presses on the section of the split list item that contains the delete icon.

The event handler deletes the list item and after some cleanup updates the list of current news category selections.

  • Recall that categoryLi='cat_business_d_li'. We invoke $.doTimeout( categoryLi, false ) in order to terminate the animation of news items for the category. The $.doTimeout() method is part of the jquery-dotimeout-plugin. We will discuss that plugin further in "Animate Function".
  • We find the list item and remove from the document using jQuery's remove() method.
  • Having just removed a news category, the function call storeCurrentNews() refreshes the HTML5 localStorage for the current selection of news categories. This function is further discussed in "Storing Current News Categories".

Going To The News Page

Pressing on the section of the split list item that contains text takes the user to the News page that has a listing of all the news items under the news category. To implement this, we define an event handler as follows:

  • Recall that the value of the variable categoryA is 'cat_business_a'. Thus, the event handler is defined on the a tag surrounding the news category.
  • We first display the progress page, showProgress(), because we want to notify the user immediately that the request has been initiated.
  • Remember the statement category='cat_business'. The variable is constructed so that its first 4 characters are always 'cat_'. Hence, category.substring(4) gives us the category identifier to construct a URL for fetching the news items under that category. In this case, category.substring(4)='business'.
  • We have reviewed getNews() before. This time, it is called with a different handler function, populateNewsItems. We will look at that function in "The News Page".

Animate News Headlines Under The Category

The last piece of code in populateSingleNews gets individual news headlines from the XML text and displays them one by one within the <p id="cat_business"> element via a fade in/out effect.

  • We cascade jQuery find() and each() methods on the XML text to extract headline of each news item (Note that the title element contains the news headline). Those headlines are placed in an array.
  • Recall that the value of variable category is 'cat_business'. Initially, first element in the array is set as the content of the <p id="cat_business"> element via jQuery's text() function.
  • Finally, we call the animate() method, passing to it the array of news headlines, the <p id="cat_business"> element, and the identifier for the list item representing the current news category (categoryLi='cat_business_d_li')

Animate Function

The code listing for the animate() function is given below.

  • Input to the function consists of a string array, a DOM element which is the target of the animation, and a handler to associate with the particular timeout function.
  • We utilize the jquery-dotimeout-plugin, which provides functionality similar to, but more advanced than, JavaScript's setTimeout() method. We define an inner function on the animation target to be executed every 2 seconds (2000 milliseconds, defined as the value of variable TWO_SECONDS). Each time the inner function is executed, content of the target is replaced with the next item in the text array via a fade out/in effect.
  • The animation will continue until $.doTimeout(..., false ) is called passing to it the handle as the first argument. Recall from earlier discussion that the event handler for deleting a news category contained the statement $.doTimeout( categoryLi, false ) where the variable categoryLi is the handle being passed to animate() for the corresponding news category. In other words, before deleting a news category, we first stop the animation on that news category.

At this point, we have completed the review of the populateSingleNews() function.


The News Page

In "Going To The News Page", we talked about populateNewsItems(). That function is part of the event handler that gets triggered when the user presses on the text section of the split list item for a news category. After displaying the progress page, the event handler calls getNews(), passing into it populateNewsItems() as the callback handler. Remember that getNews() performs an AJAX call to the Yahoo! News server, via index.php, to get the news items for a particular news category. We previously provided a sample XML response. Below, we expand the sample by providing description elements for two news headlines:

The content of the first description element is rather simple. It only contains text. The second description element has an img tag that serves as a link to further detail on the news item.

Let us look at the populateNewsItems().

  • We parse the XML text to find all the item elements, by cascading jQuery's find() and each() functions. For every item element, we then parse out the description sub-element, enclose it between p tags, add a horizontal line to the end, and append the resulting text to a variable named tmpTxt.
  • Finally, we use the jQuery html() function to set the content of <div id="contentNews"> to the value of variable tmpTxt. Remember that <div id="contentNews"> is the content section of the News page.

Storing Current News Categories

Earlier, we talked about storing the current selection of news categories in HTML5 localStorage so that they can be restored later on when the user visits the application again.
This is done in storeCurrentNews(), which is shown below.

  • We use the DST.js plugin for storing the current news category selections in HTML5 localStorage. We start with cleaning up the variable in storage associated with the key named news.
  • Then, we cascade the jQuery find() and each() functions to construct a comma separated string of news categories. The inner function passed to the find() method applies to the list item representing a news category. We cascade the jQuery find() and attr() functions on the list item to extract the value of the id attribute in the p element. Remember that value of this id attribute always starts with cat_ (e.g. id='cat_business'). Hence, calling substring(4) gives us the news category (e.g. business).
  • The news categories are concatenated into a string separated by commas. Finally, we strip off the initial comma from the string and store it in localStorage associated with the key named news.

Adding The News Category On The Category Selection Page

On the Category Selection page, pressing the 'Get Category' button adds the selected news category to Categories page. Code for the button event handler, buttonGetCategoryVar.click(), is listed below:

  • From the listing shown in Part 1, "Category Selection Page", recall that the 'Get Category' button has id="buttonGetCategory" and the
    id of the select element representing the news categories is category. If the user has selected a news category, the event handler first shows the progress page and calls the getNews() function reviewed before. The first argument to the getNews() is the selected news category. The second argument is the callback handler addNews(). If the user has not selected a news category, showCategories() is called (i.e. the Categories page is displayed immediately).
  • Recall from the review of the getNews() that the function makes an AJAX call to get the news items for a news category. The callback handler addNews() is invoked when the AJAX call returns. This function first calls populateSingleNews() to parse the XML response and add a new list item for the selected category in the Categories page. Then, it calls storeCurrentNews() for storing the current selection of news categories in localStorage. Finally, it displays the Categories page by calling showCategories().

Other Event Handlers

We are about to finish the code review of the web application with only a few event handlers left. Those are discussed below:

Going To The Category Selection Page From The Categories Page

From the listing in Part 1, "Categories Page", recall that the 'Add' button on the Categories page has: id="buttonAddCategory". The event handler for the button is shown below. It simply calls the showSelect() function to display the Category Selection page. The review of showSelect() was given in Part 1, "Displaying Pages".

Going To The Categories Page From The News Page

The listing in Part 1, "News Page" showed the structure of the News page. There are two back buttons on that page, one in the header, id="buttonHdrShowCategories", and one in the footer, id="buttonFtrShowCategories", that take the user back to the Categories page. Identical event handlers for those buttons are shown below. They simply show the Categories page by calling showCategories(), which was reviewed in Part 1, "Displaying Pages".

Going To The News Detail Page From The News Page

Observe in the previously shown XML sample that the description element of a news item may have an img tag enclosed by an a tag. This is repeated below for clarity:

Pressing on the image triggers the transition from the News page to the News Detail page, taking the user to the URL in the <a href='...'> attribute. On the News Detail page, details of the news item are shown. If the description element of a news item does not have an img tag then details of the news item will not be available.

Transitioning from the News page to the News Detail page results from a regular HTTP GET request and the News Detail page is displayed outside the index.html file running the News application. In other words, the window.location variable changes from index.html to the URL in the <a href='...'> attribute (Note that in the native Android application, as will be discussed in Part 3 of this tutorial series, transitions from the News page to the News Detail page results from an AJAX call, rather than a regular HTTP GET.)

Going To The Categories Page From The News Detail Page

A transition from the News Detail page to the Categories page happens when the user presses the back button on the browser (In many devices, this is the back button of the device). This changes the window.location variable to index.html again and the Categories page is displayed following the steps described in Application Startup.


Forwarding AJAX Requests

In Part 1, "Request/Response Model", we mentioned that AJAX requests sent via getNews() use bridge.php as an intermediary to access the Yahoo! News URLs. This is due to the same-origin restrictions of AJAX requests in most browsers, in desktop and mobile platforms. Remember how getNews() constructs the AJAX URI:

The bridge.php runs in the same server directory as index.html. The actual news URI is passed to bridge.php as the value of the fwd parameter. For example, an AJAX request from index.html is constructed as follows: bridge.php?fwd=http://rss.news.yahoo.com/rss/business. As a result, bridge.php strips off value of fwd and performs an HTTP GET request to http://rss.news.yahoo.com/rss/business. Then, bridge.php sends the response back to index.html.

The listing of bridge.php is shown below:

Implementation is based on cURL (Client URL Library). The variable val stores the value of the fwd parameter, which is the actual news URL.

  • We initialize cURL via curl_init() and open a temporary file named tmpFile.txt via the fopen().
  • We then instruct cURL to write its output to that file, via curl_setopt($curlHandle, CURLOPT_FILE, $filePointer).
  • The next steps consist of performing the HTTP call via curl_exec() and closing the cURL session via curl_close(). At this point, the response from the cURL session is already written into the temporary file. Hence, we close the file via fclose().
  • We then read the file into an array via $linesArr = file($tmpFile) where each line is in a separate entry of the array. Then, in the foreach() loop we iterate through the array and send the response back.

In a production application, rather than writing the response into a temporary file, you would store it in memory for better performance and to avoid multiple threads trying to write into the same temporary file. Writing into a file is advantageous in terms of debugging. This way, you always have a written copy of the temporary file if you need to inspect its details. Note that the temporary file is written into the same file system folder as bridge.php and your web server must be configured to allow php files to write into the file system.


iPod Touch And Android Device Demo

In figures 8 - 10 below, side-by-side screen images of the web application are shown running in iPod Touch iOS 4.1 (left) and Android OS 2.2 (right) devices. Look and feel as well as functional characteristics of the application are very similar in both platforms with obvious exceptions due to the differences in screen size.

Categories page - iPod Touch

Figure 8. Categories page - iPod Touch & Android.
Categories page - iPod Touch

Figure 9. Category Selection page - iPod Touch & Android.
News page - iPod Touch

Figure 10. News page - iPod Touch & Android.

Web Application Files

We provide an archive file named news-web.zip that contains all the files for the web application. The top level folder is named news. If you place that folder under the root directory of your web server, you can access the web application by http://<your_domain>/news/index.html. A description of the folder structure and the files in news-web.zip is given below.

  • The news folder contains index.html and bridge.php.
  • The news\css-js folder has the css and JavaScript files used by index.html. In particular:
    • jquery-1.4.4.min.js, jquery.mobile-1.0a2.min.js, jquery.mobile-1.0a2.min.css are the jQuery Mobile framework libraries.
    • jquery.ba-dotimeout.js is the jquery-dotimeout-plugin library.
    • jquery.dst.js is the DST.js plugin library.
    • news\css-js\images\icons-18-white.png is an image file referenced by the jQuery Mobile framework libraries.
  • news\img\wait.gif is the spinning icon used on the progress page.

Note that we deliver jquery-1.4.4.min.js, jquery.mobile-1.0a2.min.js, jquery.mobile-1.0a2.min.css, and icons-18-white.png directly from our web server. The css and JavaScript files are referenced in our index.html file as follows:

Another option is to reference jquery-1.4.4.min.js, jquery.mobile-1.0a2.min.js, jquery.mobile-1.0a2.min.css in your html file through the official jQuery Mobile web site as follows.

If you follow that alternative approach, jquery-1.4.4.min.js, jquery.mobile-1.0a2.min.js, jquery.mobile-1.0a2.min.css, and icons-18-white.png do not need to be delivered from your web server.


Closing Remarks

In the second part of this tutorial series, we completed the implementation of our web application. Our main focus was on dynamically constructing a split list and including an animation technique for text in the split list items. In Part 3, we will migrate the web application into a native Android application.

Tags:

Comments

Related Articles