We will first see in this article
what the added values of PhoneGap for HTML5 applications are. We’ll then
discover how to create our very first project where we will retrieve the
accelerometer’s values from our JavaScript code. At last, we will review a
complete HTML5 gaming sample almost ported as-is to PhoneGap to use the
accelerometer available on the Windows Phones.
- Introduction
- PhoneGap:
a framework filling the gap
- Let’s
create our first PhoneGap project
- Getting
the accelerometer’s values from JavaScript
- Review
of a complete sample with the HTML5 Platformer game
- Forcing
the landscape orientation
- Handling
various resolutions
- Loading
the levels with calls to the file system instead of using XHR
- Modification
of the gameplay to use the accelerometer
- Screenshots
of the result and FPS on some phones
- Complete
Visual Studio Solution to download
- Conclusion
Introduction
The Mango update for Windows
Phone came with the support of HTML5 thanks to the embedded IE9 browser.
As the desktop version, the mobile version of IE9 delivers hardware
acceleration through the GPU of your Windows Phone. Thus, combined with
JavaScript, IE9 can now serve as a base of interesting user’s experiences
usually reserved to "native code".
The Pros of using HTML5 as a development
platform is a relative promise to easily re-use parts of the code on others
compatibles platforms like Android or iOS. HTML5 has then driven a lot of
interests from the mobiles developers’ ecosystem during the last months.
However, even if the
HTML5/CSS3/SVG & JavaScript specifications have greatly evolved during the
last months, they still lack some major features to build mobile
applications. Indeed, a phone or a tablet exposes specific capabilities
like: GPS, accelerometer, camera, sending SMS, accessing contacts, etc.
To have access to these
capabilities from the JavaScript code, the W3C has been working now for a while
on what we call "Device APIs" or DAP.
Unfortunately, we can consider that no implementation currently exists of those
specifications as this document seems to confirm: Standards for
Web Applications on Mobile: November 2011 current state and roadmap .
Mozilla has started an interesting work by more or less forking those
specifications via what they call Web APIs to
support their Boot To
Gecko
project. This is then a good news as a form of implementation seems to start
with an on-going discussions with the W3C. However, even if things start to
move slowly, we will probably have to wait for several years before having a
stable official W3C specification implemented widely on all platforms.
So the question is: what should
we do in the meantime? Can HTML5 really address those scenarios?
PhoneGap:
a framework filling the gap
While
waiting on real standardized specifications, we don’t have the choice: we need
to create some bridges between JavaScript and the native code of the
targeted platform to have access to its capabilities. The idea is then the
following one: taking the native languages of each platform (C#, Objective-C
and Java) and creating a framework with these languages that will expose
interfaces to the JavaScript developer.
This is exactly what PhoneGap is doing. Let’s take the Windows
Phone case which is the main topic of this article. A Windows Phone’s PhoneGap
project is simply a Silverlight application hosting the WebBrowser control (and
thus IE9) as well as a Silverlight Assembly written in C# which does the job to
access to the accelerometer, GPS, contacts, camera and so on. In this way, as a
JavaScript developer, you will use a DLL named WP7GapClassLib.dll (the
PhoneGap core runtime) without even knowing it via the usage of the code
embedded in the phonegap-1.3.0.js file. This DLL contains some C# code
which does calls to the Silverlight runtime available on the phone. As the
runtime has access to all the capabilities of the phone, the JavaScript will do
also. The JavaScript library will then act as a gateway between both worlds.
Moreover, the good point of using this library is that your code will most of
the time works as-is on the PhoneGap versions of Android or iOS. PhoneGap
offers then an interesting form of portability.
Please note by the way that the
PhoneGap support for Windows Phone is now totally complete since the recent
1.3.0 version:
At last, PhoneGap offers also
another interesting service. It embeds your .js, .css, .html, .png resources
inside its projects to package it as a classical application. In summary, you
can use PhoneGap to package your HTML5 application for the various
applications' stores. This is for instance the case of the SujiQ
Windows Phone application built using this approach.
Let’s
create our first PhoneGap project
Prerequisites
Here are the very first steps you
need to follow:
- Download the Windows Phone
SDK: Windows
Phone SDK
- Download the last version of
Phone (1.3.0 today) on their site: http://phonegap.com/
- Unzip the downloaded file
- Copy the PhoneGapStarter.zip
and PhoneGapCustom.zip files into \Documents\Visual
Studio 2010\Templates\ProjectTemplates
File->New
project
Once the previous steps are done,
you will be able to create your first PhoneGap project. Start Visual Studio
2010, select the "Visual C#" templates and filter them via the "Gap" keyword.
You should then see a new type of project named PhoneGapStarter:
Name your project "MyFirstPhoneGapProject".
Once done, you will find the files I was talking about before in the Solution
Explorer:
You just now have to insert your
HTML5 application into the "www" directory.
Here are several tips I’d like to
share with you about this default project template:
- never ever touch the phonegap-1.3.0.js
file if you’d like to keep a portable code on other versions of
PhoneGap
- all files you will add inside the "www" directory must be set as "Content"
in the properties window
- instead of the WP7GapClassLib.dll binary file, you can add a reference
to the WP7GapClassLib.csproj C# project available in the "Windows
Phone\framework" directory of the downloaded PhoneGap archive. It will help
you debugging or discovering the native code of the PhoneGap library if needed.
Ok, let’s now start by doing
something normally impossible by default with IE9 Mango: accessing to the
accelerometer’s values from JavaScript.
Getting
the accelerometer’s values from JavaScript
We’re going to see here how to
get the values sent back by the accelerometer (of the emulator or the real
device) in a very simple way.
Open the "index.html"
page and change its default body by this one:
<body>
<h1>Accelerometer sample</h1>
<div id="valueX"></div>
<div id="valueY"></div>
<div id="valueZ"></div>
</body>
We will simply use 3 <div>
tags to display the current X, Y & Z values of the accelerometer.
Next step is to change the last
default <script> block by this one:
<script type="text/javascript">
document.addEventListener("deviceready", onDeviceReady, false);
// variable to output the current x, y & z values of the accelerometer
var valueX;
var valueY;
var valueZ;
// when PhoneGap tells us everything is ready, start watching the accelerometer
function onDeviceReady() {
valueX = document.getElementById("valueX");
valueY = document.getElementById("valueY");
valueZ = document.getElementById("valueZ");
startWatch();
}
// start monitoring the state of the accelerometer
function startWatch() {
var options = { frequency: 500 };
navigator.accelerometer.watchAcceleration(onSuccess, onError, options);
}
// if the z-axis has moved outside of our sensitivity threshold, move the aarvark function onSuccess(acceleration) {
valueX.innerHTML = "X: " + acceleration.x;
valueY.innerHTML = "Y: " + acceleration.y;
valueZ.innerHTML = "Z: " + acceleration.z;
}
function onError() {
alert( }
</script>
Well the code is relatively
self-explicit I think. The very first thing to note is that you need to wait
for the "deviceready" event raised by PhoneGap to be sure to be
in a stable state. You then need to subscribe to this event. In our case, we
will be call-backed into the OnDeviceReady() function. This function is
getting the references to the 3 <div> tags and then asks to be notified
by any changes done inside the accelerometer every 500ms with the startWatch()
function. The notifications will be sent to the onSuccess() function
that will have access to the acceleration object containing the x, y
& z values. You’ll find the complete documentation on the PhoneGap site: PhoneGap
Documentation - API Reference - Accelerometer
This is all you need to do on the
JavaScript side. However, to make it works, you need to specify in the
project’s properties that you want to request access to the device’s sensor.
The capabilities needed for the proper execution of our application are listed
inside the WMAppManifest.xml file available in the "Properties"
directory. By default, since 1.3.0, PhoneGap is listing the strict minimum
capabilities:
<Capabilities>
<Capability Name="ID_CAP_IDENTITY_DEVICE" />
<Capability Name="ID_CAP_IDENTITY_USER" />
<Capability Name="ID_CAP_LOCATION" />
<Capability Name="ID_CAP_NETWORKING" />
<Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
</Capabilities>
It’s then up to you to add the
capabilities you need for your PhoneGap application. In our case, we need to
add this line:
<Capability Name="ID_CAP_SENSORS" />
To be allowed to access to the
accelerometer. You’ll find the complete list of all available capabilities
here: Application
Manifest File for Windows Phone
Ok, we’re logically ready to test
that inside the emulator as a first phase. Press on the magical "F5" key and
let’s have a look to the result:
By moving the virtual phone in
the emulator on the right, you should see the values of the accelerometer
updated. Congratulations!
You can download the complete
source code of this first solution here: http://david.blob.core.windows.net/html5/MyFirstPhoneGapProject.zip
Issues with
phones using French locale
If you’re testing the very same
code on your phone configured to use a French local (as mine for instance! ), you will have the feeling that the application
doesn’t work at all…which is indeed the case. I’ve then spent some time
debugging the code and I’ve discovered that the following exception was raised
inside phonegap-1.3.0.js:
"Error in
success callback: Accelerometer = Syntax error"
After dumping the values, I’ve
seen that the error was due to a tentative of de-serialization of the following
bad formatted JSON string:
"{\"x\":0,00472,\"y\":-0,19879,\"z\":-0,98115}" whereas
the proper EN-US is the following one: "{\"x\":0.00472,\"y\":-0.19879,\"z\":-0.98115}"
Yes, we’re using a coma in France
as a numeric separator.
2 solutions to solve this
problem:
- The lazy one: switch your
phone into EN-US (I know, this is not a solution!)
- Fix the problem that comes from the C# code. For that, replace the
following code from the Accelometer.cs file of the WP7GalClassLib
library:
private string GetCurrentAccelerationFormatted()
{
string resultCoordinates = String.Format("\"x\":{0},\"y\":{1},\"z\":{2}",
accelerometer.CurrentValue.Acceleration.X.ToString("0.00000"),
accelerometer.CurrentValue.Acceleration.Y.ToString("0.00000"),
accelerometer.CurrentValue.Acceleration.Z.ToString("0.00000"));
resultCoordinates = "{" + resultCoordinates + "}";
return resultCoordinates;
}
By this one:
private string GetCurrentAccelerationFormatted()
{
string resultCoordinates = String.Format("\"x\":{0},\"y\":{1},\"z\":{2}",
accelerometer.CurrentValue.Acceleration.X.ToString("0.00000", CultureInfo.InvariantCulture),
accelerometer.CurrentValue.Acceleration.Y.ToString("0.00000", CultureInfo.InvariantCulture),
accelerometer.CurrentValue.Acceleration.Z.ToString("0.00000", CultureInfo.InvariantCulture));
resultCoordinates = "{" + resultCoordinates + "}";
return resultCoordinates;
}
And the sample will now work with
all locales. I’ve filled a bug on this topic here: https://issues.apache.org/jira/browse/CB-141
suggesting the same solution that will normally be shipped with the next
version (2.0.0).
By the way, you’re maybe
wondering how I’ve managed to debug the JavaScript part of my PhoneGap project?
Well, you can simply use the wonderful jsConsole project. If you want to
know more about that, please read one of my colleague’s article: jsConsole
Remote Debugging in IE9 on Windows Phone Mango
Review
of a complete sample with the HTML5 Platformer game
Let’s now review a more complex
sample. My idea was to start from the game I’ve written before. It’s exposed in
this article: HTML5
Platformer: the complete port of the XNA game to <canvas> with EaselJS . I
wanted to see what I should do to make it works on the phone.
First step is to simply
copy/paste the different .js, .png, .css files into the "www" directory and to
mark them as "Content". Here are the others steps to follow.
Forcing the
landscape orientation
The game has to be played in
landscape mode. I’ve then forced this orientation. I’ve also remove the
information bar on the side (the System Tray). For that, you need to open the MainPage.xaml
file and change these properties:
SupportedOrientations="Landscape" Orientation="Landscape"
shell:SystemTray.IsVisible="False"
Handling various
resolutions
As my main goal was to build a
game that could run on the highest number of devices, I need to handle a lot of
different resolutions.
For that, I’ve slightly modified
the initial code of the Platformer you can download in the other
article.
The game is now capable on running at any resolution by applying a rescale
ratio on the images and sprites to draw. Everything is being redrawn based on a
specific ratio that came from the Windows Phone (800x480) which is the 100%
ratio. You can test this version in your desktop browser here: HTML5
Platformer ReScale and try to dynamically resize the browser window.
Moreover, if your screen resolution has a 16/9 aspect ratio, press the F11 key
to play in fullscreen! The experience in this mode is really cool as you can
see in this screenshot:
We’re letting the browser taking
care of the anti-aliasing during this scaling operation. Based on the browser
you’ll use, you will notice also that the performance could vary a lot related
to the size of the window you’ll have. On my machine, IE9/IE10 seems relatively
indifferent to the fullscreen mode or small resolutions by maintaining a stable
60 fps framerate.
Loading the
levels with calls to the file system instead of using XHR
In the initial code, the .TXT
files linked to the each level were stored on the web server and downloaded via
XmlHttpRequest calls. As we’re now running everything client-side in offline
mode, XHR local calls are not very appropriated. I’ve then replaced the initial
code of the PlatformerGame.prototype.LoadNextLevel function by this one:
PlatformerGame.prototype.LoadNextLevel = function () {
this.levelIndex = (this.levelIndex + 1) % numberOfLevels;
var nextFileName = "app/www/assets/levels/" + this.levelIndex + ".txt";
try {
var instance = this;
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
function gotFS(fileSystem) {
fileSystem.root.getFile(nextFileName, null, gotFileEntry, fail);
}
function gotFileEntry(fileEntry) {
fileEntry.file(gotFile, fail);
}
function gotFile(file) {
readAsText(file);
}
function readAsText(file) {
var reader = new FileReader();
reader.onloadend = function (evt) {
instance.LoadThisTextLevel(evt.target.result.replace(/[\n\r\t]/g, ''));
};
reader.readAsText(file);
}
function fail(evt) {
console.log(evt.target.error.code);
}
}
catch (e) {
console.log("Error loading level: " + e.message);
this.LoadThisTextLevel(hardcodedErrorTextLevel);
}
};
I’ve just re-used the code
available in the PhoneGap documentation: FileReader . As
you can see, you have a full access to the Windows Phone file system from
JavaScript with PhoneGap.
Cool tip: to help
you debugging what’s really stored in the Isolated Storage of the phone or not,
you should have a look to this awesome tool: IsoStoreSpy written
by Samuel Blanchard.
Modification of
the gameplay to use the accelerometer
Well, last part is just to mix
all parts of this article to obtain the final result. For that, I’ve added the
following code into the constructor of the Player object in the Player.js
file:
var options = { frequency: 500 };
var that = this;
navigator.accelerometer.watchAcceleration(
function (accelerometer) { that.moveDirectionAccel(accelerometer); },
function () { console.log("Error with accelerometer"); },
options);
Here is the function that will be
call-backed during the accelerometer variations:
Player.prototype.moveDirectionAccel = function(acceleration) {
var accelValue = -acceleration.y;
if (Math.abs(accelValue) > 0.15) {
this.direction = Math.clamp(accelValue * this.AccelerometerScale, -1, 1);
}
else {
this.direction = 0;
}
};
I’ve also added an handler on the
"onmousedown" event on the canvas to jump when the user tap on
the screen.
Screenshots of
the result and FPS on some phones
First, let’s review the result
within the Windows Phone emulator:
We have a framerate varying from
54 to 60 fps on my machine. On a real device, the framerate differs logically
between models. Let’s take the first game level. On the LG E900, the
framerate is around 22 fps. On the HTC
Radar, it’s around 31 fps. On the Nokia Lumia
800, it’s around 42 fps.
The gameplay could then be not
very convincing in most cases. Indeed, I’m using a fullscreen canvas to draw
the whole game. This is not a very good idea for the mobile limited power CPU,
even if the Nokia seems powerful enough to handle this approach. A better
methodology could be to cut the screen stage into various little canvas that
will then be moved by modifying the CSS properties on them. This is what has
been done for the HTML5 version of Angry Birds for instance. Some JS gaming
frameworks start to think about handling this complexity for you. The idea
would then be to code the game once with high level APIs and the framework
could switch between a full frame canvas or various little canvas moved via CSS
based on the performance.
Well, you’ll get it: the HTML5
gaming experience on mobile is currently just at its beginning. But its future
looks very promising!
Complete Visual
Studio Solution to download
You’ll find the complete
source code of the HTML5 Platformer for PhoneGap here: HTML5GapPlatformer.zip
Other useful
resources
Conclusion
PhoneGap offers interesting
perspectives to help you writing applications on the phone. It lets you using
your existing skills in JavaScript, HTML and CSS. PhoneGap won’t necessary
cover all the scenarios currently available with the native development stack
(Silverlight or XNA). But this could be something to have a look to if you’d
like to capitalize on the HTML skills of one of your team. You will have to pay
attention to properly identify the kind of project to work on.
You can also think about mixing
both environments to create hybrid experiences: the main branch would be
written using "HTML5" and some specific parts into native code. Using this
idea, some plug-ins have been created to push a bit further the integration into
the Metro UI: PhoneGap
Plug-ins for Windows Phone . You’ll find for instance one of them
allowing a JavaScript code to update the Tiles.
At last, PhoneGap and HTML5 could
allow a relative portability to other platforms… with some limitations. But
this is a vast subject with passionate debates that would need a complete
dedicated article.
David