Friday, May 27, 2011

Five Things I Learned This Week

  1. Pepsi Throwback tastes awesome but it screws up my sleep schedule. I guess I'm not used to ingesting 28 grams of sugar in one clip. It's only got 5 ingredients people!
  2. This bit of code can scans your string for url's and wraps them with clickable anchor tags.
  3. Jim Butcher's Dresden Files > Simon R Green's Nightside series.
  4. It is cheaper to fly to Portland, Oregon than it is to fly to Vancouver, BC from Ottawa. I mean, really REALLY!  It's the same damn country.
  5. I've been called up to the big leagues and wil be blogging at the main PhoneGap Blog starting next week.

Thursday, May 26, 2011

Installing the ChildBrowser Plugin on PhoneGap Android

I'm writing up a quick tutorial on how to get the ChildBrowser plugin for PhoneGap Android installed as a few folks on the mailing list are having troubles getting it going. First lets start off with a stock PhoneGap Android project in Eclipse:


Step one is to create the directory structure for the ChildBrowser.java file. Click on the src folder then right mouse click or control click on the folder.  From the popup menu select New and Package.


In the dialog box that appears type in com.phonegap.plugins.childBrowser then click the Finish button. Please note the package name is case sensitive so make sure it matches exactly.


Step two is adding the ChildBrowser.java class to your project.  Download the file from this link and save it in your projects src/com/phonegap/plugins/childBrowser directory.  Then click on you projects name then right mouse click and select Refresh.  Your project explorer should now look like this:

You shouldn't see any red exclamation marks at this point.  If you do you've probably named your package incorrectly.

Step three is to add the childbrowser.js file to your project.  Download the file from this link and save it in your projects assets/www directory. Then click on you projects name then right mouse click and select Refresh. Your project explorer should now look like this:


In step four we will need to modify the AndroidManifest.xml file to add an intent filter for the ChildBrowser.  Paste the following lines inside the <application/> tag:

<activity android:name="com.phonegap.DroidGap" android:label="@string/app_name">
<intent-filter>
</intent-filter>
</activity>

Here is a gist of what my AndroidManifest.xml looks like.

In step five we need to modify our index.html file to a reference the childbrowser.js and call the ChildBrowser.  In the <head/> tag after the reference to phonegap.0.9.5.js add the following line:

<script type="text/javascript" charset="utf-8" src="childbrowser.js"></script>

and somewhere in your <body/> tag add a line like this:

<a href="#" onclick="window.plugins.childBrowser.showWebPage('http://www.google.com');">Open Google.com in child browser</a>

This will give you an app that shows you this:


and opens Google in the ChildBrowser when you click on the link.


The full gist of my index.html is available as well.

Hope this helps everyone!

Wednesday, May 18, 2011

Using the Media Class in PhoneGap

I've seen a lot of posts recently on the PhoneGap Google Group so I wanted to put something together illustrating the use of the PhoneGap Media class. While putting this example together I noticed a few quirks of the Media class which I'll try in describe in more detail so that other folks don't run into the same problems. Also, this example has been tested on Android but it, for the most part, should work on iOS as well. Part of PhoneGap 0.9.6 will be normalizing how the Media class works between all the major platforms so look for improvements soon.

The example will be a basic Media player with play/pause and stop functionality.  Here's a quick screen shot:


On Android you can create a Media object using one of three ways.  The first way is referencing a file in your "android_asset" directory.  For instance if you have a file called "test.mp3" in your projects "assets/www" directory you would create a new media object by doing:
myMedia = new Media("/android_asset/www/test.mp3");
The second way to create a media object is by referencing a file on your devices file system. Say for instance, you have a file at "/sdcard/test.mp3" of your device you would create the media object by doing:
myMedia = new Media("test.mp3");
You'll notice the "/sdcard/" portion of the file name is omitted. This is because on Android the Media object makes an assumption that the file is stored on your "/sdcard". Note: I may change this behaviour in an upcoming version of PhoneGap but I'll strive to retain backward compatibility.

The third and final way to create a media object is by URL:
myMedia = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3");
Now that we've got our media object we want to be able to do the sensible thing and be able to play the audio. For that we'll create a play button:
<a href="#" onclick="playAudio()"><img id="play" src="images/play.png" /></a>
and corresponding function:
function playAudio() {
  myMedia.play();
}
Now the stop button is just as simple:
<a href="#" onclick="stopAudio()"><img id="stop" src="images/stop.png" /></a>
function stopAudio() {
  myMedia.stop();
}
However, that's pretty lame as it would be great to be able to pause the audio playback so let's enhance out play and stop functions:
function playAudio() {
  if (!playing) {
    myMedia.play(); 
    document.getElementById('play').src = "images/pause.png";
    playing = true; 
  } else {
    myMedia.pause();
    document.getElementById('play').src = "images/play.png";    
    playing = false; 
  }
}

function stopAudio() {
  myMedia.stop();
  playing = false;
  document.getElementById('play').src = "images/play.png";    
}
Now when the user is playing some audio the play button image is changed to a pause button and hitting the pause button again will, well, pause the playing of the audio. I'm cheating a little bit by using a global variable called playing as it could easily be changed to check what the currently set image is and doing the correct action.

Of course it is always nice to know where we are at in a given audio clip so we can add some functionality to get the current position of where we are at in the file. So let's create a function that will run every second and update our HTML with the current position of the audio file.
<p id="audio_position">0.000 sec</p>

var mediaTimer = setInterval(function() {
  myMedia.getCurrentPosition(
    function(position) {
      if (position > -1) {
        document.getElementById('audio_position').innerHTML = (position/1000) + " sec";
      }
    },
    function(e) {
      console.log("Error getting pos=" + e);
    }
    );
}, 1000);

That's all well and good but there is one thing that's slipped by our notice so far. When an audio clip finishes playing the pause button should become a play button in case the user wants to play the clip again. That can be accomplished by adding a success call back to our Media object constructor. The success call back is executed when the clip has finished playing. So we'll now instantiate our media like this:
myMedia = new Media("/android_asset/www/test.mp3", stopAudio);
So what we have here are the beginnings of a audio player application for Android. In part two of this article I'll enhance the app with the File System and Directory API showing you how to scan your device for audio files.

The HTML and images can be found here and below is a listing of the HTML code uses in this article. I added a select box so that users could switch between the different ways of creating a Media object. You'll have to supply your own MP3 file as I don't want the RIAA coming after me.

Monday, May 9, 2011

Text to Speech Plugin for PhoneGap Android

On Saturday I participated in the Droidhack put on by Ottawa Android downtown at the Code Factory. The Droidhack is an opportunity for Android developers to get together to work on open source Android projects. Luckily, my day job is working on an open source Android project but the Droidhack gave me the opportunity to work on a plugin that I've wanted to do for awhile, text to speech.

At a former job I developed telephony applications that took advantage of speech recognition and text to speech. Since android has built in text to speech functionality I figured I would expose the functionality to PhoneGap developers via a plugin. Maybe some other folks can take advantage of this plugin and provide more accessible Android applications.

In order to use the TTS plugin wait until you get the deviceready event and then call:
window.plugins.tts.startup(startupWin, fail);
This will start the TTS service on your device. However, the service will take a bit of time to start so the startup success callback is first called with a value of TTS.INITIALIZING which tells you that the service is initializing. Once the service is completely started the success callback is executed again with a value of TTS.STARTED which means we are ready to synthesize text.
function startupWin(result) {
 // When result is equal to STARTED we are ready to play
 if (result == TTS.STARTED) {
  //Ready to go
 }
}
To have your device play some text you simply call the speak method:
window.plugins.tts.speak("The text to speech service is ready");
If you want to have some silence between utterances you can call the silence method:
window.plugins.tts.speak("Let me think.");
window.plugins.tts.silence(2000);
window.plugins.tts.speak("I do not know");
In the above example the TTS service will pause for 2 seconds between the two speak calls. Both the speak and silence methods allow you to provide optional success and error callbacks if you need that kind of information.

Of course all of the above examples assume you are using the English (American) package to speak your utterances. If want to find out what the currently assigned TTS language is you call the getLanguage method:
window.plugins.tts.getLanguage(win, fail);
The success callback has one parameter and that will be a text representation of the Locale currently set for the TTS service.

Now, Android by default supports the following languages for the TTS service English (American), English (UK), French, German, Italian and Spanish. However, it is not guaranteed that each package will be installed on your device. So before you set the TTS language you will want to check to see if it is available by calling the isLanguageAvailable method and in the success callback then you set the TTS language you want to use by executing the setLanguage method:
window.plugins.tts.isLanguageAvailable("fr", function() {
   window.plugins.tts.setLanguage("fr");
   }, fail);
And of course if you no longer need the TTS service than call the shutdown method to free up the resources:
window.plugins.tts.shutdown(win, fail);
If you don't call shutdown on the TTS service the service will be shutdown when your device exits.

The plugin is available at the Android PhoneGap Plugin repository. Please give it a download, try it out and let me know what you think.

The following listing shows a full example of using the TTS Plugin:

Wednesday, May 4, 2011

Overriding the Back Button in PhoneGap Android

Frequently PhoneGap applications consist of multiple screens represented by divs. Since Android users are conditioned to push the back button to return to the previous screen this causes some confusion as the default behaviour is to close the app. Luckily PhoneGap provides developers a way to over ride the default back button behaviour so they may provide their own back behaviour.

In release 0.9.4 the developer would have to wait for the deviceready event and call:

navigator.device.overrideBackButton();
document.addEventListener("backKeyDown", backPressed, false);

In order to setup and event handler for the back key and:

navigator.device.resetBackButton();

to revert to the default behaviour.

As of PhoneGap 0.9.5 we've made overriding the back button behaviour easier. You still have to wait for the deviceready event but the calls have been simplified to:

document.addEventListener("backbutton", onBackKey, false);

to register your event handler for the back key and:

document.removeEventListener("backbutton", onBackKey, false);

to revert to the default Android back button behaviour.

The following listing shows a full, yet contrived, example: