Sébastien Pouy
Sébastien Pouy

Reputation: 1

Impossible to send data from Wear OS app to phone app (kotlin) with DataClient (Google services)

I am using the latest version of Android Studio. I own a Samsung Galaxy Watch 6 classic and a Samsung S24 ultra (both fully up to date firmware-wise).

I coded in Kotlin a Wear OS app for ice skaters. It records several types of data during the session, like the session date, duration, average and max speed, skated distance, heart rate, calories burnt etc.

I also coded, in the same project, the companion phone app (a second module) that is supposed to receive the data. On the phone app, I click on "free skating session", and the date of the session is supposed to show up, click on the date and display the other values in a lazy column.

The problem is, I can't get the data to be sent to the phone app.

On my watch app, I am using a ViewModel that retrieves and updates the values.

When the session is done, the data are "parcelized" (I have the data class SessionData on a third module named common, it is shared between the watch and phone app) and I use putDataMapRequest to send them:

suspend fun sendSessionDataToDataLayer(sessionData: SessionData, context: Context) {
    val bundle = Bundle().apply {
        putParcelable("sessionData", sessionData)
    }
    val putDataMapRequest = PutDataMapRequest.create("/session-data").apply {
        dataMap.putAll(DataMap.fromBundle(bundle))
    }
    val putDataRequest = putDataMapRequest.asPutDataRequest()

    try {
        Log.d("sendSessionDataToDataLayer", "Sending session data: $sessionData")
        Wearable.getDataClient(context).putDataItem(putDataRequest).await()
        Log.d("sendSessionDataToDataLayer", "Data sent successfully")

        // Show a toast to the user on the watch
        Toast.makeText(context, "Data queued for transfer", Toast.LENGTH_SHORT).show()

        resetSession() // Only reset after successful data transfer
    } catch (e: Exception) {
        Log.e("sendSessionDataToDataLayer", "Failed to send session data", e)
        // Optionally show an error toast
        Toast.makeText(context, "Data send failed: ${e.message}", Toast.LENGTH_SHORT).show()
    }
}

The path is "/session-data", the same in the watch and phone app. The toast I get from this: "data queued for transfer"

Then I have my listener on the phone app with the OnDatachanged function. It is supposed to retrieve the data and deparcelize them. But the OnDataChanged never fires...

class MyDataLayerListenerService : WearableListenerService() {
override fun onDataChanged(dataEvents: DataEventBuffer) {
    // Indicate that onDataChanged was called at all
    Toast.makeText(this, "onDataChanged triggered!", Toast.LENGTH_SHORT).show()

    dataEvents.forEach { event ->
        if (event.type == DataEvent.TYPE_CHANGED && event.dataItem.uri.path == "/session-data") {
            try {
                val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap
                val bundle = dataMap.toBundle()
                val sessionData: SessionData? = bundle.getParcelable("sessionData")

                if (sessionData != null) {
                    Toast.makeText(this, "Received session data!", Toast.LENGTH_SHORT).show()
                    saveSessionDataLocally(sessionData)
                } else {
                    Toast.makeText(this, "SessionData is null", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                Toast.makeText(this, "Failed to parse session data: ${e.message}", Toast.LENGTH_LONG).show()
            }
        }
    }
}

private fun saveSessionDataLocally(sessionData: SessionData) {
    // e.g. show a final toast
    Toast.makeText(this, "Session data saved: ${sessionData.distanceSkated}", Toast.LENGTH_SHORT).show()
}

}

From this, I don't get any toasts. On the phone screen, I should see a row with the dates sessions, which does not happen. Data are not transfered.

I added helping toasts to have an idea where this fails. When the session is done, the watch toast says "data queued for transfer". So it looks like the watch thinks the job is correctly done. I also implemented toasts on the phone but they never show. Just a blank page instead of the skating session date.

In an 10 y.o. topic, they recommended that the phone and the watch must have the same application ID. Likewise, I thought I needed node IDs to send the data but it is only for MessageCLient, not DataClient.

I also did some basic checks: watch and phone properly connected to one another, also logged into the same Google account (and same Samsung account for what it's worth), on the same WIfi. I unpaired the watch and repaired. I reset completely the watch and repaired it to the phone. The google services on my watch is up to date as of January 2025 (24.49.33-705592033) and on the phone, Google play services is fully allowed and on high priority.

In the phone manifest, Google services wearable are there. I have the wearable.Bind.Listener and the wearable.data.changed. The data path is good e.g. "/session-data".

Phone manifest:

<service
        android:name=".MyDataLayerListenerService"
        android:exported="true"
        android:permission="com.google.android.gms.permission.WEARABLE_BIND_LISTENER">

        <!-- If you want to listen for data changes at a specific path, declare a DATA_CHANGED action
             and optionally specify a pathPrefix to filter for certain paths (like "/session-data"). -->
        <intent-filter>
            <!-- Listen for data changes -->
            <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />

            <!-- (Optional) If you only care about a specific path in the data layer, add <data>: -->
            <data
                android:scheme="wear"
                android:host="*"
                android:pathPrefix="/session-data"  />
        </intent-filter>

    </service>

All the modules (watch, phone, common for the data class) are recognized and declared in the gradle files.

I tried to develop a simple project just to pass a simple string from my watch to phone but couldn't get it to work.

Upvotes: 0

Views: 102

Answers (1)

Gustavo Pagani
Gustavo Pagani

Reputation: 6988

Please review the following security requirements, otherwise your apps won't communicate to each other:

  • The package name must match across devices.
  • The signature of the package must match across devices.

Other sources of information/tutorial on this subject:

  1. DataLayer sample from Wear OS samples repo in github;
    • I would recommend running the apps from this sample in your phone and watch and see them working for you.
  2. DataLayer helpers from Horologist repo in github;

Upvotes: 0

Related Questions