Moving Beem from Static XML to Azure Mobile Services

While working on Beem, I always relied on a static XML file to fetch me the list of available online radio streams. It’s a good way to keep the content dynamic, and when new stations are added, I do not have to re-submit the application for certification but rather just update the XML file. This worked fine for a while, but I thought about having a more optimal way to get the necessary data. Specifically, I wanted to have a backend that can be easily extended and modified when necessary. Relying on an XML file means that I am restricted to the static data set, that has to be re-downloaded all the time whenever something from it is needed.

The switch was done in favor of Azure Mobile Services. That way, I can go as far as run LINQ queries on my data and get a JSON-formatted output exactly for what I need, if I am building a companion app for Windows Phone 8 or Windows 8. More than that, the data can be easily updated directly from the mobile device, without having the XML file downloaded in its entirety. And while it is not that large, on devices with limited data this is a consideration I have to throw into the play.

Let’s start with the fact that there is no Azure Mobile Services SDK for Windows Phone 7.5 applications. If the application would be designed for Windows Phone 8, you can download the official toolset. Beem supports multiple devices that are running Windows Phone OS 7.5, therefore I cannot do the full switch to Windows Phone OS 8. Does this mean that I cannot use AMS? No. There are some trade-offs, such as the fact that I no longer have SDK-based access to LINQ queries out-of-the-box, but I can still access the information in the way I want due to the fact that AMS sports a REST API. So all I needed is implement my own client class.

My recommendation would also be to use JSON.NET in your application, since the data returned by HTTP requests will be JSON-formatted, but you can also parse JSON manually (who would reinvent the wheel anyway?) if you want to. Getting back to the application, I am operating on a Station model:

 using System;
using System.Windows;
using System.Xml.Serialization;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace Beem.Models
{
public class Station : INotifyPropertyChanged
{
// Required by Azure Mobile Services
// As we transition from XML to ZUMO, this will be persistent.
[XmlIgnore()]
public int? Id { get; set; }

[XmlElement("name")]
public string Name { get; set; }
[XmlElement("location")]
public string Location { get; set; }
[XmlElement("image")]
public string Image { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("jsonid")]
public string JSONID { get; set; }

private ObservableCollection<Track> _trackList;
[XmlIgnore()]
public ObservableCollection<Track> TrackList
{
get
{
return _trackList;
}
set
{
if (_trackList != value)
{
_trackList = value;
NotifyPropertyChanged("TrackList");
}
}
}

private Track _nowPlaying;
public Track NowPlaying
{
get
{
return _nowPlaying;
}
set
{
if (_nowPlaying != value)
{
_nowPlaying = value;
NotifyPropertyChanged("NowPlaying");
}
}
}

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
Deployment.Current.Dispatcher.BeginInvoke(() => { PropertyChanged(this, new PropertyChangedEventArgs(info)); });
}
}
}
}

What we need to have in the AMS storage is a reference to the station ID, name, description, stream URL (location), image URL and the JSON ID that will be subsequently used to query the currently playing track, as well as the previously played content. One way to do this is enable dynamic schema, where I can push items into the store, but I can also access the server through SQL Server Management Studio.

You can get the connection information in the Azure Management Portal.

Back to the fact that I needed to implement my own connection client. Here is what I did:

 public class MobileServicesClient
{
private const string CORE_URL = "https://beem.azure-mobile.net/tables/";
private const string KEY = "";

private WebClient client;

public MobileServicesClient()
{
client = new WebClient();
client.Headers["X-ZUMO-APPLICATION"] = KEY;
client.Headers["Accept"] = "application/json";
}

public void GetAllStations(Action<IEnumerable<Station>> onCompletion)
{
client.DownloadStringCompleted += (s, e) =>
{
IEnumerable<Station> stations = JsonConvert.DeserializeObject<IEnumerable<Station>>(e.Result);
onCompletion(stations);
};
client.DownloadStringAsync(new Uri(string.Concat(CORE_URL, "Station")));
}

public void AddStation(Station station, Action onCompletion)
{
var serializedObject = JsonConvert.SerializeObject(station,
new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
string content = serializedObject;

client.Headers[HttpRequestHeader.ContentType] = "application/json";
client.Headers[HttpRequestHeader.ContentLength] = content.Length.ToString();

var uri = new Uri(string.Concat(CORE_URL, "Station"));

client.UploadStringCompleted += (s, e) => {
onCompletion();
};
client.UploadStringAsync(uri, content);
}
}

In this class, I need to define the connection API key (once again, obtained in the Azure Management Portal). Remember that depending on the actions that you will take on the database, you need to make sure that the operation is permitted for the given key.

The requests against the store are performed with the help of a WebClient class, where I am also setting the proper authentication (X-ZUME-APPLICATION) and content headers. When I am simply trying to get the data, the only part that I need to add to the core URL is the table name – in my case, Station. When the response is received, I can deserialize it as IEnumerable<Station> and then bind it anywhere in the application, which ultimately results in this:

2 of 6

Adding data is done with the help of AddStation. The process is pretty much the opposite of how the data retrieval happens. I am serializing a station to JSON and calling UploadStringAsync to perform a POST request. Easy and effective – the new release of Beem will be running on top of AMS.

Careful With Deserialization on Windows Phone – Encoding Matters

Serialization is a process that is prone to errors, especially with a poorly structured data layer. However, that is not always the case and a seemingly normal serialization/deserialization scenario might turn out to produce unexepected results. As I was working on a Windows Phone application, I had this standard routine:

public static void SerializeToFile(object serializationSource, Type serializationType, string fileName)
{
XmlSerializer serializer = new XmlSerializer(serializationType);
MemoryStream targetStream = new MemoryStream();
serializer.Serialize(targetStream, serializationSource);
LocalStorageHelper.WriteData(fileName, targetStream.ToArray());
}

The deserialization would occur like this:

XmlSerializer serializer = new XmlSerializer(typeof(List));
string data = await LocalStorageHelper.ReadData("imagestack.xml");
TextReader reader = new StringReader(data);
var resultingObject = serializer.Deserialize(reader);

But this caused an exception to be thrown, and not just a regular one, but one that would crash the application and break the debugging process.

Looking at the snippet above, it is really hard to tell what the issue is, especially if the XML that is being retrieved from the file is valid. However, upon deserialization there is no place where the string encoding is actually specified, which results in the unexpected behavior that we are seeing. By default, the serialization engine sets the encoding to be UTF-8, which should also be respected when deserializing.

So instead of the deserialization snippet above, you need to use this:

 XmlSerializer serializer = new XmlSerializer(typeof(List));
string data = await LocalStorageHelper.ReadData("imagestack.xml");
MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(data));
var resultingObject = serializer.Deserialize(memoryStream);

Adding SkyDrive Upload Support to Your Windows Phone App

Multiple applications that are already in the Windows Phone Marketplace operate with a variety of content, such as pictures, text files and music. More often than not, that content is stored locally, in the application isolated storage, and although it is a good way to preserve that content, this method is bound to create some inconveniences in case the user decides to switch phones or do a complete device reset. To avoid this, developers can leverage the Microsoft Live SDK, specifically the SkyDrive API that is exposed through it.

To make my case more realistic, I am going to bring up my Windows Phone project – Beem (you can find the free version here and the donation build here). As I am expanding the functionality base for the Beem Plus client, I decided that I need to do something to allow users to back up the stream recordings that they have. Those are MP3 files stored internally, captured directly from the incoming radio stream.

The interesting part about these recordings is the fact that their length varies. One user might record a 10 second track, another one might have hours of online podcasts. Therefore, it would be logical to let them backup that data, in case something goes wrong. The Microsoft Live SDK comes with out-of-the box support for Windows Phone, so once you download and install the SDK, all you need to do is add a Microsoft.Live and Microsoft.Live.Controls assemblies to the project. Those should be already present in Visual Studio, in the Extensions section in the References dialog.

It is important to mention that before, it was not possible to upload MP3 files to SkyDrive from third-party applications. My workaround to that was simply adding a .wav at the end of the uploaded file name. An extension mismatch would not cause any problems during playback on a Windows machine, and if necessary, it can easily be swapped manually. Since then, the restriction has been lifted and SkyDrive supports MP3 uploads.

You will integrate the Microsoft Account authentication in your application by adding a SignInButton control, also specifying the scope of your integration with the user account. This is a built in measure that prevents applications from abusing the system by notifying the user about the actions the application will be able to perform on the account.

Basic Setup

SignInButton is added in the XAML layer, and a sample snippet would look like this:

<live:signinbutton x:name="btnSignIn" clientid="CLIENT_ID_HERE" scopes="wl.signin wl.basic wl.offline_access wl.skydrive wl.skydrive_update" branding="Skydrive" texttype="Connect" visibility="Collapsed"></live:signinbutton>

First things first, take a look at the declared scopes. The application will be able to sign the user in (wl.signin), access the basic profile information (wl.basic), access the user account when the user is not necessarily online, allowing me to preserve the login information so that there is no repetitive login once the app successfully performed the authentication (wl.offline_access), access the basic SkyDrive metadata (wl.skydrive) and update the file layout in the SkyDrive storage (wl.skydrive_update).

The ClientID identifies your application and can be obtained here. Branding and TextType determine the appearance of your button, allowing you to customize the displayed logo and caption.

When the user clicks on the button for the first time, he will be prompted with a web authentication page, where he will be asked to enter his Microsoft Account credentials. Once the account passes the verification, the user will be prompted with another web page, that shows the permissions the application requests. It is important that you declare only the necessary permissions when declaring the scopes.

Once Yes is clicked in the dialog, the session information will be automatically stored in the application if necessary, so you don’t have to worry about manually preserving those. Every time the authentication engine is invoked, the SessionChanged event handler is invoked, so make sure you hook it to the button:

btnSignIn.SessionChanged += App.MicrosoftAccount_SessionChanged;

As a result, you can potentially obtain a LiveConnectSession, that will be consequently used to perform any request on the Microsoft service endpoints. However, you also need to make sure that that account was actually successfully connected, since the user can block the application.

public static void MicrosoftAccount_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
if (e.Status == Microsoft.Live.LiveConnectSessionStatus.Connected)
{
MicrosoftAccountClient = new LiveConnectClient(e.Session);
MicrosoftAccountSession = e.Session;
MicrosoftAccountClient.GetCompleted += client_GetCompleted;
MicrosoftAccountClient.GetAsync("me", null);
}
else
{
Binder.Instance.MicrosoftAccountImage = "/Images/stock_user.png";
Binder.Instance.MicrosoftAccountName = "no Microsoft Account connected";
MicrosoftAccountClient = null;
}
}

The call to me/picture will return the URL for the profile picture for the currently authenticated user. Notice that I am getting the results by referencing the key names. When the raw JSON data is returned on a call, a dictionary is automatically created that contains the key-value pairs, that makes it easier to get the data without having to re-parse the data. The RawResult property still exposes the raw JSON.

Uploading the File

Once a file is already present in the isolated storage and the Microsoft Account is connected to the application, it is extremely easy to initiate the uploading process. Before doing that, however, you need to consider that every user has limited SkyDrive quota. The default amount of allocated storage for current users is 7GB, so make sure that you check whether there is any available space for the file that you are about to send. To do this, you need to perform a call to me/skydrive/quota.

client = new Microsoft.Live.LiveConnectClient(App.MicrosoftAccountSession);
client.GetCompleted += client_GetCompleted;
client.GetAsync("me/skydrive/quota", null);

This call will return two values – total quota and available space. Before the upload, compare the byte size of the file and the available space.

if (e.Result.ContainsKey("available"))
{
    Int64 available = Convert.ToInt64(e.Result["available"]);
    byte[] data = RecordManager.GetRecordByteArray(Binder.Instance.CurrentlyUploading);

    if (available >= data.Length)
    {
        MemoryStream stream = new MemoryStream(data);

        client = new LiveConnectClient(App.MicrosoftAccountSession);
        client.UploadCompleted += MicrosoftAccountClient_UploadCompleted;
        client.UploadProgressChanged += MicrosoftAccountClient_UploadProgressChanged;
        client.UploadAsync("me/skydrive", Binder.Instance.CurrentlyUploading,
            stream, OverwriteOption.Overwrite);
        grdUpload.Visibility = System.Windows.Visibility.Visible;
        ApplicationBar.IsVisible = false;
    }
    else
    {
        MessageBox.Show(@"Looks like you don't have enough space on your SkyDrive.
Go to http://skydrive.com and either purchase more space or clean up the existing storage.", "Upload",
            MessageBoxButton.OK);
    }
}

If there is available space, simply create a new instance of LiveConnectClient, with the current LiveConnectSession (you can keep it in the App class for global access) and use UploadAsync to pass the internal path to the file, the file name, the file stream and the overwrite method. By default, all files are being overwritten. Remember, however, that when you are specifying the path to the folder you want to upload the file to, you cannot use the normal path format, but rather refer to folders by their ID. You can find more details about it in one of my previous articles. For some folders, however, you can directly specify the path. Example: me/skydrive/my_documents.

UploadCompleted and UploadProgressChanged can be used to define application behavior during and after the upload, such as displaying a popup that shows the current upload percentage.

Making The Account Accessible App-Wide

One interesting fact worth mentioning in case you declared wl.offline_access as a pre-determined scope. The default behavior for the application is to authenticate the Microsoft Account when the page with a valid SignInButton is hit. My intent for Beem was to keep the SignInButton in the application settings, where it is expected by the end-user. However, in this case the user will only be automatically authenticated when the Settings page would be opened, which is something that does not happen too often, in contrast with the multitude of other pages that are being used internally.

The workaround to this small issue is really simple – make sure that you add a SignInButton with exactly the same scopes and ClientID in the main application page, and set its Visibility property to Collapsed. You will need to wire it up to the SessionChanged event handler, that can be the same for both sign-in buttons used in the application.

Trick or Treat – URI Associations In Windows Phone 8 Outside Bing Vision

With the release of the new Windows Phone 8 SDK, the developers are now able to create URI associations, where their application can be launched from the context of another application. For example, I can register a den: URI scheme to pass specific information to my application from any other installed application or resource, such as an email or a text message. That way, my application is no longer a standalone entity and it can interact easily with other applications in the Windows Phone ecosystem.

There is one interesting aspect, however. Custom URI schemes are not recognized by Bing Vision, which might be a problem if you generated a QR code that should launch your application. You will be able to launch http:// URLs, but custom applications will remain in their separate frame, out of reach of the stock app.

Although this might seem like an issue, it can easily be mitigated by using standard HTML redirect. Since Bing Vision correctly handles URLs, and Internet Explorer on Windows Phone supports redirects, you have a couple of options, one of them suggested by Nikola Metulev.

Have the user navigate to a URL that will redirect to the custom URI schema. Create a HTML page, that has the following source:

                        

What will happen here is when the user will navigate to this page from a Windows Phone, he will get redirected to the custom URI that was specified as the redirect target. It is not possible to enter custom URLs that do not follow the HTTP format in the Internet Explorer address bar, but you can still rely on content links and redirects. Of course, you could also use a third-party URL shortening web service that supports custom URI schemas, such as is.gd. You can shorten an URL and have a QR code automatically generated. That URL can be something like den:awesome, and from it you will obtain a valid HTTP URL that will then trigger the application launch.