Sunday, March 22, 2015

Starting with Microsoft Band and Xamarin

Recently Xamarin released their API for Microsoft Band that works with Android, iOS and Windows Phone. It was a tough choice on what to play with before vacation;  a new Xamarin Forms preview that works with Windows Store and Windows Phone 8.1 was released as well as Xamarin Everywhere that hits Android Wear and Apple Phone. A complete overload of super cool stuff.

But ... last week I got a new Microsoft Band and you know, time to play.  First off, the Band isn't really a Smart Watch.  It's more a like a fitness device on steroids.  You can pull all kinds of sensor information out of it, you can also send notifications to it and play with it's appearance. What you can't really do is input information into the Band and have to do something on the phone. That is to say the band is about collecting sensor information and consuming information but not for inputting information like a "true" smart watch allows.

Xamarin has wrapped the Microsoft platform APIs, pretty much verbatim. One thing they have done which is nice is if the calls are asynchronous on Android, they have Task returning versions. However, like the Microsoft platform specific versions the APIs do not match from platform to platform so making cross platform code will still be challenging unless you wrap them in your own unified interface.  Xamarin has a nice page on the component store that explains the basics of the APIs which are pretty easy to use.  They can be found here:

https://components.xamarin.com/view/microsoft-band-sdk

With that in mind I set out to give this a try.  Since my Band is currently synchronized with an Android device I am working with the Android version of the API.  First I figured I'd try to add and then remove an icon from my Band that could receive notifications and messages.  I created a class to open and close the connection.

public class BandConnection : IDisposable
{
    private IBandClient bandClient;
    private Activity context;

    public BandConnection(Activity context)
    {
        this.context = context;
        var pairedBands = BandClientManager.Instance.GetPairedBands();

        if (pairedBands != null && pairedBands.Length >= 1)
        {
            bandClient = BandClientManager.Instance.Create(context, pairedBands[0]);
        }
    }

    public async Task ConnectAsync()
    {
        if (bandClient == null)
        {
            throw new InvalidOperationException("BandClient is not set");
        }
        await bandClient.ConnectTaskAsync();
    }

    public async void Dispose()
    {
        if (bandClient != null && bandClient.IsConnected)
        {
            await bandClient.DisconnectTaskAsync().ConfigureAwait(false);
        }
    }
}

The class just takes in the current activity and then gets the first band registered with the device. If you were making a "real" application it may be a good idea to make some sort of screen to choose what Band the user wanted to connect to if there were more than one.  The ConnectAsync method opens the connection to the Band.  As you can see I use the ConnectTaskAsync method of the BandClient instead of ConnectAsync.  All the asynchronous Android APIs seem to be wrapped up with Task returning operations with that naming convention.  When the class is disposed, if the bandClient is connected, the connection is closed.

I added a simple method to create a new tile on my band if there is room for one.

public async Task AddTileAsync(string tileId, string tileName, BandIcon largeIcon, BandIcon smallIcon)
{
    if (bandClient == null)
    {
        throw new InvalidOperationException("BandClient is not set");
    } 
    var tiles = await bandClient.TileManager.GetTilesTaskAsync().ConfigureAwait(false);
    var id = UUID.FromString(tileId);

    var spacesRemaining = await bandClient.TileManager.GetRemainingTileCapacityTaskAsync().ConfigureAwait(false);
    if (spacesRemaining > 0 && !tiles.Any(t => t.TileId.ToString() == tileId))
    {
        var tileBuilder = new BandTile.Builder(id, tileName, largeIcon);
        if (smallIcon != null)
        {
            tileBuilder.SetTileSmallIcon(smallIcon);
        }
        var tile = tileBuilder.Build();
        await bandClient.TileManager.AddTileTaskAsync(context, tile);
    }
}

I have added two images to my Android project, as per the API specs. One image is 24x24 and the other 48x48.  I create them and convert them into the BandIcon format. The following code is what I used to call the AddTileAsync method.

using (var bandConnection = new BandConnection(this))
{
    if (bandConnection.BandClient != null)
    {
        await bandConnection.ConnectAsync().ConfigureAwait(false);
        var smallIconBitmap = ((BitmapDrawable)Resources.GetDrawable(Resource.Drawable.PhoneFinderSmall)).Bitmap;
        var smallIcon = BandIcon.ToBandIcon(Bitmap.CreateScaledBitmap(smallIconBitmap, 24, 24, false));
        var largeIconBitmap = ((BitmapDrawable)Resources.GetDrawable(Resource.Drawable.PhoneFinderLarge)).Bitmap;
        var largeIcon = BandIcon.ToBandIcon(Bitmap.CreateScaledBitmap(largeIconBitmap, 48, 48, false));

        await bandConnection.AddTileAsync(BandConstants.TileId, "Band Test", largeIcon, smallIcon).ConfigureAwait(true);
    }
}

When using this code it automatically sinks up to the device.  The tile is identified by a unique identifier that I added as a constant to my code. When running it will first ask the user if they want to add the new tile to the Band. You can see my "awesome" abilities as an icon designer:


After choosing allow the band synchronizes the new tile onto the Band.


By scrolling the tiles on the band the newly added tile appears.


I then added a method to remove the tile I just added.

public async Task RemoveTileAsync(string tileId)
{
    if (bandClient == null)
    {
        throw new InvalidOperationException("BandClient is not set");
    } 
    var tiles = await bandClient.TileManager.GetTilesTaskAsync().ConfigureAwait(false);
    var id = UUID.FromString(tileId);
    if (tiles.Any(t => t.TileId.ToString() == tileId))
    {
        await bandClient.TileManager.RemoveTileTaskAsync(id).ConfigureAwait(false);
    }
}

This worked without incident and the tile from my band went away.

Sending a message to the band was also simple. The API reference contains the following description on the difference between a message and a dialog:

App notifications come in two flavors:
- Dialogs – dialog notifications are popups meant to quickly display information to the user. Once the user dismisses the dialog, the information contained therein does not persist on the Band.
- Messages – message notifications are sent and stored in a specific tile, and a tile can keep up to 8 messages at a time. Messages may display a dialog as well.

I added this method to my class to create a message:

public async Task ShowMessageAsync(string tileId, string title, string body, DateTime date, bool showMessage)
{
    if (bandClient == null)
    {
        throw new InvalidOperationException("BandClient is not set");
    }
    var id = UUID.FromString(tileId);
    await bandClient.NotificationManager.SendMessageTaskAsync(id, title, body, date, showMessage);
}

This method calls into my ShowMessageAsync method and gets the device location, converts it to an address and displays it to the user.

using (var bandConnection = new BandConnection(this))
{
    if (bandConnection.BandClient != null)
    {
        await bandConnection.ConnectAsync().ConfigureAwait(false);

        var geocoder = new Geocoder(this);
        var addressList = await geocoder.GetFromLocationAsync(_currentLocation.Latitude, _currentLocation.Longitude, 10).ConfigureAwait(false);

        var address = addressList.FirstOrDefault();
        if (address != null)
        {
            var deviceAddress = new StringBuilder();
            for (int i = 0; i < address.MaxAddressLineIndex; i++)
            {
                deviceAddress.Append(address.GetAddressLine(i))
                .AppendLine(",");
            }

            await bandConnection.ShowMessageAsync(BandConstants.TileId, 
                "Test Message", 
                string.Format("Current address: {0}", deviceAddress), 
                DateTime.Now, 
                true).ConfigureAwait(true);
        }
    }       
}

The message comes across to the band as expected.


Additionally, as expected, a message indicator appears with our tile stating that one new message was received.


When selecting the tile the message appears fully (I blocked out my address).


Sending a dialog is similarly easy.  But as this test indicates the amount of text that can be displayed in the dialog is limited and there is no down button (see more) as with the message.  Dialogs should only be used for very short notifications.

public async Task ShowDialogAsync(string tileId, string title, string body)
{
    if (bandClient == null)
    {
        throw new InvalidOperationException("BandClient is not set");
    }
    var id = UUID.FromString(tileId);
    await bandClient.NotificationManager.ShowDialogTaskAsync(id, title, body);
}

When sending this over it shows up as a popup but does not add to the message counter or add to the message history. As the API documentation stated, the dialog is not retained.


That's it for my first go.  Vibrating the band was similarly easy. Next I'll poke around at getting data from the Band and perhaps customizing its appearance.  Generally I was pleased at how easy it was to work with but cross platform applications may want to create a unified interface.

1 comment:

  1. Great Article Yes I been Started Taking interest in Xamarin since last 2 month It is been great to read your Xamarin Services

    ReplyDelete