Building an Estimote Scavenger Hunt on iOS, Android, and Windows in C# with Xamarin

This is a guest post by James Montemagno, Developer Evangelist at Xamarin.

It seems like there’s another tech conference somewhere every week. Putting together a world class event is extremely challenging, but creating a unique digital experience for your attendees can be even more of a hurdle. First step is choosing the right tools. With the Xamarin Platform, you are able to create native iOS, Android, and Windows applications all from a shared C# code base. At this year’s Xamarin Evolve 2014 conference, I thought there would be no better way to showcase the platform, and make sure attendees have fun at the same time, than to create a scavenger hunt mobile app: The Evolve Quest. It’s based on iBeacon technology and uses over 50 Estimote Beacons placed throughout the conference venue to create a game for attendees to complete during the conference.

image

Scavenger Hunt structure

The Evolve Quest was split into a series of “quests” for participants to complete, each with a text and visual clue as to what’s the next task. One to three Estimote Beacons were placed at each strategic location around the conference that were hinted at in the app. When an attendee got close to a beacon, the app would light up, letting them know a beacon was nearby. Once they found all of the beacons for a specific part of their adventure, they had to complete a final task to be able to proceed. The tasks involved fun, pertinent activities like trivia questions, trying out Google Cardboard, and controlling a Bluetooth-enabled robot.

This structure to a scavenger hunt was perfect for using beacons, with the entire fleet encompassed under one UUID. Each quest was defined by a major number, with beacons assigned to the quest differentiated with unique minor values. This means the game data, stored and retrieved from Azure, could easily be created, updated, and used across all platforms.

The full data structure of the scavenger hunt looks like this:

image

Using this data structure, you just need to create a unique user interface for each application. Here is a full overview of the iOS application using a storyboard from the Xamarin iOS Designer for Visual Studio:

image

Detecting Beacons in-game

In iOS, you are easily able to detect beacons through the Estimote SDK or through Apple’s Core Location API. To detect a beacon with Xamarin.iOS in C#, you first simply need to create a CLLocationManager, CLBeaconRegion to monitor, register for ranging, and then ask the Location Manager to start ranging:

using System;
using CoreLocation;
using EvolveQuest.Shared.ViewModels;
//...
 
namespace EvolveQuest.iOS
{
    partial class QuestViewController2 : UIViewController
    {
        public QuestViewController2(IntPtr handle)
            : base(handle)
        {
        }
 
        CLBeaconRegion beaconRegion;
        CLLocationManager manager;
 
        const string BeaconId = "com.refractored";
        private bool ranging;
        
        //...
    
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
 
            manager = new CLLocationManager();
            manager.RequestWhenInUseAuthorization();
 
            manager.DidRangeBeacons += ManagerDidRangeBeacons;
 
            viewModel.LoadQuestCommand.Execute(null);
        }
 
        void ManagerDidRangeBeacons(object sender, CLRegionBeaconsRangedEventArgs e)
        {
            foreach (var beacon in e.Beacons)
            {
                if (beacon.Proximity != CLProximity.Immediate)
                    continue;
 
                if (beacon.Accuracy > .1)//close, but not close enough.
		              continue;
 
                //call share code to see if we found correct beacon to continue on.
                viewModel.CheckBeacon(beacon.Major.Int32Value, beacon.Minor.Int32Value);
            }
        }
 
 
        private void StopRanging()
        {
            if (!ranging)
                return;
 
            if (beaconRegion != null)
                manager.StopRangingBeacons(beaconRegion);
 
            ranging = false;
        }
 
        private void StartRanging()
        {
 
            if (viewModel == null || viewModel.Quest == null)
                return;
 
            if (ranging)
                return;
 
            ranging = true;
    
            beaconRegion = new CLBeaconRegion(new NSUuid(viewModel.UUID), (ushort)viewModel.Quest.Major, BeaconId);
            manager.StartRangingBeacons(beaconRegion);
        }
 
        public override void ViewDidAppear(bool animated)
        {
            base.ViewDidAppear(animated);
            StartRanging();
        }
 
        public override void ViewDidDisappear(bool animated)
        {
            base.ViewDidDisappear(animated);
            StopRanging();
        }
    }
}

On Android there is no built-in API to detect beacons, but Estimote has their own solution, which is quite elegant and has full compatibility when creating Android apps in C# with Xamarin using the Estimote Component. Similar to iOS, you will need to create a BeaconManager and Region with Estimote SDK for Android. The rest of the code looks extremely similar to what we have on iOS.

using EstimoteSdk;
//...
 
namespace EvolveQuest.Droid.Activities
{
  [Activity(Label = "Evolve Quest", ScreenOrientation = ScreenOrientation.Portrait, ParentActivity = typeof(WelcomeActivity))]
  public class QuestActivity : Activity, BeaconManager.IServiceReadyCallback
  {
    public static QuestViewModel ViewModel{ get; set; }
    BeaconManager beaconManager;
    EstimoteSdk.Region region;
    const string BeaconId = "com.refractored";
 
    protected override void OnCreate(Bundle bundle)
    {
      base.OnCreate(bundle);
 
      ViewModel = new QuestViewModel();
      SetContentView(Resource.Layout.quest);
        
      //...
      beaconManager = new BeaconManager(this);
 
 
      beaconManager.Ranging += BeaconManagerRanging;
    }
 
    void BeaconManagerRanging(object sender, BeaconManager.RangingEventArgs e)
    {
      bool close = false;
      foreach (var beacon in e.Beacons)
      {
        var proximity = Utils.ComputeProximity(beacon);
 
        if (proximity != Utils.Proximity.Unknown)
            close = true;
 
        if (proximity != Utils.Proximity.Immediate)
            continue;
 
        var accuracy = Utils.ComputeAccuracy(beacon);
        if (accuracy > .06)
            continue;
 
        //Call out to shared code to check beacon and progress through quest
        ViewModel.CheckBeacon(beacon.Major, beacon.Minor);
      }
    }
    
    public void OnServiceReady()
    {
      if (region != null)
      {
          beaconManager.StopRanging(region);
          region = null;
      }
 
      region = new EstimoteSdk.Region(BeaconId, ViewModel.UUID, new Java.Lang.Integer(ViewModel.Quest.Major), null);
      beaconManager.StartRanging(region);
    }
 
    protected override void OnResume()
    {
        base.OnResume();
        beaconManager.Connect(this);
    }
 
    protected override void OnPause()
    {
        base.OnPause();
        beaconManager.Disconnect();
    }
 
    protected override void OnDestroy()
    {
        // Make sure we disconnect from the Estimote.
        if (beaconsEnabled)
            beaconManager.Disconnect();
        base.OnDestroy();
    }
  }
}

Once you detect a beacon from either platform, you can then check to see if the attendee found the correct beacon and update the user interface. Since we are using Xamarin to create these apps, the same code is shared between our iOS, Android, and Windows Phone applications.

 ///
/// Checks the beacon.
///
///true, if beacon was checked,false otherwise.
///Major number of the beacon.
///Minor number of the beacon.
public void CheckBeacon(int major, int minor)
{
  bool found = true;
  try
  {
      if (quest == null)
          return;
 
      found = false;
 
      if (major != Quest.Major)
          return;
 
      foreach (Beacon beacon in Quest.Beacons)
      {
          if (beacon.Found)
          {
              if (beacon.Minor == minor)
                  found = true;
 
              continue;
          }
 
          if (beacon.Minor == minor)
          {
              beacon.Found = true;
              OnPropertyChanged(QuestsPropertyName); //refresh beacons in UI
              //Check to see if quest is complete to proceed
              CheckEndQuest();
              found = true;
          }
      }
  }
  finally
  {
      if (!found)
          messages.SendToast("So close! Try another beacon.");
  }
}

Completely cross-platform

Creating a great cross-platform mobile application means you must take into consideration the vast number of devices and device capabilities. I wanted to ensure that no matter what device an attendee had - iOS, Android, or Windows Phone - they would be able to participate. Some devices, such as older Android devices, do not have Bluetooth Smart, and on Windows Phone there is no great way to detect iBeacon. In addition to this fragmentation, I wanted to ensure that even if the attendee did have Bluetooth Smart, they should still be able to play if Bluetooth was turned off. This is where QR code scanning comes in as a backup plan so that anyone can play. By embedding the major and minor values as text into a QR code, it will scan and act just like a beacon. There are several great libraries available to do QR code scanning, but my favorite has to be ZXing.NET, which offers a simple API to scan codes view iOS, Android, and Windows Phone.

Successful quest

image

At the end of the adventure, there was a great reward: an exclusive Xamarin Evolve 2014 plush monkey. Completing the Evolve Quest was the only way to win it! In addition to this prize, attendees also learned a lot about Xamarin through trivia questions, tried some awesome new technologies, and even received free candy along the way. The Evolve Quest was a great success, with nearly half of the attendees taking the time to complete the journey. The feedback was also overwhelmingly positive, with everyone reporting that they had an amazing time running around the conference. The Evolve Quest proved for us that you can create a truly fun, engaging, and interactive experience with Estimote Beacons and mobile apps for any occasion. There are also many more places that you can take your C# code, including Android Wear and Google Glass, to take your scavenger hunt to a completely new level.

Build your own scavenger hunt

As the creators and maintainers of the Mono Project, an open source implementation of .NET, we at Xamarin are big believers in the open source community. We have completely open sourced the Evolve Quest mobile application on GitHub, so anyone can create their own interactive scavenger hunt mobile applications across iOS, Android, and Windows Phone. To learn more about Xamarin, and how you can create native mobile apps across iOS, Android, Mac, and Windows from a shared C# code base, head to Xamarin.com to get started. If you’d like to learn more about using iBeacons, be sure to watch the recording of my session from Xamarin Evolve 2014 on iBeacons and Contextual Location Awareness in iOS and Android apps.

James Montemagno, Developer Evangelist at Xamarin

  1. motzcodes-blog reblogged this from estimote
  2. estimote posted this