How to make a simple Phonegap App in an hour or less

  • Mar
    16

The objective of this project is to create a random gallery of images using Flickr API, in the first view we will select a category (tag) and in the next one we will start to see random images about that topic.

For this sample application I’m using Ubuntu 13.10 as my development environment. For starters we want to install node.js from Chris Lea’s PPA and then a few npm requirements:


add-apt-repository ppa:chris-lea/node.js
apt-get update; sudo apt-get install nodejs
npm install -g phonegap
npm install -g bower

Start project

To create a new application we need to run the following line in the command prompt


phonegap create randomimage com.talpor.rimage RandomImage

This populates the current directory with PhoneGap’s default application structure.

To test the app in your browser, you need to serve the www folder using a regular web server. I’ll use Python’s SimpleHTTPServer: just run python -m SimpleHTTPServer 8080 inside of your www folder and point your browser at http://127.0.0.1:8080.

 

To simulate the device ready event and some device features I use Ripple Emulator, testing in the browser you should see something like this:

 

Adding Jquery Mobile interface

jQuery Mobile is a HTML5-based user interface system designed to make responsive web sites and apps that are accessible on all smartphone, tablet and desktop devices. For a simple introduction to jquery-mobile I recommend the Maximiliano Firtman post.

For this example we will not need CSS files, we will use default style provided by jquery-mobile-themes.

In the www folder we need to run bower install jquery-mobile-min and bower install jquery

And this will be our new index.html


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=320, height=460, target-densitydpi=device-dpi" />
<link rel="stylesheet" type="text/css" href="bower_components/jquery-mobile-min/jquery.mobile.css" />
<title>Random Image</title>
</head>
<body>
<div data-role="page" id="home" data-theme="c">
<div data-role="header">
<h1>Random Image</h1>
</div>
<div data-role="content">
<ul data-role="listview" data-inset="true" data-dividertheme="b">
<li data-role="list-divider">Galleries</li>
<li><a href="#gallery?tag=nature">Nature</a></li>
<li><a href="#gallery?tag=city">City</a></li>
<li><a href="#gallery?tag=food">Food</a></li>
<li><a href="#gallery?tag=people">People</a></li>
</ul>
</div>
<div data-role="footer">
<h4>&copy; 2014 talPor</h4>
</div>
</div>
<div data-role="page" id="gallery">
<div data-role="header">
<h1>author</h1>
</div>
<div data-role="content">
<div class="photo"></div>
<p class="published"></p>
<a href="#home" data-role="button" data-inline="true">Home</a>
</div>
<div data-role="footer">
<h4>&copy; 2014 talPor</h4>
</div>
</div>

</div>
<script type="text/javascript" src="phonegap.js"></script>
<script type="text/javascript" src="bower_components/jquery/jquery.js"></script>
<script type="text/javascript" src="bower_components/jquery-mobile-min/jquery.mobile.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<script type="text/javascript" src="js/app.gallery.js"></script>
<script type="text/javascript">
app.init();
</script>
</body>
</html>

We only  have two pages, the first one to choose a topic, and the second for the image gallery

If you test it in your browser, you should see this:

 

How to organizate the application

In my opinion the most complicated thing about Phonegap is how to build a scalable and maintainable project. I peronsally like to use a structure based on Paul Irish technique DOM-based Routing. For a simple project like this, using DOM-based routing is not necessary but I want to show how it works.

We will put the javascript code in one file per page (data-role="page") and each file will have an init, bindEvend and load method.

The file app.js will have the Phonegap proposed structure with the global bindings necessary to work our custom DOM-based Routing.

The file look like this:


var app = {
// Application Constructor
init: function() {
$('[data-role=page]').each(function() {
var controller = $(this).attr('id');

if (!app[controller]) {
console.warn('app.' + controller + ' is undefined');
return;
}

if (app[controller].init)
app[controller].init();

if (app[controller].bindEvents)
app[controller].bindEvents();

// define a view for each page with controller
app[controller].view = $(this);
});
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);

$(document).bind('pagechange', function(e, data) {
var controller = $.mobile.activePage.attr('id');
if (!app[controller]) return;

if (app[controller].load)
app[controller].load(e, data);
});

},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicity call 'app.receivedEvent(...);'
onDeviceReady: function() {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function(id) {
if (id == 'deviceready') {
// pass
}
},
error: function() {
alert('error');
}
};

Note the loop is necessary to run init and bindEvents of each file of the application, and the pagechange event necessary to execute the load event when the application enters in a page.

Now we will create the file app.gallery.js, the purpose of this file is to manage the features in the gallery view, the file looks like this.


app.gallery = {
init: function() {
// pass
},
showing: 0,
mutex: false,
gallery: {},
next: function() {
var self = this;
if (self.mutex)
return;
self.mutex = true;
self.showing += 1;
self.showImage();
setTimeout(function() {
self.mutex = false;
}, 1000)
},
load: function(e, data) {
var self = this,
query = data.absUrl.match(/\?tag=.+/);
if (!query) return;
self.showing = 0;
query = query[0].replace('?tag=','');

$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?", {
tags: query,
format: 'json'
}).success(function(data) {
self.gallery = data;
self.next();
}).error(app.error);

},
showImage: function() {
var self = this,
img = $('<img />').attr({
src: self.gallery.items[self.showing].media.m,
width: '90%'
});
self.view.find('h1.ui-title').text(
self.gallery.items[self.showing].author);
self.view.find('.photo').html(img);
self.view.find('.published').text(
self.gallery.items[self.showing].published);
}
}

The objetive of this file is load 20 random images from Flickr public API given a tag and showing them in order.

To finish this tutorial now we will install and use the accelerometer plugin. To install a phonegap plugin we need the url to download and install from the command line. A complete list of pluins is available at build.phonegap.com/plugins

To install the accelerometer plugin, we run in the root of project phonegap local plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-device-motion.git

We could write 60 lines of code to detect the shake event, but that’s unnecesary for for this example because we only want detect a slight movement to change the image.

We put the code to detect the accelerometer event in app.receivedEvent when Phonegap calls deviceready event (if we call it before it won’t work) 


...
receivedEvent: function(id) {
if (id == 'deviceready') {
navigator.accelerometer.watchAcceleration(function(acceleration) {
if (acceleration.x > 0.5)
app.gallery.next();
}, app.error, {frequency: 1000});
}
},
...

We can test this in the Ripple emulator, using the accelerometer tool

Note: For this to work on our mobile device we need to change <access origin="http://127.0.0.1*" /> to <access origin="*" /> in www/config.xml file and add this line <gap:plugin name="org.apache.cordova.device-motion" />

 

Build application

We have two alternatives to building our application

Build locally

To build locally we will need to install the SDK of the platform, and if we want to deploy in iOS we need a machine with OSX and XCode.

I assume that this is not a problem, then, for local build we only need run in the command line:


phonegap local build <platform>

Platform can be android, blackberry, ios, wp7 or wp8

Build using Phonegap Cloud Build

To build in the cloud we will use Phonegap Cloud Build, where we can build unlimited open source applications, and only one private application for free. This alternative has the advantage that we don’t need to have an SDK installed on our machine. To build we just run:


phonegap remote build <platform>

This command requests our Adobe credentials and then uploads our code to the cloud for building.

If we loging to build.phonegap.com we can see a QR code with our applicacion installer to test in our device

Phonegap is like a very good tool for a rapid product development in multiple platforms using known technologies (if you are a web developer). The performance and size of the application are obviously better in native apps, but definitely for me (considering time/effort) it is a good alternative to avoid having to develop a mobile application.

 

GitHub: https://github.com/individuo7/RandomImage


Posted on March 16, 2014, 3:05 p.m.