Utilisez Sensor Manager pour renseigner les données de pas dans une application mobile, comme décrit dans ce . Pour en savoir plus sur la conception et la gestion d'une UI d'application d'exercice, se référer à Créer une application de fitness basique
Premiers pas
Pour commencer à mesurer les pas de votre compteur de pas de base à partir de votre
appareil mobile, vous devez ajouter les dépendances au module de votre application
build.gradle
. Assurez-vous d'utiliser les dernières versions des dépendances.
De plus, lorsque vous étendez la prise en charge de votre application
à d'autres facteurs de forme, comme Wear OS,
ajouter les dépendances requises par ces facteurs de forme.
Vous trouverez ci-dessous quelques exemples de dépendances de l'interface utilisateur. Pour obtenir une liste complète, consultez ce guide sur les éléments d'interface utilisateur.
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material")
Munissez-vous du capteur du compteur de pas
Une fois que l'utilisateur a accordé l'autorisation de reconnaissance d'activité nécessaire, vous pouvez accéder au capteur du compteur de pas:
- Obtenez l'objet
SensorManager
à partir degetSystemService()
. - Procurez-vous le capteur du compteur de pas à partir de
SensorManager
:
private val sensorManager by lazy {
getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val sensor: Sensor? by lazy {
sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) }
Certains appareils ne sont pas équipés du capteur du compteur de pas. Vous devez vérifier que le capteur et afficher un message d'erreur si l'appareil n'en est pas équipé:
if (sensor == null) {
Text(text = "Step counter sensor is not present on this device")
}
Créer votre service de premier plan
Dans une application de fitness de base, vous pouvez avoir un bouton pour recevoir les événements de démarrage et d'arrêt de l'utilisateur pour suivre les étapes.
Respectez les bonnes pratiques concernant les capteurs. En particulier, le capteur du compteur de pas ne doit compter que les pas "listener" est enregistré. En associant l'enregistrement des capteurs à un premier plan le capteur est enregistré tant que nécessaire et peut restent enregistrées lorsque l'application n'est pas exécutée au premier plan.
Utilisez l'extrait de code suivant pour annuler l'enregistrement du capteur dans la méthode onPause()
de
votre service de premier plan:
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
Analyser les données d'événements
Pour accéder aux données des capteurs, implémentez l'interface SensorEventListener
. Remarque
associer l'enregistrement des capteurs au réseau
en annulant l'enregistrement du capteur
à l'arrêt ou à la mise en pause du service. La
L'extrait de code suivant montre comment implémenter l'interface SensorEventListener
pour Sensor.TYPE_STEP_COUNTER
:
private const val TAG = "STEP_COUNT_LISTENER"
context(Context)
class StepCounter {
private val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
private val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
suspend fun steps() = suspendCancellableCoroutine { continuation ->
Log.d(TAG, "Registering sensor listener... ")
val listener: SensorEventListener by lazy {
object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent?) {
if (event == null) return
val stepsSinceLastReboot = event.values[0].toLong()
Log.d(TAG, "Steps since last reboot: $stepsSinceLastReboot")
if (continuation.isActive) {
continuation.resume(stepsSinceLastReboot)
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
Log.d(TAG, "Accuracy changed to: $accuracy")
}
}
}
val supportedAndEnabled = sensorManager.registerListener(listener,
sensor, SensorManager.SENSOR_DELAY_UI)
Log.d(TAG, "Sensor listener registered: $supportedAndEnabled")
}
}
Créer une base de données pour les événements des capteurs
Votre application peut afficher un écran permettant à l'utilisateur de voir ses pas au fil du temps. Pour fournir cette fonctionnalité dans votre application, utilisez la bibliothèque de persistance Room.
L'extrait de code suivant crée un tableau contenant un ensemble de pas mesures, ainsi que l'heure à laquelle votre application a accédé à chaque mesure:
@Entity(tableName = "steps")
data class StepCount(
@ColumnInfo(name = "steps") val steps: Long,
@ColumnInfo(name = "created_at") val createdAt: String,
)
créer un objet d'accès aux données (DAO) ; pour lire et écrire les données:
@Dao
interface StepsDao {
@Query("SELECT * FROM steps")
suspend fun getAll(): List<StepCount>
@Query("SELECT * FROM steps WHERE created_at >= date(:startDateTime) " +
"AND created_at < date(:startDateTime, '+1 day')")
suspend fun loadAllStepsFromToday(startDateTime: String): Array<StepCount>
@Insert
suspend fun insertAll(vararg steps: StepCount)
@Delete
suspend fun delete(steps: StepCount)
}
Pour instancier le DAO, créez un objet RoomDatabase
:
@Database(entities = [StepCount::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun stepsDao(): StepsDao
}
Stocker les données des capteurs dans la base de données
ViewModel utilise la nouvelle classe StepCounter pour que vous puissiez stocker les étapes dès que possible lorsque vous les lisez:
viewModelScope.launch {
val stepsFromLastBoot = stepCounter.steps()
repository.storeSteps(stepsFromLastBoot)
}
La classe repository
se présente comme suit:
class Repository(
private val stepsDao: StepsDao,
) {
suspend fun storeSteps(stepsSinceLastReboot: Long) = withContext(Dispatchers.IO) {
val stepCount = StepCount(
steps = stepsSinceLastReboot,
createdAt = Instant.now().toString()
)
Log.d(TAG, "Storing steps: $stepCount")
stepsDao.insertAll(stepCount)
}
suspend fun loadTodaySteps(): Long = withContext(Dispatchers.IO) {
printTheWholeStepsTable() // DEBUG
val todayAtMidnight = (LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT).toString())
val todayDataPoints = stepsDao.loadAllStepsFromToday(startDateTime = todayAtMidnight)
when {
todayDataPoints.isEmpty() -> 0
else -> {
val firstDataPointOfTheDay = todayDataPoints.first()
val latestDataPointSoFar = todayDataPoints.last()
val todaySteps = latestDataPointSoFar.steps - firstDataPointOfTheDay.steps
Log.d(TAG, "Today Steps: $todaySteps")
todaySteps
}
}
}
}
Récupération périodique des données des capteurs
Si vous utilisez un service de premier plan, vous n'avez pas besoin de configurer WorkManager
En effet, lorsque votre application suit activement les pas de l'utilisateur,
le nombre total de pas mis à jour devrait apparaître dans votre application.
Toutefois, si vous souhaitez regrouper vos enregistrements de pas, vous pouvez utiliser WorkManager
pour
mesurez les pas à un intervalle spécifique, par exemple une fois toutes les 15 minutes.
WorkManager
est le composant qui effectue l'arrière-plan.
pour garantir une exécution garantie. Pour en savoir plus, consultez l'atelier de programmation WorkManager.
Pour configurer l'objet Worker
afin de récupérer les données, remplacez doWork()
, comme indiqué dans l'extrait de code suivant:
private const val TAG = " StepCounterWorker"
@HiltWorker
class StepCounterWorker @AssistedInject constructor(
@Assisted appContext: Context,
@Assisted workerParams: WorkerParameters,
val repository: Repository,
val stepCounter: StepCounter
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(TAG, "Starting worker...")
val stepsSinceLastReboot = stepCounter.steps().first()
if (stepsSinceLastReboot == 0L) return Result.success()
Log.d(TAG, "Received steps from step sensor: $stepsSinceLastReboot")
repository.storeSteps(stepsSinceLastReboot)
Log.d(TAG, "Stopping worker...")
return Result.success()
}
}
Pour configurer WorkManager
afin de stocker le nombre de pas actuel toutes les 15 minutes,
les éléments suivants:
- Étendez la classe
Application
pour implémenterConfiguration.Provider
. de commande. - Dans la méthode
onCreate()
, mettez unPeriodicWorkRequestBuilder
en file d'attente.
Ce processus est indiqué dans l'extrait de code suivant:
@HiltAndroidApp
@RequiresApi(Build.VERSION_CODES.S)
internal class PulseApplication : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory
override fun onCreate() {
super.onCreate()
val myWork = PeriodicWorkRequestBuilder<StepCounterWorker>(
15, TimeUnit.MINUTES).build()
WorkManager.getInstance(this)
.enqueueUniquePeriodicWork("MyUniqueWorkName",
ExistingPeriodicWorkPolicy.UPDATE, myWork)
}
override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
}
Pour initialiser le fournisseur de contenu qui contrôle l'accès à l'étape de votre application de compteur de données immédiatement après le démarrage de l'application, ajoutez l'élément suivant à le fichier manifeste de votre application:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />