Phones are our most personal devices we bring with us everywhere, but until now it's been hard for apps to adjust their experience to a user's continually changing environment and activity. We've heard from developer after developer that they're spending valuable engineering time to combine various signals like location and sensor data just to determine when the user has started or ended an activity like walking or driving. Even worse, when apps are independently and continuously checking for changes in user activity, battery life suffers.

Activity Recognition Transition API helps in solving these problems by providing a simple API that does all the processing for you and just tells you what you actually care about: when a user's activity has changed. Your app subscribes to a transition in activities of interest and the API notifies your app only when needed.

As an example, a messaging app can ask - tell me when the user has entered or exited the vehicle to set the user's status to busy. Similarly, a parking detection app can ask - tell me when the user has exited a vehicle and started walking.

To use the Transition API in your app, you must declare a dependency to the Google Location and Activity Recognition API version 12.0.0 or higher and specify the com.google.android.gms.permission.ACTIVITY_RECOGNITION permission in the app manifest.

  1. To declare a dependency to the API, add a reference to the Google maven repository and add an implementation entry to com.google.android.gms:play-services-location:12.0.0 to the dependencies section of your app build.gradle file. For more information, see Set Up Google Play Services.
  2. To specify the com.google.android.gms.permission.ACTIVITY_RECOGNITION permission, add a <uses-permission> element in the app manifest, as shown in the following example:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.example.myapp">

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

To start receiving notifications about activity transitions, you must implement the following:

To create the ActivityTransitionRequest object, you must create a list of ActivityTransition objects, which represent the transition that you want to receive notifications about. An ActivityTransition object includes the following data:

An activity type, represented by the DetectedActivity class. The Transition API supports the following activities:

An transition type, represented by the ActivityTransition class. The transition types are:

The following code shows how to create a list of ActivityTransition objects:

List<ActivityTransition> transitions = new ArrayList<>();

transitions.add(
  new ActivityTransition.Builder()
    .setActivityType(DetectedActivity.WALKING)
    .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
    .build());

transitions.add(
  new ActivityTransition.Builder()
    .setActivityType(DetectedActivity.WALKING)
    .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
    .build());

transitions.add(
  new ActivityTransition.Builder()
    .setActivityType(DetectedActivity.STILL)
    .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
    .build());

transitions.add(
  new ActivityTransition.Builder()
    .setActivityType(DetectedActivity.STILL)
    .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
    .build());

You can create an ActivityTransitionRequest object by passing the list of ActivityTransitions to the ActivityTransitionRequest class, as shown in the following example:

ActivityTransitionRequest request = new ActivityTransitionRequest(transitions);

You can register for activity transition updates by passing your instance of ActivityTransitionRequest and your PendingIntent object to therequestActivityTransitionUpdates() method. The requestActivityTransitionUpdates() method returns a Task object that you can check for success or failure, as shown in the following code example:

// myPendingIntent is the instance of PendingIntent where the app receives callbacks.
Task<Void> task =
  ActivityRecognition.getClient(context).requestActivityTransitionUpdates(request, myPendingIntent);

  task.addOnSuccessListener(
    new OnSuccessListener<Void>() {
      @Override
      public void onSuccess(Void result) {
        // Handle success
      }
    }
  );

  task.addOnFailureListener(
    new OnFailureListener() {
      @Override
      public void onFailure(Exception e) {
        // Handle error
      }
    }
  );

After successfully registering for activity transition updates, your app receives notifications in the registered PendingIntent.

When the requested activity transition occurs, you app receives an Intent callback. An ActivityTransitionResult object can be extracted from the Intent, which includes a list of ActivityTransitionEvent objects. The events are ordered in chronological order, for example, if an app requests for the IN_VEHICLE activity type on the ACTIVITY_TRANSITION_ENTER and ACTIVITY_TRANSITION_EXIT transitions, then it receives an ActivityTransitionEvent object when the user starts driving, and another one when the user transitions to any other activity.

You can implement your callback by creating a subclass of BroadcastReceiver and implementing the onReceive() method to get the list of activity transition events. For more information, see Broadcasts. The following example shows how to implement the onReceive() method:

@Override
protected void onReceive(Context context, Intent intent) {
  if (ActivityTransitionResult.hasResult(intent)) {
    ActivityTransitionResult result = ActivityTransitionResult.extractResult(intent);
    for (ActivityTransitionEvent event : result.getTransitionEvents()) {
        // chronological sequence of events....
    }
  }
}

You can deregister for activity transition updates by calling the removeActivityTransitionUpdates() method of the ActivityRecognitionClient and passing your PendingIntent object as a parameter, as shown in the following example:

// myPendingIntent is the instance of PendingIntent where the app receives callbacks.
Task<Void> task =
  ActivityRecognition.getClient(context).removeActivityTransitionUpdates(myPendingIntent);

task.addOnSuccessListener(
  new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void result) {
      myPendingIntent.cancel();
    }
  });

task.addOnFailureListener(
  new OnFailureListener() {
    @Override
    public void onFailure(Exception e) {
      Log.e("MYCOMPONENT", e.getMessage());
    }
  });

Today we will build a Sample app that would register for Activity transitions and list them on a Console activity when a transition occurs. For the purposes of this code lab we will only register for STILL and WALKING activities -- They are easy to simulate. In practice your app can register for any number of activities that are supported

Now you're ready to open the starter project.

  1. Download and Unzip the BasicActivityTransitionsCodelab.zip from the Git repo
  2. Select the android_studio_folder.pngBasicActivityRecognitionTransitionSample directory from your sample code download (File > Open... > ActivityTransitionsCodelab).
  3. Add the Google Play Services dependency to the app (For the code lab it is included in the build.gradle

implementation 'com.google.android.gms:play-services-location:12.0.0')

  1. Enable USB debugging on your Android device.
  2. Click the gradlesync.pngGradle sync button.
  3. Click the execute.pngRun button.

You should be able to see the Sample App activity with the cold start last activity transition reported by the device, Pic1 (this is the last activity that the device has transition). In practice it could be any last known activity. Pic 2 shows the activity when the user has started walking, and then became still again.

Pic 1 Pic 2

Walkthrough of code

Open the MainActivity.java and navigate through the following methods

As covered in the earlier sections -

Set up a BroadcastReceiver for receiving the activity updates. This is the PendingIntent that will receive the callbacks when the requested activity transtitions are detected.

    /**
     * A basic BroadcastReceiver to handle intents from from the Transitions API.
     */
    public class TransitionsReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            // Handle the Activity Transition Response
            if (!TextUtils.equals(TRANSITIONS_RECEIVER_ACTION, intent.getAction())) {
                mLogFragment.getLogView()
                        .println("Received an unsupported action in TransitionsReceiver: action="
                                + intent.getAction());
                return;
            }
            if (ActivityTransitionResult.hasResult(intent)) {
                ActivityTransitionResult result = ActivityTransitionResult.extractResult(intent);
                for (ActivityTransitionEvent event : result.getTransitionEvents()) {
                    String activity = toActivityString(event.getActivityType());
                    String transitionType = toTransitionType(event.getTransitionType());
                    mLogFragment.getLogView()
                            .println("Transition: "
                                    + activity + " (" + transitionType + ")" + "   "
                                    + new SimpleDateFormat("HH:mm:ss", Locale.US)
                                    .format(new Date()));
                }
            }
        }
    }

Build an instance of ActivityTransitionRequest and use the requestActivityTransitionUpdates API. See the highlighted sections.

   

    /**
     * Sets up {@link ActivityTransitionRequest}'s for the sample app, and registers callbacks for 
     * them with a custom {@link BroadcastReceiver}
     */
    private void setupActivityTransitions() {
        List<ActivityTransition> transitions = new ArrayList<>();
        transitions.add(
                new ActivityTransition.Builder()
                        .setActivityType(DetectedActivity.WALKING)
                        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
                        .build());
        transitions.add(
                new ActivityTransition.Builder()
                        .setActivityType(DetectedActivity.WALKING)
                        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
                        .build());
        transitions.add(
                new ActivityTransition.Builder()
                        .setActivityType(DetectedActivity.STILL)
                        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
                        .build());
        transitions.add(
                new ActivityTransition.Builder()
                        .setActivityType(DetectedActivity.STILL)
                        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
                        .build());
        ActivityTransitionRequest request = new ActivityTransitionRequest(transitions);

        // Register for Transitions Updates.
        Task<Void> task =
                ActivityRecognition.getClient(this)
                        .requestActivityTransitionUpdates(request, mPendingIntent);
        task.addOnSuccessListener(
                new OnSuccessListener<Void>() {
                    @Override
                    public void onSuccess(Void result) {
                        Log.i(TAG, "Transitions Api was successfully registered.");
                    }
                });
        task.addOnFailureListener(
                new OnFailureListener() {
                    @Override
                    public void onFailure(Exception e) {
                        Log.e(TAG, "Transitions Api could not be registered: " + e);
                    }
                });
    }

Remove the Activity Transitions updates: In the sample we register for the updates in foreground, and remove when the user is leaving the activity. Use removeActivityTransitionUpdates API to unregister for transitions.

    @Override
    protected void onPause() {
        // Unregister the transitions:
        ActivityRecognition.getClient(this).removeActivityTransitionUpdates(mPendingIntent)
                .addOnSuccessListener(new OnSuccessListener<Void>() {
                    @Override
                    public void onSuccess(Void aVoid) {
                        Log.i(TAG, "Transitions successfully unregistered.");
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        Log.e(TAG, "Transitions could not be unregistered: " + e);
                    }
                });

        super.onPause();
    }