Location Services in Android (3)- Activity Detection

It was a long break. Can I blame it on the holidays and the many deadlines I had to meet?
Here is the continuation of the Location services in android that I started some weeks ago. This post will be focused on Activity Detection in Android. Please check the first post to meet the requirements needed.

Below is the new buildGoogleAPiClient method.

private void buildGoogleApiClient() {
        mGac = new GoogleApiClient.Builder(this)
                .addApi(ActivityRecognition.API) //add this for Activity recognition
                .addOnConnectionFailedListener(this)
                .build();
    }

We need to include the ACTIVITY RECOGNITION permission in the manifest file. (see snippet below.)

Actvity detection is done as a background process and therefore we will need an INTENT SERVICE that is fired when activities are detected. To get this done, we will create a new IntentService class (say, ActivityDetectionIntentService).
Add the following lines to your manifest file (if it was not added by the Android studio already).

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

public class ActivityDetectionIntentService extends IntentService {
    private static final String TAG = "ActivityDetectionIntentService";
    ArrayList dActivities;
    public ActivityDetectionIntentService() {
        super(TAG);
    }
@Override
    protected void onHandleIntent(Intent intent) {
        ActivityRecognitionResult aar = ActivityRecognitionResult.extractResult(intent);
        ArrayList<DetectedActivity> dActivities = (ArrayList) aar.getProbableActivities();
        Intent newIntent = new Intent("me.larikraun.activity.detection"); //you can change this string
        newIntent.putParcelableArrayListExtra("activities", dActivities);
        Log.d(TAG, "activities sent");
        LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(newIntent);
    }
}

For a quick explanation, we EXTRACTed RESULT from the ACTIVTY RECOGNITION RESULT that was delivered by the intent. The we got the PROBABLE ACTIVITIES from the result. This came as an arraylist of DetectedActivity. Then we pass this list as an extra to our intent which was later sent as BROADCAST.

With another assumption that you have a view already where you can request for these activities and also remove these updates, we will implement two methods that do this respectively.

private void requestActivityUpdate() {
        ActivityRecognition.ActivityRecognitionApi
                .requestActivityUpdates(mGac, 2000, getActivityDetectionPendingIntent())
                .setResultCallback(this);
    }

private void removeActivityUpdate() {
        ActivityRecognition.ActivityRecognitionApi
                .removeActivityUpdates(mGac, getActivityDetectionPendingIntent())
                .setResultCallback(this);
    }

private PendingIntent getActivityDetectionPendingIntent() {
        /*Reuse the PendingIntent if we already have it.*/
        if (mActivityDetectionPendingIntent != null) {
            return mActivityDetectionPendingIntent;
        }
        Intent intent = new Intent(this, ActivityDetectionIntentService.class);
        /* We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
         requestActivityUpdates() and removeActivityUpdates().*/
        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }

The snippet above request for updates at intervals of 2 seconds (2000ms). You can decide the interval based on your needs.

To successfully use the setResultCallback() our activity needs to implement ResultCallback which in turn implements the onResult() method

@Override
    public void onResult(Result result) {
        Toast.makeText(getApplicationContext(), "Result: " + result.getStatus(), Toast.LENGTH_LONG).show();
    }

We need a reciever that will handle the broadcast that sent from the service. Here we go:

public class DetectedActivityIntentReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals("me.larikraun.activity.detection")) {
                ArrayList<DetectedActivity> activities = intent.getParcelableArrayListExtra("activities");
                String activitiesList = "";
                for (DetectedActivity detectedActivity : activities) {
                    activitiesList = activitiesList + getActivityName(detectedActivity.getType()) + " " + detectedActivity.getConfidence() + "\n";
                    detectedActivities.setText(activitiesList);
                }
            } else {
                detectedActivities.setText("Empty");
            }
        }

    }
private String getActivityName(int typeCode) {
        switch (typeCode) {
            case DetectedActivity.IN_VEHICLE:
                return "in vehicle";
            case DetectedActivity.ON_BICYCLE:
                return "on bike";
            case DetectedActivity.ON_FOOT:
                return "on foot";
            case DetectedActivity.STILL:
                return "still";
            case DetectedActivity.UNKNOWN:
                return "unknown";
            case DetectedActivity.TILTING:
                return "tilting";
            case DetectedActivity.WALKING:
                return "walking";
            case DetectedActivity.RUNNING:
                return "running";
            default:
                return "unknown";
        }
    }

Finally, we will register our reciever. See snippet below:
 @Override
    protected void onStop() {
        super.onStop();
        if (mGac.isConnected()) {
            mGac.disconnect();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        mGac.connect();
    }

    @Override
    protected void onPause() {
        super.onPause();
        LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(new DetectedActivityIntentReceiver());
    }

    @Override
    protected void onResume() {
        super.onResume();
        LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(new DetectedActivityIntentReceiver(), new IntentFilter("me.larikraun.activity.detection"));
    }

You can get the full implementation of this here and here on github.