Sunday, August 14, 2016

Google Play Services: Awareness AP_part 2 (end)

3. Using the Snapshot API

When you want to gather information about a user's current context, you can use the snapshot functionality of the Awareness API. This API will gather information depending on the type of API call made, and will cache this information for quick access across various apps.

Headphones
One of the new additions to Play Services through the Awareness API is the ability to detect a device's headphone state (plugged in or unplugged). This can be done by calling getHeadphoneState() on the Awareness API and reading the HeadphoneState from the HeadphoneStateResult.
  1. private void detectHeadphones() {
  2.     Awareness.SnapshotApi.getHeadphoneState(mGoogleApiClient)
  3.             .setResultCallback(new ResultCallback<HeadphoneStateResult>() {
  4.                 @Override
  5.                 public void onResult(@NonNull HeadphoneStateResult headphoneStateResult) {
  6.                     HeadphoneState headphoneState = headphoneStateResult.getHeadphoneState();
  7.                     if (headphoneState.getState() == HeadphoneState.PLUGGED_IN) {
  8.                         Log.e("Tuts+", "Headphones are plugged in.");
  9.                     } else {
  10.                         Log.e("Tuts+", "Headphones are NOT plugged in.");
  11.                     }
  12.                 }
  13.             });
  14. }
Once you know the state of the headphones, your app can perform whatever actions are needed based on that information.

Location

Although previously available as a component in Google Play Services, the location feature of the Awareness API has been optimized for efficiency and battery usage. Rather than using the traditional Location API and receiving a location at specified intervals, you can request a one-time location snapshot like so.
  1. private void detectLocation() {
  2.     if( !checkLocationPermission() ) {
  3.         return;
  4.     }
  5.  
  6.     Awareness.SnapshotApi.getLocation(mGoogleApiClient)
  7.             .setResultCallback(new ResultCallback<LocationResult>() {
  8.                 @Override
  9.                 public void onResult(@NonNull LocationResult locationResult) {
  10.                     Location location = locationResult.getLocation();
  11.  
  12.                     Log.e("Tuts+", "Latitude: " + location.getLatitude() + ", Longitude: " + location.getLongitude());
  13.  
  14.                     Log.e("Tuts+", "Provider: " + location.getProvider() + " time: " + location.getTime());
  15.  
  16.                     if( location.hasAccuracy() ) {
  17.                         Log.e("Tuts+", "Accuracy: " + location.getAccuracy());
  18.                     }
  19.                     if( location.hasAltitude() ) {
  20.                         Log.e("Tuts+", "Altitude: " + location.getAltitude());
  21.                     }
  22.                     if( location.hasBearing() ) {
  23.                         Log.e("Tuts+", "Bearing: " + location.getBearing());
  24.                     }
  25.                     if( location.hasSpeed() ) {
  26.                         Log.e("Tuts+", "Speed: " + location.getSpeed());
  27.                     }
  28.                 }
  29.             });
  30. }
As you can see, you will first need to verify that the user has granted the location permission. If they have, you can retrieve a standard Location object with a large amount of data about the user's location and speed, as well as information on the accuracy of this data.

You will want to verify that a specific piece of information exists before using it, as some data may not be available. Running this code should output all available data to the Android system log.
  1. E/Tuts+: Latitude: 39.9255456, Longitude: -105.02939579999999
  2. E/Tuts+: Provider: Snapshot time: 1468696704662
  3. E/Tuts+: Accuracy: 20.0
  4. E/Tuts+: Altitude: 0.0
  5. E/Tuts+: Speed: 0.0
Places 

While not as robust as the standard Places API, the Awareness API does provide a quick and easy to use way to gather information about places near the user. This API call will return a List of PlaceLikelihood objects that contains a Place and a float representing how likely it is that a user is at that place (hence the object's name).

Each Place object may contain a name, address, phone number, place type, user rating, and other useful information. You can request the nearby places for the user after verifying that the user has the location permission granted.
  1. private void detectNearbyPlaces() {
  2.     if( !checkLocationPermission() ) {
  3.         return;
  4.     }
  5.  
  6.     Awareness.SnapshotApi.getPlaces(mGoogleApiClient)
  7.             .setResultCallback(new ResultCallback<PlacesResult>() {
  8.                 @Override
  9.                 public void onResult(@NonNull PlacesResult placesResult) {
  10.                     Place place;
  11.                     for( PlaceLikelihood placeLikelihood : placesResult.getPlaceLikelihoods() ) {
  12.                         place = placeLikelihood.getPlace();
  13.                         Log.e("Tuts+", place.getName().toString() + "\n" + place.getAddress().toString() );
  14.                         Log.e("Tuts+", "Rating: " + place.getRating() );
  15.                         Log.e("Tuts+", "Likelihood that the user is here: " + placeLikelihood.getLikelihood() * 100 + "%");
  16.                     }
  17.                 }
  18.             });
  19. }
When running the above method, you should see output similar to the following in the Android console. If a value is not available for a number, -1 will be returned.
  1. E/Tuts+: North Side Tavern
  2.          12708 Lowell Blvd, Broomfield, CO 80020, USA
  3. E/Tuts+: Rating: 4.7
  4. E/Tuts+: Likelihood that the user is here: 10.0%
  5. E/Tuts+: Quilt Store
  6.          12710 Lowell Blvd, Broomfield, CO 80020, USA
  7. E/Tuts+: Rating: 4.3
  8. E/Tuts+: Likelihood that the user is here: 10.0%
  9. E/Tuts+: Absolute Floor Care
  10.          3508 W 126th Pl, Broomfield, CO 80020, USA
  11. E/Tuts+: Rating: -1.0
Weather
Another of the new additions to Google Play Services through the Awareness API is the ability to get the weather conditions for a user. This feature also requires the location permission for users on Marshmallow and later.

Using this request, you will be able to get the temperature in the user's area in either Fahrenheit or Celsius. You can also find out what the temperature feels like, the dew point (the temperature where water in the air can begin to condense into dew), the humidity percentage, and the weather conditions.
  1. private void detectWeather() {
  2.     if( !checkLocationPermission() ) {
  3.         return;
  4.     }
  5.  
  6.     Awareness.SnapshotApi.getWeather(mGoogleApiClient)
  7.             .setResultCallback(new ResultCallback<WeatherResult>() {
  8.                 @Override
  9.                 public void onResult(@NonNull WeatherResult weatherResult) {
  10.                     Weather weather = weatherResult.getWeather();
  11.                     Log.e("Tuts+", "Temp: " + weather.getTemperature(Weather.FAHRENHEIT));
  12.                     Log.e("Tuts+", "Feels like: " + weather.getFeelsLikeTemperature(Weather.FAHRENHEIT));
  13.                     Log.e("Tuts+", "Dew point: " + weather.getDewPoint(Weather.FAHRENHEIT));
  14.                     Log.e("Tuts+", "Humidity: " + weather.getHumidity() );
  15.  
  16.                     if( weather.getConditions()[0] == Weather.CONDITION_CLOUDY ) {
  17.                         Log.e("Tuts+", "Looks like there's some clouds out there");
  18.                     }
  19.                 }
  20.             });
  21. }
The above code should output something similar to this.
  1. E/Tuts+: Temp: 88.0
  2. E/Tuts+: Feels like: 88.0
  3. E/Tuts+: Dew point: 50.0
  4. E/Tuts+: Humidity: 28
  5. E/Tuts+: Looks like there's some clouds out there
One important thing to note here is that the weather condition value is stored as an int. The entire list of condition values can be found in the Weather object.
  1. int CONDITION_UNKNOWN = 0;
  2. int CONDITION_CLEAR = 1;
  3. int CONDITION_CLOUDY = 2;
  4. int CONDITION_FOGGY = 3;
  5. int CONDITION_HAZY = 4;
  6. int CONDITION_ICY = 5;
  7. int CONDITION_RAINY = 6;
  8. int CONDITION_SNOWY = 7;
  9. int CONDITION_STORMY = 8;
  10. int CONDITION_WINDY = 9;
Activity
Your user's activity will play a large part in how they interact with their device, and detecting that activity will allow you to provide a more fluid user experience.

For example, if you have a fitness app, you may want to detect when a user is running so you can start recording a Google Fit session, or you may want to send a notification to your user if you detect that they have been still for too many hours during the day.

Using the getDetectedActivity() call in the Awareness API, you can get a list of probable activities and how long the user has been doing each one.
  1. private void detectActivity() {
  2.     Awareness.SnapshotApi.getDetectedActivity(mGoogleApiClient)
  3.             .setResultCallback(new ResultCallback<DetectedActivityResult>() {
  4.                 @Override
  5.                 public void onResult(@NonNull DetectedActivityResult detectedActivityResult) {
  6.                     ActivityRecognitionResult result = detectedActivityResult.getActivityRecognitionResult();
  7.                     Log.e("Tuts+", "time: " + result.getTime());
  8.                     Log.e("Tuts+", "elapsed time: " + result.getElapsedRealtimeMillis());
  9.                     Log.e("Tuts+", "Most likely activity: " + result.getMostProbableActivity().toString());
  10.  
  11.                     for( DetectedActivity activity : result.getProbableActivities() ) {
  12.                         Log.e("Tuts+", "Activity: " + activity.getType() + " Likelihood: " + activity.getConfidence() );
  13.                     }
  14.                 }
  15.             });
  16. }
The above method will display the most likely activity for the user, how long they've been in that state, and the list of all possible activities.
  1. E/Tuts+: time: 1468701845962
  2. E/Tuts+: elapsed time: 15693985
  3. E/Tuts+: Most likely activity: DetectedActivity [type=STILL, confidence=100]
  4. E/Tuts+: Activity: 3 Likelihood: 100
The DetectedActivity type values can be mapped to the following values:
  1. int IN_VEHICLE = 0;
  2. int ON_BICYCLE = 1;
  3. int ON_FOOT = 2;
  4. int STILL = 3;
  5. int UNKNOWN = 4;
  6. int TILTING = 5;
  7. int WALKING = 7;
  8. int RUNNING = 8;
Beacons

The final type of snapshot—and most difficult to set up because it requires a real-world component—involves BLE (Bluetooth Low Energy) beacons. While the Nearby API is beyond the scope of this tutorial, you can initialize beacons for your own Google Services project using Google's Beacon Tools app.

An important thing to note is that once you have registered a beacon to a Google API project, you cannot unregister it without resetting the beacon id. This means if you delete that project, the beacon will need to be reconfigured using your manufacturer's app.  For the Awareness API, the namespace must match the Google project that you are using for your Awareness API calls. The above beacon was already registered to a personal test Google project, hence the different namespace (reflected-disk-355) from that of the sample project associated with this tutorial.

In the above screenshot, you can see one item under Attachments. The namespace for this attachment is reflected-disk-355 (this tutorial's example project's namespace is tutsplusawareness) and the type is nearby. You will need this information for your own beacons in order to detect attachments with the Awareness API.

When you have beacons configured, you can return to your code. You will need to create a List of BeaconState.TypeFilter objects so that your app can filter out beacons and attachments that do not relate to your application.
  1. private static final List BEACON_TYPE_FILTERS = Arrays.asList(
  2.         BeaconState.TypeFilter.with(
  3.              //replace these with your beacon's values
  4.                 "namespace",
  5.                 "type") );
If you have reason to believe that your user is near a beacon, you can request attachments from beacons that fit the filter requirements above. This will require the location permission for users on Marshmallow and later.
  1. private void detectBeacons() {
  2.     if( !checkLocationPermission() ) {
  3.         return;
  4.     }
  5.  
  6.     Awareness.SnapshotApi.getBeaconState(mGoogleApiClient, BEACON_TYPE_FILTERS)
  7.             .setResultCallback(new ResultCallback<BeaconStateResult>() {
  8.                 @Override
  9.                 public void onResult(@NonNull BeaconStateResult beaconStateResult) {
  10.                     if (!beaconStateResult.getStatus().isSuccess()) {
  11.                         Log.e("Test", "Could not get beacon state.");
  12.                         return;
  13.                     }
  14.                     BeaconState beaconState = beaconStateResult.getBeaconState();
  15.                     if( beaconState == null ) {
  16.                         Log.e("Tuts+", "beacon state is null");
  17.                     } else {
  18.                         for(BeaconState.BeaconInfo info : beaconState.getBeaconInfo()) {
  19.                             Log.e("Tuts+", new String(info.getContent()));
  20.                         }
  21.                     }
  22.                 }
  23.             });
  24. }
This code will log out information for the attachment associated with the example beacon above. For this example, I have configured two beacons with the same namespace and type to demonstrate that multiple beacons can be detected at once.
  1. E/Tuts+: Oh hi tuts+
  2. E/Tuts+: Portable Beacon info
4. Using the Fences API

While the Snapshot API can grab information about the user's context at a particular time, the Fences API listens for specific conditions to be met before allowing an action to occur. The Fences API is optimized for efficient battery and data usage, so as to be courteous to your users.

There are five types of conditions that you can check for when creating fences:
  • device conditions, such as user having headphones unplugged or plugged in
  • location, similar to a standard geofence
  • the presence of specific BLE beacons
  • user activity, such as running or driving
  • time
At this time, weather conditions and places do not have support for fences. You can make a fence that uses any of the supported features; however, a really handy feature of this API is that logical operations can be applied to conditions. You can take multiple fences and use and, or, and not operations to combine the conditions to fit your app's needs.

Create a BroadcastReceiver

Before you create your fence, you will need to have a key value representing each fence that your app will listen for. To finish off this tutorial, you will build a fence that detects when a user is sitting at a set location, such as their home.
  1. private final static String KEY_SITTING_AT_HOME = "sitting_at_home";
Once you have a key defined, you can listen for a broadcast Intent that contains that key.
  1. public class FenceBroadcastReceiver extends BroadcastReceiver {
  2.  
  3.     @Override
  4.     public void onReceive(Context context, Intent intent) {
  5.         if(TextUtils.equals(ACTION_FENCE, intent.getAction())) {
  6.             FenceState fenceState = FenceState.extract(intent);
  7.  
  8.             if( TextUtils.equals(KEY_SITTING_AT_HOME, fenceState.getFenceKey() ) ) {
  9.                 if( fenceState.getCurrentState() == FenceState.TRUE ) {
  10.                     Log.e("Tuts+", "You've been sitting at home for too long");
  11.                 }
  12.             }
  13.         }
  14.     }
  15. }
Create Fences

Now that you have a receiver created to listen for user events, it's time to create your fences. The first AwarenessFence you will create will listen for when the user is in a STILL state.
  1. AwarenessFence activityFence = DetectedActivityFence.during(DetectedActivityFence.STILL);
The second fence you will create will wait for the user to be in range of a specific location. While this sample has values for latitude and longitude already set, you will want to change them to match whichever coordinates match your location for testing.
  1. AwarenessFence homeFence = LocationFence.in(39.92, -105.7, 100000, 1000 );
Now that you have two fences, you can combine them to create a third AwarenessFence by using the AwarenessFence.and operation.
  1. AwarenessFence sittingAtHomeFence = AwarenessFence.and(homeFence, activityFence);
Finally, you can create a PendingIntent that will be broadcast to your BroadcastReceiver and add it to the Awareness API using the updateFences method.
  1. Intent intent = new Intent(ACTION_FENCE);
  2. PendingIntent fencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
  3.  
  4. mFenceBroadcastReceiver = new FenceBroadcastReceiver();
  5. registerReceiver(mFenceBroadcastReceiver, new IntentFilter(ACTION_FENCE));
  6.  
  7. FenceUpdateRequest.Builder builder = new FenceUpdateRequest.Builder();
  8. builder.addFence(KEY_SITTING_AT_HOME, sittingAtHomeFence, fencePendingIntent);
  9.  
  10. Awareness.FenceApi.updateFences( mGoogleApiClient, builder.build() );
Now, the app will log a message when the user is sitting down within range of the specified location.

Conclusion

In this tutorial, you have learned about the Awareness API and how to gather current information about the user's environment. You have also learned how to register a listener for changes in the user's context and act when specific conditions have been met.

With this information, you should be able to expand your own apps and provide users with more amazing experiences based on their current location, activity, and other useful values.
Written by Paul Trebilcox-Ruiz

If you found this post interesting, follow and support us.
Suggest for you:

Android Application Programming - Build 20+ Android Apps

The Complete Android Developer Course: Beginner To Advanced!

Android: From Beginner to Paid Professional

The Complete Android Developer Course - Build 14 Apps

Python For Android Hacking Crash Course: Trojan Perspective

No comments:

Post a Comment