किसी मोबाइल डिवाइस से कदमों की संख्या मापने के लिए, सेंसर मैनेजर का इस्तेमाल करें

सेंसर मैनेजर का इस्तेमाल करके, मोबाइल ऐप्लिकेशन में कदमों का डेटा अपने-आप भर जाए, जैसा कि इस पढ़ें. कसरत से जुड़े किसी ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) को डिज़ाइन और मैनेज करने के बारे में ज़्यादा जानने के लिए, रेफ़र करें फ़िटनेस के लिए एक सामान्य ऐप्लिकेशन बनाएं.

शुरू करना

अपने बुनियादी कदम काउंटर के कदमों की गिनती करने के लिए, मोबाइल डिवाइस के लिए, आपको अपने ऐप्लिकेशन मॉड्यूल में डिपेंडेंसी जोड़नी होगी 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")

स्टेप काउंटर सेंसर पाएं

उपयोगकर्ता की ओर से ज़रूरी गतिविधि की पहचान करने की अनुमति देने के बाद, तो स्टेप काउंटर सेंसर को ऐक्सेस किया जा सकता है:

  1. getSystemService() से SensorManager ऑब्जेक्ट पाएं.
  2. 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
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)
}

डीएओ को इंस्टैंशिएट करने के लिए, 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()
    }
}

कदमों की मौजूदा संख्या को हर 15 मिनट में सेव करने के लिए, WorkManager को सेट अप करें निम्न:

  1. Configuration.Provider लागू करने के लिए, Application क्लास को बढ़ाएं इंटरफ़ेस पर कॉपी करने की सुविधा मिलती है.
  2. 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" />