androidkotlinwear-osandroid-wear-data-apiwearables

WearOS and Phone application does not communicate


Desired Application:

When I click the button on WearOS application, it should send a message data to the Phone app. When message received from phone app, the data should displayed on the screen or logcat.

I am trying to communicate a phone application and a wear OS application using the Kotlin language on Android studio. The application I want to do is simply send the data I set to the phone application when the button is pressed on WearOS. I've set up the MessageClient on both sides, and while I can send data from the Wear OS to the phone app, the onMessageReceived function on the phone app never seems to be triggered.Devices are paired.

Wear OS Code:

class MainActivity : ComponentActivity(),
    DataClient.OnDataChangedListener,
    MessageClient.OnMessageReceivedListener{

    private var activityContext: Context? = null
    private var dataClient: DataClient? = null
    private var messageClient: MessageClient? = null

    companion object {
        const val MESSAGE_PATH_SEND = "/send_message"
        const val MESSAGE_PATH_RECEIVE = "/receive_message"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            WearApp("Android")
        }

        activityContext = this

        dataClient = Wearable.getDataClient(this)
        messageClient = Wearable.getMessageClient(this)

    }



    @Composable
    fun WearApp(greetingName: String) {
        WearOSCommunicationTheme {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .background(MaterialTheme.colors.background),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                //Greeting(greetingName = greetingName)
                Text(text = "Example text")
                Button(
                    modifier = Modifier
                        .padding(10.dp)
                        .fillMaxWidth(),
                    onClick = { sendMessageToPhone("this is an example message") }) {

                    Text(text = "Send Msg")

                }
            }
        }
    }

    private fun sendMessageToPhone(message: String) {
        Wearable.getNodeClient(activityContext!!).connectedNodes.addOnSuccessListener { nodes ->
            for (node in nodes) {
                if (node.isNearby) {  // Check if the node is reachable
                    Wearable.getMessageClient(activityContext!!).sendMessage(node.id, MESSAGE_PATH_SEND, message.toByteArray(Charsets.UTF_8))
                        .addOnSuccessListener {
                            // Handle success here with a toast
                            Log.d("WearOSLog", "Message sent successfully to ${node.id}")
                        }
                        .addOnFailureListener { exception ->
                            // Handle failure here with a toast
                            Log.e("WearOSLog", "Failed to send message to ${node.id}", exception)
                        }
                } else {
                    Log.d("WearOSLog", "Node ${node.id} is not reachable")
                }
            }
        }.addOnFailureListener { exception ->
            // Handle failure to get connected nodes here
            Log.e("WearOSLog", "Failed to get connected nodes", exception)
        }
    }


    override fun onDataChanged(dataEvents: DataEventBuffer) {
        for (event in dataEvents) {
            if (event.type == DataEvent.TYPE_CHANGED){
                // Handle data change
            }
        }
    }

    override fun onMessageReceived(messageEvent: MessageEvent) {
        TODO("Not yet implemented")
    }


    override fun onResume() {
        super.onResume()

        try {
            Wearable.getDataClient(activityContext!!).addListener(this)
            Wearable.getMessageClient(activityContext!!).addListener(this)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun onPause() {
        super.onPause()
        Wearable.getDataClient(activityContext!!).removeListener(this)
        Wearable.getMessageClient(activityContext!!).removeListener(this)
    }

}

Phone App Code:

class MainActivity : AppCompatActivity(),
    MessageClient.OnMessageReceivedListener,
    DataClient.OnDataChangedListener{

    companion object {
        const val MESSAGE_PATH_SEND = "/send_message"
        const val MESSAGE_PATH_RECEIVE = "/receive_message"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onMessageReceived(messageEvent: MessageEvent) {
        try {
            if (messageEvent.path == MESSAGE_PATH_SEND) {
                val message = String(messageEvent.data, Charsets.UTF_8)
                Log.d("PhoneSide", "Message received: $message");
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Log.d("PhoneSide", "error")
        }
    }

    override fun onResume() {
        super.onResume()

        try {
            Wearable.getDataClient(this).addListener(this)
            Wearable.getMessageClient(this).addListener(this)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun onPause() {
        super.onPause()

        try {
            Wearable.getDataClient(this).removeListener(this)
            Wearable.getMessageClient(this).removeListener(this)
        }  catch (e: Exception) {
            e.printStackTrace()
        }
    }


    override fun onDataChanged(p0: DataEventBuffer) {
        TODO("Not yet implemented")
    }
}

Also related manifest documents are:

WearOS Manifets File:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

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

    <uses-feature android:name="android.hardware.type.watch" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@android:style/Theme.DeviceDefault">
        <uses-library
            android:name="com.google.android.wearable"
            android:required="true" />

        <!--
               Set to true if your app is Standalone, that is, it does not require the handheld
               app to run.
        -->
        <meta-data
            android:name="com.google.android.wearable.standalone"
            android:value="true" />

        <activity
            android:name=".presentation.MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.DeviceDefault">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Phone App Manifest File:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.WearOSCommunication"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

I read related parts of Android Developer page but I couldn't find any related path for this type of applications. I could not find almost any information or tutorial on this subject on the internet. I would be glad if you help.


Solution

  • The documentation on the Android developer page is under the Handling Data topic.

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

    After fixing your issue, consider improving your code to use capabilities to only deliver the message to the node that contains your app capabilities.

    Other sources of information/tutorial on this subject: