Clik here to view.

Android loves you
In the former tutorial of this series, we created a sample mobile application that’ll run in an Android mobile device. All the HTML, CSS and JavaScript files, which the application was using, were stored in the /assets/www folder of our mobile project.
You can click here, to get the source code of this tutorial (92.9Kb zipped archive) »»
So in order to convert our chat application into a mobile app, it will be logical to start by simply putting our assets (i.e. HTML, CSS and JS) files into /assets/www folder.
Project Folder Structure
The structure of the folder will be as follows:
Clik here to view.

Project Folder Structure
And the HTML of the page:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Chat Demo</title> <meta name="description" content="Demo Mobile Chat Application"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="shortcut icon" href="ico/chat/chat.ico"> <link rel="apple-touch-icon" href="ico/chat/chat.png"> <link rel="stylesheet" href="css/chat/style.css"> </head> <body> <div id="container"> <header> </header> <div id="main" role="main"> <div id="ChatConsole"> <div id="Loading" class="loading" style="text-align:center"><img src="image/loading.gif" style="vertical-align:middle"> loading...</div> </div> <div id="Controls"> <label for="MessageToSend">message:</label> <input name="MessageToSend" type="text" id="MessageToSend"> <input type="button" id="Action" value="Send!"> </div> </div> <footer> </footer> </div> <script type="text/javascript" charset="utf-8" src="js/o2.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.domhelper.ready.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.eventhandler.core.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.eventhandler.extend.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.stringhelper.core.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.ajax.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.ajaxcontroller.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.ajaxstate.js"></script> <script type="text/javascript" charset="utf-8" src="js/3rdparty/json2/json2.js"></script> <script type="text/javascript" charset="utf-8" src="js/chat/main.js"></script> </body> </html>
I’ll intentionally modifyjs/chat/main.js, to alert any errors or exceptions:
function listen_error(status, statusText, xhr) { alert('error status:"' + status + '" text:"' + statusText + '"'); //unregister(xhr); //retryListen(); } function listen_exception(xhr, exception) { alert('exception ' + exception + ' ' + exception.message); //unregister(xhr); //retryListen(); } Proxy = { listen: function() { var url = config.constants.url.chat.LISTEN; var get = o2.Ajax.get; var concat = o2.StringHelper.concat; var format = o2.StringHelper.format; state.lastIndex = state.lastIndex || 0; var request = get(url, { lastIndex: state.lastIndex }, { oncomplete: listen_complete, onerror: listen_error, onexception: listen_exception }); /*request.controller = new o2.AjaxController(request, { timeout:config.constants.timeout.COMET_TIMED_OUT });*/ } };
Wait… What?
Let us run the project in Android emulator, and see what we get:
Clik here to view.

Status "0" generally means a connection problem.
We get “0″ as the status text, but why?
Simple: Here are the URLs from the module configuration, that we make AJAX calls to:
var config = { ... LISTEN: 'http://10.0.0.2/service/chat_comet.php', SEND: 'http://10.0.0.2/service/chat_send.php' ... };
And let us analyze how PhoneGap retrieves our static HTML file:
package com.phonegap.exampleapp; import android.app.Activity; import android.os.Bundle; import com.phonegap.*; public class exampleapp extends DroidGap { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.loadUrl("file:///android_asset/www/home.html"); } }
That is, PhoneGap simply loads a static resource from the device’s local file system.
However, in chat/main.js, we are trying to make an AJAX call to a PHP service (http://10.0.0.2/service/chat_comet.php) that’s outside the phone’s local file system.
This AJAX call is not allowed due to the cross-domain policy restriction of the browser.
JSONP to the Rescue
We will sort out this cross-domain connectivity problem by simply converting our AJAX calls into JSONP calls. So let’s convert our application to make JSONP requests to the server instead:
We’ll add o2.jsonp.js, o2.jsonpcontroller.js and o2.jsonpstate.js and remove o2.ajax.js.
The final scripts section will be like this:
<script type="text/javascript" charset="utf-8" src="js/o2.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.domhelper.ready.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.eventhandler.core.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.eventhandler.extend.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.stringhelper.core.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.jsonp.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.ajaxcontroller.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.ajaxstate.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.jsonpcontroller.js"></script> <script type="text/javascript" charset="utf-8" src="js/o2.jsonpstate.js"></script> <script type="text/javascript" charset="utf-8" src="js/3rdparty/json2/json2.js"></script> <script type="text/javascript" charset="utf-8" src="js/chat/main.jsonp.js"></script>
And we’ll also slightly modify main.js. I’ve just made a copy of it, renamed it main.jsonp.js and made my changes there, because main.js is being used in another comet tutorial, and altering it would have broken the code of that tutorial.
Here’s the final version of main.jsonp.js:
( function(o2, window, undefined) { /* * 10.0.0.2 for device * localhost for local server * Using 10.0.0.2 just for development * This should point to the actual service url * (like www.example.com) in production. */ var kServiceUrl = 'http://localhost'; var config = { constants: { timeout: { COMET_RETRY: 2000, COMET_TIMED_OUT: 30000 }, url : { chat : { LISTEN: [kServiceUrl, '/o2js.com/trunk/o2.js/service/chat_comet.php' ].join(''), SEND: [kServiceUrl, '/o2js.com/trunk/o2.js/service/chat_send.php' ].join('') } }, keyCode: { NEW_LINE: 13 }, element: { TXT_SEND: 'MessageToSend', BTN_ACTION: 'Action', OVERLAY: 'Loading', CONSOLE: 'ChatConsole' }, template: { CHAT_LINE: '<p>{0}</p>' }, serviceKey: { MESSAGE : 'message', FORMAT: 'format', LAST_INDEX: 'lastIndex' }, serviceValue: { JSONP: 'jsonp' } } }; var state = { lastIndex: 0, retryTimerId: null }; function sendMessage() { var $ = o2.$; var trim = o2.StringHelper.compact; var get = o2.Jsonp.get; var kTxtSend = config.constants.element.TXT_SEND; var kUrl = config.constants.url.chat.SEND; var txtMessage = $(kTxtSend); var message = trim(txtMessage.value); if(!message) { return; } txtMessage.value = ''; var ccs = config.constants.serviceKey var params = {}; params[ccs.MESSAGE] = message; params[ccs.FORMAT] = config.constants.serviceValue.JSONP get(kUrl, params); } function bindEvents() { var $ = o2.$; var handle = o2.EventHandler.addEventListener; var getCode = o2.EventHandler.getKeyCode; var cce = config.constants.element; var kAction = cce.BTN_ACTION; var kTxtSend = cce.TXT_SEND; var btnAction = $(kAction); var txtMessage = $(kTxtSend); handle(btnAction, 'click', function(evt) { o2.EventHandler.preventDefault(evt); sendMessage(); }); handle(txtMessage, 'keydown', function(evt) { var keyCode = getCode(evt); if(keyCode == config.constants.keyCode.NEW_LINE) { sendMessage(); } }); } //overloaded below. var Proxy = {}; function retryListen() { clearTimeout(state.retryTimerId); state.retryTimerId = setTimeout( function() { Proxy.listen(); }, config.constants.timeout.COMET_RETRY); } function unregister(controller) { if(!controller){return;} controller.unregister(o2.JsonpState); } function listen_complete(result, xml, xhr) { var $ = o2.$; var format = o2.StringHelper.format; var output = ''; var cce = config.constants.element; var kLoading = cce.OVERLAY; var kConsole = cce.CONSOLE; var kRetryTimeout = config.constants.timeout.COMET_RETRY; var kChatLine = config.constants.template.CHAT_LINE; var divLoading = $(kLoading); var divChatConsole = $(kConsole); if(!result) { retryListen(); return; } var message = result.message; state.lastIndex = result.lastIndex || state.lastIndex; if(!message || !message.length) { retryListen(); return; } var buffer = []; for(var i=0,len = message.length; i<len; i++) { buffer.push(format(kChatLine, message[i])); } var container = document.createElement('div'); container.innerHTML = buffer.join(''); if(divLoading) { divChatConsole.innerHTML = ''; } divChatConsole.appendChild(container); Proxy.listen(); } Proxy = { listen: function() { var url = config.constants.url.chat.LISTEN; var get = o2.Jsonp.get; var concat = o2.StringHelper.concat; var format = o2.StringHelper.format; state.lastIndex = state.lastIndex || 0; var controller = null; var ccs = config.constants.serviceKey; var params = {}; params[ccs.LAST_INDEX] = state.lastIndex; params[ccs.FORMAT] = config.constants.serviceValue.JSONP; var id = get(url, params, function(response){ listen_complete(response); //We're done with the controller. Unregister before request times out. unregister(controller); }); controller = new o2.JsonpController(id, { ontimeout: function(){ retryListen(); }, timeout:config.constants.timeout.COMET_TIMED_OUT }); } }; o2.ready( function() { bindEvents(); Proxy.listen(); o2.JsonpState.init(); }); }(o2, this));
The application you develop, especially if it is a web application, is a living entity. It will inevitably, grow, change and evolve. In its growth phase, you’ll find yourself in need to rewrite/refactor certain parts of it. That’s when pattern oriented software architecture comes into play Image may be NSFW.
Clik here to view..
Here are the changes we have done:
- We’ve replaced all o2.Ajax.get calls with o2.Jsonp.get calls,
- We’ve replaced all o2.AjaxState objects with o2.JsonpState objects,
- We’ve removed listen_error and listen_exception callbacks, since they cannot be implemented with JSONP requests,
- And finally, we’ve replaced o2.AjaxController with o2.JsonpController
Writing code without thinking of its architecture first, will cost many developer-hours, programming by coincidence, and a mixture of spaghetti code with dirty hacks, which no one else will ever want to touch with the fear of breaking it.
Don’t make your code smell Image may be NSFW.
Clik here to view..
With less than 10 lines of code refactoring, we managed to convert our AJAX chat application to a JSONP chat application. That’s the beauty of the JavaScript Module Pattern, and creating re-usable modules with it. Using well-established design patterns (like the Observer pattern in this example) instead of re-inventing the wheel, also helped us a lot.
Making our Application Ready for Mobile Market
Once we’ve created our application we need to sign it to make it available for public. Different vendors have different strategies and procedures for this. Since we have been developing an Android application so far, we will be signing it for the Android Market. The process is described in depth at this “Signing Your Applications” developer.android.com article.
Creating a Keystore
Let’s begin by creating a keystore:
volkan@ronin:~$ keytool -genkey -v -keystore o2_js_keystore -alias o2_js -keyalg RSA -validity 10000 Enter keystore password: Re-enter new password: What is your first and last name? [Unknown]: Volkan Ozcelik What is the name of your organizational unit? [Unknown]: o2js.com What is the name of your organization? [Unknown]: o2js.com What is the name of your City or Locality? [Unknown]: San Francisco What is the name of your State or Province? [Unknown]: California What is the two-letter country code for this unit? [Unknown]: US Is CN=Volkan Ozcelik, OU=o2js.com, O=o2js.com, L=San Francisco, ST=California, C=US correct? [no]: yes Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 10,000 days for: CN=Volkan Ozcelik, OU=o2js.com, O=o2js.com, L=San Francisco, ST=California, C=US Enter key password for <o2_js> (RETURN if same as keystore password): [Storing o2_js_keystore] volkan@ronin:~$
Exporting the Application
To export the application, open Eclipse, right click on the project and select “Export” from the menu.
Then select “Export Android Application“.
Clik here to view.

Export Android Application from Eclipse
After that, select your project to be exported:
Clik here to view.

Select Project to Export
Then locate the keystore that we’ve just created:
Clik here to view.

Locate the Keystore
After that, choose your key:
Clik here to view.

Choose Your Key
And finally our .apk file is ready to be deployed to an Android mobile device, or to be sent to the Android Market Image may be NSFW.
Clik here to view.
Clik here to view.

Ready to Go!
Conclusion
That was the end of our “Creating A Mobile JavaScript Chat” series Image may be NSFW.
Clik here to view.
Here’s a summary of what we have done so far:
- First, we created a simple comet web service,
- Then, we created a client that consumes this service,
- After that, we implemented an AJAX Observer to handle timeout issues in our client,
- Then we created and ran a sample Android PhoneGap example in eclipse,
- Then, we learned about different ways to overcome the cross-domain problem. We created o2.Jsonp, o2.JsonpController and o2.JsonpState objects to enable cross-domain communication with error handling,
- After that, we bumped into the very same cross-domain wall when running our application in emulator, but since we had a solution at hand, it was not very difficult to handle Image may be NSFW.
Clik here to view. - Then, we saw how to create a keystore, add a key to it, and sign our application with that key to make it market-ready.
As I’ve mentioned before, our final application is not even close to being production-ready.
But, at least, we’ve learned to building blocks and we’ve had an idea of how the overall mobile app deployment and release process works.
Hope you liked it Image may be NSFW.
Clik here to view.
As always, please feel free to share your comments. I’d love to hear them Image may be NSFW.
Clik here to view. .