From Wrist to Hand: Display Mobile Device Battery Level on Your Galaxy Watch
Samiul Hossain
Engineer, Samsung Developer Program
Galaxy Watch apps can offer a range of features and services, but the watch's smaller size compared to a mobile device means there are limitations, including fewer hardware resources and a smaller screen.
To make the most of the capabilities of a mobile device, you can develop a companion mobile application for the wearable application. The companion application handles complex and resource-intensive tasks while the wearable application provides a seamless experience.
Previously in this series, we showed how to create a companion mobile application for a Galaxy Watch running Wear OS powered by Samsung and use the Wearable Data Layer API to send messages from the watch to the mobile device.
While it is easy to check the watch's battery level from the mobile device, the reverse is more complex. This tutorial describes how to establish two-way communication between the wearable and mobile applications and use it to check the mobile device's battery level from the watch.
Prerequisites
To develop a wearable application and its companion mobile application, create a multi-module project in Android Studio. The steps are described in the previous tutorial, and the same dependencies and modifications are required for the wearable application in this tutorial:
- Add the following dependencies to the build.gradle file
dependencies { ... implementation "com.google.android.gms:play-services-wearable:xx.x.x" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x.x" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:x.x.x" implementation "androidx.lifecycle:lifecycle-extensions:x.x.x" implementation "androidx.lifecycle:lifecycle-runtime-ktx:x.x.x" implementation "androidx.appcompat:appcompat:x.x.x" }
- Modify the
MainActivity
class to inheritAppCompatActivity()
instead ofActivity()
. - In the "AndroidManifest.xml" file, in the
<application>
element, change theandroid:theme
attribute value to "@style/Theme.AppCompat.NoActionBar" or another custom theme.
To test the project, you need a Galaxy Watch running Wear OS powered by Samsung and a connected Galaxy mobile device.
Request battery information from the companion application
To be able to retrieve the mobile device's battery level as a percentage from the watch, the companion application on the mobile device must advertise the "battery_percentage" capability.
In the mobile application, create an XML file named "wear.xml" in the "res/values/" directory with the following content:
<!--XML configuration file-->
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@array/android_wear_capabilities">
<string-array name="android_wear_capabilities">
<item>battery_percentage</item>
</string-array>
</resources>
While a watch can be connected to only one device at a time, a mobile device can be connected to multiple wearables at the same time. To determine which node (connected device) corresponds to the watch, use the CapabilityClient
class of the Wearable Data Layer API to retrieve all the available nodes and select the best or closest node to deliver your message.
// MainActivity class in the wearable application
private var batteryNodeId: String? = null
private fun setupBatteryPercentage() {
// Store the reachable nodes
val capabilityInfo: CapabilityInfo = Tasks.await(
Wearable.getCapabilityClient(applicationContext)
// Retrieve all connected nodes with the 'battery_percentage' capability
.getCapability(
BATTERY_PERCENTAGE_CAPABILITY_NAME,
CapabilityClient.FILTER_REACHABLE
)
)
// Use a listener to retrieve the reachable nodes
updateBatteryCapability(capabilityInfo).also { capabilityListener ->
Wearable.getCapabilityClient(this @MainActivity).addListener(
capabilityListener,
BATTERY_PERCENTAGE_CAPABILITY_NAME
)
}
}
private fun pickBestNodeId(nodes: Set<Node>): String? {
// Find the best node
return nodes.firstOrNull { it.isNearby }?.id ?: nodes.firstOrNull()?.id
}
private fun updateBatteryCapability(capabilityInfo: CapabilityInfo) {
// Specify the recipient node for the message
batteryNodeId = pickBestNodeId(capabilityInfo.nodes)
}
companion object{
private const val TAG = "MainWearActivity"
private const val BATTERY_PERCENTAGE_CAPABILITY_NAME = "battery_percentage"
private const val BATTERY_MESSAGE_PATH = "/message_battery"
}
To implement bi-directional communication between watch and mobile device, you can use the MessageClient
class of the Wearable Data Layer API.
In the wearable application UI, create a button and a textView.
To display the mobile device's battery level on the textView when the button is tapped, implement the button's onClickListener()
function. Send the battery level request message through a specific message path to the mobile device, using a coroutine that calls the setupBatteryPercentage()
and requestBatteryPercentage()
methods on a separate thread. A separate thread must be used because these are synchronous calls that block the UI thread.
// MainActivity class in the wearable application
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
Log.d(TAG, "onCreate()")
binding.apply{
phoneButton.setOnClickListener{
lifecycleScope.launch(Dispatchers.IO){
setupBatteryPercentage()
requestBatteryPercentage("Battery".toByteArray())
}
}
}
}
// Deliver the message to the selected node
private fun requestBatteryPercentage(data: ByteArray) {
batteryNodeId?.also { nodeId ->
val sendTask: Task<*> = Wearable.getMessageClient(this @MainActivity).sendMessage(
nodeId,
BATTERY_MESSAGE_PATH,
data
).apply {
addOnSuccessListener { Log.d(TAG, "OnSuccess") }
addOnFailureListener { Log.d(TAG, "OnFailure") }
}
}
}
Receive the message on the companion application
The companion application must be able to receive and respond to the message from the background. To accomplish this, implement a service that listens for incoming messages.
In the mobile application, create a class that extends WearableListenerService()
and add the service to the application manifest file:
<!--Android manifest file for the mobile application-->
<service
android:name=".PhoneListenerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data
android:host="*"
android:pathPrefix="/"
android:scheme="wear" />
</intent-filter>
</service>
Use the batteryManager
class to retrieve the current battery level of the device. To send the retrieved value back to the wearable application, use the sendMessage()
function again. For simplicity, send the message to the first connected node on the mobile device. Alternatively, you can broadcast to all connected nodes.
Implement the OnMessageReceived()
function to receive the incoming request for battery level and send the retrieved value to the wearable application.
// Service class in the mobile application
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
private var batteryNodeId: String? = null
override fun onDestroy() {
scope.cancel()
super.onDestroy()
}
override fun onMessageReceived(messageEvent: MessageEvent) {
Log.d(TAG, "onMessageReceived(): $messageEvent")
Log.d(TAG, String(messageEvent.data))
if (messageEvent.path == BATTERY_MESSAGE_PATH && String(messageEvent.data) == "Battery") {
// Check that the request and path are correct
val batteryManager = applicationContext.getSystemService(BATTERY_SERVICE) as BatteryManager
val batteryValue:Int = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
scope.launch(Dispatchers.IO){
// Send the message to the first node
batteryNodeId = getNodes().first()?.also { nodeId->
val sendTask: Task<*> = Wearable.getMessageClient(applicationContext).sendMessage(
nodeId,
BATTERY_MESSAGE_PATH,
batteryValue.toString().toByteArray()
).apply {
addOnSuccessListener { Log.d(TAG, "OnSuccess") }
addOnFailureListener { Log.d(TAG, "OnFailure") }
}
}
}
}
onDestroy()
}
private fun getNodes(): Collection<String> {
return Tasks.await(Wearable.getNodeClient(this).connectedNodes).map { it.id }
}
companion object{
private const val TAG = "PhoneListenerService"
private const val BATTERY_MESSAGE_PATH = "/message_battery"
}
Display the battery information on the wearable application
When receiving the battery information on the wearable application, because the user is actively interacting with the application, a resource-intensive service is not needed and registering a live listener is sufficient. Use the addListener()
method of the MessageClient
class to implement the MessageClient.OnMessageReceivedListener
interface within the MainActivity
class in the wearable application.
// MainActivity class in the wearable application
override fun onResume(){
super.onResume()
Log.d(TAG, "onResume()")
// Wearable API clients are not resource-intensive
Wearable.getMessageClient(this).addListener(this)
}
override fun onPause(){
super.onPause()
Log.d(TAG, "onPause()")
Wearable.getMessageClient(this).removeListener(this)
}
override fun onMessageReceived(messageEvent: MessageEvent) {
// Receive the message and display it
if(messageEvent.path == BATTERY_MESSAGE_PATH){
Log.d(TAG, "Mobile battery percentage: " + String(messageEvent.data) + "%")
binding.phoneTextView.text = String(messageEvent.data)
}
}
Conclusion
To test the project, build both applications and run them on your Galaxy Watch and mobile device. When you tap the UI button on the watch, the application retrieves the battery level from the mobile device and displays the percentage on the watch.
This demonstration has shown how the Wearable Data Layer API enables you to implement seamless bi-directional communication between a Galaxy Watch running Wear OS powered by Samsung and its connected mobile device. In addition to battery level, you can use the CapabilityClient
and MessageClient
classes to transfer various data between the devices in a similar way.
For more information about implementing communication between watch and mobile devices, see Send and receive messages on Wear.
If you have questions about or need help with the information in this tutorial, you can share your queries on the Samsung Developers Forum. For more specialized support, you can contact us through Samsung Developer Support.
Stay tuned for the next installment in this tutorial series.