Użyj Menedżera czujników do wypełniania danych kroków w aplikacji mobilnej, jak opisano w tym Google. Aby dowiedzieć się więcej o zaprojektowaniu UI aplikacji do ćwiczeń i zarządzaniu nim, patrz Utwórz podstawową aplikację do fitnessu
Pierwsze kroki
Aby rozpocząć mierzenie kroków z podstawowego licznika kroków,
Mobile, musisz dodać zależności do modułu aplikacji
build.gradle
. Używaj najnowszych wersji zależności.
Ponadto gdy aplikacja zacznie obsługiwać
inne formaty, np. Wear OS,
dodaj zależności wymagane przez te formaty.
Poniżej znajdziesz kilka przykładów niektórych zależności interfejsu. Pełną listę znajdziesz tutaj: zapoznaj się z przewodnikiem Elementy 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 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:
- Uzyskaj 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. Należy sprawdzić czujnik i wyświetla komunikat o błędzie, jeśli urządzenie nie ma takiego:
if (sensor == null) {
Text(text = "Step counter sensor is not present on this device")
}
Utwórz usługę na pierwszym planie
W podstawowej aplikacji do fitnessu może być dostępny przycisk. do otrzymywania od użytkownika zdarzeń rozpoczęcia i zatrzymania w celu śledzenia kroków.
Pamiętaj o sprawdzonych metodach dotyczących czujników. Czujnik licznika kroków powinien liczyć tylko kroki, gdy czujnik jest zarejestrowany detektor. Łącząc rejestrację czujnika z pierwszym planem usługi, czujnik jest zarejestrowany tak długo, jak to konieczne. Może też pozostają zarejestrowane, gdy aplikacja nie działa na pierwszym planie.
Użyj tego fragmentu kodu, aby wyrejestrować czujnik w metodzie onPause()
funkcji
Twoja usługa na pierwszym planie:
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
Analizowanie danych pod kątem zdarzeń
Aby uzyskać dostęp do danych z czujnika, zaimplementuj interfejs SensorEventListener
. Notatka
musisz powiązać rejestrację czujnika z parametrem usługi na pierwszym planie
cyklu życia usługi przez wyrejestrowanie czujnika, gdy usługa jest wstrzymana lub zakończona.
ten 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")
}
}
Utwórz bazę danych dla zdarzeń z czujnika
W aplikacji może się pojawić ekran, na którym użytkownik będzie mógł śledzić liczbę kroków z upływem czasu. Aby udostępnić tę funkcję w swojej aplikacji, użyj biblioteki trwałości sal.
Ten fragment kodu tworzy tabelę, która zawiera liczbę kroków pomiarów oraz czasu uzyskania przez aplikację dostępu do poszczególnych pomiarów:
@Entity(tableName = "steps")
data class StepCount(
@ColumnInfo(name = "steps") val steps: Long,
@ColumnInfo(name = "created_at") val createdAt: String,
)
utworzyć obiekt dostępu do danych (DAO), aby odczytać i zapisać 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ę DAO, utwórz obiekt RoomDatabase
:
@Database(entities = [StepCount::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun stepsDao(): StepsDao
}
Zapisywanie danych z czujnika w bazie danych
Model ViewModel używa nowej klasy StepCounter, więc możesz zapisać kroki jak najszybciej w miarę ich czytania:
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 czujnika
Jeśli korzystasz z usługi na pierwszym planie, nie musisz konfigurować WorkManager
ponieważ w czasie, gdy aplikacja aktywnie śledzi kroki użytkownika,
zaktualizowana łączna liczba kroków powinna pojawić się w aplikacji.
Jeśli chcesz grupować rekordy kroków, możesz jednak użyć polecenia WorkManager
, aby
mierz kroki w określonych odstępach czasu, np. co 15 minut.
WorkManager
to komponent, który działa w tle
gwarantowane wykonanie. Więcej informacji znajdziesz w ćwiczeniach z programowania w WorkManageru.
Aby skonfigurować pobieranie danych przez obiekt Worker
, zastąp doWork()
zgodnie z poniższym fragmentem 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ć funkcję WorkManager
do przechowywania bieżącej liczby kroków co 15 minut, wykonaj
następujące:
- Rozszerz klasę
Application
, aby zaimplementowaćConfiguration.Provider
za pomocą prostego interfejsu online. - W metodzie
onCreate()
umieśćPeriodicWorkRequestBuilder
w kolejce.
Proces ten wygląda 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 kroku aplikacji do bazy danych licznika natychmiast po uruchomieniu aplikacji dodaj poniższy element do plik manifestu aplikacji:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />