Codelab de la API de Activity Recognition Transition

1. Introducción

Llevamos teléfonos a todas partes, pero, hasta ahora, a las apps les ha costado ajustar su experiencia al entorno y la actividad en constante cambio de los usuarios.

Para hacerlo en el pasado, los desarrolladores dedicaban tiempo valioso de ingeniería a combinar varios indicadores (ubicación, sensores, etc.) para determinar cuándo comenzaba o terminaba una actividad, como caminar o conducir. Peor aún, cuando las apps verifican de forma independiente y continua si hay cambios en la actividad del usuario, la duración de la batería se ve afectada.

La API de Activity Recognition Transition resuelve estos problemas, ya que proporciona una API simple que realiza todo el procesamiento por ti y te indica lo que realmente te interesa: cuándo cambió la actividad de un usuario. Tu app simplemente se suscribe a una transición en las actividades que te interesan y la API te notifica los cambios.

Por ejemplo, una app de mensajería puede preguntar "avísame cuando el usuario haya entrado o salido de un vehículo" para establecer su estado como ocupado. De manera similar, una app de detección de estacionamiento puede preguntar "avísame cuando el usuario salga de un vehículo y comience a caminar" para guardar la ubicación de estacionamiento del usuario.

En este codelab, aprenderás a usar la API de transición de reconocimiento de actividad para determinar cuándo un usuario inicia o detiene una actividad, como caminar o correr.

Requisitos previos

Conocimientos sobre el desarrollo de Android y cierta familiaridad con las devoluciones de llamada

Qué aprenderás

  • Cómo registrarse para las transiciones de actividad
  • Procesa esos eventos
  • Cómo anular el registro de las transiciones de actividad cuando ya no sean necesarias

Requisitos

  • Android Studio Bumblebee
  • Un dispositivo o emulador de Android

2. Cómo comenzar

Clona el repositorio del proyecto inicial

Para que comiences lo antes posible, preparamos un proyecto inicial sobre el cual puedes seguir creando. Si tienes Git instalado, simplemente puedes ejecutar el comando que se indica abajo. Para comprobarlo, escribe git --version en la terminal o línea de comandos y verifica que se ejecute de forma correcta.

 git clone https://github.com/android/codelab-activity_transitionapi

Si no tienes git, puedes obtener el proyecto como un archivo ZIP:

Importa el proyecto

Inicia Android Studio, selecciona "Open an existing Android Studio project" en la pantalla de bienvenida y abre el directorio del proyecto.

Después de que se cargue el proyecto, es posible que aparezca una alerta que indique que Git no está realizando un seguimiento de todos los cambios locales. Puedes hacer clic en "Ignorar" o en la "X" en la parte superior derecha. (No enviarás ningún cambio al repositorio de Git).

En la esquina superior izquierda de la ventana del proyecto, deberías ver una imagen similar a la que se muestra más abajo si estás en la vista Android. (Si estás en la vista Project, deberás expandir el proyecto para ver lo mismo).

d2363db913d8e5ad.png

Observarás dos íconos de carpeta (base y complete). Cada uno se conoce como "módulo".

Ten en cuenta que Android Studio puede tardar varios segundos la primera vez que compile el proyecto en segundo plano. Durante este período, verás un ícono giratorio en la barra de estado, en la parte inferior de Android Studio:

c9f23d5336be3cfe.png

Te recomendamos que esperes hasta que haya finalizado antes de realizar cambios en el código. De esa manera, Android Studio podrá extraer todos los componentes necesarios.

Además, si recibes el mensaje "Reload for language changes to take effect?", o uno similar, selecciona "Yes".

Comprende el proyecto inicial

Ya está todo listo y configurado para que agregues el reconocimiento de actividad. Utilizaremos el módulo base, que es el punto de partida de este codelab. En otras palabras, agregarás a base el código de cada paso.

El módulo complete se puede usar para verificar tu trabajo o para usarlo como referencia si encuentras algún problema.

Descripción general de los componentes clave:

  • MainActivity: Contiene todo el código necesario para el reconocimiento de actividades.

Configuración del emulador

Si necesitas ayuda para configurar un emulador de Android, consulta el artículo Cómo ejecutar tu app.

Ejecuta el proyecto inicial

Ejecutemos nuestra app.

  • Conecta tu dispositivo Android a la computadora o inicia un emulador.
  • En la barra de herramientas, selecciona la configuración base en el selector desplegable y haz clic en el botón triangular verde (Run) que está a un lado:

a640a291ffaf62ad.png

  • Deberías ver la siguiente aplicación:

f58d4bb92ee77f41.png

  • Ahora, la app no hace nada más que imprimir un mensaje. Ahora agregaremos el reconocimiento de actividad.

Resumen

En este paso, aprendiste lo siguiente:

  • Configuración general del codelab.
  • Los conceptos básicos de nuestra app
  • Cómo implementar tu app

3. Revisa la biblioteca y agrega el permiso al manifiesto

Para usar la API de transición en tu app, debes declarar una dependencia a la API de ubicación y reconocimiento de actividad de Google, y especificar el permiso com.google.android.gms.permission.ACTIVITY_RECOGNITION en el manifiesto de la app.

  1. Busca el comentario TODO: Review play services library required for activity recognition en el archivo build.gradle. En este paso (paso 1), no se requiere ninguna acción. Solo debes revisar la dependencia declarada que necesitamos. Debe tener el siguiente aspecto:
    // TODO: Review play services library required for activity recognition.
    implementation 'com.google.android.gms:play-services-location:19.0.1'
  1. En el módulo base, busca el comentario TODO: Add both activity recognition permissions to the manifest en el objeto AndroidManifest.xml y agrega el siguiente código al elemento <manifest>.
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

Ahora, el código debería ser similar al siguiente:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.example.myapp">
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

  ...
</manifest>

Como puedes ver en los comentarios, debes agregar un segundo permiso para Android 10. Esto es obligatorio para el permiso de tiempo de ejecución que se agregó en la versión 29 de la API.

Eso es todo. Ahora, tu app puede admitir el reconocimiento de actividad. Solo debemos agregar el código para obtenerlo.

Cómo ejecutar una app

Ejecuta tu app desde Android Studio. Debería verse exactamente igual. En realidad, todavía no agregamos ningún código para hacer un seguimiento de las transiciones, pero lo haremos en la siguiente sección.

4. Cómo verificar o solicitar permisos de tiempo de ejecución en Android

Si bien tenemos cobertura para el permiso en el nivel de API 28 y versiones anteriores, debemos admitir permisos de tiempo de ejecución en el nivel de API 29 y versiones posteriores:

  • En MainActivity.java, verificaremos si el usuario tiene Android 10 (29) o una versión posterior y, si es así, verificaremos los permisos de reconocimiento de actividad.
  • Si no se otorgan los permisos, le enviaremos al usuario a una pantalla de presentación (PermissionRationalActivity.java) en la que se explicará por qué la app necesita el permiso y se le permitirá aprobarlo.

Revisa el código que verifica la versión de Android

En el módulo base, busca el comentario TODO: Review check for devices with Android 10 (29+) en MainActivity.java. Deberías ver el siguiente fragmento de código.

Ten en cuenta que, en esta sección, no se requiere ninguna acción.

// TODO: Review check for devices with Android 10 (29+).
private boolean runningQOrLater =
    android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q;

Como se indicó anteriormente, necesitas la aprobación del permiso de tiempo de ejecución android.permission.ACTIVITY_RECOGNITION en Android 10 y versiones posteriores. Usamos esta verificación simple para decidir si debemos verificar los permisos de tiempo de ejecución.

Revisa la verificación de permisos de tiempo de ejecución para el reconocimiento de actividad si es necesario

En el módulo base, busca el comentario TODO: Review permission check for 29+ en MainActivity.java. Deberías ver el siguiente fragmento de código.

Ten en cuenta que, en esta sección, no se requiere ninguna acción.

// TODO: Review permission check for 29+.
if (runningQOrLater) {

   return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
           this,
           Manifest.permission.ACTIVITY_RECOGNITION
   );
} else {
   return true;
}

Usamos la variable que creamos en el paso anterior para ver si necesitamos verificar los permisos de tiempo de ejecución.

Para Q y versiones posteriores, verificamos y mostramos el resultado del permiso de tiempo de ejecución. Esto forma parte de un método más grande llamado activityRecognitionPermissionApproved(), que le informa al desarrollador en una llamada simple si necesitamos solicitar un permiso o no.

Solicita permisos de tiempo de ejecución y habilita o inhabilita las transiciones de reconocimiento de actividad

En el módulo base, busca el comentario TODO: Enable/Disable activity tracking and ask for permissions if needed en MainActivity.java. Agrega el siguiente código después del comentario.

// TODO: Enable/Disable activity tracking and ask for permissions if needed.
if (activityRecognitionPermissionApproved()) {

   if (activityTrackingEnabled) {
      disableActivityTransitions();

   } else {
      enableActivityTransitions();
   }

} else {  
   // Request permission and start activity for result. If the permission is approved, we
   // want to make sure we start activity recognition tracking.
   Intent startIntent = new Intent(this, PermissionRationalActivity.class);
   startActivityForResult(startIntent, 0);

}

Aquí preguntamos si se aprobó el reconocimiento de actividad. Si es así y el reconocimiento de actividad ya está habilitado, lo inhabilitamos. De lo contrario, la habilitamos.

En caso de que no se apruebe el permiso, le enviaremos al usuario a la actividad de la pantalla de presentación que en la que se explica por qué lo necesitamos y le permitiremos que lo habilite.

Revisa el código de solicitud de permiso

En el módulo base, busca el comentario TODO: Review permission request for activity recognition en PermissionRationalActivity.java. Deberías ver el siguiente fragmento de código.

Ten en cuenta que, en esta sección, no se requiere ninguna acción.

// TODO: Review permission request for activity recognition.
ActivityCompat.requestPermissions(
             this,
             new String[]{Manifest.permission.ACTIVITY_RECOGNITION},
             PERMISSION_REQUEST_ACTIVITY_RECOGNITION)

Esta es la parte más importante de la actividad y la que debes revisar. El código activa la solicitud del permiso cuando el usuario lo solicita.

Además, la clase PermissionRationalActivity.java muestra una justificación de por qué el usuario debe aprobar el permiso de reconocimiento de actividad (práctica recomendada). El usuario puede hacer clic en el botón No gracias o en el botón Continuar (que activa el código anterior).

No dudes en revisar el archivo si quieres obtener más información.

5. Registra o anula el registro del receptor para las transiciones de actividad

Antes de configurar el código de reconocimiento de actividades, queremos asegurarnos de que nuestra actividad pueda controlar las acciones de transición que genera el sistema.

Crea un BroadcastReceiver para la transición

En el módulo base, busca el comentario TODO: Create a BroadcastReceiver to listen for activity transitions en MainActivity.java. Pega el siguiente fragmento.

// TODO: Create a BroadcastReceiver to listen for activity transitions.
// The receiver listens for the PendingIntent above that is triggered by the system when an
// activity transition occurs.
mTransitionsReceiver = new TransitionsReceiver();

Registra un BroadcastReceiver para la transición

En el módulo base, busca el comentario TODO: Register a BroadcastReceiver to listen for activity transitions en MainActivity.java. (está en onStart()). Pega el siguiente fragmento.

// TODO: Register a BroadcastReceiver to listen for activity transitions.
registerReceiver(mTransitionsReceiver, new IntentFilter(TRANSITIONS_RECEIVER_ACTION));

Ahora tenemos una forma de recibir actualizaciones cuando se generan las transiciones de actividad a través del PendingIntent.

Cómo anular el registro de BroadcastReceiver

En el módulo base, busca Unregister activity transition receiver when user leaves the app en MainActivity.java. (está en onStop()).Pega el siguiente fragmento.

// TODO: Unregister activity transition receiver when user leaves the app.
unregisterReceiver(mTransitionsReceiver);

Se recomienda anular el registro de un receptor cuando se cierra Activity.

6. Configura transiciones de actividad y solicita actualizaciones

Para comenzar a recibir actualizaciones de transiciones de actividad, debes implementar lo siguiente:

Crea una lista de ActivitiyTransitions para seguir

Para crear el objeto ActivityTransitionRequest, debes crear una lista de objetos ActivityTransition, que representan la transición de la que deseas hacer un seguimiento. Un objeto ActivityTransition incluye los siguientes datos:

  1. Un tipo de actividad, representado por la clase DetectedActivity. La API de Transition admite las siguientes actividades:
  1. Un tipo de transición, representado por la clase ActivityTransition. Los tipos de transición son los siguientes:

En el módulo base, busca el comentario TODO: Add activity transitions to track en MainActivity.java. Agrega el siguiente código después del comentario.

// TODO: Add activity transitions to track.
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.WALKING)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.WALKING)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.STILL)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.STILL)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
        .build());

Este código agrega las transiciones de las que queremos hacer un seguimiento a una lista que antes estaba vacía.

Cómo crear un PendingIntent

Como se indicó anteriormente, necesitamos un PendingIntent si queremos recibir alertas sobre cualquier cambio en nuestra ActivityTransitionRequest, por lo que, antes de configurar nuestra ActivityTransitionRequest, debemos crear un PendingIntent.

En el módulo base, busca el comentario TODO: Initialize PendingIntent that will be triggered when a activity transition occurs en MainActivity.java. Agrega el siguiente código después del comentario.

// TODO: Initialize PendingIntent that will be triggered when a activity transition occurs.
Intent intent = new Intent(TRANSITIONS_RECEIVER_ACTION);
mActivityTransitionsPendingIntent =
        PendingIntent.getBroadcast(MainActivity.this, 0, intent, 0);

Ahora tenemos un PendingIntent que podemos activar cuando se produce una de las ActivityTransition.

Crea una ActivityTransitionRequest y solicita actualizaciones

Para crear un objeto ActivityTransitionRequest, pasa la lista de ActivityTransitions a la clase ActivityTransitionRequest.

En el módulo base, busca Create request and listen for activity changes en MainActivity.java. Agrega el siguiente código después del comentario.

// TODO: Create request and listen for activity changes.
ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);

// Register for Transitions Updates.
Task<Void> task =
        ActivityRecognition.getClient(this)
                .requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);


task.addOnSuccessListener(
        new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
                activityTrackingEnabled = true;
                printToScreen("Transitions Api was successfully registered.");

            }
        });
task.addOnFailureListener(
        new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions Api could NOT be registered: " + e);
                Log.e(TAG, "Transitions Api could NOT be registered: " + e);

            }
        });

Revisemos el código. Primero, creamos una ActivityTransitionRequest a partir de nuestra lista de transiciones de actividad.

ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);

A continuación, nos registramos para las actualizaciones de transición de actividad pasando tu instancia de ActivityTransitionRequest y nuestro objeto PendingIntent que creamos en el último paso al método requestActivityTransitionUpdates(). El método requestActivityTransitionUpdates() muestra un objeto Task que puedes verificar si es correcto o no, como se muestra en el siguiente bloque de código:

// Register for Transitions Updates.
Task<Void> task =
        ActivityRecognition.getClient(this)
                .requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);


task.addOnSuccessListener(
        new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
                activityTrackingEnabled = true;
                printToScreen("Transitions Api was successfully registered.");

            }
        });
task.addOnFailureListener(
        new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions Api could NOT be registered: " + e);
                Log.e(TAG, "Transitions Api could NOT be registered: " + e);

            }
        });

Después de registrarte correctamente para las actualizaciones de transición de actividad, tu app recibirá notificaciones en el PendingIntent registrado. También configuramos una variable que habilita el seguimiento de actividad para permitirnos saber si debemos inhabilitar o habilitar el seguimiento si el usuario vuelve a hacer clic en el botón.

Cómo quitar actualizaciones cuando se cierra la app

Es importante quitar las actualizaciones de transición cuando se cierra la app.

En el módulo base, busca Stop listening for activity changes en MainActivity.java. Agrega el siguiente código después del comentario.

// TODO: Stop listening for activity changes.
ActivityRecognition.getClient(this).removeActivityTransitionUpdates(mActivityTransitionsPendingIntent)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                activityTrackingEnabled = false;
                printToScreen("Transitions successfully unregistered.");
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions could not be unregistered: " + e);
                Log.e(TAG,"Transitions could not be unregistered: " + e);
            }
        });

Ahora, debemos llamar al método que contiene el código anterior cuando se cierre la app.

En el módulo base, busca el comentario TODO: Disable activity transitions when user leaves the app en MainActivity.java en onPause(). Agrega el siguiente código después del comentario.

// TODO: Disable activity transitions when user leaves the app.
if (activityTrackingEnabled) {
    disableActivityTransitions();
}

Eso es todo para hacer un seguimiento de los cambios en las transiciones de actividad. Ahora solo debemos procesar las actualizaciones.

7. Procesa eventos

Cuando se produce la transición de actividad solicitada, tu app recibe una devolución de llamada de Intent. Se puede extraer un objeto ActivityTransitionResult del intent, que incluye una lista de objetos ActivityTransitionEvent. Los eventos se ordenan de forma cronológica; por ejemplo, si una app solicita el tipo de actividad IN_VEHICLE en las transiciones ACTIVITY_TRANSITION_ENTER y ACTIVITY_TRANSITION_EXIT, recibe un objeto ActivityTransitionEvent cuando el usuario comienza a conducir y otro cuando el usuario realiza una transición a cualquier otra actividad.

Agreguemos el código para controlar esos eventos.

En el módulo base, busca el comentario TODO: Extract activity transition information from listener en MainActivity.java en onReceive()del BroadcastReceiver que creamos antes. Agrega el siguiente código después del comentario.

// TODO: Extract activity transition information from listener.
if (ActivityTransitionResult.hasResult(intent)) {

    ActivityTransitionResult result = ActivityTransitionResult.extractResult(intent);

    for (ActivityTransitionEvent event : result.getTransitionEvents()) {

        String info = "Transition: " + toActivityString(event.getActivityType()) +
                " (" + toTransitionType(event.getTransitionType()) + ")" + "   " +
                new SimpleDateFormat("HH:mm:ss", Locale.US).format(new Date());

        printToScreen(info);
    }
}

Esto convertirá la información en un String y la imprimirá en la pantalla.

Eso es todo. ¡Terminaste! Prueba ejecutar la app.

NOTA IMPORTANTE: Es difícil reproducir los cambios de actividad en el emulador, por lo que te recomendamos que uses un dispositivo físico.

Deberías poder hacer un seguimiento de los cambios de actividad.

Para obtener mejores resultados, instala la app en un dispositivo físico y camina. :)

8. Revisa el código

Compilaste una app simple que realiza un seguimiento de las transiciones de actividad y las muestra en la pantalla.

No dudes en leer el código completo para repasar lo que hiciste y obtener una mejor idea de cómo funciona el código.