Interacting with Beacons with the Awareness API

Peter-John Welcome
AndroidPub
Published in
5 min readFeb 17, 2017

--

https://tinyurl.com/zn5orly

Continuing from my previous post on interacting with beacons, I will be showing how we can use the Awareness API to interact with beacons with very little effort.

Awareness API

The Awareness API is a really powerful platform that allows us as developers to create contextual awareness apps without doing all the hard work. This platform has 7 different signals it can provide us with and one of them is for beacons. This platform also consists of two APIs which are the: Fence API and Snapshot API.

For this post I will be focusing on the Snapshot API and how we can use it to get information from our beacon.

Getting Started

Our first form of action will be to add the gradle dependency to our build.gradle file.

compile 'com.google.android.gms:play-services-awareness:10.0.1'

Once we have done that, we will need to go to the Google Developer Console and enable the Awareness API. While we are on the developer console, we should create an API Key under the project we are enabling the Awareness API for (If we don’t already have one). Within that API Key we should add our apps package and SHA1 fingerprint so that the Google Console can identify our app.

We then need to grab the generated key as we need to add this to our Manifest.

<meta-data
android:name="com.google.android.awareness.API_KEY"
android:value="<Your_API_Key>"
/>

Awareness API Magic

With most of the Google APIs, we need to do some GoogleAPIClient setup and this is no different for the Awareness API.

private synchronized void buildGoogleApiClient() {
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Awareness.API)
.addConnectionCallbacks(this)
.enableAutoManage(this, this)
.build();
mGoogleApiClient.connect();
}
}

For this example, I’ll be using the attachment messages I set up on the Google Beacon Dashboard shown in my previous post.

We as developers once again are stuck with a situation where we want to interact with only our beacon. We will need to filter out our attachments once again so that we can get the right message for the right beacon.

private List<BeaconState.TypeFilter> setupAttachements() {
return Arrays.asList(
BeaconState.TypeFilter.with(
getString(R.string.Namespace), getString(R.string.Type))
);
}

The code above shows us how we can filter out the attachments that we are expecting from our beacon. As it’s a list of TypeFilter, we can add more than one TypeFilter if we want to interact with more than one beacon.

Once we know what attachments we want back, we can now use the Snapshot API to get the information back from the beacon. This is just one line of code with a callback attached to it.

Awareness.SnapshotApi.getBeaconState(mGoogleApiClient, setupAttachements())
.setResultCallback(new ResultCallback<BeaconStateResult>() {
@Override
public void onResult(@NonNull BeaconStateResult beaconStateResult) {
if (!beaconStateResult.getStatus().isSuccess()) {
Log.e(TAG, "Could not get beacon state.");
return;
}
BeaconState beaconState = beaconStateResult.getBeaconState();
List<BeaconState.BeaconInfo> beaconInfos = beaconState.getBeaconInfo();
for (BeaconState.BeaconInfo beacon : beaconInfos) {
Log.i(TAG, new String(beacon.getContent()));
}
}
});

We call the getBeaconState method and we give it the GoogleAPIClient and the list of TypeFilter. It will then look for any beacon transmitting that attachment that it’s close to.

This will then provide us with information from the attachment we set on the Google Beacon Dashboard.

This information is received through the BeaconState object that is returned to us. If the user’s Bluetooth or location is turned off, this will result in a null pointer exception.

There are two ways for us to solve this problem. We could either wrap the getBeaconInfo method in a null check or we could ask the user to turn on their location and Bluetooth programmatically.

Turn on Bluetooth

Asking the user to turn on their Bluetooth is really simple. Android provides us with an Intent to do this.

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, MY_BLUETOOTH_ENABLE_REQUEST_ID);

Something to not forget is that you need to add a permission to your manifest for this to work.

<uses-permission android:name="android.permission.BLUETOOTH"/>

In the onActivityResult we then handle what happens if the user chooses to turn on their Bluetooth or not.

Turn on Location

Asking the user to turn on their location is a bit more work than asking them to turn on their Bluetooth. There is an intent that does this for us but it takes the user out of your app and into settings to turn on their location. This is not always a good user experience. Luckily there is another way.

We first need to add LocationServicesAPI to our GoogleAPIClient:

private synchronized void buildGoogleApiClient() {
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Awareness.API)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.enableAutoManage(this, this)
.build();
mGoogleApiClient.connect();
}
}

We then need to create a LocationRequest object and set some properties:

protected void createLocationRequest() {

mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}

Once we have our LocationRequest we can user it to build the LocationSettingsRequest.

protected void buildLocationSettingsRequest() {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
mLocationSettingsRequest = builder.build();
}

Finally, we can now make the checkLocationSettings call which will return us a PendingResult. All we need to do is set the result callback that will let us know if the user’s location is on or if we need to show them the dialog to turn it on.

protected void checkLocationSettings() {
PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(
mGoogleApiClient,
mLocationSettingsRequest
);
result.setResultCallback(this);
}

The onResult callback is where we can either start doing location updates or we can ask the user to turn on their location within the app.

@Override
public void onResult(@NonNull LocationSettingsResult locationSettingsResult) {
final Status status = locationSettingsResult.getStatus();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
//start location updates
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
status.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException e) {
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
break;
}
}

We can then determine in onActivityResult if the user allowed for there location to be turned on or not.

It seems like a lot of code for something as simple as turning on the user’s location, but this provides the user with an in app experience, which is always better. You as a developer also won’t have to deal with external changes that are not part of your app experience.

We now have a full solution to using the Snapshot API to retrieve information from our beacons.

This is epic! So many different ways to interact with beacons, with APIs provided by Google and there Google Beacon Platform.

--

--

Peter-John Welcome
AndroidPub

Freelance Senior Mobile Engineer, Google Developer Expert for Firebase, Photographer