Repurposing PhoneGap Apps as Desktop Apps

I was inspired to write this post by several recent conversations.  I was in a debate about whether with the Flex/Flash platform you could easily repurpose content to the desktop using Adobe AIR (and vice-versa), but that you couldn’t easily do that with PhoneGap applications. (My stance was that yes, you could repurpose content.)

I wanted to make sure that people were aware that you can repurpose your content, and here’s an example of how.

A while back, I wrote a sample PhoneGap application that allows you to browse information from the 2010 US Census.  You can read more about this application and download the source code here. This application supports lots of platforms… iOS, Android, BlackBerry, and web (everything except IE because I was targetting WebKit browsers).

While this application is a mobile app wrapped in the PhoneGap container, I actually didn’t use any PhoneGap-specific libraries, so it was very easy to repurpose as a desktop application.   I created an AIR version of this application, which you can download at:

US Census Browser in OSX

You can build complete AIR applications using HTML and JavaScript, even with full access to AIR APIs (network, file access, etc.).   You can read more about building AIR apps with HTML and JavaScript at: http://help.adobe.com/en_US/air/build/WS5b3ccc516d4fbf351e63e3d118666ade46-7ecc.html

I had to use my Android 2.x branch of the US Census Browser code because the WebKit instance inside of AIR doesn’t support SVG.  I also changed the container scrolling to use normal CSS “overflow: auto” instead of using iScroll for touch-based scrolling. There were a few other one-off CSS changes to tweak the layout in the AIR web container, but otherwise the code is identical.

You just need to create an AIR application XML file and point it to your HTML content, and then package it using ADT.

Here’s my AIR application XML:

[xml]<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/3.1">
<id>com.tricedesigns.USCensusBrowser</id>
<versionNumber>1.0</versionNumber>
<filename>USCensusBrowser</filename>
<name>US Census Browser</name>
<initialWindow>
<content>www/index.html</content>
<visible>true</visible>
<width>1024</width>
<height>768</height>
<minSize>800 600</minSize>
</initialWindow>
<icon>
<image16x16>icons/icon16.png</image16x16>
<image32x32>icons/icon32.png</image32x32>
<image48x48>icons/icon48.png</image48x48>
<image128x128>icons/icon128.png</image128x128>
</icon>
</application>
[/xml]

Notice that the “content” node just points to my “index.html” file.

Here’s the command to package it:

[bash]adt -package -storetype pkcs12 -keystore sampleCert.pfx HelloWorld.air HelloWorld-app.xml *[/bash]

You can read more about this process in the Adobe AIR documentation.

If you were using PhoneGap APIs, you would have to migrate your code to take advantage of AIR APIs, but all other HTML/CSS/JS could be reused with minimal changes.

Even though I used AIR for this example, AIR isn’t the only game in town for HTML-based desktop applications…   There’s an open source project called MacGap, you can use HTA for Windows, and it’s not hard to write a HTML/Web View wrapper for any platform. It’s even been reported that you are going to be able to write apps for Windows 8 purely using HTML & JS, and you would be able to repurpose your code for this as well.

You can check out the AIR version of the US Census Browser at:

Enjoy!

Linked Source Files Across PhoneGap Projects on OSX

If you are manually building PhoneGap projects across multiple platforms, managing source files can sometimes become a little bit tricky. If you are building for Android, you need a project within Eclipse. If you are building for iOS, you need a project within Xcode. If you are building for both, you need to make sure that the code in the Eclipse project is in synch with the code in the Xcode project so that the platform-specific apps are in parity with each other.

Keeping the project source code in synch can be achieved using manual copy/paste between projects, but this is messy and error prone. The synchronization can also be scripted using ANT or other scripting language, but this requires an additional script and/or step in your build process. Although scripting is a reliable process, sometimes you just don’t need the script.

If you don’t want to manually synch things, and you don’t want a script, you can use a symlink to link directory paths. Basically, create a “src” folder outside of each project, and create a symlink reference to the src folder inside of the “www” folder for each project.   Symlinks allow a logical directory mapping, which actually points to another location on the hard disk.

From the command line, you just use the following command:

ln -s source target

To setup your project, first create your directory structure.  I created a parent folder for the project. Inside of that folder, I created a “project-ios” folder, “project-android” folder, and “src” folder.   The “src” folder will contain the shared HTML/JavaScript for the application.   The “project-ios” folder will contain the Xcode project, and the “project-android” folder will contain the Eclipse project.

Project Structure

Next, create the actual iOS and Android projects inside of these folders, following the normal setup instructions:

Once you have set up both projects, you’ll need to configure the symlinks.   Put a copy of the “index.html” file into your “src” directory.  Next, go to the “www” directory for each project and delete the “index.html” file to remove any ambiguity or chance for error.

However, DO NOT DELETE THE PHONEGAP.js files!

The phonegap.1.4.1.js files are platform specific.  The Android version will not work with iOS, and the iOS version will not work with Android.

Next, navigate to your root folder that contains the “src”, “project-iOS”, and “project-Android” folders. Here you will create the actual symlink references.   When doing so, be sure to use the full path to the source and target destinations.  You will need to create a symlink reference from the “src” directory to “project-ios/www/src”, and a symlink reference from the “src” directory to “project-android/assets/www/src”.

If you try to use a relative path from your current location, it will give you errors and a massive headache.     You can use “pwd” to get a reference to the full path of your current directory.

Here are the commands that I used on my system, where the root directory is “/Users/triceam/Documents/dev/phonegap-sharedsource”:

[bash]ln -s /Users/triceam/Documents/dev/phonegap-sharedsource/src/ /Users/triceam/Documents/dev/phonegap-sharedsource/project-ios/www/src
ln -s /Users/triceam/Documents/dev/phonegap-sharedsource/src/ /Users/triceam/Documents/dev/phonegap-sharedsource/project-android/assets/www/src[/bash]

You can see this in my console in the image below:

Create Symlinks

This will create logical links to the root “src” folder, which can be treated like any other directory structure.

Symlink References

In both Eclipse and Xcode, this will show up as a normal folder.  Any edits in one IDE will show up in the other IDE since they are pointing to the same physical directory on the hard disk.

Content in Eclipse & Xcode

Next, you’ll need to update the PhoneGap projects to point to the shared index.html file.

In the Eclipse project, open your main Android activity and change the call to super.loadUrl to refer to the index.html file inside of the linked “src” directory in the onCreate method.

[java]public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.loadUrl("file:///android_asset/www/src/index.html");
}[/java]

In the iOS project, open AppDelegate.m. You’ll also need to update it to reference the index.html file inside of “src”. You’ll just need to edit the start page to “src/index.html” inside of the function: (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions

[objc]self.viewController.wwwFolderName = @"www";
self.viewController.startPage = @"src/index.html";[/objc]

Also, be sure to update the link inside of “src/index.html” to point to the project-specific PhoneGap JavaScript files in the parent directory “../phonegap-1.4.1.js”
(they are not inside of the linked folder):

[html]<script charset="utf-8" type="text/javascript" src="../phonegap-1.4.1.js"></script>[/html]

Adobe’s View of Flex & Future Commitment

Adobe has just published a white paper detailing current views and future commitments to Apache Flex. Included topics are contributions to Apache, commitments to the Flash platform, and commitments to Adobe customers.   I strongly recommend that all Flex developers and project stakeholders read this:

Or, download and read the pdf version.

Here’s an excerpt:

Summary

Adobe believes that Flex is the best solution for enterprise and data-centric application development today and is moving Flex into a community-driven open source project to ensure the continued development and success of Flex for years to come. We are currently in the process of contributing the core Flex SDK, automation libraries, AIR SDK binaries, and documentation to the Apache Flex Project. We will also be contributing Falcon, Falcon JS, Mustella, and BlazeDS.

In addition to these contributions, Adobe is providing a team of full-time Flex SDK engineers who will contribute to and support the Apache Flex Project. These Adobe engineers will work directly with the highly skilled Flex developer community to maintain, support, and evolve the Flex SDK. We remain committed to enabling the success of all existing and new Flex projects.

“What is PhoneGap?” & Other Common Questions

While looking at the analytics for my blog, I’ve recently started to see a lot of search phrases similar to “what is phonegap?”, “how does a phonegap app look?”, “how to get started in phonegap?”, among many, many others.   In this post, I hope to shed some light on some basic questions to help you understand and start working with PhoneGap.

In case you don’t feel like reading the whole thing, here are quicklinks to each question:


What is PhoneGap?

PhoneGap is an application framework that enables you to build natively installed applications using HTML and JavaScript.  The easiest way to think of PhoneGap is a web view container that is 100% width and 100% height, with a JavaScript programming interface that allows you to access underlying operating system features.  You build your user interface using traditional web development skills (HTML, CSS, & JavaScript), and use the PhoneGap container to deploy to different application ecosystems and devices.  When packaged for deployment, the PhoneGap application is a binary distributable file that can be distributed by the “normal” application marketplaces (iTunes, Google App Market, Amazon Market, etc…).

PhoneGap is 100% open source, and also goes by the Apache name “Cordova”.  You can read more about Apache Cordova project status at: http://incubator.apache.org/projects/callback.html

PhoneGap can be used to build applications that target multiple platforms, including Apple iOS, Google Android, Windows Phone, BlackBerry, HP WebOS, Symbian, and Bada.

You can read more about the supported platforms and their supported features at http://phonegap.com/about/features

How does a PhoneGap application typically look?

Since the UI rendering engine is the mobile device’s web browser, PhoneGap applications can literally look like anything.   You can use standard HTML & CSS to make it look like a normal web page, you can use a UI framework like jQuery UI, Kendo UI, SenchaTwitter Bootstrap, or Skeleton (or any other HTML/CSS/JS user interface framework). You can also use CSS styles/themes to make your web content look like native apps, such as iUI to mimic iOS or Android, or bbUI  to mimic BlackBerry.

PhoneGap applications can have static UIs based on normal HTML, or can have dynamic & interactive experiences developed using JavaScript.   It depends upon the specific application, user experience design, target audience, and use cases to dictate how a PhoneGap application will appear.

PhoneGap applications can use pinch/zoom gestures to zoom in & out, or you can lock the viewport scale using the viewport metadata tag.   You can have the page scroll using normal browser behaviors, or you can use a library like iScroll to enable touch-based scrolling of specific container elements.

There really are lots of ways to create a user interface with HTML, CSS & JavaScript, so there really isn’t any “typical” look.   If you do not apply any CSS styles at all, then all user interface elements will use the operating system/browser default for that specific platform.   This includes buttons, links, and color/highlight states.   This behaves in the exact same manner as the operating system’s default web browser.

How do I get started in PhoneGap?

Getting started in PhoneGap is easy.   For 90% of a PhoneGap application, all you need is a text editor.  PhoneGap also integrates with device-specific development environments very easily.   You can view “getting started” guides for all application platforms at the links below:

When developing PhoneGap applications, just keep in mind that you are running code inside of a web browser instance.   You develop your applications using HTML and JavaScript, not native code, so you don’t need anything special.   In fact, I personally do most of my development on the desktop using an HTML editor and the Chrome browser.  When I need device-specific functionality, or I need to test on a device, then I switch over the the device-specific environments.

How do you debug PhoneGap applications?

Debugging PhoneGap applications can sometimes be the trickiest part of development.   If you are testing on a physical device, you can’t always get access to JavaScript exceptions when they happen.   There are a few strategies for debugging PhoneGap applications.

Develop as much as possible on the desktop browser

Since PhoneGap applications are written with HTML, CSS, and JavaScript, you can develop most of them using any HTML editor and debug them within a desktop web browser.  The latest versions of all major web browsers (including Chrome, IE, Firefox, Opera and Safari) provide rich debugging features. In the developer tools for the browsers, you can inspect HTML DOM elements, inspect CSS styles, set breakpoints in JavaScript, and introspect into memory & JavaScript variables.   You can learn more about the desktop browser development tools at:

Once you build the main aspects of your application using desktop tools, you can switch over to a device-specific environment to add device-specific behavior and integrate with PhoneGap APIs.

It is imperative that you test your applications on actual devices!   Actual devices will have different runtime performance than desktop browsers and simulators, and may unearth different bugs/issues including API differences and different UX scenarios.

Debug With debug.phonegap.com

PhoneGap provides a hosted service that allows you to perform remote, on-device debugging through debug.phonegap.com.  This uses the Weinre (Web Inspector Remote) debugging tool to allow you to remotely inspect the DOM, resource loading, network usage, timeline, and console output.   If you have used any of the developer tools listed above, this will look very familiar.  You will not be able to set breakpoints on the mobile device, but it is certainly better than nothing at all.

Remote Web Inspector Through iOS 5

There is a little known undocumented API introduced in iOS5 that allows you to perform remote debugging through the iOS5 Simulator.  You just need to enable remote debugging
[objc]- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Uncomment to enable remote debugging
[NSClassFromString(@"WebView") _enableRemoteInspector];

}[/objc]

Then launch the application in the desktop iOS Simulator. Once the app is running, open a local Safari instance to: http://localhost:9999/. This will launch the remote debugger, complete with breakpoints and script introspection.

More Debugging Info

You can also read more about debugging PhoneGap applications at:

How do you architect PhoneGap applications?

You generally architect PhoneGap applications the same way that you create mobile web experiences. The difference is that the initial HTML assets are available locally, instead of on a remote server.   The PhoneGap application loads the initial HTML, which can then request resources from a server, or from the local environment.   Since PhoneGap is based in a browser, it behaves exactly as you would expect a web browser to behave.  You can load multiple pages; however, keep in mind that once you load/unload a page you may lose any data that is stored in memory via JavaScript.   PhoneGap also supports the single-page web experience model. I strongly suggest using the single-page architecture approach.

Single-Page Architecture

A single-page architecture refers to the practice of having a single HTML page that dynamically updates based upon data and/or user input.  You can think of this as closer to a true client/server architecture where there is a client application (written with HTML & JS) and a separate server structure for serving data.  All client-side application logic resides in JavaScript.  The client application may request data and update its views without reloading the current web page.

Using a Single-Page architecture allows you to maintain data in-memory, in JavaScript, which allows you to have a stateful, yet dynamic user interface.   You can read more about single-page architectures at: http://en.wikipedia.org/wiki/Single-page_application

PhoneGap applications can be architected with any of the common JS architectural frameworks, including Angular, Ember, Backbone, Mustache, etc…   Fellow Adobe evangelist Christophe Coenraets has some great content on these topics.

How do you get PhoneGap apps on devices and into application ecosystems?

PhoneGap applications can be deployed using the same guidelines for native applications for each given platform.   You must follow the rules of each hardware platform/vendor, and there is no way to get around that.     You can compile the executables for each platform yourself using each platform’s specific build process, or you can use build.phonegap.com to compile them for you.   build.phonegap.com is a hosted service that will compile platform-specific application distributable files for you.   In either case, the output of the build process is a platform-specific binary file: IPA for iOS, APK for Android, etc…   You can read more about distributing to various application ecosystems, and each system’s signing/certificate requirements at:

What is the difference between PhoneGap and AIR?

The most fundamental differences between PhoneGap and AIR is that you develop AIR applications using tools rooted in the Flash Platform (Flex, Flash, ActionScript, MXML), and you develop PhoneGap applications using HTML, CSS, & JavaScript.   AIR applications use the AIR runtime, which allows you to have a single code base, with the exact same expected behavior across all supported platforms.   PhoneGap applications run inside of the native web browser component for each supported platform.  For this reason, a PhoneGap codebase may behave slightly different between separate platforms, and you will need to account for this during your development efforts.

Air applications can be built for iOS, Android, BlackBerry Playbook, and the desktop (mac and windows), with future support for Windows Metro (Windows 8 mobile interface). You can read more about AIR’s supported platforms at: http://www.adobe.com/products/air/tech-specs.html

PhoneGap applications can be built for iOS, Android, BlackBerry, Windows Phone 7, HP WebOS, Symbian, and Samsung Bada. You can read more about PhoneGap’s supported platforms at: http://phonegap.com/about/features

ActionScript has strongly-typed objects and supports classical inheritance programming models. AIR applications can also be built using the Flex framework, which allows you to rapidly build enterprise-class applications.   Components in AIR applications are logical objects that have behaviors, properties, and a graphics context.

JavaScript-based applications support prototypal inheritance, and have numerous open-source frameworks/tools that can be used.   HTML/JS applications are all visualized through HTML DOM elements.  HTML interfaces can be created through basic string concatenation or JavaScript templating, but in the end you are really just creating DOM elements that have properties and styles.

There are some fundamental difference in the syntax of building these applications, however the basic concepts of interactive design and interactive development are identical.   Both platforms have valid strengths, which I could write about ad nauseum… I’ll save that for another post.

Where to go next?

Go download PhoneGap and get started at: http://phonegap.com/ or check out what other people have been building in the PhoneGap showcase at: http://phonegap.com/apps

Fun Apps w/ PhoneGap

Here’s a silly/fun app I built ‘after hours’ using PhoneGap.   It is a children’s drawing app built entirely with the HTML5 Canvas element, using a PhoneGap wrapper, targeting the iPad.  I was inspired by magnetic drawing toys that I often use when drawing with my daughter, and this was really, really easy and a lot of fun to build.  I used the exact HTML5 Canvas brush image/sketching technique that I have previously demonstrated – the only change is that I added the new UI style elements and added support for multiple touch points. Otherwise, the drawing logic is identical.

Lil’ Doodle is a great new iPad application for entertaining both you and your children! If you know how to use a children’s magnetic drawing toy, then you know how to use Lil’ Doodle. Pick a “pen” shape, and start doodling. Your imagination is your only limit. If you want to erase everything and start over, just use the slider at the bottom. Doodle and have fun!

Using the HTML5 Canvas inside of PhoneGap has great performance on iOS, and building the application using purely HTML, CSS, and JavaScript made it incredibly simple. After I wrote the core drawing engine for a previous blog post, I whipped up the UI in one evening, and then started user testing with my little beta tester.  She found some issues that I had overlooked, and a few days later I submitted it to the app store.

…and yes, she really does play with it:

The app is currently available for iPad devices on iTunes – I’m about to start researching/testing performance on other platforms, so maybe soon it will be out in other ecosystems… we’ll see.   You can get it now at:

tricedesigns.com/go?lildoodle

Enjoy!

Bleeding Edge HTML5, WebRTC & Device Access

The world is changing… and oh my, it is changing fast.   In the not-too-distant future, many capabilities that were exclusive to plugin-based content will be accessible to the HTML/JavaScript world without any plugin dependencies.   This includes access to media devices (microphone and camera), as well as real time communications.   You might be reading this thinking “no way, that is still years off”, but it’s not.

Just last night I was looking at the new webRTC capabilities that were introduced in the Google Chrome Canary build in January, and I was experimenting with the new getUserMedia API.   WebRTC is an open source realtime communications API that was recently included in Chrome (Canary, the latest dev build), the latest version of Opera, and soon FireFox (if not already), and is built on top of the getUserMedia APIs. Device access & user media APIs aren’t commonly available in most users’ browsers yet, but you can be sure that they will be commonplace in the not-so-distant future.

Below you’ll see a screenshot of a simple example demonstrating camera access.

You can test this out for yourself here: http://tricedesigns.com/portfolio/getUserMedia/

Note: This requires the Google Chrome Canary Build.

The beauty of this example is that the entire experience is delivered in a whopping total of 17 lines of code.   It uses the webkitGetUserMedia API to grab a media stream from the local webcam and display it within a HTML5 <video> element.

[html]<html>
<script>
function load() {
var video = document.getElementById(‘myVideo’);
if (navigator.webkitGetUserMedia) {
navigator.webkitGetUserMedia(‘video’,
function(stream) { video.src = webkitURL.createObjectURL(stream); },
function(error) { alert(‘ERROR: ‘ + error.toString()); } );
} else {
alert(‘webkitGetUserMedia not supported’);
}
}
</script>
<body onload="load()">
<video autoplay="autoplay" id="myVideo" />
</body>
</html>[/html]

While this example is really basic, it is a foundational building block for more complicated operations, including realtime video enhancement and streaming/communications.    Check out this more advanced example from http://neave.com/webcam/html5/, which applies effects to the camera stream in real time:

Note: This also requires the Google Chrome Canary Build.

You can read more about WebRTC, get demos, and get sample code at http://www.webrtc.org

If you want to read more about some of the new “Bleeding Edge” features coming to the web, check out this slide deck by Google’s Paul Kinlan.   You can also read more about the getUserMedia API from Opera’s developer site.

Stage3D & Flex Demo w/ Source

Back in the summer, I was lucky enough to get my hands on some early builds of Stage3D for mobile. I built some simple examples, including basic geometric shapes and simple 3D bubble charts inside of mobile Flex/AIR applications. I have been asked numerous times for the source code, and I’ve finally given in, and am sharing some source code.

I am not posting the full mobile application source code, since Stage3D for mobile is not yet available. However, I have ported the 3D bubble chart example to run in a Flex application targeting the desktop (Flash Player 11). The bubble chart example extends the concepts explored in the basic geometric shapes example.

Before you say “shoot, he didn’t give us the mobile code”, let me explain… When I ported the code from the mobile project to the desktop Flex project, all I changed was code specific to the mobile Flex framework. I changed <s:ViewNavigatorapplication> to <s:Application> and the corresponding architecture changes that were required, and I changed the list item renderers to Spark item renderers based on <s:Group> instead of mobile item renderers.   In the mobile item renderers, all my drawing logic was done using the ActionScript drawing API.  For simplicity in the port, I just used <s:Rect> to add the colored regions in the desktop variant.

That is all I changed!  

The stage3D code between the desktop and mobile implementations is identical.    You can see the desktop port in action in the video below:

Or, you can test it for yourself here:

The source code was intended to be exploratory at best… I was simply experimenting with hardware accelerated content, and how it can be used within your applications.   There is one big “gotcha” that you will have to watch out for if you want Stage3D content within a Flex application… Stage3D content shows up behind Flex content on the display list.   By default, Flex apps have a background color, and they will hide the Stage3D content.   If you want to display any Stage3D content within a Flex application (regardless of web, desktop AIR, or mobile), you must set the background alpha of the Flex application to zero (0).  Otherwise you will pull out some hair trying to figure out why it doesn’t show up.

The source code for the web/Flex port of this example is available at:

This also requires inclusion of the Away3D library, available at:

You can check out my original posts, showing Stage3D on mobile here:

You can also check out a video of this code running on a mobile device (Samsung Galaxy Tab 10.1) below:

Enjoy!

Low Latency & Polyphonic Audio in PhoneGap

UPDATE 10/07/2013: This plugin has been updated to support PhoneGap 3.0 method signatures and command line interface.  You can access the latest at: https://github.com/triceam/LowLatencyAudio


If you have ever tried to develop any kind of application using HTML5 audio that is widely supported, then you have likely pulled all the hair from your head. In its current state, HTML5 Audio is wrought with issues… lack of consistent codec support across browsers & operating systems, no polyphony (a single audio clip can not be played on top of itself), and lack of concurrency (on some of the leading mobile browsers you can only play one audio file at a time, if at all). Even the leading HTML5 games for desktop browsers don’t even use HTML5 audio (they use Flash). Don’t believe me? Just take a look at Angry Birds, Cut the Rope, or Bejeweled in a proxy/resource monitor…

The Problem

You want fast & responsive audio for your mobile applications.   This is especially the case for multimedia intensive and/or gaming applications.

HTML5 audio is not *yet* ready for prime-time. There are some great libraries like SoundManager, which can help you try to use HTML5 audio with a failover to Flash, but you are still limited without polyphony or concurrency. In desktop browsers, Flash fixes these issues, and Flash is still vastly superior to HTML5 for audio programming.

If you are building mobile applications, you can have great audio capabilities by developing apps with AIR. However, what if you aren’t using AIR? In native applications, you can access the underlying audio APIs and have complete control.

If you are developing mobile applications with PhoneGap, you can use the Media class, which works great. If you want polyphony, then you will have to do some work managing audio files for yourself, which can get tricky. You can also write native plugins that integrate with the audio APIs for the native operating systems, which is what i will be covering in this post.

Before continuing further, let’s take a minute to understand what I am talking about when I refer to concurrency, polyphony, and low-latency…

Concurrency

Concurrency in audio programming refers to the ability to play multiple audio resources simultaneously.  HTML5 in most mobile devices does not support this – not in iOS, not in Android.  In fact, HTML5 Audio does not work *at all* in Android 2.x and earlier.  Native APIs do support this, and so does PhoneGap’s Media class, which is based on Android MediaPlayer and iOS AVAudioPlayer.

Polyphony

Producing many sounds simultaneously; many-voiced.

In this case, polyphony is the production of multiple sounds simultaneously (I’m not referring to the concept of polyphany in music theory). In describing concurrency, I refered to the ability to play 2 separate sounds at the same time, where with polyphony I refer to the ability to play the same sound “on top” of itself. There can be multiple “voices” of the same sound. In the most literal of definitions concurrency could be considered a part of polyphony, and polyphony a part of concurrency… Hopefully you get what I’m trying to say. In its current state, HTML5 audio supports neither concurrency or polyphony.  The PhoneGap Media class does not support polyphony, however you can probably manage multiple media instances via javascript to achieve polyphonic behavior – this requires additional work in the JavaScript side of things to juggle resources.

Low Latency

Low latency refers to “human-unnoticeable delays between an input being processed and the corresponding output providing real time characteristics” according to wikipedia.   In this case, I refer to low latency audio, meaning that there is an imperceptible delay between when a sound is triggered, and when it actually plays.   This means that sounds will play when expected, not after a wait.   This means a bouncing ball sound should be heard as you see the ball bouncing on the screen.   Not after it has already bounced.

In HTML5, you can auto-load a sound so that it is ready when you need it, but don’t expect to play more than one at a time.  With the PhoneGap Media class, the audio file isn’t actually requested until you invoke “play”.   This occurs inside “startPlaying” on Android, and “play” on iOS.   What I wanted was a way to preload the audio so that it is immediately ready for use at the time it is needed.

The Solution

PhoneGap makes it really easy to build natively installed applications using a familiar paradim: HTML & JavaScript.   Luckily, PhoneGap also allows you to tie into native code using the native plugin model.   This enables you to write your own native code and expose that code to your PhoneGap application via a JavaScript interface… and that is exactly what I did to enable low-latency, concurrent, and polyphonic audio in a PhoneGap experience.

I created PhoneGap native plugins for Android and iOS that allow you to preload audio, and playback that audio quickly, with a very simple to use API.   I’ll get into details how this works further in the post, but you can get a pretty good idea of what I mean by viewing the following two videos.

The first is a basic “Drum Machine”.  You just tap the pads to play an audio sample.

The second is a simple user interface that allows you to layer lots of complex audio, mimicking scenarios that may occur within a video gaming context.

Assets used in this example from freesound.org.  See README for specific links & attribution.

You may have noticed a slight delay in this second video between the tap and the actual sounds.  This is because I am using “touchStart” events in the first example, and just using a normal <a href=”javascript:foo()”> link in the second.  There is always a delay for “normal” links in all multi-touch devices/environments because there has to be time for the device to detect a gesture event. You can bypass this delay in mobile web browsers by using touch events for all input.

Side Note:  I have also noticed that touch events are slightly slower to be recognized on Android devices than iOS.   My assumption is that this is related to specific device capabilities – this is more noticeable on the Amazon Kindle Fire than the Motorola Atrix.   The delay does not appear to be a delay in the actual audio playback.

How it works

The native plugins expose a very simple API for hooking into native Audio capabilities.   The basic usage is:

  • Preload the audio asset
  • Play the audio asset
  • When done, unload the audio asset to conserve resources

The basic components of a PhoneGap native plugin are:

  • A JavaScript interface
  • Corresponding Native Code classes
You can learn more about getting started with native plugins on the PhoneGap wiki.

Let’s start by examining the native plugin’s JavaScript API.  You can see that it just hands off the JavaScript calls to the native layer via PhoneGap:

[js]
var PGLowLatencyAudio = {

preloadFX: function ( id, assetPath, success, fail) {
return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "preloadFX", [id, assetPath]);
},

preloadAudio: function ( id, assetPath, voices, success, fail) {
return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "preloadAudio", [id, assetPath, voices]);
},

play: function (id, success, fail) {
return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "play", [id]);
},

stop: function (id, success, fail) {
return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "stop", [id]);
},

loop: function (id, success, fail) {
return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "loop", [id]);
},

unload: function (id, success, fail) {
return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "unload", [id]);
}
};[/js]

You would invoke the native functionality by first preloading the audio files BEFORE you need them:

[js]PGLowLatencyAudio.preloadAudio(‘background’, ‘assets/background.mp3’, 1);
PGLowLatencyAudio.preloadFX(‘explosion’, ‘assets/explosion.mp3’);
PGLowLatencyAudio.preloadFX(‘machinegun’, ‘assets/machine gun.mp3’);
PGLowLatencyAudio.preloadFX(‘missilestrike’, ‘assets/missle strike.mp3’);
PGLowLatencyAudio.preloadAudio(‘thunder’, ‘assets/thunder.mp3’, 1);[/js]

When you need to play an effect you just call either the play or loop functions, passing in the unique sound ID:

[js]PGLowLatencyAudio.play(‘background’);
PGLowLatencyAudio.play(‘explosion’);
PGLowLatencyAudio.play(‘machinegun’);[/js]

Next, let’s examine some intricacies of the plugin…   One thing to keep in mind is that I do not have callbacks to the phonegap app once a media asset is loaded.   If you need “loaded” callbacks, you will need to add those yourself.


preloadFX: function ( id, assetPath, success, fail)
params:

id – string unique ID for the audio file
assetPath – the relative path to the audio asset within the www directory
success – success callback function
fail – error/fail callback function

detail:

The preloadFX function loads an audio file into memory.  These are lower-level audio methods and have minimal overhead. These assets should be short (less than 5 seconds). These assets are fully concurrent and polyphonic.

On Android, assets that are loaded using preloadFX are managed/played using the Android SoundPool class. Sound files longer than 5 seconds may have errors including (not playing, clipped content, not looping) – all will fail silently on the device (debug output will be visible if connected to debugger).

On iOS, assets that are loaded using preloadFX are managed/played using System Sound Services from the AudioToolbox framework. Audio loaded using this function is played using AudioServicesPlaySystemSound. These assets should be short, and are not intended to be looped or stopped.


preloadAudio: function ( id, assetPath, voices, success, fail)
params:

id – string unique ID for the audio file
assetPath – the relative path to the audio asset within the www directory
voicesthe number of polyphonic voices available
success – success callback function
fail – error/fail callback function

detail:

The preloadAudio function loads an audio file into memory.  These have more overhead than assets laoded via preloadFX, and can be looped/stopped. By default, there is a single “voice” – only one instance that will be stopped & restarted when you hit play. If there are multiple voices (number greater than 0), it will cycle through voices to play overlapping audio.  You must specify multiple voices to have polyphonic audio – keep in mind, this takes up more device resources.

On Android, assets that are loaded using preloadAudio are managed/played using the Android MediaPlayer.

On iOS, assets that are loaded using preloadAudio are managed/played using AVAudioPlayer.


play: function (id, success, fail)
params:

id – string unique ID for the audio file
success – success callback function
fail – error/fail callback function

detail:

Plays an audio asset.  You only need to pass the audio ID, and the native plugin will determine the type of asset and play it.


loop: function (id, success, fail)
params:

id – string unique ID for the audio file
success – success callback function
fail – error/fail callback function

detail:

Loops an audio asset infinitely.  On iOS, this only works for assets loaded via preloadAudio.  This works for all asset types for Android, however it is recommended to keep usage consistent between platforms.


stop: function (id, success, fail)
params:

id – string unique ID for the audio file
success – success callback function
fail – error/fail callback function

detail:

Stops an audio file.  On iOS, this only works for assets loaded via preloadAudio.  This works for all asset types for Android, however it is recommended to keep usage consistent between platforms.

unload: function (id, success, fail)
params:

id – string unique ID for the audio file
success – success callback function
fail – error/fail callback function

detail:

Unloads an audio file from memory.   DO NOT FORGET THIS!  Otherwise, you will cause memory leaks.


I’m not just doing this for myself, the audio is completely open source for you to take advantage of as well.  You can download the full code, as well as all examples from github at github:

UPDATE 10/07/2013: This plugin has been updated to support PhoneGap 3.0 method signatures and command line interface.  You can access the latest at: https://github.com/triceam/LowLatencyAudio

Enjoy!

2012 Flex User Group Tour: North America Dates Announced

If you hadn’t heard already, Flex has been accepted by the Apache Software Foundation. If you are wondering what exactly this means, or you have asked yourself any of the following questions, then you might want to check out the Adobe Flex User Group Tour:

  • Do you want to learn more about the future of Flex?
  • Do you want to learn more about the Flex transition to the Apache Software Foundation?
  • How can you contribute and help make Flex thrive?
  • Do you have questions that you would like to voice to Adobe?

As promised, Adobe is kicking off The Flex User Group Tour to discuss recent events surrounding Flex and the Flash Platform.   These meetings are intended to help you understand the changes happening with Flex and Flash, the impact to related tools, as well as to educate about the process & transition to Apache.   You can learn more about the user group tour and get an up-to-date listing of dates & cities from the Flex Team blog – be sure to check back periodically for updates. Initial cities include New York, Boston, Denver, Seattle, Los Angeles, Sand Diego, and Dallas.   Expect more cities & countries to be announced at a later date.

We hope to see you at one of the upcoming events. I’m scheduled to speak at the Dallas event in April, and I hop to see you there!

Mobile Web & PhoneGap HTML Dev Tips

Recently I’ve been spending a fair amount of time working on HTML-based applications – both mobile web and mobile applications using PhoneGap.   Regardless of whether you are targeting a mobile web browser or a mobile app using the PhoneGap container, you are still targeting a mobile web browser instance.  If you haven’t noticed, mobile web browsers can often have peculiarities with how content is rendered, or how you interact with that content.   This happens regardless of platform – iOS, Android, BlackBerry, etc…  All have quirks.  Here are a few tips that I have found useful for improving overall interaction and mobile HTML experiences.


Disclaimer: I’ve been targeting iOS and Android primarily, with BlackBerry support on some applications.  I don’t have a Windows Phone device to test with, so I can’t comment on support for the Windows platform.

AutoCorrect and AutoCapitalize

First things first: autocorrect and autocapitalize on Apple’s iOS can sometimes drive you to the brink of insanity.  This is especially the case if you have a text input where you are typing in a username, and it keeps “correcting” it for you (next thing you know, you are locked out of the app).   You can disable these features in web experiences by setting the “autocorrect” and “autocapitalize” attributes of an <input> instance.

Disabled AutoCorrect:
[html]<input type="text" autocorrect="off" autocapitalize="on" />[/html]

Disabled AutoCapitalize:
[html]<input type="text" autocorrect="on" autocapitalize="off" />[/html]

Managing the Keyboard

Have you ever experienced an an app or web site on a mobile device where you have to enter numeric data, and the default keyboard pops up. Before entering any text, you have you switch to the numeric input. Repeat that for 100 form inputs, and try to tell me that you aren’t frustrated… Luckily, you can manage the keyboard in mobile HTML experiences very easily using HTML5 Form elements.

Default Keyboard: Supported Everywhere [html]<input style="width: 400px;" type="text" value="default" />[/html]

Numeric Keyboard: Supported on iOS, Android & BlackBerry (QNX) [html]<input style="width: 400px;" type="number" value="numeric" />[/html]

Numeric Keyboard: Supported on iOS [html]<input style="width: 400px;" type="text" pattern="[0-9]*" value="numeric" />[/html]

Phone Keyboard: Supported on iOS [html]<input style="width: 400px;" type="tel" value="telephone" />[/html]

URL Keyboard: Supported on iOS & BlackBerry (QNX) [html]<input style="width: 400px;" type="url" value="url" />[/html]

Email Keyboard: Supported on iOS & BlackBerry (QNX) [html]<input style="width: 400px;" type="email" value="email" />[/html]

Disable User Selection

One way to easily determine that an application is really HTML is that everything on the UI is selectable and can be copied/pasted – Every single piece of text, every image, every link, etc… Not only is this annoying in some scenarios (and very useful in others), but there may be instances where you explicitly don’t want the user to be able to easily copy/paste content. You can disable user selection by applying the following CSS styles. Note: This works on iOS, and partially works on BlackBerry/QNX for the PlayBook. It did not work on Android in my testing.

[html]<style>
* {
-webkit-touch-callout: none;
-webkit-user-select: none;
}
</style>[/html]

The -webkit-touch-callout css rule disables the callout, and the -webkit-user-select rule disables the ability to select content within an element. More details on webkit css rules from the Mobile Safari CSS Reference. More detail about disabling copy/paste on iOS is available at StackOverflow.com.

Disable Zoom

If you want your content to feel like an app instead of a web page, then I strongly suggest that you disable gestures for pinch/zoom and panning for all use cases where pinch/zoom is not required. The easiest way to do this is to set the viewport size to device-width and and disable user scaling through the HTML metadata tag.

[html]<meta name="viewport" content="width=device-width, user-scalable=no" />[/html]

You can read further detail on the viewport metadata tag from the Apple Safari HTML Reference, or the Mozilla reference.

On a Phone? Integrate With It

Your application can dial phone numbers very easily. Just use a standard web location, but use the “tel:<phonenumber>” URI format.

Test it with Apple Customer Support: 800-275-2273[html]<a href="tel:800-275-2273">800-275-2273</a>[/html]

This technique works on both Android and iOS devices, and I assume other platforms. However, I don’t have the devices to test all of them.

Touch Based Scrolling

Touch-based scrolling is critical to having an application that feels native. I dont mean that the whole page should be able to scroll… Your browser will be able to take care of that alone. Instead I mean that you should be able to scroll individual elements so that they mimic clipped views, lists, or large blocks of content. You should be able to scroll content where it is, and not have to scroll an entire page to reveal something in only one area of the screen. You should minimize scrolling when it may cause poor UX scenarios. This is especially the case in tablet-based applications which have a larger UI than phone-based applications.

Luckily, this is also really easy. I personally prefer the open source iScroll JavaScript library from cubiq.org. iScroll works really well on iOS, Android and BlackBerry – I haven’t tested other platforms, but you can test them out yourself: http://code.google.com/p/iscroll-js/source/browse/#hg%2Fexamples%2Fcarousel

Remove “click” Delays

“Click” events on HTML elements on mobile devices generally have a delay that is caused by the operating system logic used to capture gestural input based on touch events. Depending on the device, this could be 300-500 MS. While this doesn’t sound like much, it is very noticeable. The workaround is to use touch events instead of mouse events: touchStart, touchMove, touchEnd. You can learn more about touch events from html5rocks.com. There’s also a great script from cubiq that adds touch events for you to optimize the experience for onClick event handlers on iOS devices.

Add To Home Screen

If you want your web app to fee like a real app and take up the full screen without using PhoneGap as an application container, then you can always add it to the device’s home screen. Although this can only be done manually through the mobile browser, there are a few open source scripts to guide the user through this processs: cubiq.org or mobile-bookmark-bubble should get you started.

Use Hardware Acceleration

Animations will generally be smoother and faster if your content is hardware accelerated (and the device supports hardware acceleration). You can make html elements hardware accelerated just by adding the translate3d(x,y,z) css style to the element (be sure to set all three x, y, and z attributes otherwise hardware acceleration may not be applied. If you don’t want any translation changes, you can use the translate3d CSS rule with all zero values: translate3d(0,0,0).
[css]transform: translate3d(0,0,0);
-webkit-transform: translate3d(0,0,0);[/css]

In your development/testing, you can even visualize which content is hardware accelerated in both desktop and mobile Safari using the technique shown at http://mir.aculo.us/.

Make You Apps Fast

Last, but certainly not least, make your apps fast. Follow best practices, and be efficient in code execution and the loading of assets (both local and remote). Here are a few links to get you going in the right direction:

I hope these get you moving in the right direction! If you have read this, and aren’t sure what it all means, check out the Adobe Developer Connection to ramp up on HTML5, or theexpressiveweb.com to see what HTML5 & CSS3 can do.