Andrej Tozon's blog

In the Attic

NAVIGATION - SEARCH

Windows 8 geolocation API: First run

Microsoft Windows 8 OS was finally revealed this week at Microsoft’s //build/ conference. It looks like a promising OS, with Metro style theme showing all over it.

image

If you want to install it, look it up on the MSDN subscriptions site or head over here (limited set of SKUs). If you’re a developer, pick the one that includes the Visual Studio 11 Developer preview (or install tools – available also as a separate download – later).

A lot is about to be said of WinRT, the new Windows Runtime and how it relates to .NET and how developing Windows applications will going to be (dis)similar to developing WPF or Silverlight applications. Skipping that discussion for later, I jumped right into developing tools to see how familiar the new API would look to me and whether my past experience with developing XAML based applications in the past few years would help or not.

Starting with the geolocation API

Having developed a couple of Windows Phone application that used my location as a crucial piece of information, I was curious how the same would be done in a Windows 8 application. I’m posting this as a walkthrough (assuming you installed the Visual Studio 11 Express for Windows Developer Preview):

[A short note before you start: the following is based on the //build/ Developer Preview bits and will most likely change in future versions]

1. Open Visual Studio 11 Express.

2. Create a New Project. A few project templates are available, but let’s start simple and choose a basic C# Application.

image

3. If you’re a XAML developer, you’ll find yourself into a very familiar environment. A big plus on that is that Window designer is loading in the background and not blocking you to fiddle with other things while it loads [yes!]

image

[I’ll look into various settings to control you application’s look and feel in one of my future posts]

4. Similarly to Windows Phone, using a location requires you as a developer, to explicitly declare that your application will require access to the location API. This is set in the application manifest. In a Solution explorer, look for Package.appmanifest item and double click on it to open – the manifest properties window will open.

5. Click on the Capabilities tab. Uncheck the Internet (Client) capability and check Location capability (just under it).

image

You’ve just declared that the application will require the Location capability. Had you not enabled this item, anything access to the location API in the application would have no effect. No errors or exceptions would bi risen, it just wouldn’t work.

6. Go back to the MainPage.Xaml and add a TextBlock named Status into the main Grid (LayoutRoot). This is where you’re going to display the current location.

7. Switch to the code view (quick heads up: the F7 keyboard shortcut is not a default for this one anymore).

8. The core class you’re going to use is called Geolocator. It’s very similar to GeoCoordinateWatcher class from Windows Phone, so working with it should be fairly familiar:

public sealed class Geolocator : IGeolocator
{
  public Geolocator();
  public PositionAccuracy DesiredAccuracy { get; set; }
  public PositionStatus LocationStatus { get; }
  public double MovementThreshold { get; set; }
  public uint ReportInterval { get; set; }

  public event TypedEventHandler<Geolocator, PositionChangedEventArgs> PositionChanged;
  public event TypedEventHandler<Geolocator, StatusChangedEventArgs> StatusChanged;
  public GetGeopositionOperation GetGeopositionAsync();
}

Similar to GeoCoordinateWatcher, it has

  • DesiredAccuracy (Default, High),
  • LocationStatus (Ready, Initializing, NoData, Disabled, NotInitialized, NotAvailable) – with two new values,
  • MovementTreshold,
  • PositionChanged and StatusChanged events,

New in Geolocator are:

  • ReportInterval – the requested minimum time interval between location updates,
  • GetGeopositionAsync() – single call method for asynchronous request for location data.

Noticed the TypedEventHandler event declarations above? What are those? Let’s continue with the example…

9. Set up the Loaded event handler and paste the following code into it:

Geolocator locator = new Geolocator();
locator.PositionChanged +=
new TypedEventHandler<Geolocator, PositionChangedEventArgs>(OnPositionChanged);

OnPositionChanged event handler signature is as follows:

void OnPositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
}

To reflect the TypedEventHandler, you can see that sender is no more a plain object, it’s typed! That means no more unnecessary casting and your code is a tad more error-prone.

10. PositionChangedEventArgs holds a reference to a Geoposition, which is declared as:

public sealed class Geoposition : IGeoposition
{
    public CivicAddress CivicAddress { get; }
    public Geocoordinate Coordinate { get; }
}

While Geocoordinate is similar to GeoCoordinate from Windows Phone (Latitude, Longitude, Altitude, Speed, Heading, Accuracy, etc.), the CivicAddress should contain address data, associated with the reported Geocoordinate. I’m saying “should” because from all address properties (City, Country, PostalCode, State) for my current location (Ljubljana, Slovenia), the API only returns my country code (SI). I’m interested to hear how the same API behaves in other locations around the world so if you’re using it, please let me know.

11. If you try setting the Status label text in the OnPositionChanged event handler, you’ll get the invalid cross-thread exception and that’s because the event gets kicked on the background thread. In order to set the text, you have to invoke that on the UI thread, using the dispatcher. This is the part where things seems to differ the most. There’s a new dispatcher called CoreDispatcher. Declare it on the class level and set to page’s Dispatcher property in page’s constructor. That will guarantee you you can use it from the background thread.

So here’s the full MainPage code:

partial class MainPage
{
    CoreDispatcher dispatcher;

    public MainPage()
    {
        InitializeComponent();
        dispatcher = Dispatcher;
        Loaded += new RoutedEventHandler(OnLoaded);
    }

    void OnLoaded(object sender, RoutedEventArgs e)
    {
        Geolocator locator = new Geolocator();
        locator.PositionChanged += 
            new TypedEventHandler<Geolocator, PositionChangedEventArgs>(OnPositionChanged);
    }

    void OnPositionChanged(Geolocator sender, PositionChangedEventArgs args)
    {
        dispatcher.InvokeAsync(CoreDispatcherPriority.Normal, DisplayLocation, this, args);
    }

    void DisplayLocation(object sender, InvokedHandlerArgs e)
    {
        IPositionChangedEventArgs args = e.Context as IPositionChangedEventArgs;
        Status.Text = string.Format("Country:{0} Lat: {1} Lng: {2}", 
            args.Position.CivicAddress.Country, args.Position.Coordinate.Latitude, 
            args.Position.Coordinate.Longitude);
    }
}

12. You can simplify the InvokeAsync code by using a lambda expression, though it may be a bit harder to read and/or debug:

void OnPositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
    dispatcher.InvokeAsync(CoreDispatcherPriority.Normal,
        (s, a) =>
        {
            Status.Text = string.Format("Country:{0} Lat: {1} Lng: {2}", 
                args.Position.CivicAddress.Country, 
                args.Position.Coordinate.Latitude, 
                args.Position.Coordinate.Longitude);
        }
        , this, args);
}

13. Now try to run the project. This is what you should see:

image

Windows is asking you if this application can use your Location. If user chooses Block, application’s request to access the location is denied. No errors or exceptions would be thrown, it just wouldn’t work. If user allows the access, the app will work as expected.

Note: the user can change this permission any time through the Settings charm: When application is running, move your mouse to the bottom left corner – the menu, similar to start screen should appear.

image

Click Settings and notice the right side bar. Click on the “Control system access for [YourAppName]” – you can change the access settings from there

image         image

If you want to test for access availability status from within the app, check Geolocator’s LocationStatus. In case the location access is disabled, it should be set to NotInitialized (although I expected Disabled).

Another fun fact is that Geolocator is not initialized on it’s instantiation, but only when you actually do something with it. In this case, it initializes when you subscribe to the PositionChanged event. This is also the same code line that will cause application (or Windows RunTime) to pop up that permissions dialog from above. After that line, the LocationStatus should be set to a valid status (Ready if access to location is enabled and NotInitialized if it’s not).

Note: Windows will only ask you for permission the first time it’s required. After that, it’s on user to change this permission. But if you want to test this, go to the Application manifest, Capabilities tab, and re-check the Location capability – that should reset the prompt.

But we’re not done yet.

Subscribing to PositionChanged event will ensure the event handler will be called each time Position is “changed” (that actually depends on your Geolocator’s MovementTreshold and ReportInterval property settings). To stop listening to position changes, you can simply unsubscribe from the PositionChanged event.

But… In a lot of cases, we only want to determine the current location once, instead of continuously tracking it. Fortunately, GetGeopositionAsync method does just that – retrieves the current position and it does it in the asynchronous manner (as suggested by Async method suffix by proposed convention).

And guess what? Async methods are unbelievable easy to handle with the new async / await keyword pair. Let’s give it a try.

14. Change the OnLoaded event handler too look like this:

async void OnLoaded(object sender, RoutedEventArgs e)
{
    Geolocator locator = new Geolocator();
    Geoposition position = await locator.GetGeopositionAsync();
    Status.Text = string.Format("Country:{0} Lat: {1} Lng: {2}",
        position.CivicAddress.Country, 
        position.Coordinate.Latitude, 
        position.Coordinate.Longitude);
}

Geolocator is instantiated as before, but the current position is accessed through the GetGeoPositionAsync() method. And notice how that method is decorated with the await keyword and the whole OnLoaded method with async. The principle will also guarantee you that your code, after that async call, can safely call into the UI thread. No dispatcher needed. And from what I know, there are many async method scattered around the new runtime, it will just make coding so much easier, not to mention the new responsive UI.

Wrapping up

That’s it! My first experience with the new API. So far I love it but porting existing and building new applications will eventually show its real value.

And to just briefly touch on the current hot topic – I really think that desktop application development has just shifted gears, although it’ll take some time to lift off the ground. What we’re looking at is an early preview and it will take some time till release, and event more to a broad Windows 8 adoption.

If you have any comments, questions, suggestions for future posts on the subject, any feedback at all, do not hesitate to contact me.