Tuesday, September 14, 2010

Windows Phone 7 – Tube Companion (Pre RTM)

Ive recently completed my Tube Companion application for Windows Phone 7.  The application was built with a pre-RTM version of the toolkit, but once the RTM version has been released then i’ll retest the code base, and barring any major breaking changes I should be good to go.

The application itself is basically a tool to assist users who utilise the London Underground.  A complete list of functionality in this first release is as follows:

Line Status - Live updates of the status of each of the tube lines.

Tube Map - A version of the tube map with pinch/zoom enabled.

Station lookup – Lookup every station on the London Underground network.

Live Departure Boards – Display the live departure boards for certain tube station/tube line pairings.

Stations Near Me – Shows the ten nearest stations to the user within a 10 mile radius,

Tube This Weekend – Displays the planned disruptions to the tube lines/stations for the forthcoming weekend.

Push Notifications – Push notifications can be enabled to alert users of problems with each of the tube lines.

Demo of the app here:

Thursday, July 8, 2010

Windows Phone 7 - Selecting device photos without PhotoChooserTask

A common feature of mobile based applications is the ability to let you view photos that have been taken with the device. For Silverlight based Windows Phone 7 applications, this functionality would generally be achieved by utilising the PhotoChooserTask.

The PhotoChooser task navigates to the phones photo gallery section, allows the user to select a photo and then returns this photo to the calling application. Now this approach is fine for the majority of circumstances, but what happens if more flexibility is required by the application for image selection? In this case the MediaLibrary class can be utilised to retrieve images from the device, and then displayed in whichever way the application chooses.

The following shows simple examples of both approaches.

PhotoChooserTask Implementation

Here we have a very simple UI that contains a button to select a photo and a currently unassigned image control.

The following code is then used to display the images on the phone.

private void butSelectPhoto_Click(object sender, RoutedEventArgs e)

{

PhotoChooserTask photo = new PhotoChooserTask();

photo.ShowCamera = true;

photo.Show();

}

This now opens up a gallery of photos to choose from.

Once the user has selected a picture from the gallery, then the image is returned as a stream to the calling application. The application then overrides the OnChooserReturn method(used to capture the results from all chooser operations) and simply assigns the returned stream to a Bitmap image, which is then assigned to the image on the user interface.

public override void OnChooserReturn(object sender, EventArgs e)

{

base.OnChooserReturn(sender, e);

TaskEventArgs<PhotoResult> photo = e as TaskEventArgs<PhotoResult>;

if (photo != null && photo.Result != null && photo.Result.ChosenPhoto != null)

{

BitmapImage image = new BitmapImage();

image.SetSource(photo.Result.ChosenPhoto);

imgPhoto.Source = image;

}

}

Media Library Implementation

The media library implementation example simply displays a horizontally scrolling list containing all of the photos on the device. When a user selects an item from the list, then a larger version of the image is simply displayed above the list.




In order to achieve this first a reference to the Microsoft.XNA.Framework assembly needs to be added to the project, as it is this assembly which contains the MediaLibrary class which does most of the work.

A simple class is now created called MediaImage, and this simply holds a reference to an individual BitmapImage which represents a photo. This is required to allow us to use data binding within the the listbox.

public class MediaImage

{

public BitmapImage ImageFile { get; set; }

}

<ListBox.ItemTemplate>

<DataTemplate>

<Image Source="{Binding ImageFile}" Width="100" Height="100"/>

DataTemplate>

ListBox.ItemTemplate>

The following method is then called from the page’s constructor, and this method is responsible for populating the list with the images.

private void GetImages()

{

MediaLibrary mediaLibrary = new MediaLibrary();

var pictures = mediaLibrary.Pictures;

foreach (var picture in pictures)

{

BitmapImage image = new BitmapImage();

image.SetSource(picture.GetImage());

MediaImage mediaImage = new MediaImage();

mediaImage.ImageFile = image;

lstImages.Items.Add(mediaImage);

}

Finally the list’s SelectionChanged event is handled to simply populate the Image with the selected image from the list.

private void lstImages_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

MediaLibrary mediaLibrary = new MediaLibrary();

BitmapImage image = new BitmapImage();

image.SetSource(mediaLibrary.Pictures[lstImages.SelectedIndex].GetImage());

imgSelectedPhoto.Source = image;

}

Tuesday, May 25, 2010

Windows Phone 7 - London Tube Companion Application


As part of my continual learning of Windows Phone 7, I decided to develop an application which displays the status of tube lines for the London Underground. As well as displaying the line status, the application also allows users to request push notifications when the status of a tube line changes.
Windows Phone 7 architecture allows for several types of push notifications, all of which are delivered by the cloud based Push Notification service. The notification types are as follows:
  • Raw Notification : Raw Data is sent to the application. Only available when the application is open.
  • Tile Notification: If the application is pinned to the phone’s start screen, then the application’s tile can be modified.
  • Toast Application: Text based notification is displayed to the currently active screen. This does not require the application to be open.
In this example I decided to use toast notifications to inform the user of any changes to the tube line status, as it would allow the user to be informed no matter which application they were using on the phone.

The article here details the push notification architecture. In my example the Windows Phone application is the Tube Companion windows phone application, and the cloud application is a service which periodically polls for changes in tube status, and then sends out notifications to the relevant subscribers.

Setting up the phone application to receive toast notifications
In order for the phone to receive toast notifications, the phone application must carry out the following:
  • Create a channel to the Push Notification service. Although the phone application itself does not create the notifications, it is the phone’s responsibility to create the notification channel between itself and the push notification service. Once the channel has been created and opened, a unique Uri for the application and device instance is created, and this can passed to any external cloud based service. It is then the responsibility of the cloud based service to post messages to the Uri, which results in the notification being sent to the device.
  • Register to receive the relevant channel events. The ChannelUriUpdated event is fired when the channel has been opened. Once this occurs the application and device specific Uri is available. The ShellNotificationReceived event is fired once a toast notification is received and the application is running. This would allow you to apply additional application logic when the application has context. If the application is not running then this event would not fire and a standard toast notification would be received in the normal manner.
  • Open the channel.
  • Bind to the relevant notification type. In order to receive toast notifications from the Push Notification service we have to explicitly request to receive toast notifications, otherwise only raw notification types would be sent to the application.
  • Subscribe to notifications. The Channel Uri is sent from the phone application to my cloud service, and stored in it’s notification subscription database. The cloud service then uses this Uri in order to send notifications back to the device.
 try
{
_httpChannel = new HttpNotificationChannel(CHANNEL_NAME, "TubeCompanionService");
_httpChannel.ChannelUriUpdated += new EventHandler(_httpChannel_ChannelUriUpdated);
_httpChannel.Open();

.....
}

void _httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{

System.Diagnostics.Debug.WriteLine("Received application uri: " + e.ChannelUri.ToString());
_httpChannel.HttpNotificationReceived += new EventHandler(_httpChannel_HttpNotificationReceived);
_httpChannel.ShellEntryPointNotificationReceived += new EventHandler(_httpChannel_ShellEntryPointNotificationReceived);
_httpChannel.ShellNotificationReceived += new EventHandler(_httpChannel_ShellNotificationReceived);
_httpChannel.ExceptionOccurred += new EventHandler(_httpChannel_ExceptionOccurred);
_httpChannel.BindToShellEntryPoint();
_httpChannel.BindToShellNotification();

_tubeCompanionWS.SubscribeToNotifications(e.ChannelUri.ToString());

}

Setting up the service to send toast notifications
In order for the service to send toast notifications, the following needs to occur:
  • The toast notification XML message is created. The XML message must conform to the schema documented here, and essentially details the notification message to be displayed.
  • A HttpWebRequest object is created, and is passed the phone/device instance Uri as it’s constructor.
  • The toast notification XML is converted to a byte array and passed as the content of the HttpWebRequest .
  • Request headers are set. Documented here
  • The HttpWebRequest is posted. This results in the notification being sent to the application on the device.
public class PushNotification
    {
const string MESSAGE_ID = "X-MessageID";
const string NOTIFICATION_CLASS = "X-NotificationClass";
const string NOTIFICATION_STATUS = "X-NotificationStatus";
const string SUBSCRIPTION_STATUS = "X-SubscriptionStatus";
const string DEVICE_CONNECTION_STATUS = "X-DeviceConnectionStatus";
const string WINDOWSPHONE_TARGET_HEADER = "X-WindowsPhone-Target";
public void SendToastNotification(Uri channelUri, string message)
{
ToastNotificationMessage toastMessage = new ToastNotificationMessage();

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(channelUri);
request.Method = "POST";
request.ContentType = "text/xml";
request.Headers = new WebHeaderCollection();
request.Headers.Add(MESSAGE_ID,Guid.NewGuid().ToString());
request.Headers.Add(NOTIFICATION_CLASS, "2");
//request.Headers[WINDOWSPHONE_TARGET_HEADER] = "toast";
byte[] toastMessageBytes = toastMessage.CreateToastMessage(message);
request.ContentLength = toastMessageBytes.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(toastMessageBytes, 0, toastMessageBytes.Length);
}

HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string notificationStatus = response.Headers[NOTIFICATION_STATUS];
string subscriptionStatus = response.Headers[SUBSCRIPTION_STATUS];
string deviceConnectionStatus = response.Headers[DEVICE_CONNECTION_STATUS];
}
}

public class ToastNotificationMessage
{
public byte[] CreateToastMessage(string message)
{
string prefix = "Content-Type: text/xml\r\nX-WindowsPhone-Target: toast\r\n\r\n";
StringBuilder builder = new StringBuilder();
builder.Append("");
builder.Append("");
builder.Append("");
builder.Append("");
builder.Append(message);
builder.Append("
");
builder.Append("
");
builder.Append("
");
ASCIIEncoding encoding = new ASCIIEncoding();
string messageXML = builder.ToString();
messageXML = prefix + messageXML;
return encoding.GetBytes(messageXML);

}
}

Windows Phone 7 CTP April Refresh
This application was built with the Windows Phone 7 CTP April refresh, and it is worth stating that there is currently an issue with this version of the emulator whereby the notification channel cannot be opened until the emulator has been running for around two minutes. Therefore a delay has to be introduced prior to creating and opening this channel.

Additionally changes to the WMAppManifest.xml also have to be made and these are documented here

Monday, May 3, 2010

Windows Phone 7 CTP April Refresh - Bing Maps Silverlight Control

The April refresh of the Windows Phone 7 CTP was released the other day, and for some reason the BingMapsTask has been removed from this release and im assuming that this had led to the Bing Maps Silverlight control not working.

Ive asked a few people on the dev team about a work around, but Ive yet to receive a response. Im guessing that it would simply be a case of adding a new capability to the WMAppManifest.xml file.

***UPDATE***

My assumption that the removal of the BingMapsTask being removed prevented the Silverlight control from working was incorrect.

As Ryan has stated below, it was merely the issue of the Authenticode Signed Assemblies failing to load, as detailed in Tim Heur's post

As Ryan correctly states, this workaround needs to be applied to
Microsoft.Maps.MapControl.dll and System.Windows.Browser, but it also needs applying to Microsoft.Maps.MapControl.Common

Apologies if my initial post misled anyone, it wasnt my intention.

Thursday, April 29, 2010

Windows Phone 7 CTP - Events when the phone is locked

If you've seen my initial blog post regarding the development of a Jogging application for Windows Phone 7 then you'll have seen that I raised a concern about developing the application further.

Basically I was concerned that when the phone is locked, either manually or due to a lack of activity from the end user, that the application would go into a paused state thereby rendering this type of application useless. I asked this question to Peter Torr (Microsoft Program Manager), and unfortunately this is the current behavior. On a positive note he has stated that this area is still under investigation, so fingers crossed this may get resolved prior to launch.

My gut feeling is that this would be addressed as lucrative Sat Nav applications would also be affected by the same problem, unless there's a way to code around this.

The full response from Peter follows:

"Hi Andy,

The current builds pause the application when the phone locks (not that you can really lock the emulator) but we have been investigating ways to change this behavior for launch. You won't notice any changes in the up-coming refresh (sorry, I don't have a date for it) but any changes we make would come after that.

Peter
"

Windows Phone 7 - Jogging Application Part 2


In an earlier blog, I produced a very basic application for Windows Phone 7 that allowed joggers to track their progress of a training session on a Bing Map. The application simply plotted a map polyline whenever there was a change in the device’s location, with the changes in the devices location being raised as events by the GeoCoordinateWatcher class, which is part of the Windows Phone 7 API.

Now although the user’s training session was plotted on the map, with the start and end points being identified with pushpins on the map, the application didn’t indicate to the user the total distance travelled, so to obtain this I decided to use the Route Service, which is one of the Bing Maps Web Services.

Route Service
As the name suggests, the route service is an API used to calculate routes between designated start and end points.

In order to use the Route service, a service reference to http://dev.virtualearth.net/webservices/v1/routeservice/routeservice.svc?wsdl needs to be added to the project.
Please note that currently you are unable to add a service reference from a Windows Phone 7 project within the Visual Studio 2010 release candidate, although this functionality does work correctly within Visual Studio 2010 Express for Windows Phone, so you can simply add the reference there, then reopen the project from within Visual Studio.

Upon adding the reference to the service, the RouteServiceClient becomes available. Now to calculate a route you would typically just pass to the RouteServiceClient the details of the start and end points for your route, now in my case this is still the case, but I also pass in the location details of the jogger’s entire route. This then ensures that the Route Service will use exactly the same route as that used by the jogger.

To pass in the route details to the client, you create an instance of the RouteRequest class. This class has a WayPoints property, which is a collection that holds instances of WayPoint classes, with a WayPoint class simply acting as a container for a single location, represented by longitude and latitude.

Once all of the Way Points have been set, you simply pass the RouteRequest to the CalculateRouteAsync method of the RouteServiceClient. Once the asynchronous call to the web service is complete a RouteResponse is returned, and this details all of the directions of the route (represented as RouteLegs), as well the total distance travelled.

Now although the distance travelled seemed accurate to me, I needed to verify that the route returned by the route service, was identical to each of the locations that I had passed to it. To do this I simply iterated through each of the returned RouteLegs and overlaid them as a new Map Polyline on my Bing Map. Seeing this overlay on the map verified that the data returned was as expected.

private void CalculateRoute()
{
RouteRequest routeRequest = new RouteRequest();
ApplicationIdCredentialsProvider mapCredentials = (ApplicationIdCredentialsProvider)Application.Current.Resources["MapCredentials"];
string credentialID = mapCredentials.ApplicationId;
routeRequest.Credentials = new Credentials() { ApplicationId = credentialID };
Waypoint[] waypoints = new Waypoint[_joggingSession.Locations.Count];
for (int i = 0; i < waypoints.Length; i++)
{
Waypoint waypoint = new Waypoint();
waypoint.Location = new Location(_joggingSession.Locations[i].Latitude, _joggingSession.Locations[i].Longitude);
waypoints[i] = waypoint;
}

routeRequest.Waypoints = waypoints;
RoutingService.RouteServiceClient routeService = new RouteServiceClient("BasicHttpBinding_IRouteService");
routeService.CalculateRouteCompleted += new EventHandler(routeService_CalculateRouteCompleted);
routeRequest.Options = new RouteOptions();
routeRequest.Options.Mode = TravelMode.Walking;
routeRequest.UserProfile = new UserProfile() { DistanceUnit = DistanceUnit.Mile };
routeService.CalculateRouteAsync(routeRequest);
}

void routeService_CalculateRouteCompleted(object sender, CalculateRouteCompletedEventArgs e)
{
RouteResponse routeResponse = e.Result;
_joggingSession.DistanceTravelled = routeResponse.Result.Summary.Distance;
DistanceTravelled.Text = _joggingSession.DistanceTravelled.ToString("000.00") + " miles";
//verfiy distance travelled by overlaying the returned route on top of the real time route.
foreach (RouteLeg routeLeg in routeResponse.Result.Legs)
{
_routePolyLine.Locations.Add(routeLeg.ActualStart);
_routePolyLine.Locations.Add(routeLeg.ActualEnd);
}
}

Tuesday, April 20, 2010

Windows Phone 7 With Location Services And Bing Maps


Recently Microsoft released the CTP toolkit for Windows Phone 7, available here , and I decided to produce a small application which uses the location services functionality of the phone, as well as the Silverlight Bing Maps user control.

The application is aimed at joggers/runners, and simply allows users to start and end a running session and view their progress on a map. At the end of their session, the relevant start and end points are displayed on the map as pins, and the total time of the workout session along with total distance travelled is also displayed.

Location Services

Location services within Windows Phone 7 provides a location based api, which allows consumers to track their current geocoordinates, as well as the status of their location based device. It is worth noting that location services is not just GPS tracking, as the service can also determine a devices location from wireless networks, assisted gps etc.

One of the challenges of utilising Location Services within the CTP version of the toolkit, is that there is no built in mechanism to emulate a devices current location, or any positional changes of the device. To get around this problem you currently have to create a mock version of the GeoCoordinateWatcher class, as it is this class that is responsible for raising events in relation to a device's status and position. My mock class, MockGeoCoordinateWatcher is based upon Tim Heur's EventListGeoLocationMock class, and I'd encourage you all to read Tim's excellent post.

Before we start receiving positional change events ,we need to create an instance of the MockGeoCoordinateWatcher class and pass it a sample of the geocoordinates that we would like to be raised as our devices position. We then wire up an event handler to the PositionChanged event, which as you can guess by its name is respsonsible for notifying us of changes of position. The _geowatcher instance also needs to be started and stopped in order to start/stop receving events.

When the PositionChanged event is raised it passes a GeoCoordinate class as an event argument, and this class consists of the time that the position changed occurred, along with a location containing the latitidude and longitude coordinates of the position. In this application these geo-coordinates are simply added to a list which is then used at a later stage by the Bing Maps routing service to determine the total distance traveled, as well as being passed to the map to draw the users current progress.
namespace TeamG.Sprinter
{
public partial class MainPage : PhoneApplicationPage
{
IGeoPositionWatcher _geoWatcher;
List> _geoPositions;
MapPolyline _joggingPolyLine;

public MainPage()
{
InitializeComponent();

SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;

_geoWatcher = new MockGeoCoordinateWatcher(MockGeoHelper.CreateMockGeoCoordinateEvents());
_geoWatcher.PositionChanged += new EventHandler>(watcher_PositionChanged);

_geoPositions = new List>();
}

void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() => GeoPositionChanged(e));
}

void GeoPositionChanged(GeoPositionChangedEventArgs e)
{
_geoPositions.Add(e.Position);
_joggingPolyLine.Locations.Add(new Location(e.Position.Location.Latitude, e.Position.Location.Longitude));
}

private void StartJogging_Click(object sender, RoutedEventArgs e)
{
_geoWatcher.Start();
}

private void StopJogging_Click(object sender, RoutedEventArgs e)
{
_geoWatcher.Stop();

}
}
}

It is worth noting that on a production device that the GeoCoordinateWatcher class should be used with care, and not kept in a Started state when not required, as this would potentially be a big drain on the device's battery.





Bing Maps


Working with Bing Maps in Silverlight, and thereby Windows Phone 7 is straight forward thanks to the Bing Maps Silverlight User Control. To use the map in our application we first need to reference the relevant assemblies within the project , then reference the MapControl namespace in the page's namespace declaration.





x:Class="TeamG.Sprinter.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"


xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"


xmlns:phoneNavigation="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Navigation"


xmlns:d="http://schemas.microsoft.com/expression/blend/2008"


xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"


xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"


mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"


FontFamily="{StaticResource PhoneFontFamilyNormal}"


FontSize="{StaticResource PhoneFontSizeNormal}"


Foreground="{StaticResource PhoneForegroundBrush}">





Next, we modify our XAML to simply create a map instance, specifiying a Bing Maps key, the required zoom level, and the mode that we wish to view the Map.






Name="JoggingMap"

CredentialsProvider="Youll jave to use your own value"


Mode="Road"


ZoomLevel="16"


Center="53.32058,-2.73706"


Grid.Row="0"/>






Before the lines can be drawn we need to create an instance of the MapPolyLine class, set it's UI properties, then pass it as a UI element to the map.




_joggingPolyLine = new MapPolyline();
_joggingPolyLine.Stroke = new SolidColorBrush(Colors.Blue);
_joggingPolyLine.StrokeThickness = 5;
_joggingPolyLine.Opacity = 0.7;
_joggingPolyLine.Locations = new LocationCollection();
JoggingMap.Children.Add(_joggingPolyLine);

Now everytime that the PositionChanged event is fired, we simply add the geo-coordinates as a new location within the MapPolyLine instance, and this has the effect of drawing a line from the previous coordinate to the new coordinate.




void GeoPositionChanged(GeoPositionChangedEventArgs<?XML:NAMESPACE PREFIX = [default] http://schemas.microsoft.com/winfx/2006/xaml/presentation NS = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" /> e)
{
_geoPositions.Add(e.Position);
_joggingPolyLine.Locations.Add(new Location(e.Position.Location.Latitude, e.Position.Location.Longitude));
}

Once the user has ended their workout session, the application then adds a push pin for both the start and end position of the workout. This is done by simply querying for the first and last geo-coordinates, out of all which have been raised, then creating a push pin for each one.




private void CreateStartEndPushpins()
{
Pushpin startPin = new Pushpin();
Pushpin endPin = new Pushpin();
startPin.Location = new Location(_geoPositions[0].Location.Latitude, _geoPositions[0].Location.Longitude);
endPin.Location = new Location(_geoPositions.Last().Location.Latitude, _geoPositions.Last().Location.Longitude);
JoggingMap.Children.Add(startPin);
JoggingMap.Children.Add(endPin);
}

In a nutshell thats the core of the application completed. The images below show the application running, and you'll notice how the user's progress is tracked as they jog around.























The next phase of the application will use the Bing Maps Route Service web service in order to determine the distance travelled.





A potential stumbling block with this kind of application could be due to its long running nature. From the documentation that Ive currently read, it is unclear whether the phone framework will attempt to pause the application thereby rendering it useless when the phone goes into a locked/inactive state. Ive raised this question to the Windows Phone 7 development team, but Ive yet to receive a response. As soon as I get a response on this then Ill post an update.





Hope this is of some use to you.