همانطور که در این راهنما توضیح داده شده است، از مدیر حسگر برای پر کردن داده های مراحل در یک برنامه تلفن همراه استفاده کنید. برای اطلاعات بیشتر در مورد نحوه طراحی و مدیریت رابط کاربری برنامه ورزشی، به ساخت یک برنامه تناسب اندام پایه مراجعه کنید.
شروع کردن
برای شروع با اندازهگیری مراحل گام شمار اصلی خود از دستگاه تلفن همراه خود، باید وابستگیها را به فایل build.gradle
ماژول برنامه خود اضافه کنید. اطمینان حاصل کنید که از آخرین نسخه های وابستگی استفاده می کنید. همچنین، هنگامی که پشتیبانی برنامه خود را به سایر فاکتورهای شکل مانند Wear OS گسترش می دهید، وابستگی هایی را که این عوامل فرم نیاز دارند اضافه کنید.
در زیر چند نمونه از برخی از وابستگی های UI آورده شده است. برای فهرست کامل، به این راهنمای عناصر رابط کاربری مراجعه کنید.
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material")
سنسور گام شمار را بدست آورید
پس از اینکه کاربر مجوز لازم برای شناسایی فعالیت را اعطا کرد، می توانید به سنسور گام شمار دسترسی داشته باشید:
- شی
SensorManager
را ازgetSystemService()
دریافت کنید. - سنسور گام شمار را از
SensorManager
دریافت کنید:
private val sensorManager by lazy {
getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val sensor: Sensor? by lazy {
sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) }
برخی از دستگاه ها سنسور گام شمار ندارند. اگر دستگاه فاقد حسگر باشد، باید آن را بررسی کنید و پیام خطا نشان دهید:
if (sensor == null) {
Text(text = "Step counter sensor is not present on this device")
}
سرویس پیش زمینه خود را ایجاد کنید
در یک برنامه اصلی تناسب اندام، ممکن است دکمه ای برای دریافت شروع و توقف رویدادها از کاربر برای پیگیری مراحل داشته باشید.
بهترین شیوه های سنسور را به خاطر داشته باشید. به ویژه، سنسور گام شمار تنها باید در زمانی که شنونده حسگر ثبت شده است، قدم ها را شمارش کند. با مرتبط کردن ثبت سنسور با یک سرویس پیش زمینه، سنسور تا زمانی که نیاز باشد ثبت می شود و زمانی که برنامه در پیش زمینه نباشد، حسگر می تواند ثبت شود.
از قطعه زیر برای لغو ثبت سنسور در روش onPause()
سرویس پیش زمینه خود استفاده کنید:
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
تجزیه و تحلیل داده ها برای رویدادها
برای دسترسی به داده های حسگر، رابط SensorEventListener
را پیاده سازی کنید. توجه داشته باشید که باید ثبت سنسور را با چرخه عمر سرویس پیشزمینه خود مرتبط کنید و زمانی که سرویس متوقف یا پایان مییابد، سنسور را لغو ثبت کنید. قطعه زیر نحوه پیاده سازی رابط SensorEventListener
را برای 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")
}
}
یک پایگاه داده برای رویدادهای حسگر ایجاد کنید
برنامه شما ممکن است صفحهای را نشان دهد که در آن کاربر بتواند مراحل خود را در طول زمان مشاهده کند. برای ارائه این قابلیت در برنامه خود، از کتابخانه ماندگاری اتاق استفاده کنید.
قطعه زیر جدولی ایجاد میکند که شامل مجموعهای از اندازهگیریهای تعداد گام به همراه زمانی است که برنامه شما به هر اندازهگیری دسترسی داشته است:
@Entity(tableName = "steps")
data class StepCount(
@ColumnInfo(name = "steps") val steps: Long,
@ColumnInfo(name = "created_at") val createdAt: String,
)
یک شی دسترسی به داده (DAO) برای خواندن و نوشتن داده ها ایجاد کنید:
@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، یک شی RoomDatabase
ایجاد کنید:
@Database(entities = [StepCount::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun stepsDao(): StepsDao
}
داده های حسگر را در پایگاه داده ذخیره کنید
ViewModel از کلاس StepCounter جدید استفاده می کند، بنابراین می توانید مراحل را به محض خواندن آنها ذخیره کنید:
viewModelScope.launch {
val stepsFromLastBoot = stepCounter.steps()
repository.storeSteps(stepsFromLastBoot)
}
کلاس repository
به شکل زیر خواهد بود:
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
}
}
}
}
بازیابی دوره ای داده های حسگر
اگر از سرویس پیش زمینه استفاده می کنید، نیازی به پیکربندی WorkManager
ندارید، زیرا در زمانی که برنامه شما به طور فعال مراحل کاربر را ردیابی می کند، تعداد کل مراحل به روز شده باید در برنامه شما ظاهر شود.
با این حال، اگر میخواهید رکوردهای گامهای خود را دستهبندی کنید، میتوانید از WorkManager
برای اندازهگیری گامها در یک بازه زمانی خاص، مثلاً هر 15 دقیقه یک بار، استفاده کنید. WorkManager
مؤلفه ای است که کار پس زمینه را برای اجرای تضمینی انجام می دهد. در WorkManager Codelab بیشتر بیاموزید.
برای پیکربندی شی Worker
برای بازیابی داده ها، همانطور که در قطعه کد زیر نشان داده شده است، روش doWork()
را لغو کنید:
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
برای ذخیره تعداد گام های فعلی هر 15 دقیقه، موارد زیر را انجام دهید:
- کلاس
Application
را برای پیاده سازی رابطConfiguration.Provider
گسترش دهید. - در متد
onCreate()
یکPeriodicWorkRequestBuilder
را در صف قرار دهید.
این فرآیند در قطعه کد زیر ظاهر می شود:
@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()
}
برای مقداردهی اولیه ارائهدهنده محتوا که دسترسی به پایگاه داده شمارنده گام برنامه شما را بلافاصله پس از راهاندازی برنامه کنترل میکند، عنصر زیر را به فایل مانیفست برنامه خود اضافه کنید:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />