Developing Google Reader client for Series 40 with Web Tools
This article explains how NewsFlow for Series 40 was developed with Web Tools. The article also covers how you can use 3rd party RESTful web services from your Web App and how to create compelling user interfaces with jQuery Mobile
Reasons: hamishwillee (11 Sep 2012)
This code does not run in Cloud Preview due to unsupported elements in JQuery (e.g. data_role does not work). It should be updated to use supported elements.
Article Metadata
Code Example
Tested with
Compatibility
Platform Security
Article
Contents |
Introduction
NewsFlow is Google Reader client where user can read aggregated news feed with simple user interface. NewsFlow was originally an application written in Qt Quick which runs on MeeGo (N9), Symbian and Maemo devices. As Qt Quick also uses JavaScript it was easy to convert the whole application to full HTML5 web app which runs also nicely on Nokia Asha devices. NewsFlow uses Google Reader APIs so you can learn how to create RESTful API calls to 3rd party services.
Applications highlights the following features of Series 40 Web Tools:
- Password manager
Application also shows on how to create user interface easily with jQuery Mobile, including:
- Pages with navigation
- Dynamic listview based on JSON data
- Displaying AJAX loader
Application UI
The user interface is created with jQuery Mobile. Different views as modeled as Pages and navigation between pages is done part scripted and partly automated.
The HTML part for the app is less than 80 lines. This index.html contains all four app pages:
<!DOCTYPE html>
<html>
<head>
<title>NewsFlow</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/jquery.mobile-1.1.1.min.css" />
<script src="js/jquery-1.7.2.min.js"></script>
<script src="js/jquery.mobile-1.1.1.min.js"></script>
<script src="js/reader.js"></script>
<script src="js/storage.js"></script>
<script src="js/common-global.js"></script>
<script src="js/script.js"></script>
</head>
<body>
<div data-role="page" id="loginpage">
<div data-role="header"><h1>NewsFlow</h1></div>
<div data-role="content">
<div data-role="fieldcontain" class="ui-field-contain ui-body ui-br">
Enter Google credentials. Visit
<a href="http://www.google.com/reader">google.com/reader</a> if
you don't have Google Reader account yet.
</div>
<div data-role="fieldcontain">
<label for="email" class="ui-input-text">Email:</label>
<input type="text" name="email" id="email" value="">
<label for="password" class="ui-input-text">Password:</label>
<input type="password" name="password" id="password" value="">
<button id="loginbutton" data-role="button">Login</button>
</div>
<div data-role="fieldcontain" class="ui-field-contain ui-body ui-br" style="text-align:right;">
© 2012 Tommi Laukkanen
</div>
</div><!-- /content -->
</div><!-- /page -->
<div data-role="page" id="menu">
<div data-role="header"><h1>Menu</h1></div>
<div data-role="content">
<ul data-role="listview">
<li><a id="allitemsbutton">All items</a></li>
<li><a id="unreaditemsbutton">Unread items</a></li>
<li><a id="starreditemsbutton">Starred items</a></li>
<li><a id="logout-button" href="#loginpage" data-transition="slide">Logout</a></li>
</ul>
</div>
</div> <!-- /menu page -->
<div data-role="page" id="itemlistpage">
<div data-role="header"><h1 style="white-space:normal;">Items</h1></div>
<div data-role="content">
<ul id="itemlist" data-role="listview">
<li>Loading...</li>
</ul>
<button data-role="button" id="loadmorebutton">More</button>
<a data-role="button" href="#menu">Back</a>
</div>
</div> <!-- /item list page -->
<div data-role="page" id="detailspage">
<div data-role="content">
<h3 id="itemtitle" style="white-space:normal;">Loading...</h3>
<div id="itemsource"></div>
<div id="itempublished"></div>
<div id="itemauthor"></div>
<div id="itemcontent"></div>
<button id="nextitembutton" data-role="button">Next</button>
<a id="backtolistbutton" data-role="button" href="#itemlistpage" data-transition="slide">Back</a>
</div>
</div> <!-- /item details page -->
</body>
</html>
There are few items that are good to highlight here:
- By using the data-role attributes the jQuery Mobile handles the styling really well and you don't have write much additional CSS at all
- Basic navigation can be easily automated without JavaScript using the href="#pageid" references as shown for example in back button in details view:
When button is pressed the view changes to #itemlistpage with sliding transition animation.
Login and passwords
Small tip: Web Apps support automated password manager so you don't have to write code which would store the username or password into HTML5 storage.
API Calls
API calls to Google Reader API are executed in reader.js JavaScript file. I usually write a single method that will take care of the HTTP requests along with authentication etc. In NewsFlow app the doWebRequest() function looks like this:
function doWebRequest(method, url, params, callback) {
var doc = new XMLHttpRequest();
$.mobile.showPageLoadingMsg();
doc.onreadystatechange = function() {
if (doc.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
var status = doc.status;
if(status!=200) {
showError("Google API returned " + status + " " + doc.statusText);
}
} else if (doc.readyState == XMLHttpRequest.DONE) {
$.mobile.hidePageLoadingMsg();
var data;
var contentType = doc.getResponseHeader("Content-Type");
data = doc.responseText;
callback(data);
}
};
doc.open(method, url);
doc.setRequestHeader("Cache-Control", "no-cache");
doc.setRequestHeader("Pragma", "no-cache");
if(sid.length>0) {
doc.setRequestHeader("Authorization", "GoogleLogin auth=" + sid);
doc.setRequestHeader("Cookie", "SID=" + sidToken);
}
if(params.length>0) {
doc.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
doc.setRequestHeader("Content-Length", String(params.length));
doc.send(params);
} else {
doc.send();
}
}
The function uses XMLHttpRequest to execute the HTTP requests. jQuery Mobile is used here to show loading indicator when request starts:
$.mobile.showPageLoadingMsg();
...and also hiding the same indicator when request readyState is DONE:
$.mobile.hidePageLoadingMsg();
Authentication is handled with "Authorization" header as described in Google Data API documentation.
When authentication is complete and we have user's SID token then API calls are made simply as:
itemsURL = "http://www.google.com/reader/api/0/stream/contents/user/-/state/com.google/reading-list?xt=user/-/state/com.google/read";
doWebRequest("GET", itemsURL, "", parseNews);
...
// Callback method which handles the HTTP response and parses the JSON data
function parseNews(data) {
var doc = JSON.parse(data);
for(var i in doc.items) {
var item = doc.items[i];
parseEntry(item); // Parse news entry to object and append to items array
}
}
HTTP requests are executed in asynchronous fashion so you'll need to pass the callback function as parameter. Callback function is called when HTTP response is done and response data is passed to function as parameter. Usually 3rd party web services provide the data also in JSON format which is easy to parse in JavaScript with JSON.parse(..) command.
Dynamic list views
News items are loaded from the API and parsed from JSON to objects in a JavaScript array. When array is ready we'll redraw the list by looping through the model and appending the object to list as new list item elements. In the following code sample you also see that it is required to refresh the jQuery Mobile listview plugin so that it behaves as expected. When list is generated the click event function is binded to the added list items.
function drawItems() {
$("#itemlist").empty();
for(var i in model) {
var item = model[i];
$("#itemlist").append("<li><a class='itembutton' href='#detailspage' id='item" + i + "'><h3 class='ui-li-header'>" + item.title + "</h3><p class='ui-li-desc'>" + item.published + "</p></a></li>");
}
$("#itemlist").listview("refresh");
$(".itembutton").click(function(){
var itemid = $(this).attr("id");
console.log("Showing item " + itemid);
var itemindex = itemid.replace("item", "");
var item = model[itemindex];
$("#itemtitle").text(item.title);
$("#itempublished").text(item.published);
$("#itemsource").text(item.source);
$("#itemcontent").html(item.desc);
});
}
Last words
Application is released as Open Source and is continually developed so actual source code in Nokia Project site can differ from snippets shown here. If you are interested in the application and how application improves over time then follow the project NewsFlow here.


Contents
Hamishwillee - Thanks for the competition entry
Hi Tommi
Thanks for entering! I've edited this for wiki style - please check it still does what you want. In summary, added a few more categories and removed "Draft" category, used Template:Icode to mark up inline code (in monospace), made the gallery images bigger, used bold to mark up filename, used "code javascript" for better markup of javascript code blocks.
In terms of the app UI (ie does not affect judging of article) I found it odd to have the Back button as something I needed to scroll too - might be worth adding a toolbar with a standard back button?
As a rule I find these "how I created a whole app" articles less useful than "how I did specific action" - simply because documentation on specific tasks is more hidden. That said, you've done a fine job. My thoughts on "further improvement":
Thanks again!
Regards
Hamishhamishwillee 05:41, 1 August 2012 (EEST)
Wmseto - Cloud Preview?
This is cool. I am able to use local preview and see the JQuery Mobile framework, but with Cloud Preview, the style is not showing. Any ideas?wmseto 22:18, 1 August 2012 (EEST)
Eetomla - Improvement ideas
Hamish: Thanks Hamish for the editing and ideas. Indeed it makes sense to spin off some of those articles as their own since there are much more I'd like to write about more specifically.
I didn't use drag'n drop tools for the UI - I wrote all the HTML and JavaScript by hand and indeed I might write an article about that too :)
Wmseto: I really don't know why Cloud Preview is behaving like that. I'll send some feedback to Web Tools forum so maybe someone could take a look into that.eetomla 23:56, 2 August 2012 (EEST)
Hamishwillee - Thanks
Thanks Tommi - spinning off those articles would be great.
Regards
Hhamishwillee 03:30, 6 August 2012 (EEST)