Awareness API:  What Is It and How It May Help?

Every year our life and daily routine are more and more closely connected with mobile phones. Modern life is extremely dynamic and that’s why mobile apps should match the users’ activity. Awareness API exists just for the purpose.

What is API in app development and how it may help?

Google Awareness API allows us to monitor the user activity, it’s possible at a certain moment as well as while the app operates.

API allows us to get the data as to the:

  • Headphones state (plugged/unplugged);
  • Location;
  • Activity (stands, drives, runs, walks);
  • Time (local user’s time o set certain frames);
  • Reaction to beacons.

There are plenty of situations, where these features can be used. If it’s a sports app for running, for instance, it’s possible to find out when the user started his training, and if it’s a restaurant app, there is an opportunity to notify the user about special offers or invite him to visit the restaurant when he is somewhere nearby.

Awareness API is divided into two parts, namely:

  • Snapshot API — it allows to get the live info at a certain moment
  • Fence API — it registers one or several triggers, which inform us if the user is now active or not.

To better understand it, please find the example of an app code here: https://github.com/stfalcon-studio/GoogleAwarenessDemo.

To begin we should get an API key, here is a perfect guide from Google - https://developers.google.com/awareness/android-api/get-a-key.

As soon as we’ve got the key, we should add it to the manifest file of your project. It’s like this:

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

Now you have only to add dependency to gradle:

implementation 'com.google.android.gms:play-services-awareness:{last_version}'

Don’t forget about the permissions, you should add the following to the manifest for the purpose:

<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/><uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>

*Don’t also forget about the permissions checking in the app.

That’s all, now you can use the possibilities of Awareness API in full.

Snapshot API

As it was already mentioned Snapshot API informs about the live status.

You should first initialize the SnapshotClient.

al snapshotClient = Awareness.getSnapshotClient(this)

Now we can get data about the user’s activity right at the moment.

Headphones

With a Snapshot API we can find the current state of headphones, whether they are plugged in or unplugged. It will be useful for sports apps development, for instance, to notify the user about his goal achievement, if his headphones are plugged in.

To get to know the headphones state, you should just call the function `getHeadphoneState()` and to get the result to add `addOnSuccessListener`, which will turn us back the `HeadphoneStateResponse`, with the `headphoneState`. Further on `getState` - will return us «1» or «2», which correspond to the status `PLUGGED_IN` or `UNPLUGGED`.

It’s also reasonable to add `addOnFailureListener`, which returns `Exception` if something has gone wrong.

Example:

private fun snapshotCheckHeadphones(){
        snapshotClient.headphoneState
            .addOnSuccessListener {
                snapshotHeadphonesContainer.text = it.headphoneState.toString(this)}.addOnFailureListener {
              handleSnapshotError(snapshotHeadphonesContainer, it)}}

`toString` - this is the expanding feature to make the code look simpler.

fun HeadphoneState.toString(context: Context): String {return context.getString(
        R.string.headphones_state,if(state == HeadphoneState.PLUGGED_IN){
            context.getString(R.string.headphones_state_connect)}else{
            context.getString(
                R.string.headphones_state_disconnected
            )})}

Location

With the help of Snapshot API we can also find out the current user’s location.

The examples of the usage are numerous in this case, if for instance, the app has a list of some places, we can offer the user to visit this or that place if he is interested in it and is somewhere nearby.

To get location you should call `getLocation()` in `OnSuccess` we will get `LocationResponse`, which contains information about the user’s locale. We can get the info with `getLocation()`, which contains latitude, longitude, altitude and so on.

Example:

private fun snapshotGetLocation(){
        snapshotClient.location
            .addOnSuccessListener {
                with(it.location){
                    snapshotLocationContainer.text = getString(
                        R.string.user_location,
                        latitude, longitude, altitude, accuracy
                    )}}.addOnFailureListener {
                handleSnapshotError(snapshotLocationContainer, it)}}

`Location` - it’s a standard class of the Android SDK, so you can check the manual for the detailed description of all the available functions.

Note: don’t forget to add `android permission ACCESS_FINE_LOCATION` to the manifest as well as permission for checking.

User Activity

As it was already mentioned, Awareness API also allows to check the current user activity. It can be extremely useful in the fitness app development or some app with news. When the user does not move he can be offered to read some article.

The realization approach is the same: we need to call `getDetectedActivity` feature, which in case of success will turn us `DetectedActivityResponse` back. From it we can take data about the user activity. We have the following features available: `getMostProbableActivity`, which will return us the most probable activity and `getProbableActivities`, which will return us the list of possible activities sorted from the most probable one.

Activity has two parameters, namely:

  • Activity type — we use `getType()` for getting the type of activity, the example will be below.
  • Probability of the activity — which is got due to the `getConfidence()` feature — it will return int value from 0 to 100.

Example:

snapshotClient.detectedActivity
            .addOnSuccessListener {
                with(it.activityRecognitionResult){
                    snapshotActivityContainer.text = getString(
                        R.string.user_activity,
                        mostProbableActivity.stateString(),
                        mostProbableActivity.confidence
                    )}}.addOnFailureListener {
                handleSnapshotError(snapshotActivityContainer, it)}

`stateString` - it’s the function of extension, which returns the text value of the activity type.

fun DetectedActivity.stateString(): String {return when (type){0->"IN_VEHICLE"1->"ON_BICYCLE"2->"ON_FOOT"3->"STILL"4->"UNKNOWN"5->"TILTING"6,9,10,11,12,13,14,15->type.toString()7->"WALKING"8->"RUNNING"16->"IN_ROAD_VEHICLE"17->"IN_RAIL_VEHICLE"18->"IN_TWO_WHEELER_VEHICLE"19->"IN_FOUR_WHEELER_VEHICLE"else->type.toString()}}

Fence API

Awareness API allows to put certain conditions and «listen» when the activity of the users coincides with it.

We can check:

  • The location of a user and check if he entered a certain area.
  • The user activity when he or she, for instance, has started/continues/stopped to walk, run, drive, etc.
  • Headphones state, when they have been plugged in or unplugged.
  • When the user has entered the area of beacons operation.
  • Time frames, when the user is in a certain timeframe of a certain weekday.

We can also combine several features, using the operators AND, OR or NOT. We can for instance check the following:

  • The user runs with headphones plugged in.
  • The user drives and has entered a certain area.
  • The headphones are plugged in in an evening time,without activity (so the user is likely in bed).

How to start using Fence.

Firstly, to get the information about the fence state change we should create our own BroadcastReceiver.

Like this for example:

inner class FenceReceiver : BroadcastReceiver(){
 
        override fun onReceive(context: Context, intent: Intent){
            val fenceState = FenceState.extract(intent)if(fenceState.fenceKey == FENCE_KEY){
                when (fenceState.currentState){
                    FenceState.TRUE->{// We've entered the fence                    }
                    FenceState.FALSE->{// We're not in the fence}
                    FenceState.UNKNOWN ->{// Something went wrong}}}}}

It will receive all the changes which refer to our registered fences. For instance, we wait till the user plugs the headphones in:

  • We check if the FENCE_KEY, which came coincides with the one we have registered (what’s it and where it’s got from we’ll see below).
  • Then we check the state, if it’s TRUE, then the headphones are plugged in if it’s FALSE, then vice versa and the UNKNOWN state means an error has occurred and the system fails to understand what the headphones’ status is.

Note: don’t forget to register FenceReceiver in `onCreate`

registerReceiver(fenceReceiver, IntentFilter(FENCE_RECEIVER_ACTION))

And to disconnect it at `onDestroy`:

unregisterReceiver(fenceReceiver)

For the system to send us changes, we should create PendingIntent.

val intent = Intent(FENCE_RECEIVER_ACTION)
        val mPendingIntent =
            PendingIntent.getBroadcast(this,0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

Then we should define which activity we are interested in. Let’s take the headphones plugged in and running for example.

val fence = AwarenessFence.and(HeadphoneFence.during(HeadphoneState.PLUGGED_IN), DetectedActivityFence.during(DetectedActivityFence.RUNNING))

Now we gather it all together and register.

fenceClient.updateFences(
            FenceUpdateRequest.Builder().addFence(
                FENCE_KEY,
                fence,
                mPendingIntent
            ).build()).addOnSuccessListener {//Fence created}.addOnFailureListener {// Error while creating}

FENCE_KEY is necessary for us to distinguish various registered fences.

You should also not forget about fence deleting when we have finished working with it. We use `removeFence()` for the purpose, where the necessary FENCE_KEY is rendered as a parameter.

fenceClient.updateFences(
            FenceUpdateRequest.Builder().removeFence(FENCE_KEY).build())

*if it’s necessary, you can also add successListener and failureListener for deleting.

Now knowing how to create and delete fence we can look at what types of fence exist.

Headphone Fence

With headphones everything is alike and we have the following fences:

  • AwarenessFence headphonesPluggedInFence = HeadphoneFence.during(HeadphoneState.PLUGGED_IN;
  • AwarenessFence headphonesUnpluggedFence = HeadphoneFence.during(HeadphoneState.UNPLUGGED;

Location Fence

We can create the so-called «locations» and as soon as the user enters them we’ll know about it. We can check if he has entered/left a certain area or stays in it.

You need to specify the data about the latitude, longitude, and radius of the location (for checking if the user is inside the area, you should also indicate how many milliseconds should a user stay in it).

AwarenessFence inLocationFence = LocationFence.in(
latitude, longitude, radius, timeInMillis); 
AwarenessFence exitingLocationFence = LocationFence.exiting(
latitude, longitude, radius); 
AwarenessFence enteringLocationFence = LocationFence.entering(
latitude, longitude, radius);

Activity Fence

We can also get to know when a user has started, continues or finished a certain activity. The class `DetectedActivityFence`, is used for the purpose, it has the `starting`, `during` and `stopping` features. The type of activity is added to these features as a parameter. Let’s for example create fences for all running states:

AwarenessFence running = DetectedActivityFence.during(DetectedActivity.RUNNING);
 AwarenessFence startRunning = DetectedActivityFence.starting(DetectedActivity.RUNNING);
 AwarenessFence stopRunning = DetectedActivityFence.stopping(DetectedActivity.RUNNING);

Time Fence

We can create fence for a certain period of time or a certain day.

AwarenessFence workingHoursFence = TimeFence.inInterval(startTime, endTime);
AwarenessFence fridayFence = TimeFence.inFridayInterval(
                                      timeZone, startTime, endTime);

That’s all.

We have had an overlook what an API is and how to use Fences and Snapshots. Thanks to them we can create more flexible apps, which facilitate the user’s usage of the application.