How to recognize user activity with Activity Recognition in Android

How to recognize user activity with Activity Recognition in Android

Google Play Services includes features to monitor user activity via the ActivityRecognitionClient. The user activity tracking service is a low-power method of receiving regular updates about what a user is doing. The service periodically monitors local sensor data on the device in short bursts rather than relying on high-power means like web services or GPS.

Using this API, applications will receive updates for one of the following events:

  • IN_VEHICLE. The user is likely driving or riding in a vehicle, such as a car, bus, or train.
  • ON_BICYCLE. The user is likely on a bicycle.
  • ON_FOOT. The user is likely walking or running.
  • STILL. The user, or at least the device, is currently sitting still.
  • TILTING. The device has recently been tilted. This can happen when the device is picked up from rest or an orientation change occurs.
  • UNKNOWN. There is not enough data to determine with significant confidence what the user is currently doing.

When working with ActivityRecognitionClient, an application initiates periodic updates by calling requestActivityUpdates(). The parameters this method takes define the frequency of updates to the application and a PendingIntent that will be used to trigger each event.

Each event includes a list of DetectedActivity instances, which wrap the activity type (one of the options described previously) and the level of confidence the service has in its prediction. The list is sorted by confidence, so the most probable user activity is first.

The first thing you need to open the build.gradle file, and import Play Services.

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

Next, create a new class, name it ActivityRecognizedService, and have it extend Service. When Google Play Services returns the user's activity, it will be sent to this Service. This will allow you to perform your application logic in the background as the user goes about their day.

public class ActivityRecognizedService extends Service {
    public ActivityRecognizedService() {}
    public static String LOCAL_BROADCAST_NAME = "LOCAL_ACT_RECOGNITION";
    public static String LOCAL_BROADCAST_EXTRA = "RESULT";

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if(ActivityRecognitionResult.hasResult(intent)) {
            ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
            handleDetectedActivities( result.getProbableActivities() );
        }

        return super.onStartCommand(intent, flags, startId);
    }

    private void handleDetectedActivities(List<DetectedActivity> probableActivities) {
        StringBuilder str = new StringBuilder();

        for(DetectedActivity activity : probableActivities) {
            switch( activity.getType() ) {
                case DetectedActivity.IN_VEHICLE: {
                    str.append("In Vehicle: " + activity.getConfidence() + "\n");
                    break;
                }
                case DetectedActivity.ON_BICYCLE: {
                    str.append("On Bicycle: " + activity.getConfidence() + "\n");
                    break;
                }
                case DetectedActivity.ON_FOOT: {
                    str.append("On Foot: " + activity.getConfidence() + "\n");
                    break;
                }
                case DetectedActivity.RUNNING: {
                    str.append("Running: " + activity.getConfidence() + "\n");
                    break;
                }
                case DetectedActivity.STILL: {
                    str.append("Still: " + activity.getConfidence() + "\n");
                    break;
                }
                case DetectedActivity.TILTING: {
                    str.append("Tilting: " + activity.getConfidence() + "\n");
                    break;
                }
                case DetectedActivity.WALKING: {
                    str.append("Walking: " + activity.getConfidence() + "\n");
                    break;
                }
                case DetectedActivity.UNKNOWN: {
                    str.append("Unknown: " + activity.getConfidence() + "\n");
                    break;
                }
            }
        }

        Intent intent = new Intent(LOCAL_BROADCAST_NAME);
        intent.putExtra(LOCAL_BROADCAST_EXTRA, str.toString());
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
}

To finish setting up, open AndroidManifest.xml. You need to declare ActivityRecognizedService and include the com.google.android.gms.permission.ACTIVITY_RECOGNITION permission for your application.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="proft.me.activityrecognation">

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

    <application
        android:allowBackup="true"
        android:name="android.support.multidex.MultiDexApplication"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".ActivityRecognizedService" />
    </application>

</manifest>

In order to use Google Play Services, you first need to connect to them. Start by opening MainActivity.java and implement the ConnectionCallbacks and OnConnectionFailedListener interfaces. You also need to create a member variable of type GoogleApiClient to keep a reference to the API client.

public class MainActivity extends AppCompatActivity implements
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    public GoogleApiClient apiClient;
    BroadcastReceiver myReceiver;
    TextView tv;
    int TIMER = 3000; // 3 sec

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv = (TextView) findViewById(R.id.tv);

        apiClient = new GoogleApiClient.Builder(this)
                .addApi(ActivityRecognition.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

        apiClient.connect();

        myReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String message = intent.getStringExtra(ActivityRecognizedService.LOCAL_BROADCAST_EXTRA);
                tv.setText(message);
            }
        };

        // verify Play Services is active and up-to-date
        checkGooglePlayServicesAvailable(this);
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Intent intent = new Intent( this, ActivityRecognizedService.class );
        PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(apiClient, TIMER, pendingIntent);
    }

    @Override
    public void onConnectionSuspended(int i) {}

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {}

    @Override
    protected void onResume() {
        super.onResume();
        LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver, new IntentFilter(ActivityRecognizedService.LOCAL_BROADCAST_NAME));
    }

    @Override
    protected void onPause() {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
        super.onPause();
    }


    public boolean checkGooglePlayServicesAvailable(Activity activity) {
        int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
        GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
        int result = googleAPI.isGooglePlayServicesAvailable(activity);
        if(result != ConnectionResult.SUCCESS) {
            if(googleAPI.isUserResolvableError(result)) {
                googleAPI.getErrorDialog(activity, result, PLAY_SERVICES_RESOLUTION_REQUEST).show();
            }

            return false;
        }

        return true;
    }

}

After implementing the required interfaces for the GoogleApiClient, you can initialize the client and connect to Google Play Services in onCreate() by requesting the ActivityRecognition.API and associating your listeners with the GoogleApiClient instance.

Once the GoogleApiClient instance has connected, onConnected() is called. When this happens, you need to create a PendingIntent that goes to the Service you created earlier, and pass it to the ActivityRecognitionApi. You also need to set an interval for how often the API should check the user's activity. For this sample application, we use a value of 3000, or three seconds, though in an actual application you may want to check less frequently to conserve power.

At this point, your application should attempt to recognize the user's activity every three seconds and send that data to ActivityRecognizedService.

In the onStartCommand() method of ActivityRecognizedService, the first thing you do is validate that the received Intent contains activity recognition data. If it does, then you can extract the ActivityRecognitionResult from the Intent to see what activities your user might be performing. You can retrieve a list of the possible activities by calling getProbableActivities() on the ActivityRecognitionResult object.

For this sample application, you simply build a string with all activities that have been detected and display how confident Google Play Services is that the user is performing that activity by calling getConfidence() on a DetectedActivity instance. If a confidence is 75 or higher, then it's safe to assume that the user is performing that activity.

Result

android_activity_recognition.png
comments powered by Disqus