About a month ago I published an article which demonstrated how to create a WP7 application using static HTML pages and PhoneGap. Whilst PhoneGap makes the packaging of HTML / JavaScript / CSS and images into a breeze, one thing it doesn't do is provide correct back-button support. Correct back-button support is a mandatory requirement for marketplace certification. Hitting the back button should navigate back through the various screens of an application. Hitting the back-button on the first screen should terminate the application.
The solution I published previously handles the WP7 back keypress in order to keep track of the back-stack depth. When the back-stack depth is just one, a back-button press exits the application. This works fine if backwards navigation is always controlled via the hardware back-button, however if your application has HTML anchor elements that navigate back to a previous page, or you use code such as javascript:history.go(-1)
, this back-stack handling code will not detect that a backwards navigation has occurred.
As an aside, WP7 applications really should use the hardware back-button for navigation. If you are writing a cross-platform PhoneGap application consider adapting your UI for each platform. This means removing the back-button you woudl have in your iOS version when 'skinning' for WP7.
In order to solve this issue I have updated the code to inspect the URL that the browser control is navigating to in order to detect backwards navigation. The class now maintains a list of URLs, which represent the navigation stack. This allows it to detect a backwards navigation.
/// <summary>
/// Handles the back-button for a PhoneGap application. When the back-button
/// is pressed, the browser history is navigated. If no history is present,
/// the application will exit.
/// </summary>
public class BackButtonHandler
{
private WebBrowser _browser;
private List<string> _backStack = new List<string>();
public BackButtonHandler(PhoneApplicationPage page, PGView phoneGapView)
{
// subscribe to the hardware back-button
page.BackKeyPress += Page_BackKeyPress;
_browser = phoneGapView.Browser;
// handle navigation events
_browser.Navigated += Browser_Navigated;
}
/// <summary>
/// Handle navigation in order to update our back-stack
/// </summary>
private void Browser_Navigated(object sender, NavigationEventArgs e)
{
string url = _browser.Source.OriginalString;
// ensure all slashes are the same
// app\www/index.html
// see: https://issues.apache.org/jira/browse/CB-184
url = url.Replace("\\", "/");
if (_backStack.Count < 2)
{
_backStack.Add(url);
}
else
{
// check whether the URL represents a backwards navigation
string previousPage = _backStack[_backStack.Count - 2];
if (previousPage == url)
{
_backStack.RemoveAt(_backStack.Count - 1);
}
}
}
/// <summary>
/// Handle the hardware back-button
/// </summary>
private void Page_BackKeyPress(object sender, CancelEventArgs e)
{
// if we have items in the back-stack, route this event
// to the browser
if (_backStack.Count > 1)
{
_browser.InvokeScript("eval", "history.go(-1)");
e.Cancel = true;
}
}
}
Note: There use of url.Replace("\\", "/")
, this is due to a minor issue with the PhoneGap WP7 native code, which I have raised in the Callback JIRA (CB-184).
To use this code simply create an instance of the class, passing the PGView (the PhoneGap user control) into teh constructor:
public MainPage()
{
InitializeComponent();
new BackButtonHandler(this, PGView);
}
Because back-button handling is a mandatory requirement for WP7 applications, hopefully Nitobi will incorporate this code (or similar) into the PhoneGap Build service (which I tried earlier this week - it is pretty amazing!)
You can download a working example here: HTML5SandwichFlow.zip
(The first three recipe pages have 'back' anchor elements, two use the URL, one uses JavaScript)
Regards,Colin E.