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.
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: