Crossing platforms between iOS and Android. Extending PhoneGap project

One of the main reasons to use hybrid mobile application framework like PhoneGap for development is to maintain you project code base in one programming language, while deploying mobile applications across many mobile platforms. PhoneGap is the wrapper for HTML5 applications to make them run on multiple mobile operating systems as native apps. Applications are written in JavaScript and framework provides API’s to access native mobile device functionality. Developers can even write their own plugins to get any specific native OS functionality they want.

I have recently published 4 piece tutorial about developing PhoneGap based mobile application for Apple iOS platform. In the beginning of that tutorial I promised to use the same application JavaScript code for another mobile app which is created to run on Android powered mobile devices. This post is about doing exactly that. I will share my experience of taking PhoneGap application code from iOS based app and moving it over to the Android platform. Some native elements will have to be changed but we will talk about them once we get there. Now, lets do some work.

Creating a new PhoneGap project for Android

First we need to setup PhoneGap framework project for Android application. Detailed steps are listed on official documentation pages, they are much similar to creating new PhoneGap project for almost any mobile platform. You have to download PhoneGap framework, navigate to the lib directory, then to specific mobile OS you are developing for, locate the bin directory there and run a simple console one liner. For Android projects it is the following

 ./create <project_folder_path> <package_name> <project_name>

You have to replace the text with actual values of your project path, package name and project name. Once executed this script will create a fresh Android project with bare bones PhoneGap application in it.
This is the structure of just created Android application project

Building PhoneGap project for Android

If you followed the previous PhoneGap app building tutorial on HTMLCenter you will recognize www directory where all HTML5, CSS and JavaScript files live. If not, www folder is where all your HTML5 application files must be located. Next, in libs directory we see cordova archive being used in the project for providing binding between JavaSript API’s and native Java code in the application. Rest of the project structure is a typical Android mobile application project. By looking at main YummyThings.java class we can see that start url is being served from configurations file which is located in xml directory in the project tree. Default start url for PgoneGap script generated new projects is www/index.html

Migrating PhoneGap application from iOS to Android

We now want to replace contents of www directory with the source files of HTML5 application we have already made earlier.
Have you noticed that files in this directory are similar in both projects, even if one of them is for iOS devices and another one is for Android? This is main power of hybrid mobile app frameworks like PhoneGap. You can keep the same HTML5 code base on all your projects with some minor platform specific tweaks.

After replacing the files I’m going to do some changes to the code of previous app, mainly removing JavaScript code related to the native navigation bar we had in earlier project. And this is a good chance to do a little bit of general refactoring to make my code more readable. Here is the final structure of www directory for this PhoneGap powered Android project

PgoneGap application structure for Android mobile

Lets take a look at the main files.
We have index.html which as we checked earlier is our entry point file and will be loaded first by default. In this file I have simple HTML5 element structure, import of CSS styles and import of JavaScript files. In my PhoneGap applications I prefer to keep index.html file simple and clean. All application dynamic and functionality is performed in JavaScript files.
One important element here is the 'viewport' metatag. Viewports are the virtual borders used by browsers while displaying content. These settings can and will affect how HTML5 elements are displayed to the user and its important to get them right. You can read more on this subject in this well written article and for our project we have to make sure this meta tag has values

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />

Below is the full code for our index.html file

<html>
    <head>
        <meta charset="utf-8" />
        <meta name="format-detection" content="telephone=no" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />

        <link rel="stylesheet" type="text/css" href="css/index.css" />
        <script type="text/javascript" src="js/cordova-2.6.0.js"></script>
        <script type="text/javascript" src="js/jquery.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
        <script type="text/javascript" src="js/webService.js"></script>

        <title>Yummy Things</title>
    </head>

    <body onload="init()">

        <div id="locationplaceholder"></div> 

        <div id="menu">    
            <input id="question" type="text"></input>
            <div type="button" onclick="callWebService()">Find recipes</div>
            <div id="yummy"></div>
        </div>
    </body>
</html>

There are few JavaScript functions referenced here and actual files imported via <script> attribute. The main JavaScript file is index.js and it does the heavy work for our application. Function init() is defined in this file and it adds event listener for initial PhoneGap event 'deviceready'. Lets take a closer look to index.js contents

// Global variables
//
var onMobile = false;

// Declaring all jQeury bindings 
// once document is ready
//
$(document).ready( function(){

    // Once search button is clicked app
    // will show the 'content' element on the screen
    //
    $(".share").click(function (e) {
        $('.content').toggleClass('show');
    });
});

// First we are going to check if this PhoneGap app is running on
// supported mobile device (in our case it will be just iOS/Android)
//
function init(){
    if (navigator.userAgent.match(/(iPhone|iPod|iPad|Android)/)) {
        onMobile = true;
        // If running on mobile device, we have to register
        // for 'deviceready' event before moving forward
        document.addEventListener("deviceready", appInit(), false);

        // Log
        //
        console.log('index.js: running on supported mobile device');
    } else {
        // Log
        //
        console.log('index.js: running in the browser');
        appInit();
    }
}

// Once Cordova is loaded we are going to perform all
// app start tasks and add funcionality to specific DOM events
//
var appInit = function () {
    // Log
    //
    console.log("index.js: appInit() runs..");

    // Getting the last used search keyword
    // and updating the input text field
    // If there is no value, means default
    // keyphrase will be used
    //
    var lastSearch = window.localStorage.getItem("searchTerm");

    // Log
    //
    console.log("index.js: lastSearch="+lastSearch);

    // Prefilling search field 
    if (lastSearch){
        $("#question").val(lastSearch);
    }else{
        $("#question").val('your ingridient?');
    }

    // Getting the location coordinates and 
    // location name
    //
    getLocationName();
}

// Geting location coordinates and updating HTML markup
// with received result
//
function getLocationName () {

    // Log
    //
    console.log("index.js: getting geolocation..");
    navigator.geolocation.getCurrentPosition(onSuccess, onError);

    // Success
    //
    function onSuccess(position) {

      // Capturing values for latitude + longitude
      //
      var latitude = position.coords.latitude;
      var longitude = position.coords.longitude;

      console.log('Received current location: ' + latitude + ',' + longitude);

      // Asking maps.googleapis for name of the city
      //
      var url = 'http://your apiurl.com/?latitude='+latitude+'&longitude='+longitude;

      $.get(url, function(data) {

        // Add results to HTML
        $("#locationplaceholder").html("<div id='location'>Your current location is:<br /><strong>"+data+"</strong><br />Choose food wisely!</div>");

        // Log received content
        console.log("Location = " + data);

      });

    }
    // Error
    //
    function onError(error) {

        console.log('Received geolocation error. code:' + error.code);
        console.log('Received geolocation error. code:' + error.message);

        // Leting user know if he has disabled 
        //
        if (error.code === 1){

            $("#locationplaceholder").html("<div id='location'>You have disabled acces to your current location. Check the app settings if you want YummyThings to know where you currently are!</div>");
        }
    }
}

First, after declaring global variables and binding jQuery events for DOM elements init() function is declared. It checks where this HTML5 application is running – on our supported mobile devices or not. If not, we presume application is running in the standard web browser (its very handy for debugging application in the browser!). If we run on mobile device we have to wait till 'deviceready' is triggered by PhoneGap before performing any API calls related to the framework.

Next we retrieve the last search parameter from local storage and pre filling search box for the user. We are also retrieving Geolocation coordinates and running quick query to Google Maps API to obtain location name based on longitude and latitude our app has. If user has disabled application access to the location data we will show him a notification.

Remaining JavaScript file webService.js performs a call to web service endpoint and updates HTML5 element with received data based on whats happening on the application screen. The web service end point being called is our own url where a simple php file is hosted and acting as a demo API. We have created this file in one of the earlier tutorials here on HTMLCenter and I’m including it with the source files for this tutorial as well.

Once compiled and installed onto device here is how Yummy application looks on Android powered phone

Porting PhoneGap project to Android

From the look and feel point of view its similar to our previous iOS version of Yummy Things application but remember its HTML5, CSS and JavaScript so with a little bit of these skills you can make application look as nice as you want.

Remaining bits and pieces

We will also need to change default Cordova application icon in /res directory within the project. There are different dimensions required for different screen resolutions. More detailed explanation is given in official Google Android developer guide. The same goes to splash screen. In order to add one to the PhoneGap Android project we have to make sure that config.xml file has the following plugin declared (by default it has)

<plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>

We then go to main YummyThings.java file and add the following lines in main onCreate() method to actually set the splash screen to be displayed for specific time frame

 
super.setIntegerProperty("splashscreen", R.drawable.splash);
super.loadUrl(Config.getStartUrl(), 5000);

This finishes migration of our example PhoneGap iOS project to Android platform. YummyThings example mobile application is now supported on both these platforms.

Conclusions and whats next?

Overall it was not very hard work to port over existing PhoneGap wraped HTML5 application to a different platform. In the real life application you probably would want to consider some specific UX element differences between platforms so application users are getting what they are used to while using their Android mobile apps.
Like menu items, navigation back and accessing application settings. All these are a little bit different for each mobile OS and to have really appealing mobile application you have to make it easy for users who are used to performing tasks in specific way.
But the core application functionality can be easily maintained within the single JavaScript code base.

As always I’m adding source code for today’s finished tutorial on GitHub. If you have any questions about the above code, leave them under this post, I’ll be happy to answer.

10 Responses

  • Michele

    Hi, i have a question about cordova project.
    If i need some HTML difference beetween Android and iOS build there is a way to have a single source or i need split the project in two single project with different file?
    For example i have a project that use a jquery mobile single page and i need an Android DatePicker that i don’t use with iOS, so the .js included for Android need to be different, and maybe also a little bit of html.
    It’s heavy manage update for two platform, the best solution is have a single source, it is possible?

    • Its a good question as usually majority of the code for PhoneGap apps will be the same for iOS and Android but some bits will be different.
      I would suggest using Grunt.js. Its a great task runner for JavaScript projects.
      It would build your files for iOS and Android apps with differences you need. And Grunt can also run tests for your mobile apps.

  • Su

    Hi,
    Thanks for good explanation. This is very helpful for me. I am new to mobile app. I have experiences in web (especially front end) developing. So I used cordova phonegap to create a cross platform app. And I purely use client side language with a java restful API to connect to database and retrieve data. In my app, whenever the users(clients) choose one item, I want to notify the admin. But I am lost at that point currently and don’t know where to start. What type of notification shall I use and how am I suppose to use to be compatible with different platform?? Thanks in advance! :)

    • Hi Su,
      Not sure I understand your question correctly.
      Are you trying to collect analytic of how mobile app users are accessing your restful API? Give us a little bit more information

  • Sangeetha Pandiyarajan

    Hi, its a nice tutorial…
    I want to develop an app using PhoneGap with visual studio. is it possible?
    can i deploy it for both android and ios devices? could you please provide some link or tutorials. thanks.

  • mourad

    thanks for your tutorial Saulius.
    I was trying to do the same thing with Intel XDK.
    2 things: 1. Yummly is not free anymore, so I used a free api ( http://www.recipepuppy.com/api/)
    2. I copied the php file on my host and I did change webService.js (var url = “http://www…./index.php/?q=” + question + “&requirePictures=true”;))
    and same thing on index.js.
    it’s not working :) any suggestions?

    • hi, mourad

      I guess whats happening is that index.php file in this example was created to look for data in responses from Yummly api.

      Why don’t you try pointing your application directly to this receipepuppy API instead?

      You can do it on this line https://github.com/sauliuz/phonegap-yummythings-android/blob/master/www/js/webService.js#L36

      If receipepuppy API supports CORS you should be fine.

      • mourad

        oh I see, you mean instead of pointing the php file hosted on my server, i will just give the like to the api? Let me try and see if it’s working. Thanks.

      • mourad

        I tried this and it’s not working.
        var url = “http://www.recipepuppy.com/api” + question + “&requirePictures=true”;
        any idea?