Microsoft Band SDK for Xamarin.Android

Xamarin and the Microsoft Band

Microsoft finally released an SDK for the Microsoft Band! Yay! The Band is very much designed for health and fitness uses with many sensors. But, it is certainly not limited to health uses. Because we have access to the sensors and can interact with the device, we can use the Band to enhance and extend our app. Working with Band essentially means communicating with the Band from the app installed on the device.

There are 4 main areas that we can interact with the Band:

  1. Reading information from the Sensors
  2. Creating themed app Tiles
  3. Sending Messages to the Band
  4. Theming and Personalizing the Band

The Library and Source Code

The Microsoft Band SDK is still in preview and is available on the official Developer page. When working with Xamarin.Android, we use the component from the Xamarin Component Store.

The binding 4, where you can fork, extend and improve. I will also be looking to create a PCL version that can be used in cross-platform apps, such as those using the Xamarin.Forms framework.

There are currently two demo apps to look at:

  1. Android Feature Demo – demonstrates all the features for Android
  2. Rotating Hand Demo – shows a simple example using the accelerometer (port from Iris Classon)

Getting Started

Before we can do anything with the Band, we need to be able to connect to it. This involves updating the Band and configuring the Xamarin.Android project.

First, we need to make sure that the latest Microsoft Health app is installed on the Android device and the Band updated with the latest firmware (done through the Health app). The Band needs to be paired with the Android device, and, the Health app needs to be synced with the Band.

Next, we need to ensure that we have set up our Xamarin.Android project with permissions to interact with the Band. The minimum supported Android version is 4.2, or level 17.

Finally, we need to make sure that we have permission to communicate with the Band over Bluetooth. This is easy to add using the [UsesPersmission] attributes:

    [assembly: UsesPermission(Android.Manifest.Permission.Bluetooth)]
    [assembly: UsesPermission(Microsoft.Band.BandClientManager.BindBandService)]

Connecting to a Band

Now that we have the Band, the device and the Xamarin.Android project set up, we can start connecting to the Band.

The first thing we want to do is get a list of the Bands paired with the device:

    var bands = BandClientManager.Instance.GetPairedBands();

Then we need to get an instance of IBandClient that will be used to communicate with the Bands:

    var client = BandClientManager.Instance.Create(this, bands[0]);

Once we have the client, we can connect to the actual Band:

    var result = await client.ConnectTaskAsync();
    if (result != ConnectionResult.Ok) {
        // we couldn't connect
    }

When we no longer require a connection to the Band, we can disconnect:

    await client.DisconnectTaskAsync();

Working with Sensors

Working with Microsoft Band Sensors

The main feature of the Band is the many sensors. The sensors that are available to us are the:

  • Accelerometer
    Provides X, Y, and Z acceleration in meters per second squared (m/s²) units.
  • Contact
    Provides a way to let the developer know if someone is currently wearing the device.
  • Distance
    Provides the total distance in centimeters, current speed in centimeters per second (cm/s), current pace in milliseconds per meter (ms/m), and the current pedometer mode (such as walking or running).
  • Gyroscope
    Provides X, Y, and Z angular velocity in degrees per second (°/sec) units.
  • Heart Rate
    Provides the number of beats per minute, also indicates if the heart rate sensor is fully locked onto the wearer’s heart rate.
  • Pedometer
    Provides the total number of steps the wearer has taken.
  • Skin Temperature
    Provides the current skin temperature of the wearer in degrees Celcius.
  • Ultraviolet (UV)
    Provides the current ultraviolet radiation exposure intensity.

Subscribing to sensors has an impact on the Band’s battery life. The use of each sensor requires power draw (some more than others), so we should only subscribe when the data is absolutely needed for our app.

Connecting to the sensors involves a few steps, namely getting hold of the sensor, attaching an event handler and then starting the sensor listener. So first, we want to get hold of a sensor:

    var accelerometer = client.SensorManager.CreateAccelerometerSensor();

Then we want to attach an event handler to the sensor so that we can be notified of updates. This is called from another thread and we need to be able to handle a high volume of events:

    accelerometer.ReadingChanged += (sender, args) => {
        var yReading = args.SensorReading.AccelerationY;
        RunOnUiThread(() => {
            label.Text = yReading;
        });
    };

Finally, we start listening to the sensor. Some sensors (namely the accelerometer and gyroscope) require a listening interval, or sample rate, which could be 16ms, 32ms or 128ms:

    await accelerometer.StartReadingsTaskAsync(SampleRate.Ms16);

When we want to stop listening, all we have to to is call one method:

    await accelerometer.StopReadingsTaskAsync();

Creating App Tiles

Creating App Tiles on the Microsoft Band

Tiles allow us to create app specific experiences on the Band. The Band supports up to 13 separate tiles, and will allow us to create as many tiles as there is space for.

If we want to find out how many tiles we can still create, we can use the TileManager:

    var capacity = await client.TileManager.GetRemainingTileCapacityTaskAsync();

Each tile consists of several properties:

  • a title or name
  • a tile icon
  • a small/badge icon
  • a theme/colour set

Creating App Tiles on the Microsoft Band

The band is required to have an ID (UUID), a title and an icon. If no small icon is provided, notification/badge numbers will not appear on the tile when a message is received. Each tile can have a custom theme, and if none is provided, then the current Band theme will be used.

The Band does not support colour icons, but only white, alpha blended icons. Also, each tile icon must be be 46×46 pixels. The small icon should be 24×24 pixels. Because the Band does not directly support the native Android bitmap, we use the BandIcon type:

    var options = new BitmapFactory.Options();
    options.InScaled = false;
    var icon = await BitmapFactory.DecodeResourceAsync(Resources, Resource.Raw.tile, options)
    var tileIcon = new BandIcon(icon);

If we want to create a new tile, we use the BandTile.Builder:

    var uuid = Java.Util.UUID.RandomUUID();
    var tileName = "My Tile";
    var tile = new BandTile.Builder(uuid, tileName, tileIcon).Build();

We can also set a small icon and a custom theme:

    var tile = new BandTile.Builder(uuid, tileName, tileIcon)
        .SetTileSmallIcon(badgeIcon)
        .SetTheme(BandTheme.TuxedoTheme)
        .Build();

To add a tile to the band, we make use of the TileManager property:

    var success = await client.TileManager.AddTileTaskAsync(this, tile);

Removing a tile is also a single method on the tile manager:

    var success = await client.TileManager.RemoveTileTaskAsync(tile);

We can either pass the tile to the remove method, or we can just use the ID:

    var success = await client.TileManager.RemoveTileTaskAsync(uuid);

If we want to get a list of all the tiles on the device for our app, we can query the tile manager

    var tiles = await client.TileManager.GetTilesTaskAsync();

Sending Messages

We can display a notification for a tile using the NotificationManager property. There are three types of notifications:

  1. Dialogs
    A quick popup that the user can dismiss and the message will not be persisted on the Band. Dialogs have an accompanying single, short vibration.
  2. Messages
    A persistent notification that will remain in the tile. Up to 8 messages can be persisted before the oldest ones are removed to make room. If the tile has a small icon, the number of unread messages will appear on the tile. Messages may also have an accompanying dialog.
  3. Vibrations
    A haptic feedback mechanism that can be used to alert the user to a notification on the Band.

Each dialog consists of several properties:

  • a tile ID
  • a title
  • a body

A message is similar to a dialog, but contains an extra property:

  • a tile ID
  • a title
  • a body
  • a time stamp

To display a dialog, we need the tile ID, the title and a body:

    var title = "My Message";
    var body = "My explanation for the message.";
    await client.NotificationManager.ShowDialogTaskAsync(uuid, title, body);

Displaying messages is very similar, except there are a few extra parameters:

    var date = DateTime.Now;
    await client.NotificationManager.SendMessageTaskAsync(uuid, title, body, date);

If we want a dialog to appear along with the message, we can add an additional parameter:

    await client.NotificationManager.SendMessageTaskAsync(uuid, title, body, date, true);

To cause the Band to vibrate, we can specify a type of vibration:

    VibrationType vibration = ...
    await client.NotificationManager.VibrateTaskAsync(vibration);

Working with Themes

Working with Themes on the Microsoft Band

Using a theme on the devices could be the tile theme or changing the wallpaper. We can customize the Band in three ways:

  1. App Tile Theme
    This theme applies to only the app, and only when the app is open.
  2. Band Theme
    This theme applies to all the tiles and apps on the Band, except where the app has a custom theme. The tile on the Start Strip will use the Band theme, but once the app opens, it will use the app theme.
  3. Me Tile
    This is the background image on the Me tile. This tile is the first, large tile in the Start Strip.

Whether we customize our app tile theme, or the overall theme, we are provided a few, pre-defined themes. These include:

  • CornFlower
  • Cyber
  • Joule
  • Orchard
  • Penguin
  • Storm
  • Tuxedo
  • Violet

There are two parts to a theme, the tile colours and the app colours. The tile colours are:

  • Base
    This is the normal colour of the tile in the Start Strip. This colour is also used for the app’s back/app bar.
  • High Contrast
    This is the colour of the tile when there is a notification available.
  • Lowlight
    This is the colour of the visual feedback, when the user taps the tile or any buttons in the app.

The app colours are:

  • Highlight
    This is the colour of the app’s header text, such as the title in messages or dialogs.
  • Secondary Text
    This is the colour for other informational text in the app, whether the secondary header or date information in messages.
  • Muted
    This is the colour used when displaying notifications when the app is open, such as when a message is received.

When working with themes, we use the PersonalizationManager property. This provides a way to work with the theme or the Me tile background. In order to get the current theme that is set, we use the GetThemeTaskAsync method:

    var theme = await client.PersonalizationManager.GetThemeTaskAsync();

When we want to create a new theme, we can either modify an existing theme, or we can create a new BandTheme instance:

    var theme = new BandTheme {
        BaseColor = ...,
        HighContrastColor = ...,
        LowlightColor = ...,
        HighlightColor = ...,
        SecondaryTextColor = ...,
        MutedColor = ...
    }

Once we have our theme created, or just modified, we can apply it to the entire Band:

    await client.PersonalizationManager.SetThemeTaskAsync(theme);

If we want to use the theme with a single app tile, we must set it during the construction of the tile:

    var tile = new BandTile.Builder(uuid, tileName, tileIcon)
        .SetTileSmallIcon(badgeIcon)
        .SetTheme(theme)
        .Build();

Customizing the Me Tile

One of the first thing the user sees when looking at the Band is the Me tile. This is the first tile on the Start Strip and is where the date lies.

The background image on this tile must be exactly 210×102 pixels and should be in the RGB565 bitmap format. The bitmap instance type used by the API is Android.Graphics.Bitmap, the same type ised by the usual Android widgets.

If we want to access the image already on the Band, we can use the GetMeTileImageTaskAsync method:

    var bitmap = await client.PersonalizationManager.GetMeTileImageTaskAsync();

To set a new image, we use the SetMeTileImageTaskAsync method:

    await client.PersonalizationManager.SetMeTileImageTaskAsync(bitmap);

OneDrive Picker for Android

The picker is the easiest way to browse, select, open and save OneDrive files an Android app with OneDrive.

OneDrive Picker in Action

The source can be found on GitHub and there is a component on the Xamarin Component Store.

Requirements

Before we can access the OneDrive, we will need to get an App ID. Register the app here to get an App ID (Client ID).

The OneDrive picker library is supported at runtime for Android API revision 14 and greater.

The picker requires the OneDrive app to be installed, in order to function. If the OneDrive app is not installed, the user will be prompted to download the app when either the StartPicking() or StartSaving() method is invoked.

Opening files

Our app needs to give the user a way to start opening files from OneDrive. Here we set up a click handler that launches the open picker:

    // keep a reference to the picker
    private IPicker picker;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // ...

        // create a picker
        picker = Picker.CreatePicker("ONEDRIVE_APP_ID");

        button.Click += delegate {
            // start the picker activity
            picker.StartPicking(this, LinkType.DownloadLink);
        };
    }

    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
        // get the results from the picker activity
        var result = picker.GetPickerResult(requestCode, resultCode, data);
        if (result != null) {
            // use the result
        } else {
            // continue as normal
            base.OnActivityResult(requestCode, resultCode, data);
        }
    }

Start Picking

The picker can be configured to return a URL for the selected file in one of these
formats:

  • LinkType.DownloadLink – the URL is valid for 1 hour and can be used to download a file
  • LinkType.WebViewLink – the readonly URL is valid until the user deletes the sharing link

Picker Result Object

In addition to the filename and link for the file, you can access several other properties on the IPickerResult object that provide more details about the file selected:

    public interface IPickerResult
    {
        Uri Link { get; }
        LinkType LinkType { get; }
        string Name { get; }
        long Size { get; }
        // keys: "small", "medium", and "large"
        IDictionary<string, Uri> ThumbnailLinks { get; }
    }

Saving Files

Similar to when opening files, we should provide a way for the user to save a file to OneDrive. Before we can upload a file, we need a filename for OneDrive and the path to the file on the local file system:

    private ISaver saver;

    protected override void OnCreate (Bundle savedInstanceState)
    {
        base.OnCreate (savedInstanceState);

        // ...

        // create the saver
        saver = Saver.createSaver("ONEDRIVE_APP_ID");

        button.Click += delegate {
            // launch the saver
            saver.startSaving(this, "FILENAME", "file://PATH/TO/FILE");
        }
    };

    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
        try {
            // the file saved successfully
            saver.HandleSave (requestCode, resultCode, data);
        } catch (SaverException ex) {
            // there was an error
        }
    }

Start Saving

The saver currently supports the content:// and file:// file URI scheme. If a different URI scheme is used, the saver will return a NoFileSpecified error type.

Saver Result

The error message provided by DebugErrorInfo is primarily for development and debugging and can change at any time. When handling errors, you can use ErrorType to determine the general cause of the error.

Saver Error Types

When the saver is unable to complete saving a file and throws an exception, it provides a SaverError through the ErrorType property that indicates one of a set of possible error types:

    public enum SaverError {
        Unknown,
        Cancelled,
        // The OneDrive account did not have enough space
        OutOfQuota,
        InvalidFileName,
        NoNetworkConnectivity,
        // The Uri to the file could not be accessed
        CouldNotAccessFile,
        // No file was specified to be saved, 
        // or the file URI scheme was not supported
        NoFileSpecified
    }

HttpClient and Xamarin.iOS Unified

I was creating a NuGet that was dependent on several other NuGets, one of them being Microsoft.Net.Http. This brings in the great HttpClient for all platforms.

However, there is a small problem right now: Microsoft has not yet updated their NuGet to support Xamarin.iOS Unified. They are working on it, but in the meantime what can we do to still create a NuGet that relies on HttpClient?

Well, we don’t actually need the Microsoft.Net.Http NuGet on the Xamarin platforms as the HttpClient type comes with the actual framework. This is because the Xamarin platforms target the 4.5 version of .NET.

So, we can update our .nuspec to exclude these NuGet dependencies on the Xamarin platforms. If our current .nuspec is similar to this:

<dependencies>
    <dependency id="Microsoft.Bcl" version="1.1.9" />
    <dependency id="Microsoft.Bcl.Build" version="1.0.14" />
    <dependency id="Microsoft.Bcl.Compression" version="3.9.85" />
    <dependency id="Microsoft.Net.Http" version="2.2.28" />
    <dependency id="Rx-Core" version="2.2.5" />
    <dependency id="Rx-Interfaces" version="2.2.5" />
    <dependency id="Rx-Linq" version="2.2.5" />
    <dependency id="Rx-Main" version="2.2.5" />
    <dependency id="Rx-PlatformServices" version="2.2.5" />
</dependencies>

All we have to do is move it into a general “ and add a specific group to the offending Xamarin.iOS platform:

<dependencies>
    <!-- for all platforms -->
    <group>
        <dependency id="Microsoft.Bcl" version="1.1.9" />
        <dependency id="Microsoft.Bcl.Build" version="1.0.14" />
        <dependency id="Microsoft.Bcl.Compression" version="3.9.85" />
        <dependency id="Microsoft.Net.Http" version="2.2.28" />
        <dependency id="Rx-Core" version="2.2.5" />
        <dependency id="Rx-Interfaces" version="2.2.5" />
        <dependency id="Rx-Linq" version="2.2.5" />
        <dependency id="Rx-Main" version="2.2.5" />
        <dependency id="Rx-PlatformServices" version="2.2.5" />
    </group>
    <!-- no Microsoft on iOS Unified -->
    <group targetFramework="XamariniOS1.0">
        <dependency id="Rx-Core" version="2.2.5" />
        <dependency id="Rx-Interfaces" version="2.2.5" />
        <dependency id="Rx-Linq" version="2.2.5" />
        <dependency id="Rx-Main" version="2.2.5" />
        <dependency id="Rx-PlatformServices" version="2.2.5" />
    </group>
</dependencies>

This works, as NuGet will use the more specific for Xamarin.iOS, but fall back to the empty for all other platforms. We can use this mechanism for all platforms. We can even use this for the various files as well. In fact, this is how Microsoft does it now: They include everything for all platforms, and then exclude the specific ones that already have HttpClient built in.

When Microsoft updates their NuGet, all we need to do is drop the “ group from the .nuspec and everything is back to normal.

SurfaceView, MediaPlayer and Prepare Failed

I was trying to get a video to play on a SurfaceView, but I kept getting an exception:

IOException: Prepare failed.: status=0x1

After looking around, I still was not able to find out how to get the video to play. It was working fine in a VideoView, but as soon as I tried the MediaPlayer, it threw. When I called the Prepare method, it threw, but if I called PrepareAsync, it just failed with an error to the log.

I eventually found the problem, it was just a single line. I am not sure if this is a bug, or there is some other reason:

mediaPlayer.SetDataSource(path);

If I specify the path to the video using a Uri, all works fine:

mediaPlayer.SetDataSource(this, Uri.Parse(path));

So that was annoying, but it’s all over now.

Playing a video on a SurfaceView using a MediaPlayer is very useful if you want to create your own custom media playback controls. The VideoView with the MediaController is great, but there is no way to easily customize the actual controls.

Here is the snippet of code that can be used to play video. It is assumed that the surface has been created already and available in the holder variable:

var path = string.Format(
    "android.resource://{0}/{1}", 
    PackageName, Resource.Raw.video);

// create the player
var mediaPlayer = new MediaPlayer();
mediaPlayer.SetDataSource(this, Uri.Parse(path));
mediaPlayer.SetAudioStreamType(Stream.Music);
mediaPlayer.SetDisplay(holder);

// prepare the player and the surface
bool isPrepared = false;
bool isSizeSet = false;
mediaPlayer.Prepared += (sender, e) => {
    isPrepared = true;
    if (isPrepared && isSizeSet) {
        mediaPlayer.Start();
    }
};
mediaPlayer.VideoSizeChanged += (sender, e) => {
    holder.SetFixedSize(e.Width, e.Height);
    isSizeSet = true;
    if (isPrepared && isSizeSet) {
        mediaPlayer.Start();
    }
};

// start the process
mediaPlayer.PrepareAsync();

Tasks: ContinueWith on UI Thread

Sometimes we need to perform a task in the background, but then we want to update the UI when it is completed.

We can make use of a Task and the ContinueWith method so that we can chain tasks together. To ensure that a task is performed on a specific thread, we specify which TaskSheduler to use. To get the UI thread scheduler, we can use TaskScheduler.FromCurrentSynchronizationContext() when we are on the UI thread:

If we are going to run a background task:

var uiThread = TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(() =&gt; {
    // do some work on a background thread
}).ContinueWith(task =&gt; {
    // do some work on the UI thread
}, uiThread);

If we need to carry a result from the previous background task:

var uiThread = TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(() =&gt; {
    // do some work on a background thread

    // return a value for the next task
    return true;
}).ContinueWith(task =&gt; {
    // do some work on the UI thread

    if (task.Result) {
        // make use of the result from the previous task
    }
}, uiThread));

The Quick How & Why of Xamarin Components

We at Xamarin were working hard around Christmas to get the new version of Xamarin.iOS out to you guys!

Cool Updates

After a short delay to finish fixing the last few bugs and getting in a few extra improvements, we released Xamarin.iOS 8.6 and updated some of our other products. The Profiler got a new preview and Xamarin.Forms recieved many enhancements.

We, The Components Team, updated hundreds of iOS components to add support for the 64-bit Unified API. There are a few components still outstanding, so if you are the owner, be sure to update them as soon as possible so that they can be used in many more apps!

From inside the team, I can see a great many new components being added daily. Be sure to update or create new components and get them out to developers.

Creating Components

If you find a great native Objective-C or Java control or library that you would like to see in the component store, you don’t have to re-write the whole library. Just create a binding and package that, and, when the original author updates, you can too.

You can also create what we call “Empty Shell Components”. This is simply a component that does not include any actual assemblies, but references a NuGet from NuGet.org. The component still has to include the required docs and samples, but will be featured on the component store.

Creating components can be done in many ways:

  • “traditional” components that include iOS and Android assemblies in the package
  • “empty shell” components that reference a NuGet
  • “hybrid” components that include assemblies and have a dependency on a NuGet

I get my code for the components from many sources:

  • “managed” source code that is compiled into assemblies
  • “bindings” of native libraries
  • “NuGet” packages just packaged along with docs and samples
  • “GitHub” repositories and add new Xamarin projects

If you are faced with a library that does not have support for iOS or Android, you can help that developer out and create the bit he needs. GitHub is a place where you can fork, add Xamarin support and pull. Maybe in the next release there will be a Xamarin library in the NuGet for you to componentize!

NuGet vs Component

You might ask why you need to create a component when you can just create a NuGet. Well, the NuGet store is like a large pit with everything in, and the component store is like a shelf with labels. Also, many of the NuGets can’t be used in iOS or Android. If you submit to the component store, developers know that the library has been tested and approved for iOS and/or Android apps.

It is not that NuGet is bad, don’t get me wrong, but I can never find anything new. The Xamarin component store also supports extra features such as ratings and reviews that can help you find that great library. I think that the component store is like NuGet with Intellisense.

Why not just create a NuGet and then create an empty shell component? You get support for both worlds with only a tiny bit of extra work, and you can get you library directly to Xamarin developers.

Downloading Expansion Files in Xamarin.Android

Google Play currently requires that our APK file be no more than 50MB. For most apps, this is plenty of space for all the app’s code and assets. However, some apps need more space for high-fidelity graphics, media files, or other large assets. Google Play allows us to attach two large expansion files that supplement our APK.

Google Play hosts the expansion files for our app and serves them to the device at no cost to us. The expansion files are saved to the device’s shared storage location (the SD card or USB-mountable partition, also known as the “external” storage) where our app can access them. On most devices, Google Play downloads the expansion file(s) at the same time it downloads the APK, so our app has everything it needs when the user opens it for the first time. In some cases, however, our app must download the files from Google Play when our app starts.

We can obtain the library from NuGet or we can compile the code directly from GitHub.

The Expansion Files

Expansion files are treated as opaque binary blobs (obb) and each may be up to 2GB in size. Android does not perform any special processing on these files after they are downloaded – the files can be in any format that is appropriate for the app. Conceptually, each expansion file plays a different role:

  • The main expansion file is the primary expansion file for additional resources
  • The patch expansion file is optional and intended for small updates to the main expansion file

When Google Play downloads our expansion files to a device, it saves them to the system’s shared storage location. To ensure proper behavior, we must not delete, move, or rename the expansion files. In the event that our app must perform the download from Google Play itself, we must save the files to the exact same location. Any updates to the expansion files overwrite the existing files.

The specific location for our expansion files is:
[shared-storage]/Android/obb/[package-name]/

  • [shared-storage] is the path to the shared storage space, available from Environment.ExternalStorageDirectory
  • [package-name] is our app’s Java-style package name, available from PackageName

Each expansion file we upload will be renamed to match the pattern:
[main|patch].[expansion-version].[package-name].obb

  • [main|patch] specifies whether the file is the main or patch expansion file. There can be only one main file and one patch file for each APK
  • [expansion-version] is an integer that matches the version code of the APK with which the expansion is first associated
    “First” is emphasized because although the Developer Console allows we to reuse an uploaded expansion file with a new APK, the expansion file’s name does not change – it retains the version applied to it when we first uploaded the file*
  • [package-name] the Java-style app package name

For example, suppose our APK version is 23 and our package name is com.example.app. If we upload a main expansion file, the file is renamed to main.23.com.example.app.obb.

Getting Things Ready

We will need to obtain the base64-encoded RSA public key. More information on this in the Getting Things Ready section of this post.

Implementing the Downloader

Once we have installed the library and have our key, we need to ensure that the app has the appropriate permissions to access Play, the licensing service, the Internet, the network state and the external storage:

    [assembly: UsesPermission(Manifest.Permission.Internet)]
    [assembly: UsesPermission(Manifest.Permission.WriteExternalStorage)]
    // required to poll the state of the network connection
    // and respond to changes
    [assembly: UsesPermission(Manifest.Permission.AccessNetworkState)]
    // required to keep CPU alive while downloading files
    [assembly: UsesPermission(Manifest.Permission.WakeLock)]
    [assembly: UsesPermission(Manifest.Permission.AccessWifiState)]
    [assembly: UsesPermission("com.android.vending.CHECK_LICENSE")]

Once we have permission, we can then create a DownloaderService service that will manage the downloads of the expansion files. In addition to the download management, the service will create an alarm to resume downloads and build and update a notification that displays download progress. We can create the service by simply overriding three properties:

    [Service]
    public class SampleDownloaderService : DownloaderService
    {
        // the API key used to access Play
        protected override string PublicKey
        {
            get { return "Base64 API Public Key"; }
        }

        // the salt used to encrypt the cached server response
        protected override byte[] Salt
        {
            get { return new byte[] { ... }; }
        }

        // the name of the broadcast reciever that will resume 
        // the downloads if the service is stopped
        protected override string AlarmReceiverClassName
        {
            get { return "namespace.ClassName"; }
        }
    }

Finally, we will need to implement a BroadcastReciever that will be used to resume any downloads if the service is stopped:

    [BroadcastReceiver(Exported = false)]
    public class SampleAlarmReceiver : BroadcastReceiver
    {
        public override void OnReceive(Context context, Intent intent)
        {
            // start the service if necessary
            DownloaderService.StartDownloadServiceIfRequired(
                context, intent, typeof(SampleDownloaderService));
        }
    }

Starting the Check

Once we have the download service and the broadcast receiver, we can then start the downloading. Before we start any downloads, we should make sure that we have not already downloaded all the files:

    // get a list of all the downloaded expansion files
    var downloads = DownloadsDatabase.GetDownloads();
    if (!downloads.Any())
    {
        // start the download as nothing is here
    }
    foreach (var file in downloads)
    {
        if (!Helpers.DoesFileExist(this, file.FileName, file.TotalBytes, false))
        {
            // start the download as this file is incomplete
            break;
        }
    }

If the expansion files are not there or not complete, we need to start the download from the OnCreate method. In order to start the service, we need to provide an Intent that will be used to launch the app when the user taps the notification:

    // build the intent that launches this activity.
    Intent launchIntent = this.Intent;
    var intent = new Intent(this, typeof(SuperSimpleActivity));
    intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTop);
    intent.SetAction(launchIntent.Action);
    if (launchIntent.Categories != null)
    {
        foreach (string category in launchIntent.Categories)
        {
            intent.AddCategory(category);
        }
    }

    // build PendingIntent used to open this activity when user 
    // taps the notification.
    PendingIntent pendingIntent = PendingIntent.GetActivity(
        this, 0, intent, PendingIntentFlags.UpdateCurrent);

Now that we have the Intent, we can start the service. Starting the service will return a result that indicates whether the service was started:

    // request to start the download
    var startResult = DownloaderService.StartDownloadServiceIfRequired(
        this, pendingIntent, typeof(SampleDownloaderService));

If the service was started, we obtain an IDownloaderServiceConnection to the service which will be used to communicate with the service from the activity:

    // the DownloaderService has started downloading the files
    if (startResult != DownloadServiceRequirement.NoDownloadRequired)
    {
        // create the connection to the service so that we can show progress.
        // when creating the marshaller, we pass in the IDownloaderClient
        // that will be used to handle the updates from the service
        connection = ClientMarshaller.CreateStub(
          this, typeof(SampleDownloaderService));
    }
    else
    {
        // all files have finished downloading already
    }

Once we have started the downloader and created a connection, we need to connect and disconnect in line with the activity’s lifecycle. In the OnResume method of the activity, we connect to the service:

    if (connection != null)
    {
        connection.Connect(this);
    }

And, in the OnStop method, we disconnect from the service:

    if (connection != null)
    {
        connection.Disconnect(this);
    }

Receiving Download Progress Updates

As the download progresses, we will receive updates such as the number of bytes downloaded, the download speed and various network states. We can use all of this to display information on the user interface. This is all in addition to the notification that is automatically created and managed by the service.

In order to receive updates, we need to get hold of an IDownloaderService from the IDownloaderServiceConnection. To do this, we have to implement the IDownloaderClient somewhere, such as on the activity:

    public class SuperSimpleActivity : Activity, IDownloaderClient
    {
        public void OnServiceConnected(Messenger m)
        {
            // create the proxy that is used to communicate with the service
            service = ServiceMarshaller.CreateProxy(m);
            // let the service know about us and request an update
            service.OnClientUpdated(connection.GetMessenger());
        }
        public void OnDownloadProgress(DownloadProgressInfo progress)
        {
            // handle download progress updates
        }
        public void OnDownloadStateChanged(DownloaderState newState)
        {
            // handle download states, such as completion or pause
        }
    }

Managing the Service

Once we have the service started and the download going, we may want to be able to pause the download. We can request the download be paused using the IDownloaderService:

    service.RequestPauseDownload();

Similarly, we can resume a download:

    service.RequestContinueDownload();

We may also want to change various properties, such as whether to download over mobile or not when the download is paused due to Wi-Fi being unavailable. To do this we use the SetDownloadFlags method on the IDownloaderService:

    public void OnDownloadStateChanged(DownloaderState newState)
    {
        if (newState == DownloaderState.PausedNeedCellularPermission ||
            newState == DownloaderState.PausedWifiDisabledNeedCellularPermission)
        {
            // let the service know that it can download over mobile
            service.SetDownloadFlags(ServiceFlags.FlagsDownloadOverCellular);
            // resume the download
            service.RequestContinueDownload();
        }
    }

Reading the Expansion Files

Because the expansion files are saved to a specific location, namely [shared-storage]/Android/obb/[package-name]/, we could read the files using any file means available.

If we must unpack the contents of our expansion files, do not delete the .obb expansion files afterwards and do not save the unpacked data in the same directory. We should save our unpacked files in the directory specified by GetExternalFilesDir(). However, if possible, it’s best if we use an expansion file format that allows we to read directly from the file instead of requiring we to unpack the data. The reason for this is that the expansion files will now exist twice on the device.

Using a ContentProvider

One way in which we can read the expansion files is to make use of a content provider that can read them. In the library, there is a special content provider, the ApezProvider, that can read uncompressed zip files. If all the expansion resources are bundled in an uncompressed, storage zip archive, this provider allows access to the individual resources without having to first extract them.

Using this provider is simple and easy to implement:

    [ContentProvider(new[] { ContentProviderAuthority }, Exported = false)]
    [MetaData(ApezProvider.MetaData.MainVersion, Value = "14")]
    [MetaData(ApezProvider.MetaData.PatchVersion, Value = "14")]
    public class ZipFileContentProvider : ApezProvider
    {
        public const string ContentProviderAuthority = "expansiondownloader.sample.ZipFileContentProvider";

        protected override string Authority
        {
            get { return ContentProviderAuthority; }
        }
    }

The [MetaData] attributes let the provider know what version of the expansion files to load. For example, if we have uploaded the app with a version number of 5 and new expansion files, the version we place in the attributes will be 5. When we update the app, we may choose to use the same expansion files. So, our new app version will be 6, but because we selected the old expansion files, they are still version 5. Thus, the provider will still use the version numbers of 5 in the attributes.

If we want to access a file in this provider, we can use a Uri to the resource in the provider:

    var uri = Uri.Parse(string.Format(
        "content://{0}/relative/path/to/movie.mp4",
        ZipFileContentProvider.ContentProviderAuthority));
    videoView.SetVideoURI(uri);

It is important to note that the provider uses file descriptors and cannot support reading compressed expansion files.

Using the Zip Archive

Another way to access the files is to access them directly by filename. We can use the ApkExpansionSupport type to obtain the files stored in each expansion file:

    var files = ApkExpansionSupport.GetApkExpansionZipFile(context, 14, 14);
    var entry = files.GetEntry("relative/path/to/file.extension");
    using (var zip = new ZipFile(entry.ZipFileName))
    using (var stream = zip.ReadFile(entry))
    {
        // process the stream of the contained file
    }

Files accessed this way can be stored in a compressed format as a decompression stream is provided. This stream can be processed using typical .NET means, such as providing this stream to a deserializer or some other reader.

The ApkExpansionSupport.GetApkExpansionZipFile method returns a combined collection of all the items in either of the expansion files. We can then query this for a specific item using the GetEntry method on the collection. We pass a relative path to the item in expansion file. Once the entry is returned, we can then create a ZipFile using the path of the entry’s containing expansion file. And, after we have the zip file, we read the stream of the item inside using the ReadFile method.

Testing the Downloader

Testing the download manager is very similar to testing the licensing. More information on this in the Testing the Licensing section of this post. The downloader performs the license check internally, and in the response, it receives the expansion files URIs from Play. It then uses these to initiate the download.

In order to be able to download the expansion files, we have to have uploaded them when updating the app. The first upload does not allow the expansion files to be added, but we can just re-upload the same package twice. On the second time, we can upload the expansion files with the actual app package, remembering to increase the version number beforehand.

We can start testing our app by checking its ability to read the expansion files. We can do this by placing and naming the files just as the downloader would. Because the downloader always places the files at the [shared-storage]/Android/obb/[package-name]/ location, with the name [main|patch].[expansion-version].[package-name].obb, we just have to place our files there. By skipping the downloading, we don’t have to upload the app first.

Once we are sure that the app can access and use the expansion files, we can upload the files with the app to Play and publish to any channel to test the download. Any channel can be used, including Alpha and Beta.

Important Things to Remember

Testing the expansion files, from downloading to reading, requires that we have the various bits in place:

  1. The app version on the device must be the same as the app that is on the store
  2. Make sure that the app is indeed published, and not in draft
  3. Ensure that the Expansion files have been associated with the app
  4. Make sure that the response in “Settings” is set to “RESPOND_NORMALLY” as this is the only response that returns the expansion files
  5. Make sure the app has all the required permissions, there are 6 at least

Play Licensing in Xamarin.Android

Google Play offers a licensing service that lets us enforce licensing policies for applications that us publish on Google Play. With Google Play Licensing, our application can query Google Play at runtime to obtain the licensing status for the current user, then allow or disallow further use as appropriate.

The Google Play Licensing service is primarily intended for paid applications that wish to verify that the current user did in fact pay for the application on Google Play. However, any app (including free apps) may use the licensing service to initiate the download of an APK expansion file.

Getting Things Ready

In order to add licensing to our app, all we will need is the licensing verification library. We can get this from NuGet.org or the Component Store (coming soon), or, we can build the source using the GitHub repository.

After adding the library, we need to get hold of our API key for this app:

  1. Browse to the Google Play Developer Console
  2. Select “All Applications” from the sidebar
  3. Select the app we want to implement licensing for
    • If we are going to create a new app, select “Add new application”
    • Enter a name for the app
    • Select “Prepare store listing”
    • Enter the required details for publishing
  4. Select “Services & APIs” from the left menu
  5. Scroll to the “Licensing & In-app billing” section
  6. Under the “Your license key for this application” heading, copy the base64-encoded RSA public key
  7. Paste it into a string constant in the app code:
    const string ApiKey = "XXX";

All Applications in Play
Public Key

Adding the License Handlers

Once we have installed the library and have our key, we need to ensure that the app has the appropriate permissions to access Play and the licensing service:

    [assembly: UsesPermission("com.android.vending.CHECK_LICENSE")] 

Once we have permission, We can then implement the ILicenseCheckerCallback interface. This can be implemented on the activity, but does not have to:

    public class MainActivity : Activity, ILicenseCheckerCallback
    {
      public void Allow(PolicyServerResponse response)
      {
        // Play has determined that the app is owned,
        // either purchased or free
      }
      public void DontAllow(PolicyServerResponse response)
      {
        // Play has determined that the app should not be available to the user,
        // either because they haven't paid for it or it is not a valid app

        // However, there may have been a problem when Play tried to connect,
        // so if this is the case, allow the user to try again
        if (response == PolicyServerResponse.Retry)
        {
          // try the check again
        }
      }
      public void ApplicationError(CallbackErrorCode errorCode)
      {
        // There was an error accessing the license
      }
    }

Starting the Check

Once we have implemented the interface, all we need to do now is start the check. There are two basic methods provided in order do this, one with caching and one without.

Using StrictPolicy

To make things easier to start off with, I will first demonstrate the one without caching, the StrictPolicy:

    // create the policy we want to use
    var policy = new StrictPolicy();
    // instantiate a checker, passing a Context, an IPolicy and the Public Key
    var checker = new LicenseChecker(this, policy, "Base64 Public Key");
    // start the actual check, passing the callback
    checker.CheckAccess(this);

As soon as the check has completed, either with an error or successfully, one of the methods on the callback will be called, either Allow, DontAllow or ApplicationError.

Allow will receive a Licensed response
DontAllow will receive a NotLicensed response
ApplicationError will have the reason for the error, such as NotMarketManaged, InvalidPublicKey or some other reason.

Using ServerManagedPolicy

Although checking with Play each time the app launches is not a problem, doing so requires additional time and resources before the app can start. Usually, we can use the one with caching, the ServerManagedPolicy policy. This is very much the same as the StrictPolicy, but with an additional step to provide an IObfuscator to store the response:

    // create a device-unique identifier to prevent other devices from decrypting the responses
    string deviceId = Settings.Secure.GetString(ContentResolver, Settings.Secure.AndroidId);
    // create a app-unique identifer to prevent other apps from decrypting the responses
    var appId = this.PackageName;
    // create a random salt to be used by the AES encryption process
    byte[] salt = new byte[] { 46, 65, 30, 128, 103, 57, 74, 64, 51, 88, 95, 45, 77, 117, 36, 113, 11, 32, 64, 89 };

    // create the obfuscator that will read and write the saved responses, 
    // passing the salt, the package name and the device identifier
    var obfuscator = new AesObfuscator(salt, appId, deviceId);
    // create the policy, passing a Context and the obfuscator
    var policy = new ServerManagedPolicy(this, obfuscator);
    // create the checker
    var checker = new LicenseChecker(this, policy, Base64PublicKey);
    // start the actual check, passing the callback
    checker.CheckAccess(this); 

As soon as the checker has returned and we know that we can start the application, we should destroy the cecker in order to free up resources and close connections:

    // free resources and close connections
    checker.OnDestroy();

Testing the Licensing

The last thing that is needed is testing. To do this, we have to be sure that we have uploaded and published the app to Play. Publishing to any of the channels, including Alpha and Beta, will work.
In order for the Alpha or Beta channels to be used on devices other than the publisher’s device, those people have to be added to the Alph or Beta testers group.
If we want to test different responses that our app may receive from Play, we can select the desired response from the settings:

  1. Select “Settings” from the sidebar
  2. Select “Account details” from the left menu
  3. Scroll dow to the “License Testing” section
  4. Select the desired response from the drop down titled “License Test Response”
    • For other testers, make sure we enter their Google account email address in the text area above titled “Gmail accounts with testing access”

Custom License Responses

Important Things to Remember

Testing licensing is easy to do, provided we have all the required bits in place. Here are some common things that we may have to check:

The Play Store

  1. The app version on the device must be the same as the app that is on the store
  2. Provide enough time for the app to appear on the store, this can be determined by a little exclamation sign next to the app title when viewing the app details
  3. Make sure that the app is indeed published, and not in draft

For Other Testers

  1. Ensure that the testers have been added to the Alpha or Beta testers group
  2. If we are testing custom responses, make sure that their emails are added to the “Gmail accounts with testing access” text area in “Settings”

For The App

  1. Make sure we have the Android permission
    com.android.vending.CHECK_LICENSE
  2. Ensure that the app has the same version code/number and package name as that which is on the store

Returning with the iOS Keyboard

Almost all mobile apps have some sort of text entry. One of the main ways to do this is to simply add a text box. Adding a text box is both simple and easy to do, but we can take this a little further and improve the user experience.

Hiding the Keyboard

When the keyboard is shown by tapping a text box, the natural thing to do in order to hide it would be to tap outside the text box. However, this is not the case by default. But, this is easy to add by overriding the TouchesBegan method of the ViewController:

    public override void TouchesBegan (NSSet touches, UIEvent evt)
    {
        // hide the keyboard from all views
        View.EndEditing (true);

        base.TouchesBegan (touches, evt);
    }

Enabling/Disabling the Return Key

There is a very simple way to let the user know that some text is required: disable the return key if no text is provided. This can be done in both the code and in the designer. In the code we set the EnablesReturnKeyAutomatically property:

    textField.EnablesReturnKeyAutomatically = true;

There is a checkbox in the designer labeled “Auto-enable Return Key”.

It is important to note that this does not apply if the keyboard type is NumberPad as there is no return key for this keyboard type.

Changing the Keyboard Type

Another way to provide a basic restriction of the UI is to select a keyboard type. By selecting an appropriate keyboard type, the user can know what is expected to be entered as well as make the available characters easier to reach. If we only want numbers, we use the NumberPad as this removes the other keys. If we are providing an entry for an email address, we use the EmailAddress type which makes the @ and the . key more available:

    textField.KeyboardType = UIKeyboardType.NumberPad;

There is a dropdown in the designer labeled “Keyboard” which provides all the available options.

Keyboard Types on iOS

Changing the Return Key Type

Another nice user experience is to change the text for the “return” key, which provides additional hints as to what is going to happen with the input. There are several options that are available, such as Done, Search or Next:

    textField.ReturnKeyType = UIReturnKeyType.Done;

There is a dropdown in the designer labeled “Return Key” which provides all the available options.

Adding an Action to the Return Key

When the user presses the return key on the keyboard, we would expect something to happen, especially if it is a single line textbox. But, on iOS nothing happens, so we attach a delegate that can hide the keyboard and maybe do some more work. This is easy to add by assiging a delegate to the ShouldReturn property of the UITextFied:

    textField.ShouldReturn = delegate {
        // hide the keyboard
        textField.ResignFirstResponder ();

        // the "return" logic here

        return true;
    };

Adding “Return” to the Number Pad

As the NumberPad keyboard type does not have a return key, there is no default way to hide the keyboard, we have to manually add a handler to hide the keyboard when another view is tapped. Another way we can do this is to add a “Cancel” or a “Done” button accessory to the keyboard:

    // create the toolbar
    UIToolbar numberToolbar = new UIToolbar ();
    numberToolbar.BarStyle = UIBarStyle.Default;
    numberToolbar.Translucent = true;
    // set the items
    numberToolbar.Items = new [] { 
        // the cancel button
        new UIBarButtonItem (UIBarButtonSystemItem.Cancel, delegate {
            // hide the keyboard
            idEntryTextField.ResignFirstResponder ();
        }),
        // the space to separate the buttons
        new UIBarButtonItem (UIBarButtonSystemItem.FlexibleSpace, null),
        // the "continue" button
        new UIBarButtonItem ("Login"), UIBarButtonItemStyle.Done, delegate {
            // hide the keyboard
            idEntryTextField.ResignFirstResponder ();

            // the "return" logic here
        })
    };
    // fill the screen width
    numberToolbar.SizeToFit ();

    // set the accessory
    textField.InputAccessoryView = numberToolbar;

Xamarin Insights for Older Androids

Recently, Xamarin announced a real-time crash reporting and analytics solution for mobile apps, Xamarin Insights.

This is an awesome tool with many great features. They support all the major platforms, iOS Android Windows Phone, Windows RT and traditional desktop applications. Support for Xamarin.Mac is coming soon.

Some of the major features:

  • Report details about the device statistics such as operating system and screen resolution
  • Gather and report any uncaught native or managed exceptions
  • Report any caught exception, along with any additional data
  • Track both past and active sessions
  • Track any event that occurs in your application, including the time it takes to execute
  • Cross-platform across almost all devices, including support for PCL libraries

Old Androids

Recently they added support for Android versions below API level 14 (Android Version 4.0 – Ice Cream Sandwich). To use Xamarin Insights in an Android app below API level 14, such as API Level 8 (Android Version 2.3 – Gingerbread), there are a few things that need to happen:

  1. Set your ‘Compile using Android version’ or ‘Target framework’ value to be API level 14 or higher, such as API level 19
  2. Set the ‘Minimum Android to target’ or ‘Minimum Android version’ to be the value you are wanting to support, such as API level 8.

This will result in your app building against the higher API level, but can run on earlier devices.

However, even after all this, there is still a limitation, namely the inability to record sessions. This may not be a deal breaker for you, but for some, this is. The reason for this is that Xamarin Insights is using the IActivityLifecycleCallbacks interface. This is a relatively new addition to the Android API and is very useful in that it allows you to hook into the Activity lifecycle without having to use the Activity itself. As this interface is not available for the older versions, they simple can’t record the Activity sessions.

The Code

I am here to give you that solution that you always wanted: Manually starting and stopping sessions from within the activity. To do this I have created a simple class InsightsCompat:

    public static class InsightsCompat
    {
        public static void Initialize(string apiKey)
        {
            Insights.Initialize(apiKey, Application.Context);
        }

        public static void StartSession()
        {
            EmitBackgroundChange(false);
        }

        public static void StopSession()
        {
            EmitBackgroundChange(true);
        }

        private static void EmitBackgroundChange(bool backgroundState)
        {
            try
            {
                var status = backgroundState ? "Background" : "Resumed";
                var table = new Dictionary<string, string> {{"Status", status}};
                Insights.Track("X-App-State", table);
            }
            catch (Exception ex)
            {
                Insights.Report(ex);
            }
        }
    }

The Usage

This simple class can do all the work that is needed, plus there is an additional method, Initialize, that allows you to start up Xamarin Insights without having to find the context:

    // from anywhere in your code
    InsightsCompat.Initialize("your API key");

To start and stop sessions, you can override the OnResume and OnPause methods:

    protected override void OnPause()
    {
        InsightsCompat.StopSession();
        base.OnPause();
    }

    protected override void OnResume()
    {
        base.OnResume();        
        InsightsCompat.StartSession();
    }

We only have to override these two methods as android will always call these methods, no matter where the Activity came from or where it is going.

There’s More

Even though Xamarin Insights has many features, it is still in preview and there can still be many more features. If you have any feature requests or ideas, be sure to drop a message on the Xamarin Insights User Voice.

There is a small docs page for Xamarin Insights here. So head over to the dashboard and get started creating and supporting awesome cross-platform apps now.