Bir mobil cihazdaki adımları ölçmek için Sensör Yöneticisi'ni kullanın

Mobil uygulamadaki adım verilerini bu bölümde açıklandığı şekilde doldurmak için Sensör Yöneticisi'ni kullanın rehberini inceleyin. Bir egzersiz uygulamasının kullanıcı arayüzünü tasarlayıp yönetmeyle ilgili daha fazla bilgi için referans Temel bir fitness uygulaması geliştirin.

Başlarken

Temel adım sayacınızın adımlarını ölçmeye başlamak için kullanıyorsanız, bağımlılıkları uygulama modülünüze eklemeniz build.gradle dosyası yükleyin. Bağımlılıkların en yeni sürümlerini kullandığınızdan emin olun. Ayrıca, uygulamanızın desteğini Wear OS gibi diğer form faktörlerini de kapsayacak şekilde genişlettiğinizde ve bu form faktörlerinin gerektirdiği bağımlılıkları toplayın.

Aşağıda, kullanıcı arayüzü bağımlılıklarından bazılarına dair birkaç örnek verilmiştir. Tam liste için bu Kullanıcı Arayüzü Öğeleri kılavuzuna bakın.

implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material")

Adım sayacı sensörünü edinme

Kullanıcı gerekli etkinlik tanıma iznini verdikten sonra, adım sayacı sensörüne erişebilirsiniz:

  1. getSystemService() öğesinden SensorManager nesnesini alın.
  2. SensorManager cihazından adım sayacı sensörünü alın:
private val sensorManager by lazy {
        getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val sensor: Sensor? by lazy {
        sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) }

Bazı cihazlarda adım sayacı sensörü yoktur. Sensörü kontrol etmelisiniz ve cihazda yoksa bir hata mesajı gösterilir:

if (sensor == null) {
    Text(text = "Step counter sensor is not present on this device")
}

Ön plan hizmetinizi oluşturma

Temel fitness uygulamalarında bir düğme izleme adımları için kullanıcıdan başlangıç ve bitiş etkinlikleri almak için.

Sensör en iyi uygulamalarını göz önünde bulundurun. Özellikle, adım sayacı sensörü adımları sayarken sensör sadece kayıtlı olduğundan emin olun. Sensör kaydını bir ön planla ilişkilendirerek sensör gerektiği süre boyunca kayıtlı kalır ve sensör Uygulama ön planda olmadığında kayıtlı kalır.

onPause() yönteminde sensörün kaydını iptal etmek için aşağıdaki snippet'i kullanın: ön plan hizmetiniz:

override fun onPause() {
    super.onPause()
    sensorManager.unregisterListener(this)
}

Etkinlikler için verileri analiz etme

Sensör verilerine erişmek için SensorEventListener arayüzünü uygulayın. Not ön plan hizmetinizin yaşam döngüsü boyunca, hizmet duraklatıldığında veya sonlandırıldığında sensörün kaydı silinir. İlgili içeriği oluşturmak için kullanılan aşağıdaki snippet, SensorEventListener arayüzünün nasıl uygulanacağını gösterir Sensor.TYPE_STEP_COUNTER için:

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")
    }
}

Sensör etkinlikleri için veritabanı oluşturma

Uygulamanızda, kullanıcının zaman içindeki adımlarını görüntüleyebileceği bir ekran gösterilebilir. Uygulamanızda bu özelliği sağlamak için Oda kalıcılık kitaplığını kullanın.

Aşağıdaki snippet, adım sayısı grubunu içeren bir tablo oluşturur uygulamanızın her bir ölçüme ne zaman eriştiğiyle birlikte

@Entity(tableName = "steps")
data class StepCount(
  @ColumnInfo(name = "steps") val steps: Long,
  @ColumnInfo(name = "created_at") val createdAt: String,
)

Veri erişim nesnesi (DAO) oluşturma okuması ve yazması için:

@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)
}

DAO'yu örneklendirmek için RoomDatabase nesnesi oluşturun:

@Database(entities = [StepCount::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun stepsDao(): StepsDao
}

Sensör verilerini veritabanında depolayın

ViewModel, yeni StepCounter sınıfını kullanır, böylece adımları en kısa sürede depolayabilirsiniz Siz bu e-postaları okurken:

viewModelScope.launch {
    val stepsFromLastBoot = stepCounter.steps()
    repository.storeSteps(stepsFromLastBoot)
}

repository sınıfı aşağıdaki gibi görünür:

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
            }
        }
    }
}

Sensör verilerini düzenli olarak alma

Ön plan hizmeti kullanıyorsanız WorkManager uygulamasını yapılandırmanız gerekmez Çünkü uygulamanızın kullanıcının adımlarını aktif olarak izlediği sırada güncellenmiş toplam adım sayısı uygulamanızda görünecektir.

Bununla birlikte, adım kayıtlarınızı toplu hale getirmek istiyorsanız WorkManager öğesini kullanarak Belirli bir aralıktaki adımları (ör. 15 dakikada bir) ölçmelidir. WorkManager, arka planı gerçekleştiren bileşendir sağlamak için çok önemlidir. WorkManager codelab'de daha fazla bilgi edinin.

Verileri alacak şekilde Worker nesnesini yapılandırmak için doWork() öğesini geçersiz kılın yöntemini çağırın:

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()
    }
}

WorkManager uygulamasını geçerli adım sayısını her 15 dakikada bir kaydedecek şekilde ayarlamak için şunları yapın: şu:

  1. Configuration.Provider öğesini uygulamak için Application sınıfını genişletin kullanır.
  2. onCreate() yönteminde bir PeriodicWorkRequestBuilder sıraya koyun.

Bu işlem aşağıdaki kod snippet'inde görünür:

@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()
}

Uygulamanızın adımına erişimi kontrol eden içerik sağlayıcıyı başlatmak için Karşı veritabanını ayarlamak için aşağıdaki öğeyi uygulamanızın manifest dosyasına ekleyin:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="remove" />