Search code examples
androidgoogle-play-servicesactivity-recognition

Activity Recognition API


Anyone have trouble with the Activity Recognition API in the recent Google Play Services update?

I have it implemented in an app. It was working perfectly fine before the 5.0 update. Now it returns IN_VEHICLE when the user is walking or sitting still. :/

And doesn't return WALKING, RUNNING or ON_FOOT at all.

Were there any changes to the Activity Recognition API I should be aware of?

Let me know if you need any more details.


Solution

  • The WALKING and RUNNING activities come in as secondary activities in a list (ActivityRecognitionResult.getProbableActivities()), and you'll need to parse them out.

    // Get the update
    ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
    
    // Get the most probable activity from the list of activities in the update
    DetectedActivity mostProbableActivity = result.getMostProbableActivity();
    
    // Get the type of activity
    int activityType = mostProbableActivity.getType();
    
    if (activityType == DetectedActivity.ON_FOOT) {
        DetectedActivity betterActivity = walkingOrRunning(result.getProbableActivities());
        if (null != betterActivity)
            mostProbableActivity = betterActivity;
    }
    
    private DetectedActivity walkingOrRunning(List<DetectedActivity> probableActivities) {
        DetectedActivity myActivity = null;
        int confidence = 0;
        for (DetectedActivity activity : probableActivities) {
            if (activity.getType() != DetectedActivity.RUNNING && activity.getType() != DetectedActivity.WALKING)
                continue;
    
            if (activity.getConfidence() > confidence)
                myActivity = activity;
        }
    
        return myActivity;
    }
    

    I tested the above code this evening, both walking and running and it seemed to do fairly well. If you don't explicitly filter on only RUNNING or WALKING, you will likely get erroneous results.

    Below is a full method for handling new activity results. I pulled this straight out of the sample app, and have been testing it for a couple of days with good results.

    /**
     * Called when a new activity detection update is available.
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "onHandleIntent");
    
        // Get a handle to the repository
        mPrefs = getApplicationContext().getSharedPreferences(
                Constants.SHARED_PREFERENCES, Context.MODE_PRIVATE);
    
        // Get a date formatter, and catch errors in the returned timestamp
        try {
            mDateFormat = (SimpleDateFormat) DateFormat.getDateTimeInstance();
        } catch (Exception e) {
            Log.e(TAG, getString(R.string.date_format_error));
        }
    
        // Format the timestamp according to the pattern, then localize the pattern
        mDateFormat.applyPattern(DATE_FORMAT_PATTERN);
        mDateFormat.applyLocalizedPattern(mDateFormat.toLocalizedPattern());
    
        // If the intent contains an update
        if (ActivityRecognitionResult.hasResult(intent)) {
    
            // Get the update
            ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
    
            // Log the update
            logActivityRecognitionResult(result);
    
            // Get the most probable activity from the list of activities in the update
            DetectedActivity mostProbableActivity = result.getMostProbableActivity();
    
            // Get the confidence percentage for the most probable activity
            int confidence = mostProbableActivity.getConfidence();
    
            // Get the type of activity
            int activityType = mostProbableActivity.getType();
            mostProbableActivity.getVersionCode();
    
            Log.d(TAG, "acitivty: " + getNameFromType(activityType));
    
            if (confidence >= 50) {
                String mode = getNameFromType(activityType);
    
                if (activityType == DetectedActivity.ON_FOOT) {
                    DetectedActivity betterActivity = walkingOrRunning(result.getProbableActivities());
    
                    if (null != betterActivity)
                        mode = getNameFromType(betterActivity.getType());
                }
    
                sendNotification(mode);
            }
        }
    }
    
    private DetectedActivity walkingOrRunning(List<DetectedActivity> probableActivities) {
        DetectedActivity myActivity = null;
        int confidence = 0;
        for (DetectedActivity activity : probableActivities) {
            if (activity.getType() != DetectedActivity.RUNNING && activity.getType() != DetectedActivity.WALKING)
                continue;
    
            if (activity.getConfidence() > confidence)
                myActivity = activity;
        }
    
        return myActivity;
    }
    
    /**
     * Map detected activity types to strings
     *
     * @param activityType The detected activity type
     * @return A user-readable name for the type
     */
    private String getNameFromType(int activityType) {
        switch (activityType) {
            case DetectedActivity.IN_VEHICLE:
                return "in_vehicle";
            case DetectedActivity.ON_BICYCLE:
                return RIDE;
            case DetectedActivity.RUNNING:
                return RUN;
            case DetectedActivity.WALKING:
                return "walking";
            case DetectedActivity.ON_FOOT:
                return "on_foot";
            case DetectedActivity.STILL:
                return "still";
            case DetectedActivity.TILTING:
                return "tilting";
            default:
                return "unknown";
        }
    }