.NET Zone is brought to you in partnership with:

Den is a DZone Zone Leader and has posted 460 posts at DZone. You can read more from them at their website. View Full User Profile

SkyDrive Sync on Windows Phone - Basic Harness

08.22.2013
| 3363 views |
  • submit to reddit

Windows Phone comes with great capabilities when it comes to synchronizing "boxed" content, such as pictures and videos. However, with the release of the Windows Phone 8 platform the developers also got access to the external storage (SD card). More often than not, you'd want to make sure that the content that you make available through your application is in sync, regardless of what device the user is currently holding in their hands, as long as those are linked to the same account.

In this article, I am going to show you how to accomplish this with the help of Microsoft SkyDrive and Microsoft Account.

Given that you already have the Windows Phone SDK installed, create a new C# Windows Phone application. In MainPage.xaml, make sure that you eliminate all the unnecessary content - we will create everything from scratch.


Now to the important part - adding a reference to the Microsoft Live SDK. You can get it for free through the NuGet Package Manager. Simply right-click on your References for your project in Solution Explorer, and select Manage NuGet Packages.

.

In the Package Manager, you can easily get the Live SDK if you use its name as the search term in the official NuGet repository. Once found, install it and a reference to proper libraries will be automatically added to your project.


Add a XML namespace reference in MainPage.xaml that would allow you to leverage the sign in button, exposed through the Live SDK:

xmlns:live="clr-namespace:Microsoft.Live.Controls;assembly=Microsoft.Live.Controls"

And now it's obviously the time you added the actual sign in button on the page:

<live:SignInButton Height="100" VerticalAlignment="Top"></live:SignInButton>

Here comes an interesting aspect of using the Live SDK - just like with any other API, you would need to somehow identify your app to Microsoft, so that it is always known which app tries to access the user's data.

For that, you need to make sure that you create your own app identity key here. Simply create a new application and copy the Client ID that was assigned to it. It is important to mention that the app itself should be marked as mobile.

Once the Client ID is obtained, insert it as the value of the ClientId property in the SignInButton you've just created:

<live:SignInButton Height="100" VerticalAlignment="Top"
                   ClientId="YOUR_ID_HERE"></live:SignInButton>

Let's also talk about scopes. When you create an application that relies on the user's Microsoft Account, you have to be explicit in terms of what data you want to access and when you want to access it. In the Live API, this is determined with the help of scopes

For our sample application, we will need the following scopes:

  • wl.signin
  • wl.basic 
  • wl.skydrive 
  • wl.skydrive_update
  • wl.offline_access

All of these are described in the above-linked MSDN document on scopes, so feel free to loop it in if you are unsure of what each of the items covers. These scopes should be included in the Scopes property, in the sign in button, each separated by a space:

Scopes="wl.signin wl.basic wl.skydrive wl.skydrive_update wl.offline_access"

At this point, you should be able to successfully direct the user to the sign in page:


Also, if you sign in with an active Microsoft Account, you will see the detailed breakdown of the capabilities the app will use, given that the user enables account access for the given app.


Great, but this is still pointless - what happens when the user signs in? In this situation, we need to make sure that we capture the session. To do this, in XAML you need to make sure to hook the SessionChanged event for the SignInButton:

SessionChanged="SignInButton_SessionChanged"

Switch to code-behind and create a placeholder object for the LiveConnectSession that we're going to be working with (anywhere within the MainPage class):

LiveConnectSession _currentSession;

When the SessionChanged event is being fired, in case the sign in process was successful, you will get a LiveConnectSession as a result:

private void SignInButton_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
    if (e.Status == Microsoft.Live.LiveConnectSessionStatus.Connected)
    {
        _currentSession = e.Session;
    }
}

Fantastic, now let's create a helper function that would let me pull the contents of the currently linked SkyDrive account. First of all, a function that would let me dynamically specify the name of the folder I want to query:

public async static Task<List<SkyDriveEntity>> GetFolderContents(LiveConnectSession session, string folderId)
{
    List<SkyDriveEntity> data = new List<SkyDriveEntity>();

    try
    {
        LiveConnectClient client = new LiveConnectClient(session);
        LiveOperationResult result = await client.GetAsync(folderId + "/files");

        List<object> container = (List<object>)result.Result["data"];
        foreach (var item in container)
        {
            SkyDriveEntity entity = new SkyDriveEntity();

            IDictionary<string, object> dictItem = (IDictionary<string, object>)item;
            string type = dictItem["type"].ToString();
            entity.IsFolder = type == "folder" || type == "album" ? true : false;
            entity.ID = dictItem["id"].ToString();
            entity.Name = dictItem["name"].ToString();

            data.Add(entity);
        }
        return data;
    }
    catch
    {
        return null;
    }
}

Because the returned data is already formatted, I need to make sure that I extract it and assign it to a model. Notice that there is a SkyDriveEntity class, that is not a part of the official SDK. I have created a helper model that would let me easily manage and query cloud storage contents:

public class SkyDriveEntity
{
    public string Name { get; set; }
    public string ID { get; set; }
    public bool IsFolder { get; set; }
}

I would also need to have a root folder, in which I would be able to store the synced content - it is generally a bad approach to dump everything directly in the SkyDrive root from a third-party application, so let's add this function:

public async static Task<string> CreateRootFolder(LiveConnectSession session)
{
    string folderId = string.Empty;
    try
    {
        var data = await GetFolderContents(session, "me/skydrive");

        if (data != null)
        {
            var targetRootFolder = (from c in data where c.IsFolder && c.Name == "SYNCFOLDER" select c).FirstOrDefault();

            if (targetRootFolder == null)
            {
                LiveConnectClient client = new LiveConnectClient(session);
                LiveOperationResult result = await client.PostAsync("me/skydrive", "{\"name\":\"SYNCFOLDER\",\"description\":\"SYNCED FOLDER\"}");
                folderId = result.Result["id"].ToString();
            }
            else
            {
                folderId = targetRootFolder.ID;
            }
        }

        return folderId;
    }
    catch
    {
        return folderId;
    }
}

Now, back in the SessionChanged event handler you can add this line, to create the root application folder:

var coreContent = await CreateRootFolder(_currentSession);

Run the app, sign in with a Microsoft account and see the folder being created for you:


Congratulations, you have the basic harness set up. In the next article, I will be talking about how to actually sync content across devices that are linked to the same account, even if the content was not created on either of the devices.