Registrar dados de condicionamento físico usando a API Recording

A API Recording em dispositivos móveis permite que seu app grave dados de condicionamento físico de um dispositivo móvel de maneira eficiente em termos de bateria. Por exemplo, use essa API para registrar etapas, semelhante a um podômetro que recupera dados de contagem de passos. Essa API não exige conta, ou seja, não é necessário ter uma Conta do Google para usar o serviço, e os dados são armazenados no dispositivo.

Este guia mostra como usar a API Recording em dispositivos móveis nas suas experiências de saúde e fitness.

Consulte o exemplo da API Recording em dispositivos móveis no GitHub.

Detalhes importantes

Há vários recursos notáveis exclusivos da API Recording em dispositivos móveis:

  • Quando a assinatura de gravação começa ou é renovada, os dados desde a última assinatura (por até 10 dias) ficam acessíveis.
  • Os dados só ficam disponíveis quando há uma assinatura ativa. Se uma assinatura for removida chamando unsubscribe, os dados coletados não poderão ser acessados.

Tipos de dados

A API Recording em dispositivos móveis pode gravar os seguintes tipos de dados:

Primeiros passos

Para começar, adicione a seguinte dependência ao arquivo build.gradle:

DSL do Kotlin

plugin {
  id("com.android.application")
}

...

dependencies {
  implementation("com.google.android.gms:play-services-fitness:21.2.0")
}

DSL do Groovy

apply plugin: 'com.android.application'

...

dependencies {
  implementation 'com.google.android.gms:play-services-fitness:21.2.0'
}

Solicitar permissões

Para gravar dados usando a API Recording em dispositivos móveis, seu app precisa solicitar a seguinte permissão:

  • android.permission.ACTIVITY_RECOGNITION

Realizar uma verificação da versão do Play Services

Para usar a API Recording em dispositivos móveis, o usuário precisa ter o Google Play Services atualizado para LOCAL_RECORDING_CLIENT_MIN_VERSION_CODE. Para verificar isso, use o método isGooglePlayServicesAvailable:

val hasMinPlayServices = isGooglePlayServicesAvailable(context, LocalRecordingClient.LOCAL_RECORDING_CLIENT_MIN_VERSION_CODE)

if(hasMinPlayServices != ConnectionResult.SUCCESS) {
  // Prompt user to update their device's Google Play services app and return
}

// Continue with Recording API functions

Caso contrário, se a versão do Google Play Services do usuário for muito baixa, o sistema vai gerar uma exceção ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED.

Inscrever-se nos dados do Fitness

Para solicitar a coleta em segundo plano de dados de passos, use o método subscribe, conforme mostrado no snippet de código a seguir:

val localRecordingClient = FitnessLocal.getLocalRecordingClient(this)
// Subscribe to steps data
localRecordingClient.subscribe(LocalDataType.TYPE_STEP_COUNT_DELTA)
  .addOnSuccessListener {
    Log.i(TAG, "Successfully subscribed!")
  }
  .addOnFailureListener { e ->
    Log.w(TAG, "There was a problem subscribing.", e)
  }

Ler e processar dados de condicionamento físico

Depois de se inscrever, solicite os dados usando o método readData. Em seguida, você pode extrair LocalDataPoints do LocalDataSet resultante fazendo um LocalDataReadRequest, conforme mostrado no snippet de código a seguir:

val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusWeeks(1)
val readRequest =
  LocalDataReadRequest.Builder()
    // The data request can specify multiple data types to return,
    // effectively combining multiple data queries into one call.
    // This example demonstrates aggregating only one data type.
    .aggregate(LocalDataType.TYPE_STEP_COUNT_DELTA)
    // Analogous to a "Group By" in SQL, defines how data should be
    // aggregated. bucketByTime allows bucketing by time span.
    .bucketByTime(1, TimeUnit.DAYS)
    .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .build()

  localRecordingClient.readData(readRequest).addOnSuccessListener { response ->
    // The aggregate query puts datasets into buckets, so flatten into a
    // single list of datasets.
    for (dataSet in response.buckets.flatMap { it.dataSets }) {
      dumpDataSet(dataSet)
    }
  }
  .addOnFailureListener { e ->
    Log.w(TAG,"There was an error reading data", e)
  }

fun dumpDataSet(dataSet: LocalDataSet) {
  Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name}")
  for (dp in dataSet.dataPoints) {
    Log.i(TAG,"Data point:")
    Log.i(TAG,"\tType: ${dp.dataType.name}")
    Log.i(TAG,"\tStart: ${dp.getStartTime(TimeUnit.HOURS)}")
    Log.i(TAG,"\tEnd: ${dp.getEndTime(TimeUnit.HOURS)}")
    for (field in dp.dataType.fields) {
      Log.i(TAG,"\tLocalField: ${field.name.toString()} LocalValue: ${dp.getValue(field)}")
    }
  }
}

O LocalRecordingClient atualiza continuamente a coleta de dados. Você pode usar readData para extrair os números mais recentes a qualquer momento.

O LocalRecordingClient armazena dados de até 10 dias. Para reduzir o risco de perda de dados, use o WorkManager para coletar dados periodicamente em segundo plano.

Cancelar a inscrição nos dados de condicionamento físico

Para liberar recursos, cancele a inscrição na coleta de dados do sensor quando o app não precisar mais deles. Para cancelar a inscrição, use o método unsubscribe:

val localRecordingClient = FitnessLocal.getLocalRecordingClient(this)
// Unsubscribe from steps data
localRecordingClient.unsubscribe(LocalDataType.TYPE_STEP_COUNT_DELTA)
  .addOnSuccessListener {
    Log.i(TAG, "Successfully unsubscribed!")
  }
  .addOnFailureListener { e ->
    Log.w(TAG, "There was a problem unsubscribing.", e)
  }