Plugins for PhoneGap + Windows Phone Mango

We recently added plugin support to WP7 PhoneGap apps. I will outline here what you need to do to start extending the functionality for WP7 PhoneGap projects. All WP7 PhoneGap plugins must derive from the PhoneGap base command. More specifically, the WP7GapClassLib.PhoneGap.Commands.BaseCommand class. Plugin commands are dealt with generically throughout the framework, and base command provides the base interface for you to pass data back to JavaScript. This approach also prevents JavaScript code from calling arbitrary native code.

I recently posted a plugin titled PGSimpleShare to my GitHub repo. You can clone/fork it here :

The PGSimpleShare allows for simple updates to share links and status updates on social networks. Windows Phone Mango includes accounts for popular social networks, and the updated SDK provides a simple way to post data to them. The PGSimpleShare plugin exposes this same functionality to JavaScript running in a PhoneGap application. When you call this plugin to share a statusUpdate, the user will be presented with a native dialog previewing the update and allowing them to choose which networks they want to post it to. On my phone I see Facebook, Twitter, LinkedIn, and Window's Live, all updated simultaneously ( as far as my code is concerned )

Plugins provide their interface to the application via some JavaScript code, this is done as expected inside a js file. Here is some JS for a simple plugin, taken from the recent PGSocialShare plugin I posted on GitHub: To call your native side implementation, simply call PhoneGap.exec(winCallbackFunction, failCallbackFunction, pluginName, methodName, argsObj); The WP7 version of PhoneGap.exec will do the work of managing your callbacks if supplied, as well as turning the argsObj into a JSON object that can be passed to your native code. In the case of the shareStatus function defined above, the framework, on the native side will look for a class named PGSocialShare. ( fully qualified: WP7GapClassLib.PhoneGap.Commands.PGSocialShare ) When it finds this class it will instantiate it if it hasn't already, then it will attempt to call the 'share' method on it. Here is a portion of the native implementation of the 'share' function :

Note the implementation of the share function is public, returns void, and expects 1 string argument, this is currently the signature for ALL plugin methods that are callable via the framework.

The above code then continues on to deserialize the JSON representation of the arguments it is expecting via a data-contract. See the full source code for details on this. Based on the shareType passed in, we will call another method in our class; namely 'shareStatus'. This method then proceeds to pass the status message on to the SDK ShareStatusTask. There is no return value from the native SDK's ShareStatusTask, so we have no way of knowing if the user cancelled it, or which networks they chose to post the status to. PGSocialShare::shareStatus then calls DispatchCommandResult which is defined by WP7GapClassLib.PhoneGap.Commands.BaseCommand, and will signal to JavaScript that the call has succeeded. In cases where we need to pass back some data, or signal an error condition vs a success, we can use the PluginResult`s enum of statuses ( statii ? ) We can also signal that we want JavaScript to keep our callback, for cases where we are monitoring sensor data, like the Accelerometer command.

Hopefully this is enough for everyone to get started developing plugins for Windows Phone! Also, I have attempted to keep PGSocialShare device-agnostic in the definition of the methods, so hopefully we will see the same plugin implemented on other devices as well. I look forward to your comments, questions, and critriques!

Cheers, Jesse

Implementing WebStorage for PhoneGap + Windows Phone Mango ( Part 1 )

Windows Phone 7 with the Mango updates includes the new IE9 which is a very capable browser, and has support for many new standard HTML5 APIs. Some of the feature highlights (IMHO) include:

  • addEventListener : no more conditional event code
  • querySelectorAll : easily grab DOM elements by id/class/type etc.
  • WebStorage APIs : localStorage + sessionStorage
  • ES5 : Function.bind, Array.forEach, ...

The reader: But wait, I thought this post was going to be about how the WebStorage APIs were added to PhoneGap for WP7, but you just said IE9 supports it, what gives?

The writer: I'm getting there just wait ...

So, IE9 implements the complete spec for WebStorage as defined here: Unfortunately, this implementation is only available to web pages that are loaded from a domain because of the way the data stores are sandboxed per domain. In the context of a PhoneGap application, our page is loaded from IsolatedStorage ( aka: file:// ) so we are not assigned a domain by IE9, so we do not have access to localStorage.

Okay, so having seen this I realized that I needed to polyfill here, and provide an implementation. However, upon attempting to set window.localStorage to my own object, I received an error stating that window.localStorage was read-only? oh crap! After a little more exploration, I remembered my ECMA. ECMAScript 5th Edition to be precise, and IE9 supports it. So after a quick test, I discovered that I could duck-punch/polyfill window.localStorage using Object.defineProperty(obj, prop, descriptor); Sweet!

You can look ahead to the JavaScript implementation here : DomStorage.js

The JavaScript implementation supplies the following interface to window.localStorage and window.sessionStorage :

interface Storage { readonly attribute unsigned long length; DOMString? key(unsigned long index); getter DOMString getItem(DOMString key); setter creator void setItem(DOMString key, DOMString value); deleter void removeItem(DOMString key); void clear(); };

Implementing this on the native side was a little different than a typical plugin. Many of the calls are synchronous, so I needed to have some data up front, and a method to call native code synchronously. Note: I could have implemented the entire interface using the FileAPI, but this would mean storing the entire key/value set in JavaScript so that it could be called synchronously. After some experimentation, I discovered that even though C# code cannot return a value to a JavaScript call to ScriptNotify, it can set a value from C# before execution is passed back to JavaScript, so we can mimic the behavior of a return value.

In this example, the value of this._result is set before window.external.Notify returns, so it is available as a return value from the JavaScript function.

var retVal = null; if(this.keys.indexOf(key) > -1) { window.external.Notify("DOMStorage/" + this._type + "/get/" + key); retVal = this._result; this._result = null; } return retVal;

Note: While this works, perfectly, we do have to be careful that we don't again call ScriptNotify in the callback to set the _result value, otherwise we could overflow the stack with multiple nested calls.

More to come soon, done a lot of blogging and not enough coding for today. Stay tuned.