Part 2: Background monitoring

In part 1, we’ve done all the heavy-lifting required to set up an Android Studio project with an Estimote SDK. Now it’s time to make use of that and implement our app’s first feature by making it monitor beacons and show a notification with current security wait times when a user enters the airport.

You can download the full source code of the tutorial and follow along.

What’s ahead (aka Table of Contents)

What is beacon monitoring?

You can think of beacon monitoring as a geofence, i.e., a virtual barrier that’s usually defined using a set of geographic coordinates. Moving and out of the area it encloses triggers “enter” and “exit” events, which the app can react to.

In case of iBeacon, the area is defined by the range of one or more beacons. This allows more granularity and precision than regular geofencing—the latter being based on a mix of signals from cell towers, WiFi and GPS. Beacon geofences are also more responsive: “enter” and “exit” events usually take up only 30 seconds to trigger.

Note: With the Estimote Android SDK, you can tweak the responsiveness of monitoring via the BeaconManager#setBackgroundScanPeriod method. By default, monitoring invokes scanning in 25-second intervals. Shortening this pause between scans will increase the responsiveness, but this comes at the expense of draining more of the smartphone’s battery.

Beacon regions

How to define the set of beacons to use as a geofence? That’s what beacon regions are for. Beacon regions are a fundamental concept when working with the Estimote SDK, so pay attention!

Beacon region is like a filter or a regular expression. Each beacon is identified by three values:

  • UUID, most commonly represented as a string, e.g. “B9407F30-F5F8-466E-AFF9-25556B57FE6D”,
  • major number, an unsigned short integer, i.e., an integer ranging from 1 to 65535, (0 is a reserved value)
  • minor number, also an unsigned short integer, like the major number.

With beacon regions, you can say, “I’m only interested in beacons with UUID ‘ABC’ and major ‘XYZ’.” Or you can be more specific, adding a minor requirement to the mix. Or less specific, only requiring a certain UUID. Or just be super vague and skip the UUID too, which then means “all Estimote Beacons.” These are all the options though: UUID + major + minor, UUID + major, UUID alone, or none of it.

Keep in mind: When monitoring a region that spans multiple beacons, there will be a single “enter” event when the first matching beacon is detected; and a single “exit” event when none of the matching beacons can be detected anymore. There’s no way to keep track of “interim” beacons’ “enters” and “exits”—other than creating a single region per each beacon of course.

Add a beacon manager

To enable beacon monitoring, let’s first create ourselves an instance of BeaconManager—the gateway to (almost all) interactions with Estimote Beacons.

We expect the monitoring in our app to work at all times, no matter which activity is currently in use. To achieve that, we’ll instantiate and store our BeaconManager object in a subclass of the Application class. Android documentation says this about the Application: Base class for those who need to maintain global application state. Ha, exactly what we need!

Start by creating a new MyApplication Java Class next to the MainActivity class:

package com.example.airport;

import android.app.Application;
import com.estimote.sdk.BeaconManager;

public class MyApplication extends Application {

    private BeaconManager beaconManager;

    @Override
    public void onCreate() {
        super.onCreate();

        beaconManager = new BeaconManager(getApplicationContext());
     }
}

We also need to declare it in the “AndroidManifest.xml” file:

<application
    <!-- add this: -->
    android:name=".MyApplication"
    <!-- the rest of the file follows -->
    android:allowBackup="true"
    <!-- etc. ... -->

Start monitoring

Next step, let’s create a beacon region defining our monitoring geofence, and use it to start monitoring. For the purposes of this tutorial, we’ll stick to a single beacon, but it’s just as easy to target entire groups of beacons by setting the major and/or minor to null (something we’ll do later with ranging).

Continuing in the MyApplication class:

// this is were we left off:
beaconManager = new BeaconManager(getApplicationContext());
// add this below:
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
    @Override
    public void onServiceReady() {
        beaconManager.startMonitoring(new BeaconRegion(
                "monitored region",
                UUID.fromString("B9407F30-F5F8-466E-AFF9-25556B57FE6D"),
                22504, 48827));
    }
});

As you’re adding this code, replace the example UUID, major, and minor values with those of one of your beacons. The easiest way to go about that is to log in to Estimote Cloud and copy-paste them off your “Beacons” dashboard.

Note: By default, beacons are assigned to the e-mail address provided when placing an order. You can transfer them to another Estimote Account by following this short guide.

Another option is to get the Estimote app and to find your beacon on the radar in the “Devices” section. Tap on it, and it’ll bring you to the “Beacon Details” view, with the beacon’s UUID, major, and minor right there.

Tip: If there’s a lot of beacons nearby, it might be easier to find the one you want using the “List” view instead of the “Radar” view. The list shows the beacons sorted by their approximate distance, so put the beacon you’re interested in next to the device and it should bubble up to the top.

You can switch to the “List” view using the tab bar at the top of the “Beacons in range” screen.

Show an “enter” notification

Time for some real action—let’s add a notification to show up whenever user enters the range of our monitored beacon.

First, let’s add a helper method to the MyApplication class:

public void showNotification(String title, String message) {
    Intent notifyIntent = new Intent(this, MainActivity.class);
    notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivities(this, 0,
            new Intent[] { notifyIntent }, PendingIntent.FLAG_UPDATE_CURRENT);
    Notification notification = new Notification.Builder(this)
            .setSmallIcon(android.R.drawable.ic_dialog_info)
            .setContentTitle(title)
            .setContentText(message)
            .setAutoCancel(true)
            .setContentIntent(pendingIntent)
            .build();
    notification.defaults |= Notification.DEFAULT_SOUND;
    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(1, notification);
}

Now, let’s make use of it. We’ll use a simple static string as a message. If building an actual airport app, we’d fetch all the data dynamically from the airport’s API. It’s the 21st century, surely every airport has one! (-:

// find this line:
beaconManager = new BeaconManager(getApplicationContext());
// add this below:
beaconManager.setMonitoringListener(new BeaconManager.BeaconMonitoringListener() {
    @Override
    public void onEnteredRegion(BeaconRegion region, List<Beacon> beacons) {
        showNotification(
                "Your gate closes in 47 minutes.",
                "Current security wait time is 15 minutes, "
                        + "and it's a 5 minute walk from security to the gate. "
                        + "Looks like you've got plenty of time!");
    }
    @Override
    public void onExitedRegion(BeaconRegion region) {
        // could add an "exit" notification too if you want (-:
    }
});

Test monitoring: “flip to sleep”

Playtime! Run the app on your Android device. If you’re already in range of the beacon, the “enter” event will trigger immediately and you’ll see the notification. Otherwise, it’ll trigger as soon as you enter the range of the beacon.

Now, if we wanted to test the “exit” event as well, or simply re-trigger the “enter” event, ordinarily we would have to move the beacon and the device physically away from each other, and then bring them back together … which is great the first time you to do it, but long-term, constantly walking back and forth to test monitoring doesn’t sound too convenient.

Fortunately, there’s a better option—use the Estimote app to enable the “flip to sleep” mode:

  1. Go to the “Devices” section, find your beacon on the radar or the list and tap on it.
  2. If you’re not yet logged in, you’ll be asked to do it now. (As a security measure, you can only change settings of your own beacons, and logging in with your Estimote Account allows the app to confirm the ownership.)
  3. Tap the “Remaining Battery Life” section.
  4. Find “Flip to Sleep Mode” and enable it.
  5. Now move back all the way to the radar/list view to disconnect from the beacon. (Beacons stop broadcasting when connected to, so disconnecting is an important step!)

Once enabled, you can turn your beacon upside-down to turn it off, which simulates going out of range. Let’s try that: run the app on your device and put the beacon to sleep.

Important: Once the beacon is out of range, it still takes about 30 seconds to truly recognize that fact. This built-in, non-adjustable delay is there to prevent false “exit” events, e.g., when a crowd temporarily obstructs the beacon and it stops being detectable for a few seconds.

Ready? Flip the beacon back, and you should see the notification show up within a few seconds.

Key takeaways

  • Beacon monitoring allows apps to be notified whenever the device enters and exits range of a predefined set of beacons.

  • Beacon region is a “filter” which defines which beacons to scan for. There are 4 sets of possible criteria on Android: UUID + major + minor, UUID + major, UUID only, or none of them (= any Estimote Beacons).

  • Use startMonitoring and stopMonitoring to control monitoring. Events are delivered to onEnteredRegion and onExitedRegion methods of the MonitoringListener.

  • Enabling “flip to sleep” makes it easy to simulate going out of range.

  • “Enter” and “exit” events can take up to 30 seconds to trigger by default. You can tweak this via the setBackgroundScanPeriod method, but keep the battery drainage in mind!

Part 3: Ranging beacons