Showing posts with label wpdev. Show all posts
Showing posts with label wpdev. Show all posts

12 August 2015

Gotcha: custom Microsoft Band page shows only data in last added UI element

Today I finally nailed a very odd issue in the Microsoft Band SDK - or actually, my understanding thereof. For my current research project, I am building a custom UI on my Microsoft Band. I should contain two panels: one with a text, an icon and a button, and a second panel containing two texts. Details of how to setup up something like this, can be found in the band samples.

Building a Band UI is not for the faint hearted, it includes building the UI from code without any kind of designer. Filling those UI elements with a value then is a separate step, which contains quite a big gotcha that you cannot derive from the samples.

My Band UI creation code is like this:

private IEnumerable<PageLayout> BuildTileUi()
{
  var bandUi = new List<PageLayout>();
  var page1Elements = new List<PageElement>
  {
	new Icon {ElementId = IconId, Rect = new PageRect(60,10,24,24)},
	new TextBlock  {ElementId = TextTemperatureId, Rect = new PageRect(90, 10, 50, 40)},
	new TextButton {ElementId = ButtonToggleFanId, Rect = new PageRect(10, 50, 220, 40), 
	    HorizontalAlignment = HorizontalAlignment.Center}
  };
  var firstPanel = new FilledPanel(page1Elements) { Rect = new PageRect(0, 0, 240, 150) };

  var page2Elements = new List<PageElement>
  {
	new TextBlock {ElementId = TextTimeId, Rect = new PageRect(10, 10, 220, 40)},
	new TextBlock {ElementId = TextDateId, Rect = new PageRect(10, 58, 220, 40)}
  };
  var secondPanel = new FilledPanel(page2Elements) { Rect = new PageRect(0, 0, 240, 150) };

  bandUi.Add(new PageLayout(firstPanel));
  bandUi.Add(new PageLayout(secondPanel));

  return bandUi;
}

That is quite a handful, right? Then for setting values to those UI element, I had this little piece of code:

private List<PageData> BuildTileData1(string timeText,
                                      string dateText,
                                      string temperature,
                                      string buttonText)
{
  var result = new List<PageData>
  {
    new PageData(Page2Id, 1,
      new TextBlockData(TextTimeId, timeText)),
    new PageData(Page2Id, 1,
      new TextBlockData(TextDateId, dateText)),
    new PageData(Page1Id, 0, new IconData(IconId, 1)),
    new PageData(Page1Id, 0,
      new TextBlockData(TextTemperatureId, temperature)),
    new PageData(Page1Id, 0, new TextButtonData(ButtonToggleFanId, buttonText))
  };

  return result;
}

And this is set to my tile using bandClient.TileManager.SetPagesAsync. Which gave me a quite puzzling result: only the last element on each page actually had a value. So that means only the datetext and the button actually showed text. As the sample in the Band SDK samples only uses one UI element, I was like, repeat adding PageData elements, right?

Wrong!

It turns out you can only add values to a PageData element once per SetPagesAsync call. So what you need to do, actually, is:

private List<PageData> BuildTileData(string timeText, 
                                     string dateText, 
                                     string temperature, 
                                     string buttonText)
{
  var result = new List<PageData>
  {
    new PageData(Page2Id, 1,
      new TextBlockData(TextTimeId, timeText),
      new TextBlockData(TextDateId, dateText)),
    new PageData(Page1Id, 0, 
      new IconData(IconId, 1), 
      new TextButtonData(ButtonToggleFanId, buttonText), 
      new TextBlockData(TextTemperatureId, temperature)),
  };

  return result;
}

See the difference? Only add 2 PageData elements, one for every Page, not one for every page element data object. Maybe elementary, but I overlooked it. For quite some time. The constructors of PageData give a bit of a hint:

public PageData(Guid pageId, int pageLayoutIndex, IEnumerable<PageElementData> values);
public PageData(Guid pageId, int pageLayoutIndex, params PageElementData[] values);

as the third parameter is not a page element data object but a list of it. The thing is, I created the UI on demand (when I tapped the tile) and then it worked as expected - but not the seconds time around.

It took me quite some time to figure this one out, so I thought it best to blog about it right away. Maybe I am giving help here, maybe I am just documenting my own stupidity, but nevertheless ;)

I skip the demo sample this time - if you run into this problem, you are quite far with the Band UI already, and the demo I am working on contains this code anyway and will - hopefully - arrive online soon anyway. Attentive readers will have some inkling of what that demo exactly comprises.

Happy Band coding!

08 February 2015

Using a full-size none-stretched background image in a Xamarin.Forms app

Intro

I always like to use a kind of a translucent background image to my app’s screens, that makes it look a bit more professional than just a plain single-colored screen – a trick I learned from my fellow MVP Mark Monster in the very early days of Windows Phone development. Now that I am trying to learn some Xamarin development, I want to do the same thing – but it turns out that works a bit different from what I am used to.

Setting up the basic application

I created a Xamarin Forms portable app “BackGroundImageDemo”, but when you create a new Xamarin Forms application using the newest templates in Xamarin 3.9, you get an application that uses forms, but no XAML. Having lived and dreamed XAML for the last 5 years I don’t quite like that, so start out with making the following changes:

1. Update all NuGet packages - this will get you (at the time of this writing) the 1.3.2 forms packages

2. Add StartPage.Xaml to BackGroundImageDemo (Portable)

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="BackGroundImageDemo.StartPage">
	<Label Text="Hello world from XAML" VerticalOptions="Center" 
         HorizontalOptions="Center" FontSize="30" />
</ContentPage>

3. Make some changes to the App.cs in BackGroundImageDemo (Portable) to make it use the XAML page:

namespace BackGroundImageDemo
{
  public class App : Application
  {
    public App()
    {
      // The root page of your application
      MainPage = new StartPage();
    }
    
    // stuff omitted
  }
}

And when you run that, for instance on Windows Phone, it looks like this:

image

JupiterAdding a background picture

Now suppose I want to make an app related to astronomy – then I might use this beautiful picture of Jupiter, that I nicked off Wikipedia, as a background image:

It has a nice transparent background, so that will do. And guess what, the ContentPage class has a nice BackgroundImage attribute, so we are nearly done, right?

As per instructions found on the Xamarin developer pages, images will need to be:

  • For Windows Phone, in the root
  • For Android, in the Resources/drawable folder
  • For iOS, in Resources folder

In addition, you must set the right build properties for this image:imageimage

  • For Windows Phone, set “Build Action” to “Content” (this is default) and “Copy to Output Directory” to “Copy if newer”
  • For Android, this is “AndroidResource” and “Do not copy”
  • For iOS, this is “BundleResource” and “Do not copy”

So I copy Jupiter.png three times in all folders (yeah I know, there are smarter ways to do that, that’s not the point here) add BackgroundImage=’'Jupiter.png” to the ContentPage tag and… the result, as we can see on the to the right, is not quite what we hoped for. On Android, Jupiter is looking like a giant Easter egg. Windows Phone gives the same display. On the Cupertino side, we get a different but equally undesirable effect.

RelativeLayout to the rescue

Using RelativeLayout and constraint expressions, we can more or less achieve the same result as Windows XAML’s “Uniform”.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="BackGroundImageDemo.StartPage" >
  <RelativeLayout>
    <Image Source="Jupiter.png" Opacity="0.3"
                RelativeLayout.WidthConstraint=
                  "{ConstraintExpression Type=RelativeToParent, Property=Width}"
                RelativeLayout.HeightConstraint=
                  "{ConstraintExpression Type=RelativeToParent, Property=Height}"/>
    <Grid RelativeLayout.WidthConstraint=
              "{ConstraintExpression Type=RelativeToParent, Property=Width}"
            RelativeLayout.HeightConstraint=
              "{ConstraintExpression Type=RelativeToParent, Property=Height}">

      <Label Text="Hello world from XAML" VerticalOptions="Center"
         HorizontalOptions="Center" FontSize="30"/>
    </Grid>
  </RelativeLayout>
</ContentPage>

All elements within a RelativeLayout will essentially be drawn on top of each other, unless you specify a BoundsConstraint. I don’t do that here, so essentially every object will drawn from 0,0. By setting width and height of the RelativeLayout’s children to essentially the width and height of the RelativeLayout itself, is will automatically stretch to fill the screen. And thus the image ends up in the middle, as does the Grid with the actual UI in it. Just make sure you put the image Image first and the Grid second, or else the image will appear over your text.

I also added Opacity = “0.3” to make the image translucent and not so bright that it actually wipes out your UI. The exactly value of the opacity is a matter of taste and you will need to determine how it affects the readability of the actual UI on a real device. Also, you might consider editing the image in Paint.Net or the like and set its to 0.3 opacity hard coded in the image, I guess that would save the device some work.

Anyway, net result:

imageimageimage

Demo solution, as always, can be downloaded here.

22 December 2014

A behavior to show a MessageDialog from a MVVMLight viewmodel in Universal apps–with callbacks

Of course you are now all writing MVVM based apps, after all I have been advocating that for years, and you are listening this here old venerable MVP, right? ;). Anyway, some UI interactions between UI and viewmodel can seem to be a bit complicated, but these kinds of problems are usually pretty simple to solve with a combination of a behavior, the MVVMLight messenger, and a message containing a callback. And as I hate to write things more than once, I  made this into a reusable behavior. It ain’t rocket science, but it’s nice and clean.

I start with a class holding the actual message, and in this class file I have defined a callback delegate as well:

using System.Threading.Tasks;
using GalaSoft.MvvmLight.Messaging;

namespace WpWinNl.Behaviors
{
  public class MessageDialogMessage : MessageBase
  {
    public MessageDialogMessage(string message, string title,
                                string okText, string cancelText,
                                MessageDialogCallBack okCallback = null,
                                MessageDialogCallBack cancelCallback = null,
                                object sender = null, object target = null)
                                : base(sender, target)
    {
      Message = message;
      Title = title;
      OkText = okText;
      CancelText = cancelText;
      OkCallback = okCallback;
      CancelCallback = cancelCallback;
    }

    public MessageDialogCallBack OkCallback { get; private set; }
    public MessageDialogCallBack CancelCallback { get; private set; }
    public string Title { get; private set; }
    public string Message { get; private set; }
    public string OkText { get; private set; }
    public string CancelText { get; private set; }
  }

  public delegate Task MessageDialogCallBack();
}

It’s a basic MVVMLight BaseMessage child class, with properties for various texts, and two optional callbacks for OK and cancel actions.

The behavior itself is, as behaviors go, pretty simple:

using System;
using System.Diagnostics;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using GalaSoft.MvvmLight.Messaging;

namespace WpWinNl.Behaviors
{
  public class MessageDialogBehavior : SafeBehavior<FrameworkElement>
  {
    protected override void OnSetup()
    {
      Messenger.Default.Register<MessageDialogMessage>(this, ProcessMessage);
      base.OnSetup();
    }

    private async void ProcessMessage(MessageDialogMessage m)
    {
      bool result = false;
      var dialog = new MessageDialog(m.Message, m.Title);

      if (!string.IsNullOrWhiteSpace(m.OkText))
      {
        dialog.Commands.Add(new UICommand(m.OkText, cmd => result = true));
      }

      if (!string.IsNullOrWhiteSpace(m.CancelText))
      {
        dialog.Commands.Add(new UICommand(m.CancelText, cmd => result = false));
      }

      try
      {
        await dialog.ShowAsync();
        if (result && m.OkCallback != null)
        {
          await m.OkCallback();
        }

        if (!result && m.CancelCallback != null)
        {
          await m.CancelCallback();
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine("double call - ain't going to work");
      }
    }
  }
}

This being once again a SafeBehavior, it simply starts to listen to MessageDialogMessage messages. When one is received, a MessageDialog is constructed with the message and title to display, as well as an optional Ok and Cancelbutton – and callback. If you send a message, you can provide optionally provide simple method from your viewmodel to be called by the behavior when Ok or Cancel is called, thus providing a two-way communication channel. Mind, though, all this is async, which has a curious side effect: if you send another message before the user has clicked Ok or Cancel, this will raise an exception.This will be displayed in the Visual Studio output window while you debug, but it won’t show up in release, and your message won’t be displayed either – so you need to carefully control sending messages, and not throw them out like there’s no tomorrow.

I have made a little sample app – a Universal app of course, because there have been some pretty clear hints by Microsoft this is the way to go forward. You will notice everything worthwhile is the shared project, up to the MainPage.xaml. People who were present at the latest Lowlands WpDev day last October will remember my fellow MVP Nico Vermeir saying in his talk “don’t do that, there’s pain in there”, and while this is true, it’s an ideal thing for lazy demo writers.

I started out with a blank Universal app, and moved the MainPage.Xaml and it’s companion MainPage.Xaml.cs to the shared project. Then I brought in my WpWinNl project that, amongst other things, drags in MVVMLight and everything else you need. Then I created this whopping complex viewmodel:

using System.Threading.Tasks;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using WpWinNl.Behaviors;

namespace SampleMessagePopup
{
  public class MainViewModel : ViewModelBase
  {
    public ICommand MessageCommand
    {
      get
      {
        return new RelayCommand(
          () => 
            Messenger.Default.Send(new MessageDialogMessage(
              "Do you really want to do this?", "My Title", 
              "Hell yeah!", "No way", 
              HellYeah, Nope)));
      }
    }

    private async Task HellYeah()
    {
      Result = "Hell yeah!";
    }

    private async Task Nope()
    {
      Result = "NOOOO!";
    }


    private string result = "what?";
    public string Result
    {
      get { return result; }
      set { Set(() => Result, ref result, value); }
    }
  }
}

imageimageIf you fire the command, it will show a dialog that looks like the left on these two screens (on Windows Phone) and if you press the “no way” button it will show the very right screen. A few things are noticeable:

  • Both methods are async, although they don’t implement anything asynchronous. That’s because I wanted to be able do awaitable things in the callback and a contract is a contract – were not making JavaScript here. Only tools like ReSharper (highly recommended) will make you notice it, but you can ignore it for now.
  • You might notice the callback methods are private but still the behavior is apparently able to call these methods. This, my dear friends who grew up in the nice era of managed OO languages that shield you from the things that move below you in the dark dungeons of your processor, is because you basically give a pointer to a method – and that’s accessible anywhere. Your compiler may tell you it’s a private method, but that’s more like it has a secret telephone number – if you got that, you can access it anywhere ;-). Us oldies who grew up with C or Assembler can tell you all about the times when boats were made of wood and men were made of steel and the great ways you can shoot yourself in the foot with this kind of things. If you want to make you app testable it makes more sense to make the methods public, by the way, but I could not let the opportunity to show this great example of the law of leaky abstractions pass.

But I digress, I know, that is also a sign of age ;)

The Xaml is of course also pretty simple – as this app does not do much:

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" 
    xmlns:Behaviors="using:WpWinNl.Behaviors"
    x:Class="SampleMessagePopup.MainPage"
    DataContext="{StaticResource MainViewModel}">
  <Interactivity:Interaction.Behaviors>
    <Behaviors:MessageDialogBehavior/>
  </Interactivity:Interaction.Behaviors>
  <Page.BottomAppBar>
    <CommandBar>
      <AppBarButton Icon="Accept" Label="go ask!" 
                    Command="{Binding MessageCommand}"/>
    </CommandBar>
  </Page.BottomAppBar>

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
  	<TextBlock Text="{Binding Result}" HorizontalAlignment="Center" 
                   VerticalAlignment="Center" FontSize="56"/>
  </Grid>
</Page>

Notice the behavior sitting on the page itself. For obvious reasons, it’s the logical place to put. You can also put it on the top grid. But whatever you do, use only one per page. They all listen to the same message, and if you put more of them on one page you will get an interesting situation where multiple behaviors will try to show the same MessageDialog. I have not tried this, but I assume it won’t be very useful.

To make this work, by the way, you need of course to declare your viewmodel in App.Xaml. You have no other choice here, as the App Bar sits at the top level of the page, the button calls the command and thus the resource must be one level higher, i.e. the app itself.

<Application
    x:Class="SampleMessagePopup.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SampleMessagePopup"
    xmlns:system="using:System">

  <Application.Resources>
    <local:MainViewModel x:Key="MainViewModel" />
  </Application.Resources>
</Application>

Again, if you use something like ReSharper a simple click on the declaration in MainPage.Xaml will make this happen.

So, that’s all there is to it. You won’t find the code for this behavior in the solution, as it is already in WpWinNl. Just like the previous one ;). As usual, a sample solution is provided, but in inspired by my fellow MVP Diederik Krols I’ve have decided that going forward I will publish those on GitHub.

23 October 2014

Making the Application Bar display on of top of the UI in Windows Phone 8.1 Store Apps

In Windows Phone 7, 8 and 8.1 Silverlight apps this is very easy: you just set the Opacity of the Application bar to any value less than 1 as described here, and the app does not reserve space for the app bar – it just displays the bar on top of the main UI like this:

image

The problem is, if you set the opacity of what is now called the CommandBar in Windows Phone 8.1 Store apps, the Application bar is translucent indeed - which you can see if you open it - but the apps still reserves space for it in it’s closed state:

image

The fix for this is surprisingly easy, but also pretty hard to find if you don’t know what you are looking for. In the end I got a tip by mail from my famous fellow MVP Rudy Huyn. You just add the following, not very obvious line of code to your app. I added it to my App.Xaml.cs, just before the line “Window.Current.Activate();”

 ApplicationView.GetForCurrentView().SetDesiredBoundsMode(
   ApplicationViewBoundsMode.UseCoreWindow);

Right. And then you get what you want (as is displayed in the first image). And once you enter this line of code into a search engine, then you find hits enough, dating back to even April. But well, that did not help me much. Hopefully this simple article will save someone else a fruitless seach ;)

To make this code compile, you will need to add a “using Windows.UI.ViewManagement;” on top of your file. And although it is in fact an article about code, I will skip the sample download app this time – for just one line of code it feels like a bit of overkill.

Update – be aware that not only the Application Bar now falls over the UI, but the status bar on top as well. So you may want to take that into account when designing your UI: keep some extra space at the top, or make in partially translucent as wel. Thanks to my fellow MVP Shawn Kendrot for pointing me to this article that he has written some time ago

01 October 2014

Visualizing geofences as circles on the map in Universal Apps

Intro

If you tried to work with geofences, you may have encountered this problem. You want to have an easy way to show where the darn things are on the map, so you can test the application. This is actually pretty simple, building upon stuff to draw circles on Windows Phone 8 I wrote earlier.

What is important to know – there are now more or universally applicable objects to depict a location. The first one is BasicGeoposition – this is a struct – that basically only holds latitude, longitude and elevation. The second one is Geopoint. You create a Geopoint from a BasicGeoposition. This kind of wrapper class that allows for setting of some additional location settings – like how coordinates and height should be interpreted. I don’t think any of this stuff is really used yet, but I have the feeling Geopoint is going to get more important over time, and anyway as a GIS person I tend to go with the fullest set of location designation. But you might as just well use just BasicGeoposition as a way to pass on coordinates in a universal app.

Do the math!

This is essentially the same method as I wrote before, but adapted for Geopoint. Some other changes come from my former colleague Jarno Peschier, who in his typical way saw possibilities to optimize speed up the circle calculation code (and optimizations actually work – I tested).

using System;
using System.Collections.Generic;
using Windows.Devices.Geolocation;

namespace MappingUtilities
{
  public static class GeopointExtensions
  {
    private const double circle = 2 * Math.PI;
    private const double degreesToRadian = Math.PI / 180.0;
    private const double radianToDegrees = 180.0 / Math.PI;
    private const double earthRadius = 6378137.0;
    
    public static IList<Geopoint> GetCirclePoints(this Geopoint center,
                                   double radius, int nrOfPoints = 50)
    {
      var locations = new List<Geopoint>();
      double latA = center.Position.Latitude * degreesToRadian;
      double lonA = center.Position.Longitude * degreesToRadian;
      double angularDistance = radius / earthRadius;

      double sinLatA = Math.Sin(latA);
      double cosLatA = Math.Cos(latA);
      double sinDistance = Math.Sin(angularDistance);
      double cosDistance = Math.Cos(angularDistance);
      double sinLatAtimeCosDistance = sinLatA * cosDistance;
      double cosLatAtimeSinDistance = cosLatA * sinDistance;

      double step = circle / nrOfPoints;
      for (double angle = 0; angle < circle; angle += step)
      {
        var lat = Math.Asin(sinLatAtimeCosDistance + cosLatAtimeSinDistance * 
                            Math.Cos(angle));
        var dlon = Math.Atan2(Math.Sin(angle) * cosLatAtimeSinDistance, 
                              cosDistance - sinLatA * Math.Sin(lat));
        var lon = ((lonA + dlon + Math.PI) % circle) - Math.PI;

        locations.Add(new Geopoint(new BasicGeoposition { 
        Latitude = lat * radianToDegrees, Longitude = lon * radianToDegrees }));
      }
      return locations;
    }
  }
}

I think this is the Haversine formula but I am not sure and the point is – it gives list of points that, when drawn on a map, gives a reasonable approximation of a circle, where ever you draw it. And that’s what this is about. The radius is the radius in meters, and the numberOfPoints is still the number of points that is used to draw the polygon. For that’s what we are making – as long as there are enough points, our eyes will see this as a circle.

The method is implemented as an extension method to Geopoint, and while I was at it, I added a helper method for BasicGeoposition as well, which was not quite rocket science.

public static IList<Geopoint> GetCirclePoints(this BasicGeoposition center,
                              double radius, int nrOfPoints = 50)
{
  return new Geopoint(center).GetCirclePoints(radius, nrOfPoints);
}

Geofences you said sir?

So far, I have been mumbling only about coordinates – stuff I tend to talk about a lot – but how do I go from geofences to things on a map? Well, pretty easily, actually. It turns out that a geofence has a property Geoshape of type IGeoshape, but since a geofence can only be a circle right now, you can try to cast it to a Geocircle. And a Geocircle has a Center property of type BasicGeoposition, and a Radius property in meters. And that’s what we needed to use the GetCirclePoints extension method. So it’s actually pretty easy to define a new extension method

using System.Collections.Generic;
using Windows.Devices.Geolocation.Geofencing;
using Windows.Devices.Geolocation;

namespace MappingUtilities.Geofencing
{
  public static class GeofenceExtensions
  {
    public static IList<Geopoint> ToCirclePoints(this Geofence fence, 
      int nrOfPoints = 50)
    {
      var geoCircle = fence.Geoshape as Geocircle;

      if (geoCircle != null)
      {
        return geoCircle.Center.GetCirclePoints(geoCircle.Radius, nrOfPoints);
      }
      return null;
    }
  }
}

I always do the as-and-if-null check. I could have make a hard cast, but I am all for defensive programming.

Now the thing is, the GeofenceMonitor has a Geofences collection that can be used to add geofences (duh) but of course you can also retrieve them. And thus you can make yet another simple extension method to retrieve a list of list of points of your current GeoFenceMonitor

using System.Collections.Generic;
using System.Linq;
using Windows.Devices.Geolocation;
using Windows.Devices.Geolocation.Geofencing;

namespace MappingUtilities.Geofencing
{
  public static class GeofenceMonitorExtensions
  {
    public static IList<IList<Geopoint>> GetFenceGeometries(this GeofenceMonitor monitor)
    {
      return monitor.Geofences.Select( p=> p.ToCirclePoints()).ToList();
    }
  }
}

And since the GeofenceMonitor is a static class with a singleton instance, getting all the points of all the geofences in you app in a handy list by one simple call goes like this:

GeofenceMonitor.Current.GetFenceGeometries();

And by iterating over them, you can draw shapes on the map. The fun thing is, since we now have Universal Apps, you can define all this code in a PCL and use it both on Windows Phone and Windows ,and reference this as a Binary. Today. No need to wait for Windows 10. So I put these methods in a PCL called MappingUtilities. And because the current GeofenceMonitor is a singleton, you can use this code in some button method in your app to just get a quick list of the active geofences and draw them on a map, without having to retrieve data from your models or viewmodels, or however you choose to implement your logic.

But there is a tiny detail though. Bing Maps does not using Geopoint or BasicGeoposition, but still the Location class, and that does not exist on Windows Phone.

One more thing to extend

Since I was creating extension methods anyway, I made a new assembly, this time Windows specific, called MappingUtilities.Windows - I bet you did not see that one coming :) – and added the following two extension methods:

using System.Linq;
using Bing.Maps;
using System.Collections.Generic;
using Windows.Devices.Geolocation;

namespace MappingUtilities
{
  public static class PointHelpers
  {
    public static LocationCollection ToLocationCollection(
      this IList<Geopoint> pointList)
    {
      var locations = new LocationCollection();
      foreach (var p in pointList)
      {
        locations.Add(p.ToLocation());
      }
      return locations;
    }

    public static Location ToLocation(this Geopoint location)
    {
      return new Location(location.Position.Latitude, 
                          location.Position.Longitude);
    }
  }
}

And these you can simply use to quickly turn a Geopoint into a Location, or a list of Geopoints into a LocationCollection. How this is used is displayed in the demo solution.

Wrapping it up

The demo solution creates two geofences, and shows how the location of said fences can be displayed pretty easily by calling the GetFenceGeometries extension method, both on Windows Phone and Windows.

image image

The drawing on the map is done by a simple method that I won’t go into here. I wish to stress the fact that the creation of geofences is in the Shared portion of the app, as well as the facts that these geofences are pretty minimal – I just create them with the default settings - and that I don’t do anything with these geofences apart from creating them. The only thing what this app shows is how easy it is to display geofences using this extension methods, if you have geofences.

If you want to know more about using geofences in Universal Apps and how you could actually be doing something useful with them, please visit my talk on October 18th 2014 where I will be doing a session about this subject on the Lowland Windows Phone Hacker day. This day is organized in a joint effort by both the Dutch and Belgian developer communities in Tilburg, the Netherlands, a place very near the Belgian-Dutch border. Apart from great content - we also have some prizes you may be interested in

image

Just saying :)

13 August 2014

Querying the Windows Phone 8.1 map when there are child objects over it

With the Windows Phone 8.1 SDK came (yet again) an improved Map Control. This brought, among other things, the MapIcon. In ye olde days, the map could only draw lines and polygons natively – when you had to draw icons you had to add child objects to the map itself, which were drawn by the UI thread and not by the map. This is slower and eats more resources.

imageThe new map still offers this possibility. And sometimes we need it, too, for a couple of reasons. First of all, a MapIcon can only display an image and an optional label. Second, MapIcons are drawn on the map using a ‘best effort’, which mean overlapping icons don’t get displayed at all and – worse – if you query the map, using the FindMapElementsAtOffset method, they are not found either.

So in some cases we just need to resort to drawing XAML elements by adding to the map’s Children collection – an option, which luckily has been improved tremendously as we now, for instance, can data bind these elements using the MapItemsControl, as explained by my smart fellow MVP Shawn Kendrot. Before 8.1, we needed the Windows Phone Toolkit for that.

But I noticed something very odd. I have been trained to use the MapTapped event to trap the location where the user tapped, as the Tapped event is being trapped by the map itself. It never fires. So I used MapTapped, and then FindMapElementsAtOffset to query the map;

But to my dismay I also discovered that the MapTapped event does not fire either when you tap on a location where a child object is displayed. Ergo – if you tap on the cross where the Windows Phone logo is displayed, nothing happens. So how am I to find out what’s underneath there?

After some trashing around I stumbled upon the answer – if you tap on a child element that’s on the map – not the MapTapped, but the Tapped event fires. “The event that never fires” comes to the rescue. In addition, the Tapped event on the child object itself fires. So I created the following method to query the map:

private void QueryMap(Point offset)
{
  foreach (var obj in MyMap.FindMapElementsAtOffset(offset))
  {
    Debug.WriteLine(obj.GetType());
  };
}
The regular method to trap MapTapped:
private void MyMap_OnMapTapped(MapControl sender, MapInputEventArgs args)
{
  Debug.WriteLine("MyMap.MapTapped occurred");
  QueryMap(args.Position);
}

And a method to trap Tapped when that occurs, and that also routes to the QueryMap method, with a little help from the GetPosition event method:

private void MyMap_OnTapped(object sender, TappedRoutedEventArgs e)
{
  Debug.WriteLine("MayMap.Tapped occurred");
  QueryMap(e.GetPosition(MyMap));
}

And because the events never fire simultaneously, you will see the map finds one PolyLine when you tap on a line just next to the symbol, will and it fill find two lines (the crossing of both) when you tap on the symbol- but never will you get a double hit from both events.

By attaching to the Tapped event of the Child object I can also detect if the symbol itself is tapped:

var childObj = new Image { 
  Source = new BitmapImage(new Uri("ms-appx:///Assets/wplogo.png")) };
MapControl.SetNormalizedAnchorPoint(childObj, new Point(0.5, 0.5));
MapControl.SetLocation(childObj, p5);
MyMap.Children.Add(childObj);
childObj.Tapped += ChildObj_Tapped;



void ChildObj_Tapped(object sender, TappedRoutedEventArgs e)
{
  Debug.WriteLine("ChildObj.Tapped occurred");
}

And thus we can find everything on the map, provided you attach the right methods to the right events. The MapTapped/Tapped combo of the map is an odd one but once you know how to do it – a few extra lines of code does it all.

A demo app demonstrating the whole concept can be found here.The only thing it does is write things to the debug console when you do something, so pay attention what happens there ;-)

30 July 2014

Sharing code between a Xamarin Forms/MVVMLight app and EF-code first backend using Shared Reference Project Manager

Disclaimer

I am in the very early early stages of toying with Xamarin and it may well be that whatever I am doing here, is not the smartest thing in the world. This is a much as a report of my learning (may ‘struggle’ is a better word) as how-to. But what I describe here works – more or less – although it was a mighty hassle to actually get it working.

The objective

While I was having my first trials with Xamarin stuff, I turned to Entity Framework Code First because, well, when setting up a backend I am lazy. Without much thinking I made the models, using data annotations, and soon found out I had painted myself into a corner with a model that (very simplified) looks like this

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace DemoShared.Models
{
   [Table("Pictures")]
    public class Photo
    {
      public long ID { get; set; }
      
     [Required]
      public string Name { get; set; }
    }
}

imageGuess what - data annotations don't work in PCL. Now what? Make a shadow class library for use on the client? That kind of went against my principles. So I decided to use the Shared Reference Project Manager.  This is a Visual Studio extension that can make basically any project shared, in stead of only between Windows Phone 8.1 and Windows 8.1 You can get it here.

Setting up the initial application

Using this tutorial by Laurent Bugnion I did set up my first Xamarin MVVM application. I created an app "DemoShared" and used the tutorial it up to the point where he starts making a new new xaml page (“Creating a new XAML page”).

Try to build the project. If the Windows Phone project fails with “The 'ProductID' attribute is invalid.. “ (etc), manually open it’s Properties/WMAppManifest.xml, for instance with NotePad, find “ProductID and “PubisherID” and place the generate GUIDs between accolades. This is a bug the Xamarin project template that I already reported.image

Basic setup of the backend

  • File/New Project/Web/ASP.Net Web application (it’s the only choice you have)
  • Choose a name (I chose DemoShared.Backend) and hit OK
  • Choose “Web Api” and UNSELECT “Host in the cloud”
  • Go to the NuGet Package manager and update all the packages, because a lot of them will be horribly outdated. Hit “accept” or “yes” on any questions
  • Delete the all folders except “App_Data”, “App_Start” and “Controllers” because we don’t need them
  • Delete BundleConfig.cs from App_Start, and remove the reference to it from Global.asax.cs as well
  • Add a (.NET) class library DemoShared.DataAccess
  • Install NuGet Package “EntityFrameWork” in DemoShared.Backend and DemoShared.DataAccess
  • Create a project DemoShared.Models of type Shared Project (Empty) of type Visual C#:

image

  • Right-click the DemoShared (Portable) project, select Add, then “Add Shared Project reference”, then select “DemoShared.Models
  • Repeat this procedure for DemoShared.DataAccess.
  • Also add a reference to System.ComponentModel.DataAnnotations to DemoShared.DataAccess.
  • Add DemoShared.DataAccess as a reference to DemoShared.Backend

Re-defining the models

The key point of this whole exercise is to re-use models. For one model class this is quite some overkill, but if you have a large collection of models, things becomes quite different. So anyway. I redefined the model class as follows:

#if NET45
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
#endif

namespace DemoShared.Models
{
#if NET45
   [Table("Pictures")]
#endif

  public class Photo
  {
    public long ID { get; set; }

#if NET45     
     [Required]
#endif
    public string Name { get; set; }
  }
}

Now in order to make this compile work the way it is intended on server, you will need to add the conditional compilation symbol “NET45” to all configurations of DemoShared.DataAccess

image

And then it compiles both into PCL and into the DataAccess project – without the attributes in the first, but with the attributes the second. Remember, this is just a really nifty formalized way of ye olde way of file linking.

Kick starting the Entity Framework

Following this tutorial – more or less, I added the following class to the DataAccess project:

using System.Data.Entity;
using DemoShared.Models;

namespace DemoShared.DataAccess
{
  public class DemoContext : DbContext
  {
    public DemoContext()
      : base("DemoContext")
        {
        }

    public DbSet<Photo> Photos { get; set; }
  }
}

And then to the web.config I added this rather hopeless long connection string:

<connectionStrings>
  <add name="DemoContext" connectionString="Data Source=(localdb)\v11.0;AttachDbFilename=|DataDirectory|DemoDb.mdf;Initial Catalog=DemoDb;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
</connectionStrings>

Rebuild the project. Now go to the Backend Controller folder, right click Add/Controller, select “Web API 2 Controller with actions, using Entity Framework” and in the following dialog, select the Model and Data Context class as displayed to the right

imageimage

imageNow if you set “DemoShared.Backend” as start-up project, set it’s startup URL to “api/photos” and start the project, you will automatically get a database “DemoDb” in “App_Data”.Is EF code first cool or what? And my data annotations are used correctly – the Photo type is stored in the “Pictures” table, just as I wanted.

image

Now I manually entered some data in the tables to get something to show, but of course you can also write an initializer like the EF tutorial describes, or write a separate project that prefills the the database. This is what I usually do, and that’s why I have put the DemoContext in a separate assembly and not in the web project – to be able to reference it form another project.

And now finally, to the Xamarin app

Adding the view model

First of all – start the NuGet package manager and add Newtonsoft Json.NET to DemoShared (Portable).

Then, add a “ViewModels” folder to DemoShared (Portable) to hold this class:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net;
using DemoShared.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using Newtonsoft.Json;

namespace DemoShared.ViewModels
{
  public class MainViewModel : ViewModelBase
  {
    public MainViewModel()
    {
      Photos = new ObservableCollection<Photo>();
    }

    public string DataUrl
    {
      get { return "http://169.254.80.80:28552/api/Photos"; }
    }

    public ObservableCollection<Photo> Photos { get; set; }

    private RelayCommand loadCommand;

    public RelayCommand LoadCommand
    {
      get
      {
        return loadCommand ?? (loadCommand = new RelayCommand(
            () =>
            {
              var httpReq = (HttpWebRequest)WebRequest.Create(new Uri(DataUrl));
              httpReq.BeginGetResponse((ar) =>
              {
                var request = (HttpWebRequest)ar.AsyncState;
                using (var response = (HttpWebResponse)request.EndGetResponse(ar))
                {
                  if (response.StatusCode == HttpStatusCode.OK)
                  {
                    using (var reader = new StreamReader(response.GetResponseStream()))
                    {
                      string content = reader.ReadToEnd();
                      if (!string.IsNullOrWhiteSpace(content))
                      {
                        var result = JsonConvert.DeserializeObject<List<Photo>>(content);
                        foreach (var g in result)
                        {
                          Photos.Add(g);
                        }
                      }
                    }
                  }
                }
              }, httpReq);
            }));
      }
    }

    private static MainViewModel instance;
    public static MainViewModel Instance
    {
      get
      {
        CreateNew();
        return instance;
      }
      set { instance = value; }
    }

    public static MainViewModel CreateNew()
    {
      if (instance == null)
      {
        instance = new MainViewModel();
      }
      return instance;
    }
  }
}

A rather standard MVVMLight viewmodel I might say, with the usual singleton pattern I use (I am not a big fan of "Locators"). When the command is fired, data is loaded from the WebApi url, deserialized to the same model code as was used to create it on the server, and loaded into the ObservableCollection. Nothing much special here.

There's one fishy detail - in stead of a computer name or “localhost”, there is this hard coded IP adress. We will get to that later.

Adding the Forms XAML page

Add a new Forms XAML page like this

image

Be aware that you have to enter the name manually, that’s a bug in the Xamarin tools, like Laurent Bugnion already described in his tutorial

Now, go to the DemoPage.Xaml.cs and set up the data context by adding one line of code to the constructor of the page:

public DemoPage()
{
  InitializeComponent();
  BindingContext = MainViewModel.Instance;
}

This will of course require a “using DemoShared.ViewModels;” at the top. Then we add our “XAML” to start page

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
		xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
		x:Class="DemoShared.DemoPage">
  <StackLayout Orientation="Vertical" Spacing="0">
    <Button Text="Click here" Command="{Binding LoadCommand}" 
            VerticalOptions="Start" HorizontalOptions="Center"  />
    <ListView ItemsSource="{Binding Photos}"
           RowHeight="50">
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <ViewCell.View>
              <StackLayout Padding="5, 5, 0, 5"
                           Orientation="Vertical"
                           Spacing="2">
                <Label Text="{Binding ID}" Font="Bold, Large" 
                       TextColor="Red"/>
              </StackLayout>
            </ViewCell.View>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </StackLayout>
</ContentPage>

Now this looks kind of familiar, but also kind of - not really. Binding looks kind of you would expect, some controls have weird options, as some... well, have you ever seen a ViewCell before? Working with this stuff really makes you appreciate things like IntelliSense and Blend, for you have none of the above. Good old handcrafted XML. But - it does carry the magic of Xamarin Forms.

Finally, we make our start page the start page of the app. Go to App.cs in DemoShared (Portable) and change the GetMainPage method to:

public static Page GetMainPage()
{
  return new DemoPage();
}

imageimageThen I set a multiple startup project, setting both the Windows Phone project as well as the backend project as startup.

And sure enough, there’s the Windows Phone startup screen with one button. And if I click it…

 

I get an error.

And now for the fishy detail

So your backend is a website - running under IISExpress. By default, that’s not accessible by anything else but localhost, and not by IP adress. What you have to do is explained mostly here. First, find your applicationhost.config. It’s usually in

  • C:\Users\%USERNAME%\Documents\IISExpress\config or
  • D:\Users\%USERNAME%\Documents\IISExpress\config

In my case it’s the second one. Now before you start messing around in it, you might want to make a backup. Then open the file, and search for the text "*:28552”. This should yield you the following line:

<binding protocol="http" bindingInformation="*:28552:localhost" />

Make a copy of this line, directly below it. Replace localhost by the IP adress you found earlier using "ping -t –4 <hostname>". Net result, in my case:

<binding protocol="http" bindingInformation="*:28552:localhost" />
<binding protocol="http" bindingInformation="*:28552:169.254.80.80" />

Then – very important – close Visual Studio and restart as Administrator. For then and only then IISExpress will be allowed to bind to something else than localhost. That crucial bit of information is unfortunately hard to find.

And then, sure enough:

imageimage

It runs on Windows Phone and Android. And I am sure, on Apple stuff too but lacking Apple hardware and developer account I could not test it.

BTW – and alternative to messing around with IIS Express settings is of course host the website in IIS. But that requires apparently the database being hosted in SQL*Server Express, so it can’t be in App_Data like this. Or something like that. I found this made a quick test easier.

One more thing

If you want to test this from outside your computer, on a phone, you might want to open the port for TCP traffic:

image

Conclusion

Using Xamarin allowed us to shared code over multiple platform, but the Shared Reference Project Manager we could also share with the server. And the ease with which you can get a database powered backend up and running using EF code first was also quite impressive to me. Now tiny details, like security and upgrades, I leave as exercise to the reader.

Code can be found here.

Post scriptum

My fellow MVP Corrado Cavelli pointed out to me that the LoadCommand method in the MainViewModel can be made considerably less complex by using the Microsoft ASP.NET Web API 2.2 Client NuGet Package. He turns out to be quite right. When you add this to the portable class library you can rewrite the command to a relatively simple

public RelayCommand LoadCommand
{
  get
  {
    return loadCommand ?? (loadCommand = new RelayCommand(
        async () =>
              {
                var client = new HttpClient { 
                   BaseAddress = new Uri("http://169.254.80.80:28552") };
                var response = await client.GetAsync("api/Photos");
                var result = await response.Content.ReadAsAsync<List<Group>>();
                foreach (var g in result)
                {
                  Groups.Add(g);
                }
              }));
  }
}
This requires "using System.Net.Http;" and "using System.Threading.Tasks;" to be added to the file, but it's a lot less cluttered than my original - copied somewhere from the interwebz - code. Thanks Corrado!

16 July 2014

Cross-platform behavior for making screenshots in Windows (Phone) 8.1

Currently I am developing an app that should be able to share or save whatever is on the screen. I came upon this article by Loek van den Ouweland about RenderTargetBitmap and wondered if I could a) make this more generally (re)usable and b) make it play nice with MVVM.

The answer was – you guessed it – a behavior. The fun thing is, you can drag it onto any UI element, and it will create a screenshot of whatever what’s inside that element ( and that’s not necessary the whole screen!) and save it to storage. Two dependency properties, Target and Prefix, determine what message the behavior listens to, and what the file prefix is.

The main code of the functionality – which still looks a lot like Loek’s original sample – is in the behavior itself. To invoke it from the viewmodel, I call in the help of the MVVMLight messenger. So I start with the message that the viewmodel and the behavior use to communicate:

using GalaSoft.MvvmLight.Messaging;

namespace WpWinNl.Behaviors
{
  public class ScreenshotMessage : MessageBase
  {
    public ScreenshotMessage(object sender = null, object target = null, 
      ScreenshotCallback callback = null) : base(sender, target)
    {
      Callback = callback;
    }

    public ScreenshotCallback Callback { get; set; }
  }

  public delegate void ScreenshotCallback(string fileName);
}

This is a standard MVVMLight message, with the added extra that it can carry an optional payload of a callback. The behavior I created saves the file to a KnownFolder, but it can send the name of the file that’s been created back to the calling object by calling said callback.

The basics of the behavior – implemented once again as a SafeBehavior – is actually pretty simple:

using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Imaging;
using GalaSoft.MvvmLight.Messaging;

namespace WpWinNl.Behaviors
{
  public class ScreenshotBehavior : SafeBehavior<FrameworkElement>
  {
    protected override void OnSetup()
    {
      Messenger.Default.Register<ScreenshotMessage>(this, ProcessMessage );
      base.OnSetup();
    }

    protected override void OnCleanup()
    {
      Messenger.Default.Unregister(this);
      base.OnCleanup();
    }

    private async void ProcessMessage(ScreenshotMessage m)
    {
      if (m.Target != null && Target != null)
      {
        if (m.Target.Equals(Target))
        {
          await DoRender(m.Callback);
        }
      }
      else
      {
        await DoRender(m.Callback);       
      }
    }
  }
}

So it listens to a ScreenshotMessage coming by. When Target (a dependency property in this behavior) is equal to the target specified in the message, the rendering is done. Think of Target as a shared key – this enables a viewmodel to fire a specific behavior using – usually - a string. This I show in the sample solution. If both the behavior’s target and the message’s target are null, it fires as well. You can use this if you only have one behavior and one call. If, however, the behavior is used on multiple places I strongly recommend specifying Target, or else you might get very interesting race conditions.

The actual rendering method is nearly all Loek’s code with some little adaptions:

private async Task DoRender(ScreenshotCallback callback)
{
  var renderTargetBitmap = new RenderTargetBitmap();
  await renderTargetBitmap.RenderAsync(AssociatedObject);
  var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();

  var storageFile = 
    await KnownFolders.SavedPictures.CreateFileAsync(
      string.Concat(Prefix, ".png"),
      CreationCollisionOption.GenerateUniqueName);
  using (var stream = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
  {
    var encoder = 
await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream); encoder.SetPixelData( BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint) renderTargetBitmap.PixelWidth, (uint) renderTargetBitmap.PixelHeight, 96d, 96d, pixelBuffer.ToArray()); await encoder.FlushAsync(); if (callback != null) { callback(storageFile.Name); } } }

It creates an unique file based on the Prefix dependency property in the “SavedPictures” known folder, renders the screen as a png file, saves it, and if so desired calls a callback transmitting the name back to that callback – most likely a method of the viewmodel, which can then act on it.

The rest of the behavior are the two dependency properties that I leave out for brevity's sake.You don’t need to use them at all – if you don’t set Prefix, it will use “Screenshot”

To use it: as stated earlier, drag it on top of the UI element you want to make screenshots of, then add for instance the following code to your viewmodel:

public ICommand ScreenshotCommand
{
  get
  {
    return new RelayCommand(() =>
    {
      var m = new ScreenshotMessage(this, 
        "screenshot", MyCallback);
      Messenger.Default.Send((m));
    });
  }
}

private void MyCallback(string name)
{
  Debug.WriteLine(name);
}
In the message I have set the target to "screenshot", so in the XAML it should now says
<Behaviors:ScreenshotBehavior Target="screenshot" Prefix="MyScreenshot"/>

or else the behavior won’t respond to the message. When it’s done, it writes the name of the created file to the console – not very useful in production scenario’s, but it shows it’s working. In this callback you can for instance inform the user the file has been saved, activate a share contract or whatever you feel is neccesary.

The fun thing is, of course, that in this awesome world we live today behaviors can actually be cross-platform and be defined in a PCL and run both on Windows Phone 8.1 as on “big Windows” 8.1 :).

MyScreenshotMyScreenshot (2)

In the demo solution, that contains both a Windows Phone App, a Windows App, and a shared project you will see I have once again dragged the Main.Xaml to the shared project just for the fun of it. Don’t forget to set access to the pictures library in both the app manifests. I always forget that.

MyScreenshotFor Windows, the pictures end up in my “USERPROFILE%\Picures” folder, in Windows Phone they end up in “Pictures\Saved Pictures”.

Interesting detail – I have set the application’s root grid background to gray. If I don’t set a color, the background of the picture on Windows is not black, but cropped to 1297x1080 in stead of 1920x1080 as is my native resolution. I have not been able to determine yet why this is.

I built this behavior on top of my WpWinNl 2.0.3 package, but you can easily adapt it to get it to work as a normal behavior just using the procedure I described here.

Oh and the picture? It’s just an old picture of my wife’s Mercedes truck back in 2004, when she and a colleague joined a Guinness Book of records attempt to create the longest truck convoy ever. This is a short stop at dyke road shoulder before the actual 9.5 km long convoy was assembled. Interesting detail: all 416 drivers were women. The convoy drove 22 km without any problems, and as my wife so aptly said – it was the first time she joined a traffic jam for fun.

25 June 2014

Using SensorCore and MVVMLight to build a ‘what did I do where and when’ Windows Phone 8.1 app

Introduction

As you might have seen, the SensorCore SDK has been released. I intended to bring out a full fledged app utilizing this new and exiting SDK, but I found out that I first had to explain a bit about the ‘supporting acts’ I was going to use – if you study my last four posts you will see they all contain parts of the sample app that goes what this post. In addition, the last month I had a rather time- and energy-consuming, frustrating, ultimately fruitless and disappointing thing cross my way with which I won’t bother you with – sufficient to say I would rather have spent this time on writing apps.

Anyway. I set out to make an app that would use the both the SensorCore ActivityMonitor and TrackPointMonitor, as well as the existing MapLocationFinder to find out what I was doing where, and when.

For those who are not familiar with the concept of SensorCore: a few newer devices, among which the Lumia 630, have hardware aboard that allows you to track where the user has been about every 5 minutes, and what he was doing, for the last 10 days. Based upon this information, it designates places that are apparently important to the owner (like home, work). In addition, it has a step counter. Everything is handled by a special, very low power piece of hardware that continuously tracks location and movement with very little battery drain.

Now for the tin foil hat crew: there are some key differences to the way other platforms handle this. First of all, it can only happen with the user’s consent – it’s a phone setting that has to be explicitly turned on, and then and only then the hardware starts to collect data. Furthermore, this data never leaves the phone all by itself – there is no Bing server tracking your actions to clutter your phone with ads or other stuff you may not necessarily appreciate. The only way this data can ever leave your phone is by some for of app – but first you have to give the phone consent, then the app, and said app has to pass certification as well. This Windows/Windows Phone ecosystem takes your privacy very seriously. Ask any developer – almost everyone has been rejected at least once over some tiny detail they missed in the privacy sections of the certification requirements ;-)

The app I am going to show you looks like this. With sincere apologies for the video quality – apparently maps on a slow device is a bit too much for a cable connection with a Lumia 630, but it shows the gist.

SensorCore and MVVMLight

Setting the stage

So I created a Windows Phone 8.1 (Store) app WhatWhenWhere with one supporting class library WpWinNl.MvvmLight.Contrib. I added my WpWinNlMaps NuGet packages to both projects, and added PropertyChanged.Fody as well to it. The contrib package holds BaseNotifyingModel and TypedViewModelBase – as described in this article on using PropertyChanged.Fody for Model-to-Viewmodel communication

An other minor detail – you might want to pull in the Lumia SensorCore NuGet package as well ;-)

A model to hold one location

So I needed a model to know where and when something happened (a TrackPointMonitor TrackPoint), what I was doing there (an ActivityMonitorReading) and an approximate street address (a MapLocationFinder MapLocation). First analysis learns that an ActivityMonitorReading only holds a Timestamp and an Activity. This activity describes what I was doing (Idle, Moving, Stationary, Walking or Running). As I already know the time from the TrackPoint, and I only use that Timestamp to get the accompanying Activity, we might as just only hold that Activity, and not the whole ActivityMonitorReading.

So I created the following model class:

using System;
using System.Linq;
using Windows.Devices.Geolocation;
using Windows.Services.Maps;
using Lumia.Sense;
using WpWinNl.MvvmLight;

namespace WhatWhenWhere.Models
{
  public class ActivityPoint : BaseNotifyingModel
  {
    public ActivityPoint()
    {
    }

    public ActivityPoint(TrackPoint p)
    {
      LengthOfStay = p.LengthOfStay;
      Position = p.Position;
      Radius = p.Radius;
      Timestamp = p.Timestamp;
    }

    public ActivityPoint(TimeSpan lengthOfStay, BasicGeoposition position, 
      double radius, DateTimeOffset timestamp, Activity activity)
    {
      LengthOfStay = lengthOfStay;
      Position = position;
      Radius = radius;
      Timestamp = timestamp;
      Activity = activity;
    }

    public TimeSpan LengthOfStay { get; set; }
    public BasicGeoposition Position { get; set; }
    public double Radius { get; set; }
    public DateTimeOffset Timestamp { get; set; }
    public Activity Activity { get; set; }

    public MapLocation LocationData { get; set; }

    public void LoadAddress()
    {
      if (LocationData == null)
      {
        MapLocationFinder.FindLocationsAtAsync(
new Geopoint(Position)).AsTask().ContinueWith(p => { var firstLocationData = p.Result.Locations; if (firstLocationData != null) { LocationData = firstLocationData.FirstOrDefault(); } }); } } } }

Which has, unlike the TrackPoint and the ActivityMonitorReading, the added benefit of being serializable on account of having a default constructor (ahem) which comes in handy when doing storing state when the app is deactivated (note: the demo app does not handle that).

You might notice the LoadAddress method that can called externally to load the address on demand (in stead of just doing that in the constructor. This has been done for performance reasons – geocoding and loading addresses is expensive and  slow, and you don’t want to do that if you are not sure if the user actually wants to see those addresses or not. So you defer that to the last possible moment – when the user actually selects this object.This technique is described in detail in this article .Notice it carefully avoids the way how you obtain these locations ;-) .

A model to load the data.

The bulk of the DataLoaderModel is shamelessly stolen from the samples.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Messaging;
using Lumia.Sense;
using WhatWhenWhere.Models.Messages;
using Tracker = Lumia.Sense.TrackPointMonitor;

namespace WhatWhenWhere.Models
{
  public class DataLoaderModel
  {
    public DataLoaderModel()
    {
    }

    public async Task Init()
    {
      await InitTracker();
      await InitActivityMonitor();
    }

    private Tracker tracker;
    private async Task<bool> InitTracker()
    {
      if (await Tracker.IsSupportedAsync())
      {
        if (tracker == null)
        {
          if (await CallSensorcoreApiAsync(async () =>
              tracker = await Tracker.GetDefaultAsync()))
          {
            return true;
          }
        }
      }
      return false;
    }

    private ActivityMonitor activityMonitor;

    private async Task<bool> InitActivityMonitor()
    {
      if (await ActivityMonitor.IsSupportedAsync())
      {
        if (activityMonitor == null)
        {
          if (await CallSensorcoreApiAsync(async () =>
              activityMonitor = await ActivityMonitor.GetDefaultAsync()))
          {
            return true;
          }
        }
      }
      return false;
    }

    public async Task SetSensorState(bool enable)
    {
      await SetSensorState(tracker, enable);
      await SetSensorState(activityMonitor, enable);
    }

    private async Task SetSensorState(ISensor sensor, bool active)
    {
      if (sensor != null)
      {
        if (!active)
        {
          await CallSensorcoreApiAsync(async () => 
{ await sensor.DeactivateAsync(); }); } else { await CallSensorcoreApiAsync(async () =>
{ await sensor.ActivateAsync(); }); } } } private async Task<bool> CallSensorcoreApiAsync(Func<Task> action) { try { await action(); } catch (Exception ex) { SenseHelper.GetSenseError(ex.HResult); Messenger.Default.Send( new SenseFailureMessage(SenseHelper.GetSenseError(ex.HResult), this)); // This is now not handled, but should really be handled as described here // (Calling the SensorCore SDK safely) return false; } return true; } } }

It has some clever tricks with Lambdas to be able to call SensorCore methods without having to copy & past the whole try/catch stuff. I basically only added in both sensors, and let it send out an error message via the MVVMLight Messenger if things go wrong (note: app does not handle this, but I think it’s a good pattern).

I did add some code to this stolen-from-sample class, to actually load the data into the model:

public public async Task<bool> LoadInitialData()
{
  if (tracker != null && activityMonitor != null)
  {
    var result = await tracker.GetTrackPointsAsync(
       DateTime.Now - TimeSpan.FromDays(10), TimeSpan.FromDays(10));
    if (RoutePoints == null)
    {
      RoutePoints = new List<ActivityPoint>(result.Select(p => new ActivityPoint(p)));
    }
    else
    {
      RoutePoints.Clear();
    }
    await LoadActivities();
    return true;
  }
  return false;
}

private async Task LoadActivities()
{
  foreach (var r in RoutePoints)
  {
    try
    {
      var reading = await activityMonitor.GetActivityAtAsync(r.Timestamp);
      r.Activity = reading.Mode;
    }
    catch (Exception ex)
    {  // lame – I know.
    }
  }
}

public List<ActivityPoint> RoutePoints { get; set; }

This is the core of the whole app – the rest is fluff to make things visible. LoadInitialData first loads all the TrackPoints from the last 10 days (which is the maximal available time anyway) and converts them into my ActivityPoint model. It then proceeds to find the actual activity that was performed at the time the TrackPoint was recorded. And if you call the ActivityPoint’s LoadAddress method, it will find the (approximate) address of the location it was recorded – it will even return highway names (it’s won’t find a house number then, but I don’t think that will surprise anyone).

A view model for a single point

Quite a lot of how this this works, is already described in my article on using PropertyChanged.Fody. I can directly bind to the model, but for formatting output and commands I like to keep my model clean and employ a viewmodel for that.

using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows.Input;
using Windows.Devices.Geolocation;
using Windows.Services.Maps;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using GalaSoft.MvvmLight.Threading;
using WhatWhenWhereMessages;
using WhatWhenWhere.Models;
using WpWinNl.MvvmLight;

namespace WhatWhenWhere.ViewModels
{
  public class ActivityViewModel : TypedViewModelBase<ActivityPoint>
  {
    public ActivityViewModel()
    {
    }

    public ActivityViewModel(ActivityPoint p) : base (p)
    {
    }
  
    public Geopath Location
    {
      get
      {
        return new Geopath(new[] { Model.Position });
      }
      set
      {
        var p = value.Positions.FirstOrDefault();
        if (Model.Position.Equals(p))
        {
          Model.Position = p;
          RaisePropertyChanged(() => Location);
        }
      }
    }

    public string DateAndTime
    {
      get
      {
        return Model != null ? 
Model.Timestamp.ToString("dd-MM-yyyy hh:mm:ss",
CultureInfo.InvariantCulture) : string.Empty; } } public string LocationName { get { return Model != null && Model.LocationData != null ?
GetFormattedAdress(Model.LocationData.Address) : string.Empty; } } private string GetFormattedAdress(MapAddress a) { if (a == null) throw new ArgumentNullException("a"); return string.Format("{0} {1} {2} {3}",
a.Street, a.StreetNumber, a.Town, a.Country); } public ICommand SelectCommand { get { return new RelayCommand( () => { Messenger.Default.Send(new SelectedObjectMessage(this)); Model.LoadAddress(); }); } } public ICommand DeSelectCommand { get { return new RelayCommand( () => Messenger.Default.Send(new SelectedObjectMessage(null))); } } protected override void ModelPropertyChanged(object sender,
PropertyChangedEventArgs e) { if (e.PropertyName == "LocationData") { DispatcherHelper.CheckBeginInvokeOnUI(() =>
RaisePropertyChanged(() => LocationName)); } } } }

There are a few things that might attract your attention:

  • The Location in this viewmodel is a GeoPath of only one position. This is because I want to re-use my MapBinding assembly that I originally created for Windows Phone 8, and recently ported to Windows Phone 8.1
  • The SelectCommand explicitly launches the loading of the address when the viewmodel is selected. In my previous sample I showed a way to do this in the getter of a property, but this is a better way I think (as I already said then)
  • The ModelPropertyChanged method launches a RaisePropertyChanged using MVVMLight’s DispatcherHelper. While this is very useful, it requires the DispatcherHelper to be initialized in the App.Xaml.cs. We will get to that later.

Bringing it all together

The MainViewModel is always my ‘class that brings it all together’. I won’t show all details here, or this article will be even longer than it already is. I start with some initialization stuff:

using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.Devices.Geolocation;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using WhatWhenWhereMessages;
using WhatWhenWhere.Models;
using WpWinNl.Messages;
using WpWinNl.Utilities;

namespace WhatWhenWhere.ViewModels
{
  public class MainViewModel : ViewModelBase
  {
    public async Task Start()
    {
      if (Activities == null)
      {
        Activities = new ObservableCollection<ActivityViewModel>();
      }

      if (Route == null)
      {
        Route = new ObservableCollection<RouteViewModel>();
      }

      Messenger.Default.Register<WindowVisibilityMessage>(this, async m =>
      {
        await ProcessWindowVisibilityMessage(m);
      });

      Messenger.Default.Register<SelectedObjectMessage>(this, 
ProcessSelectedObjectMessage); await Model.Init(); } private void ProcessSelectedObjectMessage(SelectedObjectMessage message) { SelectedItem = message.Activity; } private async Task ProcessWindowVisibilityMessage(WindowVisibilityMessage m) { if (Model != null) { if (!IsInDesignMode) { await Model.SetSensorState(m.Visible); } } } } }

And you can also see the viewmodel listens to two messages: one that is fired when an object is selected, and one that is fired when the main windows becomes visible (or invisible) - and sets the sensor state according to that. The second half of the MainViewModel mostly contains data properties and a command:

public ICommand LoadCommand
{
  get
  {
    return new RelayCommand(
      async () =>
      {
        await Model.LoadInitialData();
        Model.RoutePoints.ForEach(p => Activities.Add(new ActivityViewModel(p)));
        Route.Clear();
        var route = new RouteViewModel(Activities);
        Route.Add(route);
        ViewArea = GeoboundingBox.TryCompute(route.Path.Positions);
      });
  }
}

public ObservableCollection<RouteViewModel> Route { get; set; }

public ObservableCollection<ActivityViewModel> Activities { get; set; }

private GeoboundingBox viewArea = GeoboundingBox.TryCompute(new[] 
  { new BasicGeoposition { Latitude = -90, Longitude = -90 }, 
    new BasicGeoposition { Latitude = 90, Longitude = 90 } });

public GeoboundingBox ViewArea
{
  get { return viewArea; }
  set
  {
    if (viewArea != value)
    {
      viewArea = value;
      RaisePropertyChanged(() => ViewArea);
    }
  }
}

private ActivityViewModel selectedItem;
public ActivityViewModel SelectedItem
{
  get { return selectedItem; }
  set
  {
    if (selectedItem != value)
    {
      selectedItem = value;
      RaisePropertyChanged(() => SelectedItem);
    }
  }
}

Notice the LoadCommand: not only it loads the activities into a view model, but it also makes a new RouteViewModel (for drawing one line between all the points) and a bounding box to make all the points fit into the view. The RouteViewModel itself is a very simple thing that creates a GeoPath from all the points of all activities:

using System.Collections.Generic;
using System.Linq;
using Windows.Devices.Geolocation;
using GalaSoft.MvvmLight;

namespace WhatWhenWhere.ViewModels
{
  public class RouteViewModel : ViewModelBase
  {
    public RouteViewModel()
    {
    }

    public RouteViewModel(IEnumerable activities)
    {
      Path = new Geopath(activities.Select(p => p.Location.Positions.First()));

    }

    private Geopath geoPath;
    public Geopath Path
    {
      get { return geoPath; }
      set
      {
        if (geoPath != value)
        {
          geoPath = value;
          RaisePropertyChanged(() => Path);
        }
      }
    }
  }
}

And a wee bit of XAML to glue it all together

Being a lazy ****** and not wanting to think of something to draw all the stuff on a map, I reused both my Map Drawing behavior as the trick to show a popup, and came out with pretty little XAML indeed:

<Page.BottomAppBar>
  <CommandBar>
    <AppBarButton Icon="Accept" Label="Load" Command="{Binding LoadCommand, Mode=OneWay}"/>
  </CommandBar>
</Page.BottomAppBar>

<!-- stuff snipped -->

<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0">
  <interactivity:Interaction.Behaviors>
    <behaviors:SizeListenerBehavior x:Name="ContentRootListener"/>
  </interactivity:Interaction.Behaviors>
  <Button Content="Button" HorizontalAlignment="Left" Margin="246,208,0,0" 
        VerticalAlignment="Top" Command="{Binding LoadCommand}"/>

  <Maps:MapControl maps:MapBindingHelpers.MapViewArea="{Binding ViewArea}">
    <interactivity:Interaction.Behaviors>
      <maps:MapShapeDrawBehavior LayerName="Locations" 
        ItemsSource="{Binding Activities}" 
        PathPropertyName="Location">
        <maps:MapShapeDrawBehavior.EventToCommandMappers>
          <maps:EventToCommandMapper EventName="MapTapped" 
            CommandName="SelectCommand"/>
        </maps:MapShapeDrawBehavior.EventToCommandMappers>
        <maps:MapShapeDrawBehavior.ShapeDrawer>
          <maps1:MapActivityDrawer/>
        </maps:MapShapeDrawBehavior.ShapeDrawer>
      </maps:MapShapeDrawBehavior>
        
      <maps:MapShapeDrawBehavior LayerName="Route" 
        ItemsSource="{Binding Route}" 
        PathPropertyName="Path">
        <maps:MapShapeDrawBehavior.ShapeDrawer>
          <maps:MapPolylineDrawer Color="Green" Width="3" 
                     StrokeDashed="True"/>
        </maps:MapShapeDrawBehavior.ShapeDrawer>
      </maps:MapShapeDrawBehavior>    
     </interactivity:Interaction.Behaviors>
  </Maps:MapControl>
    <Grid DataContext="{Binding SelectedItem}" 
        Height="{Binding WatchedObjectHeight, ElementName=ContentRootListener, 
           Converter={StaticResource PercentageConverter}, ConverterParameter=30}" 
           VerticalAlignment="Bottom" Background="Black" >
      <interactivity:Interaction.Behaviors>
        <behaviors:UnfoldBehavior RenderTransformY="1"  Direction="Vertical"  
        Activated="{Binding Converter={StaticResource NullToBooleanConverter}}"/>
      </interactivity:Interaction.Behaviors>
      <userControls:RouteItemPopup >
    </userControls:RouteItemPopup>
    </Grid>
  </Grid>
</Grid>

If you want more info on how these behaviors for binding elements to a MapControl work, I suggest you look at the original article explaining how they should be used. If a user taps on a map symbol, a popup with activity data is shown, and the address where that happened is loaded. Basically the same trick as I used here

Start it up

It’s important to initialize the Dispatcher helper, start new MainViewModel, and setup the WindowVisibilityMessage. I have talked about this before, but I thought it wise to repeat it one more time.

protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
    DispatcherHelper.Initialize();
    MainViewModel.CreateNew();
    await MainViewModel.Instance.Start();
    WindowVisibilityMessage.Setup();
// more
Notice the OnLaunched needs to be async, because of the async nature of the Start method.

…and a class to draw a different image for every activity

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Geolocation;
using Windows.Foundation;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Controls.Maps;
using Lumia.Sense;
using WhatWhenWhere.ViewModels;
using WpWinNl.Maps;

namespace WhatWhenWhere.Maps
{
  public class MapActivityDrawer : MapShapeDrawer
  {
    public override MapElement CreateShape(object viewModel, Geopath path)
    {
      var activityModel = ((ActivityViewModel) viewModel).Model;
      return new MapIcon
      {
        Location = new Geopoint(path.Positions.First()),
        Title = activityModel.Activity.ToString(),
        NormalizedAnchorPoint = new Point(0.5,1),
        Image = GetImage(activityModel.Activity)
      };
    }

    private static RandomAccessStreamReference GetImage(Activity activity)
    {
      var image = "Other";
      switch (activity)
      {
        case Activity.Idle:
        {
          image = "Idle";
          break;
        }
        case Activity.Walking:
        {
          image = "Walking";
          break;
        }

        case Activity.Moving:
        {
          image = "Moving";
          break;
        }

        case Activity.Stationary:
        {
          image = "Stationary";
          break;
        }
      }
      return RandomAccessStreamReference.CreateFromUri(
         new Uri(string.Concat("ms-appx:///Images/", image, ".png")));

    }
  }
}

Basically, translates every bound object back to ActivityViewModel, and draws a different image for it. This can be made more efficient, but this shows a way it could work.

Conclusion: the good and the bad parts

As you have seen, using SensorCore is actually pretty easy. Making it work together with MVVMLight as well, although I am very well aware that all the stuff I used around it may make it look a bit more convoluted that it actually is. The DataLoaderModel is all you have to understand. The rest is just the app around it.

The bad parts of SensorCore are pretty simple:

  • It’s still in beta
  • It requires a Windows Phone 8.1 phone with a Lumia Cyan update and the required hardware aboard. Currently, to the best of my knowledge, only the Lumia 630 has both the required hardware and software, although I assume the 930 will have it too.
  • The TrackPointMonitor only gives a position every 5 minutes – at the very best. Sometimes it misses points. So forget about a super-duper-detailed tracking of your location.

Another bad part – but that is more my bad – is that I have not included a way to run this on simulated data. A NuGet package allows you to test against fake or prerecorded data, so you can try your app without having access to an actual device have the sensors on board. I made this using a real live 630. To get this to work properly, you will need one too. The good part is: they are dirt cheap.

The good parts are a lot better:

  • Very simple and easy-to-use API.
  • Possibility to do geographical tracking and analysis after-the-fact
  • All kinds of fun apps possible, like fitness apps, life logging, and stuff like that
  • Very low power consumption (unlike ‘active’ GPS enabled apps)
  • Privacy is secured – no big brother watching over your shoulder. The data is yours and yours alone.

So. A new class of apps is enabled by this new SDK. I hope I have inspired you to have a spin with it. If you have read all the way to this, I am very impressed by your perseverance ;-)

Demo solution, as (nearly) always, can be found here.

Edit 26-06-2014: made some minor updates after suggestions from my Finland fellow MVP Jani Nevalainen.