Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Wellness & Droid

Wellness & Droid

Slides of the talk I gave at Droidcon Italy together with Daniele Bonaldo

Roberto Orgiu

April 04, 2022
Tweet

More Decks by Roberto Orgiu

Other Decks in Programming

Transcript

  1. WELLNESS
    & DROID
    Photo by Jeremy Thomas on Unsplash

    View Slide

  2. Daniele Bonaldo
    @danybony_
    Roberto Orgiu
    @_
    tiwiz

    View Slide

  3. SLEEP
    EXERCISE RECORD

    View Slide

  4. ACTIVITY
    RECOGNITION API
    EXERCISE

    View Slide

  5. Let’s get
    started
    •Track user


    •Compute distance


    •Steps measurement

    View Slide

  6. Getting last user’s
    location
    //Manifest.permission.ACCESS_FINE_LOCATION


    private val client =


    LocationServices.getFusedLocationProviderClient(


    activity


    )


    client.lastLocation.addOnSuccessListener {


    location
    -> . ..

    }

    View Slide

  7. Getting updates
    on user’s location
    private val locationCallback =


    object : LocationCallback() {


    override fun onLocationResult(r: LocationResult) {


    //
    calculate here


    }


    }

    View Slide

  8. Getting updates
    on user’s location
    val locationRequest = LocationRequest


    .create().apply {


    priority = PRIORITY_HIGH_ACCURACY


    interval = 5000


    }

    View Slide

  9. Getting updates
    on user’s location
    client.requestLocationUpdates(


    locationRequest,


    locationCallback,


    Looper.getMainLooper()


    )

    View Slide

  10. Compute
    distances
    SphericalUtil


    .computeDistanceBetween(


    lastLatLng,


    oldLatLng


    )

    View Slide

  11. class StepCounter(


    private val activity: AppCompatActivity


    ) : SensorEventListener
    Every step counts

    View Slide

  12. Every step counts
    private val sensorManager : SensorManager =


    activity.getSystemService(SENSOR_SERVICE)


    private val stepCounterSensor =


    sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)


    private var initialSteps = -1

    View Slide

  13. Every step counts
    fun setupStepCounter() {


    if (stepCounterSensor
    !=
    null) {


    sensorManager.registerListener(this,


    stepCounterSensor, SENSOR_DELAY_FASTEST)


    }


    }


    fun unloadStepCounter() {


    if (stepCounterSensor
    !=
    null) {


    sensorManager.unregisterListener(this)


    }


    }

    View Slide

  14. Every step counts
    override fun onSensorChanged(event: SensorEvent) {


    event.values.firstOrNull()
    ?.
    toInt()
    ?.
    let { newSteps
    ->

    if (initialSteps
    ==
    -1) {


    initialSteps = newSteps


    }


    currentSteps = newSteps - initialSteps


    }


    }


    override fun onAccuracyChanged(


    sensor: Sensor,


    accuracy: Int


    ) = Unit

    View Slide

  15. SLEEP
    EXERCISE RECORD

    View Slide

  16. SLEEP
    SLEEP RECOGNITION API

    View Slide

  17. Sleep classify event
    Sleep segment event

    View Slide

  18. Sleep segment
    event
    •Start timestamp


    •End timestamp


    •Duration


    •Status


    •Successful


    •Not detected


    •Missing data

    View Slide

  19. Sleep classify
    event
    •Confidence


    •Light level


    •Motion level


    •Timestamp

    View Slide

  20. M T W T F S S

    View Slide

  21. M T W T F S S

    View Slide

  22. Setup
    Requirements


    •Android API Level > v29


    •Android Build Tools > v21


    •Google Play Services


    Dependency


    •com.google.android.gms:play-services-location:21.0.0

    View Slide

  23. Receiver
    Android Manifest




    android:name=".receiver.SleepReceiver"


    android:enabled="true"


    android:exported="true"
    />

    View Slide

  24. Receiver
    Android Manifest




    android:name=".receiver.SleepReceiver"


    android:enabled="true"


    android:exported="true"
    />

    SleepReceiver : BroadcastReceiver()


    override fun onReceive(context: Context, intent: Intent) {


    if (SleepSegmentEvent.hasEvents(intent)) {


    .. .

    } else if (SleepClassifyEvent.hasEvents(intent)) {


    .. .

    }


    }


    View Slide

  25. Permissions
    Android Manifest




    android:name=“android.permission.ACTIVITY_RECOGNITION"


    />

    View Slide

  26. Permissions
    Android Manifest


    / >

    MainActivity


    ContextCompat.checkSelfPermission(


    context,


    permission.ACTIVITY_RECOGNITION


    )_


    View Slide

  27. Permissions
    Android Manifest


    / >

    MainActivity


    ContextCompat.checkSelfPermission(context,permission.ACTIVITY_RECOGNITION)_


    val requestPermissionLauncher: ActivityResultLauncher =


    registerForActivityResult(ActivityResultContracts.RequestPermission())


    { isGranted
    ->

    if (!isGranted) {





    } else {


    //
    ready


    }


    }


    requestPermissionLauncher.launch(permission.ACTIVITY_RECOGNITION)


    View Slide

  28. Permissions
    Android Manifest


    / >

    MainActivity


    ContextCompat.checkSelfPermission(context,permission.ACTIVITY_RECOGNITION)_


    val requestPermissionLauncher: ActivityResultLauncher =


    registerForActivityResult(ActivityResultContracts.RequestPermission())


    { isGranted
    ->

    if (!isGranted) {


    requestActivityRecognitionPermission()


    } else {


    //
    ready


    }


    }


    requestPermissionLauncher.launch(permission.ACTIVITY_RECOGNITION)


    private fun requestActivityRecognitionPermission() {


    val intent = Intent().apply {


    action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS


    data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)


    flags = Intent.FLAG_ACTIVITY_NEW_TASK


    }


    startActivity(intent)


    }


    View Slide

  29. Subscribe to
    sleep updates
    MainActivity


    ActivityRecognition.getClient(context)


    .requestSleepSegmentUpdates(


    pendingIntent,


    SleepSegmentRequest.getDefaultSleepSegmentRequest()


    )


    View Slide

  30. Confidence
    Motion
    Light
    Segments

    View Slide

  31. SLEEP
    EXERCISE RECORD

    View Slide

  32. GOOGLE FIT API
    RECORD

    View Slide

  33. Choose options
    val fitnessOptions = FitnessOptions.builder()


    .addDataType(DataType.TYPE_STEP_COUNT_DELTA,


    FitnessOptions.ACCESS_READ)


    .addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA,


    FitnessOptions.ACCESS_READ)


    .build()

    View Slide

  34. Request
    permissions
    val account =


    GoogleSignIn.getAccountForExtension(


    activity, fitnessOptions)


    if (!GoogleSignIn.hasPermissions(account, fitnessOptions)) {


    GoogleSignIn.requestPermissions(


    activity,


    PERMISSIONS_REQUEST_CODE,


    account,


    fitnessOptions)


    } else {


    recordToGoogleFit()


    }

    View Slide

  35. Choose options
    override fun onActivityResult(requestCode: Int,


    resultCode: Int,


    data: Intent?) {




    super.onActivityResult(requestCode, resultCode, data)




    when (resultCode) {


    Activity.RESULT_OK
    -
    >
    when (requestCode) {


    PERMISSIONS_REQUEST_CODE
    ->
    recordToGoogleFit()


    else
    ->
    {
    .
    ..
    }


    }


    else
    -
    >
    {
    ...
    }


    }


    }

    View Slide

  36. Create a session
    val session = Session.Builder()


    .setName("Today's Run")


    .setIdentifier("Unique_Identifier_Here")


    .setDescription("Long run around home")


    .setActivity(FitnessActivities.RUNNING)


    .setStartTime(startTime, TimeUnit.MILLISECONDS)


    .setEndTime(endTime, TimeUnit.MILLISECONDS)


    .build()


    View Slide

  37. Create a request
    val insertRequest =


    SessionInsertRequest.Builder()


    .setSession(session)


    .build()

    View Slide

  38. Insert request
    Fitness.getSessionsClient(this,


    GoogleSignIn.getAccountForExtension(this, fitnessOptions)


    )


    .insertSession(insertRequest)


    .addOnSuccessListener {


    ...

    }


    .addOnFailureListener { e
    ->

    ...

    }

    View Slide

  39. Q&A
    Photo by Simone Secci on Unsplash

    View Slide