Użyj Menedżera czujników, aby wypełnić dane o krokach w aplikacji mobilnej, zgodnie z opisem w tym przewodniku. Więcej informacji o projektowaniu interfejsu aplikacji do ćwiczeń i zarządzaniu nim znajdziesz w artykule Tworzenie podstawowej aplikacji do ćwiczeń.
Pierwsze kroki
Aby rozpocząć pomiar kroków za pomocą podstawowego krokomierza na urządzeniu mobilnym, musisz dodać zależności do pliku modułu aplikacji build.gradle
. Sprawdź, czy używasz najnowszych wersji zależności.
Jeśli rozszerzysz obsługę aplikacji na inne formaty, takie jak Wear OS, dodaj zależności wymagane przez te formaty.
Oto kilka przykładów zależności interfejsu: Pełną listę znajdziesz w tym przewodniku po elementach interfejsu.
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material")
Uzyskiwanie dostępu do czujnika licznika kroków
Gdy użytkownik przyzna niezbędne uprawnienia do rozpoznawania aktywności, możesz uzyskać dostęp do czujnika licznika kroków:
- Pobierz obiekt
SensorManager
zgetSystemService()
. - Pobierz czujnik licznika kroków z
SensorManager
:
private val sensorManager by lazy {
getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val sensor: Sensor? by lazy {
sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) }
Niektóre urządzenia nie mają czujnika licznika kroków. Sprawdź, czy urządzenie ma czujnik, i wyświetl komunikat o błędzie, jeśli go nie ma:
if (sensor == null) {
Text(text = "Step counter sensor is not present on this device")
}
Tworzenie usługi działającej na pierwszym planie
W podstawowej aplikacji do fitnessu możesz mieć przycisk, który będzie odbierać od użytkownika zdarzenia rozpoczęcia i zakończenia śledzenia kroków.
Pamiętaj o sprawdzonych metodach dotyczących czujników. W szczególności czujnik licznika kroków powinien zliczać kroki tylko wtedy, gdy zarejestrowany jest odbiornik czujnika. Dzięki powiązaniu rejestracji czujnika z usługą na pierwszym planie czujnik jest zarejestrowany tak długo, jak jest to potrzebne, i może pozostać zarejestrowany, gdy aplikacja nie jest na pierwszym planie.
Aby wyrejestrować czujnik w metodzie onPause()
usługi na pierwszym planie, użyj tego fragmentu kodu:
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
Analizowanie danych dotyczących zdarzeń
Aby uzyskać dostęp do danych z czujników, zaimplementuj interfejs SensorEventListener
. Pamiętaj, że rejestrację czujnika należy powiązać z cyklem życia usługi działającej na pierwszym planie, a wyrejestrować czujnik, gdy usługa zostanie wstrzymana lub zakończona. Poniższy fragment kodu pokazuje, jak wdrożyć interfejs SensorEventListener
dla 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")
}
}
Tworzenie bazy danych zdarzeń czujnika
Aplikacja może wyświetlać ekran, na którym użytkownik może zobaczyć liczbę kroków w danym okresie. Aby udostępnić tę funkcję w aplikacji, użyj biblioteki trwałości danych Room.
Poniższy fragment kodu tworzy tabelę zawierającą zestaw pomiarów liczby kroków wraz z czasem, w którym aplikacja uzyskała dostęp do każdego pomiaru:
@Entity(tableName = "steps")
data class StepCount(
@ColumnInfo(name = "steps") val steps: Long,
@ColumnInfo(name = "created_at") val createdAt: String,
)
Utwórz obiekt dostępu do danych (DAO), aby odczytywać i zapisywać dane:
@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)
}
Aby utworzyć instancję obiektu DAO, utwórz obiekt RoomDatabase
:
@Database(entities = [StepCount::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun stepsDao(): StepsDao
}
Przechowywanie danych z czujników w bazie danych
ViewModel korzysta z nowej klasy StepCounter, dzięki czemu możesz zapisywać kroki od razu po ich odczytaniu:
viewModelScope.launch {
val stepsFromLastBoot = stepCounter.steps()
repository.storeSteps(stepsFromLastBoot)
}
Klasa repository
będzie wyglądać tak:
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
}
}
}
}
Okresowe pobieranie danych z czujników
Jeśli używasz usługi działającej na pierwszym planie, nie musisz konfigurować WorkManager
, ponieważ w czasie, gdy aplikacja aktywnie śledzi kroki użytkownika, zaktualizowana łączna liczba kroków powinna być widoczna w aplikacji.
Jeśli jednak chcesz rejestrować kroki w pakietach, możesz użyć ikony WorkManager
, aby mierzyć kroki w określonych odstępach czasu, np. co 15 minut.
WorkManager
to komponent, który wykonuje pracę w tle, aby zapewnić niezawodne działanie. Więcej informacji znajdziesz w samouczku WorkManager.
Aby skonfigurować obiekt Worker
do pobierania danych, zastąp metodę doWork()
, jak pokazano w poniższym fragmencie kodu:
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()
}
}
Aby skonfigurować WorkManager
tak, aby co 15 minut zapisywał bieżącą liczbę kroków, wykonaj te czynności:
- Rozszerz klasę
Application
, aby zaimplementować interfejsConfiguration.Provider
. - W metodzie
onCreate()
umieść w kolejcePeriodicWorkRequestBuilder
.
Ten proces jest widoczny w tym fragmencie kodu:
@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()
}
Aby zainicjować dostawcę treści, który kontroluje dostęp do bazy danych licznika kroków aplikacji, natychmiast po uruchomieniu aplikacji, dodaj ten element do pliku manifestu aplikacji:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />