Introduction to the Awareness API

Making an application that is aware and reacts to the user's context can be difficult, consume large amounts of memory, and may deplete battery life if not done correctly. To simplify common contextual application needs, Google released the Awareness API. The Awareness API is a unified platform to enable applications to query and react to changing user context such as weather, places, beacons, location, activity, headphone state, and time. The Awareness API not only unifies the implementation of these contextual needs, but does so in a manner that reduces battery and memory usage.

What is it?

As mentioned above, the Awareness API is a platform to simplify the querying and reacting to commonly needed contextual information. The Awareness API allows you to request contextual information on demand while also allowing you to be notified when specific contextual criteria you define has been met. This is accomplished through two distinct APIs that make up the Awareness API.

Snapshot API

The Snapshot API allows you to query for current contextual information on demand. Making a request to the Snapshot API for information, such as the user's current weather condition, is nearly instantaneous. The Snapshot API allows you to request for any of the following contextual information on demand: Location, Weather, Place, Activity, Beacon Information, and Headphone State. Requesting contextual information is simple and easy to use. The details for making these queries will be discussed in greater detail below.

Fences API

The Fences API provides notifications to your application when the user's context changes to meet rules that you define. For instance, you can request that the API notify you anytime the user is within 10 meters of your store or begins an activity such as a run. Furthermore, you can combine contextual rules to limit notifications to when the user is running AND is within 10 meters of your store. The Fences API allows you to define rules regarding Time, Location, Activity, Beacon, and Headphone Status. You can define these rules individually or by combining multiple with "AND" or "OR" Fences, working as their name suggests. Registering a fence is simple and the details for defining them will be discussed in greater detail below.

Why Use the Awareness API?

There are two important reasons to leverage the Awareness API today. The first is just simplicity. The Awareness API makes requesting and reacting to information a breeze. Rather than having to implement multiple libraries or tools to access the various contextual information the Awareness API provides, why not implement one to take care of them all? Secondly, by leveraging the Awareness API you are being responsible with your user's battery and memory. It is not uncommon for a user to delete an application that seemingly makes their device run slow or drains their battery. By implementing the Awareness API, you can reduce your application's footprint, which will hopefully lead to a much lower uninstall rate.

Possible Use Cases

You may not be leveraging contextual information today in your application, which might leave you wondering when and where you could Awareness to use. The possibilities with the Awareness API are endless, but below are a few ideas to get you going:

  • A music application could deliver music suggestions to their user's when the headphone gets plugged in based on the current weather conditions outside.
    • "Maybe your beach playlist will get you through this winter day!"
  • An alarm clock app that has a smart snooze which continues to go off if the device is still motionless at a specific time.
    • "Your device is not moving, get out of bed!"
  • A restaurant application could start a user's favorite order when they get within a specific distance of the restaurant.
    • "We see you are just outside; we will get your order started."
  • A store that reminds the user of a special offer that they showed interest in when they are near your store.
    • "Here is an extra 10% off the hockey stick you showed interest in before!"

In the sample application I demonstrate a few of above use cases. In one instance I listen for when the headphones become plugged in, query for the current weather and present the user with a music option to go with the day. In another example, I create an alert when the user is "still" 1 minute from the time of registering the Fence. The possibilities are endless!


Music Suggestion Awareness APIAwareness Movement Alert

How Do You Use It?

Sample Application

The remaining portion of this blog will go into detail about how to implement the Awareness API in your application. To demonstrate the implementation, please find the attached sample application. The attached sample application demonstrates how to request current weather, location, activity, place, and headphone status on demand via the Snapshot API. Furthermore, it demonstrates how to set up a fences based on the users location, time, headphone status, and activity.

Getting Started

The Awareness API is a part of Google Play Services. To leverage the API be sure that you have the Play Services SDK downloaded. You can do this via your SDK Manager. After you have downloaded the Play Services SDK, you have the choice of including the entire SDK in your application or just the Awareness API. For my purposes, the Awareness API alone is sufficient. To include just the Awareness API, add the following dependency to your build.gradle file.

compile 'com.google.android.gms:play-services-awareness:9.6.1'
Please note that at the time of this writing, the latest Play Services version is 9.6.1

If you have ever leveraged any other Google API, such as Maps, you are familiar with the registration process to receive your API key. The Awareness API is not without exception. For the sake of brevity, I will not detail this process here. To find more information on how to register your application and receive your API key, please go here. Once you have received your API key, you must add the following meta data within the application tags of your application's manifest.

 android:name="com.google.android.awareness.API_KEY"
android:value="YOUR_API_KEY"/>

If you plan on leveraging the Places tool within the Snapshots API, the following meta data is also required.

 android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY"/>

If you plan on leveraging the beacon information in either the Fences API or the Snapshot API, the following meta data is also required.

 android:name="com.google.android.nearby.messages.API_KEY"
android:value="YOUR_API_KEY"/>

Permissions

There is a maximum of two permissions needed to leverage the Awareness API in its entirety. If you plan on utilizing the Weather, Beacon, Places, or Location data via the Snapshot API, the ACCESS_FINE_LOCATION permission is required. The location permission is also required if you desire to setup a Location or Beacon fence via the Fences API. Since the location permission is considered dangerous, you must request this permission at runtime for all devices running Marshmallow or later. On all lower versions, the below permission declaration is sufficient. Please find the attached article for more information regarding properly requesting permissions at runtime. It is important to note that for the Fences API, the location permission must be accepted by the user before a fence can be registered.

  

If you plan on setting up an Activity fence via the Fences API or querying the current Activity via the Snapshot API, the ACTIVITY_RECOGNITION permission is required. Unlike the location permission, the activity permission is not considered dangerous and a manifest declaration is sufficient.

  

Connecting to Play Services

Much like other Play Services API's, the Awareness API requires a connection to the Google Play Services API Client. In the sample application, I demonstrate how to set up a singleton of the GoogleApiClient, however, you may implement your client however you see fit in your Activity/Fragment. To connect and get an instance of the GoogleApiClient, add the following to your Fragment or Activity.

 GoogleApiClient client = new GoogleApiClient.Builder(context)
.addApi(Awareness.API)
.build();
client.connect();

In some cases, it is necessary to know when the connection is complete or when it fails. To add connection callbacks, use the following:

 GoogleApiClient client = new GoogleApiClient.Builder(context)
.addApi(Awareness.API)
.addConnectionCallbacks(myConnectionCallbackInstance)
.build();
client.connect();

Once you have connected to the Google Play Services API you are now ready to use the Awareness API.

Using the Snapshot API

If you need contextual information on demand, the Snapshot API is what you need. It is very important that you ensure that your GoogleApiClient instance is connected, otherwise, any attempt to retrieve data will result in a failure. To retrieve contextual data, such as your current activity simply implement the following:

Awareness.SnapshotApi.getDetectedActivity(mGoogleApiClient)
.setResultCallback(new ResultCallback() {
@Override
public void onResult(@NonNull DetectedActivityResult detectedActivityResult) {
if (!detectedActivityResult.getStatus().isSuccess()) {
//handle failure
} else {
ActivityRecognitionResult ar =
detectedActivityResult.getActivityRecognitionResult();
DetectedActivity probableActivity = ar.getMostProbableActivity();

//Your handling here here.
}
});

In the above example, we are retrieving the user's most probably activity. This activity could vary from walking, running, in a vehicle, and much more. You can find the full list of possible activities here. The Activity portion of the Snapshot API, also returns the likelihood of the detected activity. This likelihood is returned in a range from 0-100 with 100 indicating that it is definite. In my experience, the most probably activity is usually correct, however, you do have the option to query for the entire list of probable activities.

 List probableActivity = ar.getProbableActivities();
At many of my clients, it is necessary to grab a single instance of the user's current location. At times like these, when you don't need to be updated of the user's every move, it seems overkill to create and implement LocationRequests and LocationListeners. The Snapshot API makes retrieving the current location easy and simple without all the overhead of the FusedLocationApi.

 if (isLocationPermissionGranted()) {
Awareness.SnapshotApi.getLocation(mGoogleApiClient)
.setResultCallback(new ResultCallback() {
@Override
public void onResult(@NonNull LocationResult locationResult) {
if (!locationResult.getStatus().isSuccess()) {
//This failed.. add your handling
return;
}
Location location = locationResult.getLocation();
//location.getLatitude() & location.getLongitude());
}
});
} else {
//be sure you get permission!
}

As you can see, the Snapshot API makes grabbing current contextual data quick and easy. Querying for other data such as the Headphone Status, current Place, current Weather, or current Beacon State is just as easy with slightly varying result parameters based on the respective query. In my experience the only query to use caution with is that for the current Weather. This data is seemingly slow to update and based on a region and not their exact location. You may want to use caution before presenting this to your user as the "current weather outside."

Using the Fences API

The Fences API works much like the Snapshot API, but with a few extra steps. The Fences API requires you to register a BroadcastReceiver, which will receive a notification when the context pertaining to your registered Fence changes. In the attached sample application, the BroadcastReceivers being leveraged are global receivers and registered in the applications manifest. This allows my receivers to receive events even when the application is not opened. Your uses may vary slightly and you may only need to register your receiver while a specific Fragment or Activity is open. For my purposes, I am going to demonstrate how to use a global BroadcastReceiver. The Fences API also does not include the ability to define Fences based Place or Weather. In the attached sample application, I demonstrate how to request Weather data via the Snapshot API once a defined Fence has been met as a workaround.

First, you must create your receiver and implement the onReceive() method.

public class HeadphoneBroadcastReceiver extends BroadcastReceiver{

@Override
public void onReceive(Context context, Intent intent) {
if (!TextUtils.equals(FENCE_RECEIVER_ACTION, intent.getAction())) {
return;
}

// The state information for the given fence
FenceState fenceState = FenceState.extract(intent);
if (TextUtils.equals(fenceState.getFenceKey(), HEADPHONE_FENCE_KEY) &&
fenceState.getCurrentState() == FenceState.TRUE) {
//add your handling
}

}

}
Next, let's register our receiver in our manifest. Note that in my implementation above, I have to static constants FENCE_RECEIVER_ACTION and HEADPHONE_FENCE_KEY. The action constant is just to ensure that our receiver only receives broadcasts of intents with that specific action. The latter, HEADPHONE_FENCE_KEY, is used to ensure that when our receiver does receive a callback that it is for the fence that we are looking for. This may become useful if you use one receiver for multiple Fences.







Please note that the action in the above manifest code, corresponds to the action defined in the manifest. Now we are ready to register our Fence.

First, just as we did for the Snapshot API, we must be sure we have an instance of a connected GoogleApiClient. Next, we are ready to create our Fence.

 AwarenessFence headphoneFence = HeadphoneFence.during(HeadphoneState.PLUGGED_IN);
The above fence, will be set to "true" when the headphones become plugged in. We can also go a step further and add other criteria to our Fence. For example, say we wanted to wait and be notified only when the user has plugged in their headphones AND is running, we could do the following.

 //create our primitive fences
AwarenessFence headphoneFence = HeadphoneFence.during(HeadphoneState.PLUGGED_IN);
AwarenessFence runningFence = DetectedActivityFence.starting(DetectedActivityFence.RUNNING);
//create combo fence
AwarenessFence ANDFence = AwarenessFence.and(headphoneFence, runningFence);
A similar Fence could be registered with an OR declaration as well. Combination fences may contain as many primitive Fences that you need. With the above AND Fence, our receiver will not be notified unless our headphones are plugged and we begin a run. Note that the running fence is defined as "starting" indicating that I only want this criterion to be true at the beginning of my run. If I were to plug my headphones in while I was already running, my receiver would never be notified.

Next, we need to create the PendingIntent which will be fired when our Fence Criteria is met in order to deliver the results to my registered BroadcastReceiver. Please be sure to create your intent with the same Action that you defined in your manifest and in your BroadcastReceiver.

 Intent intent = new Intent(FENCE_RECEIVER_ACTION);
PendingIntent mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
Finally, we are ready to register our fence. In doing so, be sure that you register your Fence with the same key that you are looking for in your BroadcastReceiver.

Awareness.FenceApi.updateFences(mApiClient,
new FenceUpdateRequest.Builder().addFence(HEADPHONE_FENCE_KEY, headphoneFence, mPendingIntent).build())
.setResultCallback(new ResultCallback() {
@Override
public void onResult(@NonNull Status status) {
if (status.isSuccess()) {
//your headphone fence was registered successfully
} else {
//your headphone fence was not registered. Try again.
}
}
});

That is all there is to it. Your BroadcastReceiver will now be notified when your headphones are plugged in. Note, that you can and will receive broadcasts for events that are an indication of the context changing but not an indication that your Fence criteria was met. For example, if you register a receiver to receive a notification when the Headphones are plugged in, you will also receive a notification when the headphones are not plugged or when they are being unplugged. Due to this, it is important that you check your FenceState in your receiver to ensure proper handling.


At times, you may need to unregister your Fence to prevent future notifications. To do so, simply ensure you have a connected GoogleApiClient and implement the following code being sure to use the same Fence key you defined your Fence with.

Awareness.FenceApi.updateFences(mApiClient,
new FenceUpdateRequest.Builder().removeFence(HEADPHONE_FENCE_KEY).build())
.setResultCallback(new ResultCallbacks() {
@Override
public void onSuccess(@NonNull Status status) {
//your fence was successfully removed
}

@Override
public void onFailure(@NonNull Status status) {
//your fence is still registerd
}
});

Final Thoughts

The steps above are just a brief overview of what is accomplished in the demo application attached. I encourage you to download and run the code to learn even more about what the Awareness API can do. Though the Awareness API is still in its early days, it shows immediate benefits for including it in your application today. Not only can the Awareness API make your code less tricky, but it can also ensure fast and efficient contextual data whenever you need it. I am excited to present the growth in options to my clients as a result of Awareness which will hopefully result in even more happy users.