استخدِم "مدير أدوات الاستشعار" لتعبئة بيانات الخطوات في تطبيق متوافق مع الأجهزة الجوّالة، كما هو موضّح في هذه الشريحة. الدليل. لمزيد من المعلومات حول كيفية تصميم وإدارة واجهة مستخدم تطبيق التمرين، مراجعة إنشاء تطبيق أساسي للياقة البدنية
الخطوات الأولى
للبدء بقياس خطوات عدّاد الخطوات الأساسية من
الجهاز المحمول، فستحتاج إلى إضافة التبعيات إلى وحدة تطبيقك
ملف build.gradle
. تأكد من استخدام أحدث إصدارات التبعيات.
بالإضافة إلى ذلك، عند توسيع نطاق دعم تطبيقك ليشمل أشكال الأجهزة الأخرى، مثل Wear OS،
وإضافة التبعيات التي تتطلبها عوامل الشكل هذه.
فيما يلي بعض الأمثلة على بعض تبعيات واجهة المستخدم. للحصول على قائمة كاملة، راجِع دليل عناصر واجهة المستخدم هذا.
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 فئة عدد الخطوات الجديدة، لذا يمكنك تخزين الخطوات في أقرب وقت بينما تقرأها:
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.
لضبط كائن 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
من واجهة pyplot. - في الطريقة
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" />