androidandroid-jetpack-composewear-osambient

Capability Info, Health Service, and Ambient Mode


I'm trying to write a fitness companion watch app that would collect heart rate, and calories via HealthServices API, and send them to the device, where we display a workout. I've been following suggested examples: https://github.com/android/wear-os-samples/tree/main/AlwaysOnKotlin, https://github.com/android/health-samples/tree/2220ea6611770b56350d26502faefc28791f3cbd/health-services/ExerciseSample, and https://github.com/googlecodelabs/ongoing-activity .

I'm trying to achieve the following workflow:

All of these work somewhat well until the watch goes into ambient mode. Then I run into the following problems:

I use message client on phone to send message to the wearable. But nothing happens here, since the current node is empty.

currentNode?.also { nodeId ->
            val sendTask: Task<*>? =
                messageClient
                    ?.sendMessage(nodeId, WORKOUT_STATUS_MESSAGE_PATH, "START.toByteArray())

Code for the ambient mode in MainActivity (I'm still learning Compose, so right now Main activity is where it's at, to eliminate other Compose specific errors):

In Manifest:

<uses-permission android:name="android.permission.WAKE_LOCK" />

In Main Activity:

class MainActivity : FragmentActivity(), AmbientModeSupport.AmbientCallbackProvider {

...

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    ambientController = AmbientModeSupport.attach(this)

    setContent {
        val ambientEvent by mainViewModel.ambientEventFlow.collectAsState()
        StatsScreen(ambientEvent)
    }
        ...
}

override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback = AmbientModeCallback()

inner class AmbientModeCallback : AmbientModeSupport.AmbientCallback() {
    override fun onEnterAmbient(ambientDetails: Bundle) {
        Timber.e("ambient event: enter: $ambientDetails")
        mainViewModel.sendAmbientEvent(AmbientEvent.Enter(ambientDetails))
    }

    override fun onExitAmbient() {
        Timber.e("ambient event: exit")
        mainViewModel.sendAmbientEvent(AmbientEvent.Exit)
    }

    override fun onUpdateAmbient() {
        Timber.e("ambient event: update")
        mainViewModel.sendAmbientEvent(AmbientEvent.Update)
    }
}

I don't see anything printed in this callback, and then consequently, by StateScreen doesn't really do anything when the device enters in the ambient mode.


Solution

  • On Android 13 you need BODY_SENSORS_BACKGROUND permission. See: https://developer.android.com/about/versions/13/behavior-changes-13#body-sensors-background-permission