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.

Thinking of joining us?

If you enjoyed this blog post and are interested in working with smart Developers on challenging software projects, check out our current vacancies.