Building Cross-Platform Mobile Applications with HTML5 and Mango

5 Comments July 14, 2011

One of the most exciting new features coming in Windows Phone 7.1, code-named “Mango,” is an HTML5 Web browser based on IE9. One by-product of this addition is that you can use Mango phones to browse HTML5 Web sites. But the greater implication is that developers can now use HTML5 to build cross-platform mobile applications that run on Windows phones as well as iPhones and Android phones.

HTML5 isn’t a perfect solution for building cross-platform mobile apps – you can’t, for example, leverage native features such as Mango’s Pivot and Panorama controls from HTML5, nor would you want to since those controls don’t exist on other platforms – but for apps that don’t need platform-specific features anyway, HTML5 offers a handy solution to the problem of having to write the same app multiple times to get it running on different devices.

To demonstrate the basic steps involved in writing Mango apps in HTML5 and packaging them and deploying them to a phone, I wrote the app pictured below. It’s a classic sliding-puzzle game, in which you scramble the picture and then try to get the pieces back in the right place. To move, simply touch a piece that’s adjacent to the empty (gray) square. Each puzzle piece is an HTML5 canvas, and I used jQuery animations to slide puzzle pieces up, down, and sideways. You can download the source code and play with it on your phone if your phone has been flashed for Mango. If not, you’ll have to use the emulator, but be aware that the app only works in the emulator if the PC it’s running on has sufficient GPU support.

HTML5Demo Screen

Writing an HTML5 app for a Windows phone – or any phone, for that matter – begins with the HTML assets. My app contains three such assets:

  • Puzzle.html, the page that comprises the app’s UI
  • jquery.js, which contains version 1.5 of the popular jQuery library
  • scene.jpg, which contains the 480 x 800 image from which puzzle pieces are generated

Puzzle.html is reproduced below. Nothing magical here – just 14 HTML5 canvases, or one for each puzzle piece; some JavaScript code that cuts up the 480 x 800 image and paints the puzzle pieces; and more JavaScript code to move a puzzle piece in response to click events. I tested this page in IE9 and other HTML5 browsers before incorporating it into my phone app.

 

<!DOCTYPE html>

<html>

<head>

<meta name="viewport" content="width=480, height=800, user-scalable=no" />

<style type="text/css">

body {

    margin: 0;

    height: 100%;

    background-color: #808080;

}

#main {

    position: absolute;

    width: 480px;

    height: 800px;

}

</style>

 

<script type="text/javascript" src="jquery.js"></script>

 

<script type="text/javascript">

    var _row = 4; // Row containing empty square

    var _col = 2; // Column containing empty square

 

    $(document).ready(function () {

        // Load the puzzle image

        var image = new Image();

        image.src = "scene.jpg";

        image.onload = function () {

            // Draw sections of the image into the puzzle pieces

            for (var row = 0; row < 5; row++) {

                for (var col = 0; col < 3; col++) {

                    if (!(row == 4 && col == 2)) {

                        var dc = document.getElementById("c" +

                            row + col).getContext("2d");

                        dc.drawImage(image, col * 160, row * 160, 160, 160,

                            0, 0, 160, 160);

                    }

                }

            }

 

            // Wire event handlers to all the puzzle pieces

            $("canvas").click(onClick);

        }

    });

 

    function onClick(e) {

        var piece = $(this);

 

        // Get the row and column

        var row = piece.position().top / 160;

        var col = piece.position().left / 160;

 

        if (row == _row && col == _col + 1) {

            // Move left

            piece.animate({ left: "-=160px" }, 100);

            _col++;

        }

        else if (row == _row && col == _col - 1) {

            // Move right

            piece.animate({ left: "+=160px" }, 100);

            _col--;

        }

        else if (col == _col && row == _row + 1) {

            // Move up

            piece.animate({ top: "-=160px" }, 100);

            _row++;

        }

        else if (col == _col && row == _row - 1) {

            // Move down

            piece.animate({ top: "+=160px" }, 100);

            _row--;

        }

    }

</script>

 

</head>

<body>

 

<div id="main">

    <canvas id="c00" width="160px" height="160px" style="position: absolute;

        left: 0px; top: 0px; border: 1px solid gray"></canvas>

    <canvas id="c01" width="160px" height="160px" style="position: absolute;

        left: 160px; top: 0px; border: 1px solid gray"></canvas>

    <canvas id="c02" width="160px" height="160px" style="position: absolute;

        left: 320px; top: 0px; border: 1px solid gray"></canvas>

    <canvas id="c10" width="160px" height="160px" style="position: absolute;

        left: 0px; top: 160px; border: 1px solid gray"></canvas>

    <canvas id="c11" width="160px" height="160px" style="position: absolute;

        left: 160px; top: 160px; border: 1px solid gray"></canvas>

    <canvas id="c12" width="160px" height="160px" style="position: absolute;

        left: 320px; top: 160px; border: 1px solid gray"></canvas>

    <canvas id="c20" width="160px" height="160px" style="position: absolute;

        left: 0px; top: 320px; border: 1px solid gray"></canvas>

    <canvas id="c21" width="160px" height="160px" style="position: absolute;

        left: 160px; top: 320px; border: 1px solid gray"></canvas>

    <canvas id="c22" width="160px" height="160px" style="position: absolute;

        left: 320px; top: 320px; border: 1px solid gray"></canvas>

    <canvas id="c30" width="160px" height="160px" style="position: absolute;

        left: 0px; top: 480px; border: 1px solid gray"></canvas>

    <canvas id="c31" width="160px" height="160px" style="position: absolute;

        left: 160px; top: 480px; border: 1px solid gray"></canvas>

    <canvas id="c32" width="160px" height="160px" style="position: absolute;

        left: 320px; top: 480px; border: 1px solid gray"></canvas>

    <canvas id="c40" width="160px" height="160px" style="position: absolute;

        left: 0px; top: 640px; border: 1px solid gray"></canvas>

    <canvas id="c41" width="160px" height="160px" style="position: absolute;

        left: 160px; top: 640px; border: 1px solid gray"></canvas>

</div>

 

</body>

</html>

 

Once the HTML assets are finalized, the challenge is to package them into a native phone app – one that can be deployed through the Windows Phone Marketplace like any other app. Here were the steps involved in this example:

  1. Create copies of Puzzle.html, jquery.js, and scene.jpg in isolated storage
  2. Create a WebBrowser control and point it to Puzzle.html

Here’s the corresponding code in MainPage.xaml.cs. “Browser” is the name assigned to the WebBrowser control I declared in MainPage.xaml. (Be sure to set the control’s IsScriptEnabled property to true; otherwise, your JavaScript won’t work very well, to put it mildly.)

 

// Constructor

public MainPage()

{

    InitializeComponent();

 

    // If the HTML assets aren't in isolated storage, put them there

    using (IsolatedStorageFile store =

        IsolatedStorageFile.GetUserStoreForApplication())

    {

        if (!store.FileExists("Puzzle.html"))

            CreateFile(store, "Puzzle.html", Application.GetResourceStream

                (new Uri("Assets/Puzzle.html", UriKind.Relative)).Stream);

 

        if (!store.FileExists("jquery.js"))

            CreateFile(store, "jquery.js", Application.GetResourceStream

                (new Uri("Assets/jquery.js", UriKind.Relative)).Stream);

 

        if (!store.FileExists("scene.jpg"))

            CreateFile(store, "scene.jpg", Application.GetResourceStream

                (new Uri("Assets/scene.jpg", UriKind.Relative)).Stream);

    }

 

    // Once the WebBrowser control has loaded, point it to Puzzle.html

    Browser.Loaded += (s, e) => Browser.Source =

        new Uri("Puzzle.html", UriKind.Relative);

}

 

private void CreateFile(IsolatedStorageFile store, string file, Stream content)

{

    using (IsolatedStorageFileStream stream =

        new IsolatedStorageFileStream(file, FileMode.Create, store))

    {

        using (BinaryReader reader = new BinaryReader(content))

        {

            byte[] data = reader.ReadBytes((int)content.Length);

 

            using (BinaryWriter writer = new BinaryWriter(stream))

            {

                writer.Write(data);

            }

        }

    }

}

 

I added Puzzle.html, jquery.js, and scene.jpg to the phone project and set each file’s build action to Content. (I placed these files in a folder named Assets, which explains the “Assets” in the path names passed to Application.GetResourceStream. For reference, see the solution structure below.) Each time the app starts up, it checks to see if these files are in isolated storage. If they’re not, it puts them there. I chose to put all three files in the root directory of the application’s isolated storage, but you could just as easily segregate them into subdirectories and modify the URIs referencing them in Puzzle.html accordingly.

image

The net result is an app that looks like a native Mango app, but is in fact an HTML5 app that is easily ported to other platforms. Is this the future of mobile development? You be the judge. What’s important is that it’s finally an option on Windows phones, and that the presence of HTML5 support will only bolster the variety of apps available for these devices.


5 Comments

  • Gravatar Image
    Colin E. July 15, 2011 12:20 PM

    Greta article - I definitely think HTML5 will play a big part in future cross-platform mobile applications. However, it is not going to be an easy journey!

    Have you seen my recent codeproject article where I port a complex WP7 control to HTML5 making it corss-platform:

    http://www.codeproject.com/KB/scripting/SilverlightToHTML5.aspx

    Regards, Colin E.

  • Gravatar Image
    Building Cross-Platform Mobile Applications with HTML5 and Mango | WP7 Developers Links July 15, 2011 1:24 PM

    PingBack from http://wpdevelopers.ginktage.com/2011/07/15/building-cross-platform-mobile-applications-with-html5-and-mango/

  • Gravatar Image
    Chris Love July 25, 2011 9:38 AM

    Actually, I have created an HTML version of the Panorama control ;). I just have not publically written about it at this point. I have presented it at user groups this year. I also created an HTML5 appbar too.
    As for Mango and HTML5, IE 9 was a good leap to get to where everyone else was about 18 months ago. It is still very weak. It does not support applicationcache, poor hashchange support, lack of animations, transtions, etc make it still very weak.
    As for hardware access, that is coming. I suspect MSFT will have to fully support all the device apis to make Windows 8 work the way they have presented it.

  • Gravatar Image
    Geri November 8, 2011 3:27 PM

    Jeff - Thanks for the HTML 5 articles. I just started to get into it and find your recent blog posts on HTML 5 to be VERY useful. A spec is one thing, but having good examples really speeds the learning process.

    Thanks much!

    Geri

  • Gravatar Image
    Brain Dumps January 13, 2012 6:45 AM

    Jeff's blog is just amazing.

Have a Comment?

Archives

Tags