Compilar una app de Android para Matter

1. Te damos la bienvenida

Matter se creó con el objetivo de unificar los estándares de IoT y conecta dispositivos de casa inteligente en varios ecosistemas, como Google Home, Zigbee, Bluetooth Mesh, Z-Wave y muchos más.

Los dispositivos móviles son un punto de interacción central con los dispositivos de casa inteligente. Si quieres compilar tus propias apps para Android compatibles con dispositivos Matter, podemos ayudarte a comenzar rápidamente.

La app de ejemplo de Google Home para Matter (GHSA para Matter) muestra las APIs del SDK de Home para dispositivos móviles, lo que permite a los usuarios crear y compartir dispositivos. También puedes usar la app de ejemplo como herramienta de aprendizaje a fin de comprender mejor los conceptos clave de Matter, así como para depurar y solucionar problemas de interacción con dispositivos Matter.

Actividades

En este Codelab, descargarás el código fuente de la app de ejemplo y aprenderás a usar el SDK para dispositivos móviles de Home a fin de asignar y compartir dispositivos. También aprenderás a usar las bibliotecas de asignación y Clúster del repositorio de Matter (connectedhomeip).

Después de descargar la app de ejemplo, revisaremos el código fuente en Android Studio y, luego, implementaremos las siguientes APIs del SDK de Home Mobile:

También obtendrás más información sobre los conceptos de asignación, las telas de Matter y cómo controlar los dispositivos Matter.

Requisitos

Antes de comenzar, asegúrate de completar los siguientes pasos:

No necesitas un concentrador (por ejemplo, un Google Nest Hub (2a gen.)) para asignar y controlar dispositivos con la app de ejemplo.

2. Prepárate

El repositorio de GitHub de la app de ejemplo incluye bibliotecas de terceros del repositorio de Matter (connectedhomeip). Estas bibliotecas nativas superan los 50 MB y requieren el uso de Git Large File Storage (LFS).

La app de inicio del codelab se encuentra en la rama codelab. Para comenzar a trabajar con el código fuente del codelab, puedes descargar el archivo ZIP. Este archivo ZIP incluye las bibliotecas nativas del SDK de Matter sin la necesidad de usar LFS de Git:

Usarás este archivo ZIP codelab para compilar una muestra funcional.

Versiones de Codelab

La rama codelab está etiquetada con la versión 1.2.2 de la app de ejemplo. Para comparar las actualizaciones a medida que avanzas en cada paso, puedes descargar el código fuente completo de esta versión.

Si deseas clonar el repositorio de GitHub, sigue las instrucciones en el archivo README de la app de ejemplo.

Dependencias

Te guiaremos a través del código fuente necesario para compartir y poner en marcha los dispositivos, pero puede ser útil tener en cuenta las siguientes dependencias antes de comenzar:

  • SDK de Home Mobile.
    implementation 'com.google.android.gms:play-services-home:16.0.0'
    
  • Bibliotecas de SDK de Matter.
    // Native libs
    implementation fileTree(dir: "third_party/connectedhomeip/libs", include: ["*.jar", "*.so"])
    
  • Material Design. Para obtener más información, consulta MDC-103 de Android: Temas de Material con color, elevación y tipo (Kotlin) y Material Theme Builder.
    implementation 'com.google.android.material:material:1.5.0'
    
  • Proto DataStore, que se usa para conservar los datos de apps. Los repositorios y repositorios de Datastore se almacenan en java/data, incluidos esquemas para dispositivos y preferencias de usuario. Para obtener más información sobre DataStore, consulta Cómo trabajar con Proto DataStore.
    implementation "androidx.datastore:datastore:$dataStoreVersion"
    implementation 'androidx.datastore:datastore-core:1.0.0'
    implementation 'com.google.protobuf:protobuf-javalite:3.18.0'
    
  • Hilt para conservar datos y admitir la inserción de dependencias
    kapt 'com.google.dagger:hilt-compiler:2.41'
    implementation 'com.google.dagger:hilt-android:2.41'
    

Código fuente

La interfaz de usuario y la mayor parte de la funcionalidad ya se crearon para usted.

En este codelab, agregaremos la función Matter a los siguientes archivos:

  • java/commissioning/AppCommissioningService: Te permite asignar dispositivos a la estructura de desarrollo.
  • java/screens/HomeFragment y java/screens/HomeViewModel.kt: Incluyen la funcionalidad de asignación del SDK de Home para dispositivos móviles.
  • java/screens/DeviceViewModel: Incluye las llamadas a la API de Share Device.

Cada archivo se comenta con el bloque de código que usted modificará, por ejemplo:

// CODELAB: add commissioningFunction()

Esto te permite ubicar rápidamente la sección correspondiente en el codelab.

3. Comisión para Google

Para poder controlar los dispositivos y permitirles comunicarse entre sí dentro de la misma estructura, es necesario que un comisionado la encargue, en este caso, esta aplicación de ejemplo, la app de ejemplo de Google Home.

Es importante comprender los siguientes conceptos sobre la asignación de Matter:

  • Las telas permiten que los dispositivos se comuniquen entre sí.
  • Los fabricantes mantienen un conjunto compartido de credenciales únicas.
  • Los ecosistemas son responsables de emitir certificados raíz de confianza y asignar ID de tejido y ID de nodos únicos. Un ecosistema es el servicio de backend de un comisionado, por ejemplo, el Home Graph para el ecosistema de Google Home.
  • Los dispositivos se pueden asignar a más de un tejido (función de varios administradores).

Para cobrar un dispositivo, deberás usar la API de CommissioningClient. Una llamada a .commissionDevice() muestra un IntentSender, que inicia la actividad adecuada en los Servicios de Google Play:

interface CommissioningClient {
  Task<IntentSender> commissionDevice(CommissioningRequest request);
}

En las siguientes secciones, revisaremos el código mínimo necesario para asignar dispositivos a la estructura de Google.

Paso 1: Selector de actividades

Para controlar IntentSender desde CommissioningClient, puedes usar un ActivityResultLauncher:

private lateinit var commissioningLauncher: ActivityResultLauncher<IntentSenderRequest>
commissioningLauncher = registerForActivityResult(
    StartIntentSenderForResult()
) { result: ActivityResult ->
    if (result.resultCode == RESULT_OK) {
        Timber.d(TAG, "Commissioning succeeded.")
    } else {
        Timber.d(TAG, "Commissioning failed. " + result.resultCode)
    }
}

Paso 2: Función de puesta en marcha

Este es un ejemplo básico que usa la API de CommissioningClient para asignar un dispositivo a la estructura de Google.

  1. El proceso de asignación comienza con la función commissionDevice(). Primero, se define un CommissioningRequest. Con esta configuración predeterminada, los dispositivos se encargan solo a la estructura de Android local.
  2. Matter es el punto de entrada del SDK de Home para dispositivos móviles. En la próxima llamada, .getCommissioningClient obtiene un CommissioningClient de this (Actividad).
  3. .commissionDevice() acepta CommissioningRequest.
  4. Por último, se llama a .addOnSuccessListener para procesar CommissioningResult e iniciar la actividad Dispositivo de comisión de los Servicios de Google Play (GPS).
private fun commissionDevice() {
    val request: CommissioningRequest = CommissioningRequest.builder().build()
    Matter.getCommissioningClient(this)
        .commissionDevice(request)
        .addOnSuccessListener { result ->
            commissioningLauncher.launch(IntentSenderRequest.Builder(result).build())
        }
}

Puedes aprovechar Android Fabric local en la configuración de Android para simplificar el proceso de puesta en marcha de sus dispositivos a otras telas.

A continuación, aprenderás cómo asignar un dispositivo a una estructura de desarrollo.

Para obtener una descripción general de la interfaz de usuario durante el proceso de asignación, consulta la Guía de ejemplo de la app de Google Home para Matter.

4. Comisión a una estructura de desarrollo

Los dispositivos se pueden asignar a más de un tejido. Para administrar las vinculaciones de confianza, los dispositivos almacenan un FabricTable que contiene varios miembros de FabricInfo, por ejemplo:

  • Identificación de Fabric
  • ID de nodo asignado por la estructura al dispositivo
  • ID de proveedor
  • ID de Fabric
  • Credenciales operativas del dispositivo

El administrador de dominio administrativo (ADM) define las credenciales de tejido. En la situación anterior, los Servicios de Google Play son el ecosistema que actúa como una autoridad certificadora raíz (CA) de confianza. Cuando se encargan dispositivos a la estructura local de Android, todos los dispositivos incluyen el mismo conjunto de credenciales de tela y el mismo conjunto de CA.

Servicios de encargo personalizados

A fin de asignar la estructura local de Android, usamos los parámetros predeterminados para compilar CommissioningRequest en la API de CommissioningClient:

val request: CommissioningRequest = CommissioningRequest.builder().build()

Si quieres controlar y administrar dispositivos nuevos desde tu app, debes crear una estructura de desarrollo local y obtener las credenciales operativas para asignar dispositivos. En este caso, tu app se convierte en un ecosistema independiente y único que asigna a los dispositivos las credenciales de nodo adecuadas.

Puedes informar a Home Mobile SDK que deseas asignar dispositivos a tu propia red pasando un servicio personalizado a CommissioningRequest:

class CommissioningRequest {
  static CommissioningRequest.Builder builder();

  class Builder {
    Builder setCommissioningService(@Nullable ComponentName commissioningService);

    CommissioningRequest build();
  }
}

En los próximos pasos, modificaremos la función commissionDevice() para usar un servicio personalizado. También agregaremos un selector de actividades al fragmento de la página principal y usaremos objetos LiveData para administrar el flujo de la API.

Paso 1: Crea un selector de actividades de GPS

Primero, crearemos un selector de actividades para controlar IntentSender desde la API de CommissioningClient.

  1. Abre HomeFragment en la carpeta java/screens/home/.
  2. Reemplaza el comentario // CODELAB: commissionDeviceLauncher declaration por la siguiente declaración:
    // The ActivityResult launcher that launches the "commissionDevice" activity in Google Play
    // Services.
    private lateinit var commissionDeviceLauncher: ActivityResultLauncher<IntentSenderRequest>
    
  3. Reemplaza el comentario // CODELAB: commissionDeviceLauncher definition por el siguiente código para registrar y controlar el resultado de la actividad de puesta en marcha:
    commissionDeviceLauncher =
        registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
          // Commission Device Step 5.
          // The Commission Device activity in GPS has completed.
          val resultCode = result.resultCode
          if (resultCode == Activity.RESULT_OK) {
            Timber.d("CommissionDevice: Success")
            // We now need to capture the device information for the app's fabric.
            // Once this completes, a call is made to the viewModel to persist the information
            // about that device in the app.
            showNewDeviceAlertDialog(result)
          } else {
            viewModel.commissionDeviceFailed(resultCode)
          }
        }    ```
    
    

Paso 2: Crea objetos LiveData

La devolución de llamada exitosa de la API de .commissionDevice() proporciona la IntentSender que se usará para iniciar la actividad del dispositivo de comisión en los Servicios de Google Play. En el HomeViewModel, crearemos dos objetos LiveData para informar sobre el resultado de esta llamada a la API:

  • commissionDeviceStatus para realizar un seguimiento de TaskStatus
  • commissionDeviceIntentSender para controlar el resultado de la llamada a .commissionDevice() Este objeto de LiveData iniciará el ActivityLauncher que acabamos de crear y le mostrará al usuario la actividad de dispositivo de comisión de GPS.
  1. En private fun setupObservers(), reemplaza el comentario // CODELAB: commissionDeviceStatus por el siguiente observador:
    // The current status of the share device action.
    viewModel.commissionDeviceStatus.observe(viewLifecycleOwner) { status ->
      Timber.d("commissionDeviceStatus.observe: status [${status}]")
    }
    
  2. Luego, reemplaza el comentario // CODELAB: commissionDeviceIntentSender por el siguiente observador:
    viewModel.commissionDeviceIntentSender.observe(viewLifecycleOwner) { sender ->
      Timber.d(
          "commissionDeviceIntentSender.observe is called with [${intentSenderToString(sender)}]")
      if (sender != null) {
        // Commission Device Step 4: Launch the activity described in the IntentSender that
        // was returned in Step 3 (where the viewModel calls the GPS API to commission
        // the device).
        Timber.d("CommissionDevice: Launch GPS activity to commission device")
        commissionDeviceLauncher.launch(IntentSenderRequest.Builder(sender).build())
        viewModel.consumeCommissionDeviceIntentSender()
      }
    }
    

Paso 3: Llama a la API

Ahora que escribimos el código para controlar el flujo de la API, es momento de llamar a la API, pasar un servicio personalizado (que definiremos en el siguiente paso) y publicar en nuestros objetos LiveData.

  1. Abre HomeViewModel.kt en la carpeta java/screens/home/.
  2. Reemplaza el comentario // CODELAB: commissionDevice por el siguiente commissionDeviceRequest. setCommissioningService vincula AppCommissioningService a una instancia de CommissioningService, que se muestra en una función de devolución de llamada. Cuando pases un servicio personalizado, el SDK para dispositivos móviles de la casa encargará primero los dispositivos a la estructura de Google y, luego, enviará la carga útil de integración a AppCommissioningService.
    fun commissionDevice(context: Context) {
      _commissionDeviceStatus.postValue(TaskStatus.InProgress)
    
      val commissionDeviceRequest =
          CommissioningRequest.builder()
              .setCommissioningService(ComponentName(context, AppCommissioningService::class.java))
              .build()
    
  3. Llama a .getCommissioningClient() y, luego, a .commissionDevice().
    Matter.getCommissioningClient(context)
        .commissionDevice(commissionDeviceRequest)
    

Para completar nuestra función commissionDevice, agrega addOnSuccessListener y addOnFailureListener, y publica en los objetos LiveData:

      .addOnSuccessListener { result ->
        // Communication with fragment is via livedata
        _commissionDeviceIntentSender.postValue(result)
      }
      .addOnFailureListener { error ->
        Timber.e(error)
        _commissionDeviceStatus.postValue(
          TaskStatus.Failed("Failed to to get the IntentSender.", error)
      }
}

Después de usar el remitente, se debe llamar a consumeCommissionDeviceIntentSender() para evitar recibirlo nuevamente después de un cambio de configuración.

  /**
   * Consumes the value in [_commissionDeviceIntentSender] and sets it back to null. Needs to be
   * called to avoid re-processing the IntentSender after a configuration change (where the LiveData
   * is re-posted).
   */
  fun consumeCommissionDeviceIntentSender() {
    _commissionDeviceIntentSender.postValue(null)
  }

5. Crea un servicio de Commissioning

En la función commissionDevice(), solicitamos obtener un CommissioningService de la API de CommissioningClient. En este flujo, la API de CommissioningClient encarga los dispositivos a la estructura local de Android y, luego, muestra una devolución de llamada que incluye el objeto CommissioningRequestMetadata:

public interface CommissioningService {
interface Callback {
    void onCommissioningRequested(CommissioningRequestMetadata metadata);
  }
}

Ahora, debemos heredar CommissioningService.Callback y proporcionar la funcionalidad necesaria para asignar dispositivos a nuestra app de ejemplo. A continuación, se muestra un ejemplo de una implementación básica de CommissioningService:

class MatterCommissioningService : Service(), CommissioningService.Callback {
   private val commissioningServiceDelegate =
     CommissioningService.Builder(this)
       .setCallback(this)
       .build()

   override fun onBind(intent: Intent) = commissioningServiceDelegate.asBinder()

   override fun onCommissioningRequested(metadata: CommissioningRequestMetadata) {
     // perform commissioning

     commissioningServiceDelegate
       .sendCommissioningComplete(CommissioningCompleteMetadata.builder().build())
   }
 }

Paso 1: Explora el AppCommissioningService personalizado

A fin de ayudarte a comenzar, ya definimos la estructura de clase básica para nuestro objeto CommissioningService personalizado. Esta es una descripción general rápida de la funcionalidad del servicio. Para seguir, abre AppCommissioningService en java/commissioning.

Agregamos las siguientes importaciones a las APIs del SDK de Home Mobile:

import com.google.android.gms.home.matter.commissioning.CommissioningCompleteMetadata
import com.google.android.gms.home.matter.commissioning.CommissioningRequestMetadata
import com.google.android.gms.home.matter.commissioning.CommissioningService

AppCommissioningService también incluye bibliotecas del repositorio de Matter (connectedhomeip):

import com.google.home_sample_app_for_matter.chip.ChipClient

Por último, el servicio incluye importaciones compatibles con corrutinas de Hilt y Kotlin.

A continuación, creamos el constructor y configuramos algunos aspectos, incluido el commissioningServiceDelegate, que usaremos para informarle a los Servicios de Google Play cuando se complete la asignación.

private lateinit var commissioningServiceDelegate: CommissioningService
...
commissioningServiceDelegate = CommissioningService.Builder(this).setCallback(this).build()

Ahora es el momento de agregar las funciones de asignación.

Paso 2: Anula onCommissioningRequested

Para asignar dispositivos a la estructura de desarrollo de la app, completa los siguientes pasos:

  1. Abre AppCommissioningService en java/commissioning.
  2. Ubica la función onCommissioningRequested(). Proporcionamos un mensaje de registro que imprime CommissioningRequestMetadata. Reemplaza el comentario // CODELAB: onCommissioningRequested() para iniciar la corrutina serviceScope y obtener deviceId.
    // Perform commissioning on custom fabric for the sample app.
    serviceScope.launch {
      val deviceId = devicesRepository.incrementAndReturnLastDeviceId()
    
  3. Realice la puesta en marcha. En este paso, podemos pasar la información del dispositivo que se muestra en el objeto CommissioningRequestMetadata. ChipClient usa esta información de metadatos para crear un canal seguro entre la app de GHSA para Matter y tu dispositivo.
    Timber.d("Commissioning: App fabric -> ChipClient.establishPaseConnection(): deviceId [${deviceId}]")
    chipClient.awaitEstablishPaseConnection(
        deviceId,
        metadata.networkLocation.ipAddress.hostAddress!!,
        metadata.networkLocation.port,
        metadata.passcode)
    Timber.d("Commissioning: App fabric -> ChipClient.commissionDevice(): deviceId [${deviceId}]")
    chipClient.awaitCommissionDevice(deviceId, null)
    
  4. Usa commissioningServiceDelegate para informar a los Servicios de Google Play que se completó la asignación. En .sendCommissioningComplete(), pasa CommissioningCompleteMetadata.
      Timber.d("Commissioning: Calling commissioningServiceDelegate.sendCommissioningComplete()")
      commissioningServiceDelegate
          .sendCommissioningComplete(
              CommissioningCompleteMetadata.builder().setToken(deviceId.toString()).build())
          .addOnSuccessListener {
              Timber.d("Commissioning: OnSuccess for commissioningServiceDelegate.sendCommissioningComplete()")
          }
          .addOnFailureListener { ex -> 
            Timber.e(ex, "Commissioning: Failed to send commissioning complete.", ex)
          }
    }
    

Ejecuta la app

Ahora que todo el código necesario está en uso para la asignación de nuestra tela local, es momento de probarlo. Elige tu dispositivo Android y ejecuta la app. En la pantalla principal, presiona Agregar dispositivo y completa los pasos que se solicitan para el dispositivo.

Cuando se complete la asignación, tu dispositivo participará en dos tejidos: el de Android local y el de desarrollo local. Cada tela tiene su propio conjunto de credenciales y un ID de tela único de 64 bits.

6. Controla dispositivos

Las comisiones a una estructura de desarrollo te permiten usar las bibliotecas del repositorio de Matter (connectedhomeip) para controlar dispositivos desde la app de ejemplo.

Creamos algunas clases auxiliares para facilitar el acceso a los clústeres del dispositivo y el envío de comandos. Para obtener más información, abre ClustersHelper en java/clusters. Este asistente de singleton importa las siguientes bibliotecas para acceder a la información del dispositivo:

import chip.devicecontroller.ChipClusters
import chip.devicecontroller.ChipStructs

Podemos usar esta clase para obtener el clúster de encendido y apagado de un dispositivo y, luego, llamar a .toggle:

suspend fun toggleDeviceStateOnOffCluster(deviceId: Long, endpoint: Int) {
  Timber.d("toggleDeviceStateOnOffCluster())")
  val connectedDevicePtr =
      try {
        chipClient.getConnectedDevicePointer(deviceId)
      } catch (e: IllegalStateException) {
        Timber.e("Can't get connectedDevicePointer.")
        return
      }
  return suspendCoroutine { continuation ->
    getOnOffClusterForDevice(connectedDevicePtr, endpoint)
        .toggle(
            object : ChipClusters.DefaultClusterCallback {
              override fun onSuccess() {
                continuation.resume(Unit)
              }
              override fun onError(ex: Exception) {
                Timber.e("readOnOffAttribute command failure: $ex")
                continuation.resumeWithException(ex)
              }
            })
  }
}

Activar o desactivar un dispositivo

Después de encargar un dispositivo, la carga útil que se muestra en CommissioningResult se agrega a DataStore. Esto le da a nuestra app acceso a la información del dispositivo que podemos usar para enviar comandos.

Las apps de Matter son controladas por eventos. Cuando se inicializa la pila de Matter, los servicios del clúster detectan los mensajes entrantes. Una vez que se encarga un dispositivo, los clientes de Matter envían comandos a través del canal operativo seguro que se estableció durante la asignación del dispositivo.

En el dispositivo, los paquetes se validan, se desencriptan y, luego, se envían con una devolución de llamada. Las funciones de devolución de llamada incluyen EndpointId, ClusterId y AttributeId, a las que se puede acceder desde attributePath. Por ejemplo, este código se puede implementar en un dispositivo Matter:

void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t mask, uint8_t type,
                                       uint16_t size, uint8_t * value)
{
    // handle callback
    ClusterId clusterId     = attributePath.mClusterId;
    AttributeId attributeId = attributePath.mAttributeId;
}

En los próximos pasos, usarás el SDK de Matter y ClustersHelper para activar o desactivar un dispositivo.

  1. Ve a DeviceViewModel en java/screens/device.
  2. Ubica la función updateDeviceStateOn.
  3. Reemplaza el comentario // CODELAB: toggle por el código para llamar a clustersHelper y, luego, actualiza el repositorio del dispositivo:
    Timber.d("Handling real device")
        try {
          clustersHelper.setOnOffDeviceStateOnOffCluster(deviceUiModel.device.deviceId, isOn, 1)
          devicesStateRepository.updateDeviceState(deviceUiModel.device.deviceId, true, isOn)
        } catch (e: Throwable) {
          Timber.e("Failed setting on/off state")
        }
    

Se llama a esta función desde DeviceFragment:

// Change the on/off state of the device
binding.onoffSwitch.setOnClickListener {
  val isOn = binding.onoffSwitch.isChecked
  viewModel.updateDeviceStateOn(selectedDeviceViewModel.selectedDeviceLiveData.value!!, isOn)
}

Ejecuta la app

Ejecuta la app para volver a cargar las actualizaciones. En la pantalla principal, activa o desactiva el dispositivo.

7. Compartir dispositivos con otros ecosistemas

En la especificación de Matter, compartir un dispositivo se conoce como flujo de varios administradores.

En los pasos anteriores, aprendimos que el SDK para dispositivos móviles de la casa permite asignar dispositivos a la estructura local de Android y también a una estructura de desarrollo para la app de ejemplo. Este es un ejemplo de flujo multiadministrador, en el que los dispositivos se pueden asignar a más de una estructura.

Es posible que quieras compartir dispositivos con más telas, especialmente si se trata de un grupo familiar en el que las personas tienen sus propias preferencias con respecto a las aplicaciones y plataformas.

El SDK para dispositivos móviles de Home proporciona esta funcionalidad en la API de ShareDeviceRequest, lo que te permite hacer lo siguiente:

  1. Abre un período de comisión temporal para los dispositivos.
  2. Cambia el estado de los dispositivos para que se puedan asignar a otra estructura.
  3. Controla tus dispositivos desde otras apps y ecosistemas.

En los próximos pasos, usarás el SDK para dispositivos móviles de Home a fin de compartir dispositivos.

Paso 1: Crea un selector de actividades de GPS

Al igual que el Selector de actividades de comisión que creamos cuando nos encargamos de un tejido de desarrollo, creamos un selector de actividad de dispositivos compartidos para controlar IntentSender desde la API de CommissioningClient.

  1. Abre DeviceFragment en la carpeta java/screens/device/.
  2. Reemplaza el comentario // CODELAB: shareDeviceLauncher definition por el siguiente código para registrar y controlar el resultado de la actividad .shareDevice():
    shareDeviceLauncher =
        registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
          // Share Device Step 5.
          // The Share Device activity in GPS (step 4) has completed.
          val resultCode = result.resultCode
          if (resultCode == RESULT_OK) {
            Timber.d("ShareDevice: Success")
            viewModel.shareDeviceSucceeded(selectedDeviceViewModel.selectedDeviceLiveData.value!!)
          } else {
            viewModel.shareDeviceFailed(
                selectedDeviceViewModel.selectedDeviceLiveData.value!!, resultCode)
          }
        }
    

Paso 2: Revisa los objetos LiveData

La devolución de llamada exitosa de la API de .shareDevice() proporciona la IntentSender que se usará para iniciar la actividad del dispositivo compartido en los Servicios de Google Play. En el DeviceViewModel, creamos dos objetos LiveData para informar sobre el resultado de esta llamada a la API:

  • _shareDeviceStatus para realizar un seguimiento de TaskStatus
    // The current status of the share device action.
    viewModel.shareDeviceStatus.observe(viewLifecycleOwner) { status ->
      val isButtonEnabled = status !is InProgress
      updateShareDeviceButton(isButtonEnabled)
      if (status is TaskStatus.Failed) {
        showAlertDialog(errorAlertDialog, status.message, status.cause!!.toString())
      }
    }
    
  • _shareDeviceIntentSender para controlar el resultado de la llamada a .sharedevice().
    viewModel.shareDeviceIntentSender.observe(viewLifecycleOwner) { sender ->
      Timber.d("shareDeviceIntentSender.observe is called with [${intentSenderToString(sender)}]")
      if (sender != null) {
        // Share Device Step 4: Launch the activity described in the IntentSender that
        // was returned in Step 3 (where the viewModel calls the GPS API to commission
        // the device).
        Timber.d("ShareDevice: Launch GPS activity to share device")
        shareDeviceLauncher.launch(IntentSenderRequest.Builder(sender).build())
        viewModel.consumeShareDeviceIntentSender()
      }
    }
    

En los próximos pasos, usaremos estos objetos de LiveData en nuestra llamada a la API .shareDevice().

Paso 3: Llama a la API

Ahora es el momento de iniciar una tarea para compartir un dispositivo.

  1. Abre DeviceViewModel.kt en la carpeta java/screens/device/.
  2. Ubica la función shareDevice(). Reemplaza el comentario // CODELAB: shareDevice por ShareDeviceRequest. El DeviceDescriptor proporciona información específica sobre el dispositivo, como el ID de proveedor, el ID del producto y el tipo de dispositivo. En este ejemplo, codificamos los valores de forma fija.
      Timber.d("ShareDevice: Setting up the IntentSender")
      val shareDeviceRequest =
          ShareDeviceRequest.builder()
              .setDeviceDescriptor(DeviceDescriptor.builder().build())
              .setDeviceName("temp device name")
    
  3. Establece el parámetro CommissioningWindow y los parámetros. En este punto, el período de comisión temporal está abierto en el dispositivo.
              .setCommissioningWindow(
                  CommissioningWindow.builder()
                      .setDiscriminator(Discriminator.forLongValue(DISCRIMINATOR))
                      .setPasscode(SETUP_PIN_CODE)
                      .setWindowOpenMillis(SystemClock.elapsedRealtime())
                      .setDurationSeconds(OPEN_COMMISSIONING_WINDOW_DURATION_SECONDS.toLong())
                      .build())
              .build()
    
  4. Llama a .getCommissioningClient(), solo que esta vez, usa la API de .shareDevice(). La devolución de llamada exitosa de la API de commissioningClient.shareDevice() proporciona el IntentSender que se usará para iniciar la actividad del dispositivo de uso compartido en Servicios de Google Play.
    Matter.getCommissioningClient(activity)
        .shareDevice(shareDeviceRequest)
    
  5. Para completar nuestra función shareDevice, agrega addOnSuccessListener y addOnFailureListener, y publica en los objetos LiveData:
          .addOnSuccessListener { result ->
            Timber.d("ShareDevice: Success getting the IntentSender: result [${result}]")
            // Communication with fragment is via livedata
            _backgroundWorkAlertDialogAction.postValue(BackgroundWorkAlertDialogAction.Hide)
            _shareDeviceIntentSender.postValue(result)
          }
          .addOnFailureListener { error ->
            Timber.e(error)
            _backgroundWorkAlertDialogAction.postValue(BackgroundWorkAlertDialogAction.Hide)
            _shareDeviceStatus.postValue(
                TaskStatus.Failed("Setting up the IntentSender failed", error))
          }
    

Después de usar el remitente, se debe llamar a consumeShareDeviceIntentSender para evitar recibirlo nuevamente después de un cambio de configuración.

  /**
   * Consumes the value in [_shareDeviceIntentSender] and sets it back to null. Needs to be called
   * to avoid re-processing an IntentSender after a configuration change where the LiveData is
   * re-posted.
   */
  fun consumeShareDeviceIntentSender() {
    _shareDeviceIntentSender.postValue(null)
  }

Ejecuta la app

Para compartir tu dispositivo Matter con otros ecosistemas, deberás tener instalada otra plataforma en tu dispositivo Android. Creamos otra instancia de la app de ejemplo que puedes usar como comisionado de destino.

Una vez que hayas instalado el comisionador de destino en tu dispositivo Android, verifica que puedes compartir tu dispositivo Matter. La aplicación de comisiones objetivo está etiquetada como GHSAFM-TC.

Ahora tus dispositivos pueden participar en tres tejidos:

  1. Tela local de Android.
  2. Tu tejido de desarrollo (esta app).
  3. Esta tercera tela con la que acabas de compartir el dispositivo.

8. Próximos pasos

Felicitaciones

¡Felicitaciones! Completaste correctamente este codelab y aprendiste a encargar y compartir dispositivos con el SDK de Home Mobile.

Si tienes problemas con la app de ejemplo, intenta completar los pasos para verificar el entorno:

Si tienes preguntas sobre el uso de la app de ejemplo o descubres un error de código, puedes enviar problemas a la Herramienta de seguimiento de errores en el repositorio de GitHub:

Para obtener orientación oficial de Google sobre preguntas técnicas, usa el Foro para desarrolladores de casas inteligentes:

Para obtener asistencia técnica de la comunidad, usa la etiqueta google-smart-home en Stack Overflow: