Building mobile apps with Sencha Touch and Phonegap

I recently came into posesion of an Advent Vega android tablet – it’s a 2.2 tablet with a nice big screen and a very sweet price spot (£200). Having already installed a custom ROM and tweaked the settings to  no end to get the tablet to behave how I like, I’ve been more and more interested in actually building mobile apps (actually, the fact that to flash a new ROM onto a virgin device requires you to set up the android SDK gets well on your way!).

This piost will describe how to go about building a fledgling andriod app without having to learn Java, but instead build the entire thing in HTML, CSS and JavaScript. What we won’t go though is all the intial stuff such as getting the JDK, installing the SDK and installing Eclipse – that is all nicely covered in Phonegap’s quick-start documentation.

What we will cover is how to get started with Phonegap and Sencha Touch – the two frameworks that I feel are the best options out there for someone who isn’t versed in Java or Objective-C and wants to rapidly prototype applications that can get up and go.

In this post we’ll walk through building a simple Reddit client for Tablet PC’s – at the moment only for android as I don’t have the spare cash to get a Mac as a build machine.

Our app will be simple – it will pull the posts from the /r/pics subreddit, and display the images in a right hand panel of the app, the application will also enable forward and backward movement through the posts list.

What is Phonegap?

Phonegap is a set of libraries that have been created to build a bridge between a mobile platforms’ native API and a JavaScript ‘Hook’ – what all mobile platform API’s have in common is that they all have a web view – some way of rendering a web page in a discrete window. What PhoneGap does is help you set up a basic natural-API environment that will host your app, and then let you focus on actually building the app in languages more familliar to you such as HTML/CSS for layout and Javascript for functionality.

What is Sencha Touch?

Strictly speaking you don’t need this library, you can start building your apps straight off with Phonegap – however, as all smartphone users can attest to, there’s something about the interface of a mobile app, the momentum-asssisted scrolling, pinch to zoom and swipe gestures to navgate between screens. Sencha Touch (along with XUI and JQuery Mobile) are pre-built frameworks that make building simple User Interfaces easy and pre-styled.

I selected Sencha touch for this post becuase it is by far the most advanced of the lot – jQuery mobile is still in Alpha and XUI doesn’t quite cover what I want out of my layout language.

Above and beyond this is also the fact that Sencha Touch is based on ExtJS, which is a framework for web-app creation and follows a (loosely) MVC approach. It’s a bit of a paradigm shift if you are used to jQuery, as Sencha Touch can generate most of your HTML for you without you ever writing a line yourself, while easily adding hooks to actions that hapen to your on-screen items. At some ppoint in the future I may repeat this process with jQuery mobile, however as it is still in alpha I feel this isn’t for practical use in production scenarios.

Getting Started

First off – the best thing to do to debug and test your app is to set up Xamp or WampServer on your dev machine and allow it to be accessed from anyone in your network.

The reason being that you will be able to use your tablet / phone device to access the web version of the app with the native mobile browser – which would pretty much be exactly the same as the phonegap application – so you can test without deploying via eclipse.

Once you’re happy with everything – go ahead and download Sencha Touch and unzip it to a folder in your www directory so you can access it like this: http://localhost/sencha. This will give you access to the demos, source code and most importantly the API documentation.

Setting up your first project:

First, create a folder in your www directory for your project, in this case we’ll call it andreddit – the exact structure is laid out below:

  • www
  • – andreddit
  • —- lib
  • —— touch
  • —— andreddit
  • —— phonegap
  • —- imgs
  • —- index.html

Pretty straight-forward so far. Your app will be running in index.html, and it will reference your application logic in the libs/andreddit folder, while the sencha files will live under the /touch folder.

Your first job is to copy (or symlink) the sencha folder into the /touch folder, this will make those files available to your application.

Writing some code

Open up your favourit editor (I use Notepad++) and edit index.html, copy and paste the code below into the file:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

&nbsp;

    <link href="lib/touch/resources/css/sencha-touch.css" rel="stylesheet" type="text/css" />

<script type="text/javascript" src="lib/phonegap.0.9.5.js"></script>
<script type="text/javascript" src="lib/andreddit/application.js"></script>
<script type="text/javascript" src="lib/andreddit/models.js"></script>

&nbsp;

&nbsp;

The most important bits here are the script and link tags:

  • sencha-touch-debug.js – this is the sencha touch library in a friendly debug version that will give meaningful error messages
  • sencha-touch.css – all they styling info for your app, not to be used for any custom styles!
  • phonegap.0.9.5.js – Phonegap, you won’t actually be using this
  • application.js – your sencha-touch application
  • models.js – your sencha touch models, more on this later

The models file is a little odd, and it may be worthwhile explaining here how Sencha touch is organised in terms of handling data sources…

Models and Data Stores

Sencha uses Models to represent the structure of a data source – that can be XML or JSON data that s comming in via a web service and the beauty of the model is that it follows a very simple layout – essentially you define the ‘model’ of the data to create a programmatic representation of it’s structure – this is a fairly detailed topic, I’d recommend reading up on it in the main API documentation to find out more – for now, suffice  it to say that you’ll be using models to represent the structure of the data you will be accessing from the Reddit feed.

Having a model is all well and good, however it is irelevant if you don’t have a place to store your data, this is where Sencha has the concept of the ‘Data Stores’, these are essentially the machinery that do all the calling, processing and filtering of your data according to the rules you set out in your models.

To recap – you plug your models into the data store, you plug the data store into a web service / XML feed and then you access the data store from your application logic. This way, if the data changes, you can just modify the store to make that data available throughout your application.

Setting up your model

The reddit API works by sticking a ‘.json’ parameter at the end of a URL, so to get the structure, you could pull the front page and run the returned text through JSONLint – it will give you a much better picture of what the structure of your JSON looks like (instead of the gunk you get back normally!):

Loking at the JSON data – you can see that the root of the data we really want is under data -> children, so we will set up our model respectively, paste the folling into your models.js file:

Ext.regModel('RedditModel', {
fields: [
{name: 'kind', type: 'string'},
{name: 'data'},
],

});

So what is going on here? First, we create a new Model instance – this is called the Ext.regModel, we then give the model a name (‘RedditModel’), and then go on to define the fields that will be found in the data set. You can explicitly define field types using the common identifiers such as boolean, string, long, int, float etc. However in the case of the ‘data’ field, since it is actually further nested JSON, we don’t assign anything and let Javascript figure out that it’s a JSON object on its own.

Models are complicated, and can be nested for related data sets (for example if you have a list of customers and their related orders) – the documentation in Sencha is very good for this, so I’d recommend taking a peek at that if you are applying this to another data set.

Now that we’ve created the model, we can create the data store – this will handle the calls to the service as well as manage the ‘active’ data as you change the parameters of the store programmatically later. Paste the folling underneath your model code:

AndReddit.stores.Posts = new Ext.data.Store({
model: 'RedditModel',
proxy: {
type: 'scripttag',
url: 'http://www.reddit.com/r/pics.json',
method: 'GET' ,
callbackParam: 'jsonp',
reader: {
type: 'json',
root: 'data.children',
},
},
autoLoad:true
});

The Data Store does four things:

  1. Assign the model to the store
  2. Set up a proxy – this is the thing that will access the service, since this is a mobile app, you are 95% guaranteed that you will be accessing data from third-party domains, which means you will need to make JSONP calls instead of standard Ajax JSON requessts. The proxy will take care of this for you by using a ‘scripttag’ type instead of a ‘httpproxy’, the ‘scripttag’ type will manage all the crap around making JSONP calls (HttpProxy will juist perform a standard ‘GET”) so you wonl;t need to worry about the ins and outs, however if you want to know more, please see this page which explains it quite nicely.
  3. Set up a reader – the reader is the thing that interprets the data t put into the store – you can see here that we are using the ‘json’ type (there is also an XML type) and have set a ‘root’ parameter – remember how we stated earlier that all the interesting bits live under data -> children? Well, by setting up the root parameter we can skip all the boring parsing and tell the store to focus only on this one node of the JSON data.
  4. Autoload – this will re-load the store when it is created, you can also call the load() method against the model to manually load the store programatically later (we actually dot his later on, so hold your horses).

So far so boring – we still havenlt made any pretty things! Strictly speaking we’re building this app the wrong way round – starting with the data and then moving to the UI, most processes will focus on UI first. However, in terms of ‘building up’ the application layers, this approach makes the most sense in terms of a step-by-step approach as the display layers consume the data stores in order to work correctly, so having them set up first will save you a lot of time and debugging.

Setting up your Viewport and User Interface

On to the more exciting stuff – Sencha makes it very easy to start prototyping a UI programatically without having to directly manuipulate the HTML yourself – this is done mainly through objects called Panels.

But we’re getting ahead of ourselves here – first you need to set up your application – this is pretty straightforward:

Ext.ns("AndReddit", "AndReddit.stores");
post_index = 0

Ext.setup({
onReady: function() {
new Ext.Panel({
fullscreen: true,
layout: {
type: 'vbox',
align: 'left'
},
items: [],
dockedItems: []
})

}
});

Here we do two things – first we create a namespace for the application (you’ll notice we used this namespace in the models.js to define our data store), we then run the setup function to create our main Panel. The most interesting bit here is the dockedItems property – this is where you can plug in a bunch of new Panels and UI elements.

So lets do just that – paste the following into the docketItems list:

dockedItems: [
{
dock: "top",
xtype: 'toolbar',
ui: "light",
title: "AndReddit - a Reddit client"
},

new Ext.Panel({
dock: "left",
xtype: "panel",
width: "30%",
dockedItems: []

}),
// Add the ViewDock
viewDock

]

 

Also, add this above the Ext.Setup() function:

viewDock = new Ext.Panel({
dock: "left",
xtype: "panel",
id:'viewDock',
width: "70%",
scroll: 'both',
height: "100%",
style: "background-color: #fff",

html: '

<center><img style="margin-top: 30px;" alt="" src="imgs/reddit-logo-webtreatsetc.png" border="0" /></center>'

})

What have we done here? We’ve built our User Interface! I realise it looks like a horrible mess of nested curly brackets – but essentially what we have done is added two items to the main panel:

  1. A toolbar
  2. A left hand panel
  3. A right hand panel (this is just called ViewDock, this is because we want to be able to manipulate it later directly you can use the Cmp() function to call it by name, however being old-school I just assigned it a full blown variable name)

Lets look at the more interesting bits:

First of all, xtype and ui – these are properties that tell Sencha Touch what the item you are creating is – we are using ‘toolbar’ and ‘panel’ in this example.

Next, you’ll notice how we are fitting objects – there are properties we are using a percentage to handle widths as this way we can be sure the UI will display on variable-width screens.

Most importantly, you will notice in the left hand panel that there is another dockedItems[] array, we will be using this to set up our post list and forward and back buttons as follows:

dockedItems: [
{
dock: "top",
xtype: "toolbar",
ui: "light",
style: "border-right: thick solid #000",
items: [{
text: "Back",
ui: "back",
align: "left",
handler: function(button, event){
if (post_index &lt;= 0) {
post_index = 0
}
else {
post_index -= 25
last_id = AndReddit.stores.Posts.getAt(0).get("data").id
AndReddit.stores.Posts.proxy.extraParams = {
'count': post_index,
'before': "t3_" + last_id
},
AndReddit.stores.Posts.load()
Ext.getCmp('post-list').scroller.scrollTo('top', 0);
}

}
},
{
xtype: 'spacer'
},
{
text: "Next",
ui: "forward",
align: "right",
handler: function(button, event){
post_index += 25
numposts = AndReddit.stores.Posts.data.length -1
last_id = AndReddit.stores.Posts.getAt(numposts).get("data").id
AndReddit.stores.Posts.proxy.extraParams = {
'count': post_index,
'after': "t3_" + last_id
},
AndReddit.stores.Posts.load()
Ext.getCmp('post-list').scroller.scrollTo('top', 0)

}
}]

},
{
dock: "left",
id: "post-list",
xtype : 'list',
style: "border-right: thick solid #000",
store : AndReddit.stores.Posts,
itemTpl : '
<div id="{data.url}">{data.title}</div>
'
,
width: "100%",
onItemTap: function(item, index) {

waitTpl = '

<center><img style="margin-top: 30px;" alt="" src="imgs/loading.gif" border="0" /></center>'

viewDock.update(waitTpl)

pxHeight = '200px' //viewDock.getHeight()
pxWidth = '300px' //viewDock.getWidth()
var thisPost = AndReddit.stores.Posts.getAt(index).get("data").url
//var newTpl = "<iframe id="iframe-external" src="&quot; + thisPost + &quot;" height="240" width="320" frameborder="0" scrolling="auto"></iframe>"

var newTpl = '<img style="padding: 20px; max-width: 600px;" alt="" src="'+ thisPost +'" />'
viewDock.update(newTpl)

}
}]

In this code snippet we are:

  1. Adding a toolbar to the left hand panel
  2. Adding a list to the left hand panel
  3. Adding a forward and back button tot he left hand toolbar

Furthermore, we are attaching functionality to the ‘handler’ property of the buttons to move forward and back and onItemTap event of the post list object.

As you can see the post list is plugged into our Data Store using the store property, using the temTpl proeprty we can define how the list item will look and what elements from the data store we want to plug in. We then add the onItemTap event handler, which essentially pulls the record from the data store and modifies the html property of the viewDock, followed by an update() call to make the change visible.

The buttons on the other hand do something more interesting, we use the handlers to modify the extraParams property of the store to modify the web service endpoint to pull the next 25 records from the web service. Now this is specifically for reddit but you can see how just modifying these params and calling load() on the store will also update the list itself – so you won’t need to update display objects that are bound to data stores as updating one will trigger an update in the other.

You’ll have noticed we haven’t written any HTML yet – if you browse to you rindex.html file now though, you should see something that looks like this:

 

What Sencha does is create the HTML for you based on your docking options and how you nest your panels, to create the above we had an overall framework of top bar, left bar and right panel, then sub-divided the left panel into the back/forward toolbar and the post list – Sencha was clever enough to interpret this properly and build up the interface appropriately.

Using the models/stores approach, the list was populated form a web service with relatively little fuss and the loading of new or old posts only required a minor change to the store paramters to make possible.

Testing this on your tablet / Phonegap

Now you may say this is all well and good – but if you’ve set up your HelloWorld app as advised in the Phonegap quick start then you should be able to re-map that to a new AndReddit application. If you have your basic project set up – simply put your lib folder and the index.html file into the assets/www directory of your phonegap project (copy them, do not link them).

Once this is done, plugging in your tablet via USB (make sure debug mode is on and you’ve installed the coreect drivers for your tablet) and right clicking the project and selecting Run As -> Android Application, your tablet should display on the list of available devices, clicking ‘Run’ will then install and run the app on your device.

Round-Up

You’ll have noticed we didn’t actually use any of Phonegap’s functionality to do anything with the device – more on that later, this tutorial was to get you started building a UI that seems familliar using one of the more popular frameworks and how to interface that with the Phonegap method of building Android apps.

However, had you set up your UI the same way and used a list of locations you could activate the mapping API of phonegap and display it in one of your panels, enabling the nice UI interfaces as well as interfacing directly with the device.

I’llbe making the full project avaialble soon, ion the meantime if you want a copy leave a comment :-)

Happy hacking,

Martin