Boris Smus

interaction engineering

WebIntent, an Android PhoneGap plugin

PhoneGap is a very useful cross-platform web application wrapper that lets developers package web applications into native apps. It also lets web apps access functionality traditionally only available in native apps. This how-to is about exposing additional Android functionality to the web with PhoneGap plugins. Intents are a fundamental part of the Android ecosystem, allowing a sort of message-passing mechanism between applications, but they are not exposed to web applications. The sample Android plugin I wrote is called WebIntent, which lets you create a first class Android applications in JavaScript.

WebIntent

Firstly, the WebIntent plugin is a means of creating Android activities via Intents. With just four parameters, action, url, type, extras, it's possible to get a lot of mileage from the Android OS. For example, you can send email:

Android.sendEmail = function(subject, body) { 
  var extras = {};
  extras[WebIntent.EXTRA_SUBJECT] = subject;
  extras[WebIntent.EXTRA_TEXT] = body;
  window.plugins.webintent.startActivity({ 
      action: WebIntent.ACTION_SEND,
      type: 'text/plain', 
      extras: extras 
    }, 
    function() {}, 
    function() {
      alert('Failed to send email via Android Intent');
    }
  ); 
};

Or, you can load Google Maps:

Android.showMap = function (address) {
  window.plugins.webintent.startActivity({
    action: WebIntent.ACTION_VIEW,
    url: 'geo:0,0?q=' + address,
  }, function () {}, function () {
    alert('Failed to open URL via Android Intent');
  });
};

Secondly, the plugin lets you react when your PhoneGap application gets invoked with certain intents. To do this, you need to setup correct intent-filters in the AndroidManifest.xml. For example, to respond to ACTION_SEND, something like the following should appear in the manifest:

<intent-filter> 
  <action android:name="android.intent.action.SEND" />
  <category android:name="android.intent.category.DEFAULT" />
  <data android:mimeType="text/plain" />
</intent-filter>

Then, in the JavaScript, you can check what extras were specified during invocation:

// deviceready is PhoneGap's init event
document.addEventListener('deviceready', function () {
  window.plugins.webintent.getExtra(WebIntent.EXTRA\_TEXT, function (url) {
    // url is the value of EXTRA_TEXT 
  }, function() {
    // There was no extra supplied.
  });
});

This lets you respond to Intents without writing native code. The plugin code is available for your use and/or perusal on my github. To install the plugin, move webintent.js to your project's www folder and include a reference to it in your html files. Create a folder called "borismus" within your project's src/com/ folder and move WebIntent.java into it.

Writing Android PhoneGap Plugins

The PhoneGap project provides a plugin architecture, which isn't very well documented. Perhaps as a result, there are very few Android plugins (see the github). There are two parts of an Android plugin: a native Java class that extends com.phonegap.api.Plugin, and a JavaScript wrapper for that Java class. The JavaScript wrapper registers the plugin with the PhoneGap plugin manager. A plugin API is defined in JavaScript, and exposed via window.plugins.myplugin. Calling the API is done as follows:

window.plugins.webintent.foo({ arg1: 'val1', arg2: 'val2', // etc });

The plugin API is defined in the MyPlugin JavaScript class:

var MyPlugin = function () {};
MyPlugin.prototype.foo = function (params, success, fail) {
  return PhoneGap.exec(success, fail, 'MyPlugin', 'startActivity', [params]);
};

The plugin needs to be registered with PhoneGap before it can be used.

PhoneGap.addConstructor(function () {
  // Creates window.plugins.myplugin, an instance of MyPlugin
  PhoneGap.addPlugin('myplugin', new MyPlugin()); 
  // Binds MyPlugin to the Java class com.example.MyPlugin 
  PluginManager.addService('MyPlugin', 'com.example.MyPlugin');
});

Finally, there needs to be a Java class that actually implements the desired behavior. The execute method takes an action parameter, which is the name of the function (foo in this example), and an array of arguments:

public class MyPlugin extends Plugin {
  public PluginResult execute(String
  action, JSONArray args, String callbackId) {
    if (action.equals("foo")) {
      // Implementation 
    }
  }
}

This architecture is the key to unlock any Android functionality to your PhoneGap-wrapped mobile web application. One of huge benefits of PhoneGap and mobile web is that it's a cross-platform solution. I admit that writing platform-specific plugins seems counter-productive to this end. However until the mobile web gets the love it deserves (via extra sweet mobile browsers), there will be a place for platform-specific PhoneGap plugins to make web applications fit better into native mobile platforms.