You can use these GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener methods but they are deprecated
here is the example
class WearOsActivity : AppCompatActivity(), CapabilityClient.OnCapabilityChangedListener,
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private lateinit var signInAccount: GoogleSignInAccount
private lateinit var mGoogleApiClient: GoogleApiClient
private lateinit var capabilityClient: CapabilityClient
private lateinit var nodeClient: NodeClient
private lateinit var remoteActivityHelper: RemoteActivityHelper
private var wearNodesWithApp: Set<Node>? = null
private var allConnectedNodes: List<Node>? = null
val remoteOpenButton by lazy { findViewById<Button>(R.id.connect_watch) }
val informationTextView by lazy { findViewById<TextView>(R.id.informationTextView) }
private val fitnessOptions =
FitnessOptions.builder().addDataType(DataType.TYPE_STEP_COUNT_DELTA).build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_wear_os)
capabilityClient = Wearable.getCapabilityClient(this)
nodeClient = Wearable.getNodeClient(this)
remoteActivityHelper = RemoteActivityHelper(this)
mGoogleApiClient = GoogleApiClient.Builder(this)
.addApi(Fitness.HISTORY_API)
.addApi(Fitness.RECORDING_API)
.addScope(Scope(Scopes.PROFILE))
.build()
mGoogleApiClient.connect()
if (!GoogleSignIn.hasPermissions(
GoogleSignIn.getLastSignedInAccount(this),
fitnessOptions
)
) {
signInAccount = GoogleSignIn.getAccountForExtension(this, fitnessOptions)
GoogleSignIn.requestPermissions(
this, // or FragmentActivity
0,
signInAccount,
fitnessOptions
)
} else {
// If permissions are already granted, directly call the function to get total calories
getTotalCalories()
}
val fitnessOptions = FitnessOptions.builder()
.addDataType(DataType.TYPE_CALORIES_EXPENDED, FitnessOptions.ACCESS_READ)
.build()
if (!GoogleSignIn.hasPermissions(
GoogleSignIn.getLastSignedInAccount(this),
fitnessOptions
)
) {
val signInAccount = GoogleSignIn.getAccountForExtension(this, fitnessOptions)
GoogleSignIn.requestPermissions(
this, // or FragmentActivity
0,
signInAccount,
fitnessOptions
)
} else {
// If permissions are already granted, directly call the function to get total calories
getTotalCalories()
}
remoteOpenButton.setOnClickListener {
openPlayStoreOnWearDevicesWithoutApp()
}
updateUI()
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
launch {
// Initial request for devices with our capability, aka, our Wear app installed.
findWearDevicesWithApp()
}
launch {
// Initial request for all Wear devices connected (with or without our capability).
// Additional Note: Because there isn't a listener for ALL Nodes added/removed from network
// that isn't deprecated, we simply update the full list when the Google API Client is
// connected and when capability changes come through in the onCapabilityChanged() method.
findAllWearDevices()
}
}
}
}
override fun onCapabilityChanged(capabilityInfo: CapabilityInfo) {
Log.d(TAG, "onCapabilityChanged(): $capabilityInfo")
wearNodesWithApp = capabilityInfo.nodes
lifecycleScope.launch {
// Because we have an updated list of devices with/without our app, we need to also update
// our list of active Wear devices.
findAllWearDevices()
}
}
override fun onPause() {
Log.d(TAG, "onPause()")
super.onPause()
capabilityClient.removeListener(this, CAPABILITY_WEAR_APP)
}
override fun onResume() {
Log.d(TAG, "onResume()")
super.onResume()
capabilityClient.addListener(this, CAPABILITY_WEAR_APP)
}
private fun getTotalCalories() {
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusDays(1)
Log.i(TAG, "Range Start: $startTime")
Log.i(TAG, "Range End: $endTime")
val readStepsRequest =
DataReadRequest.Builder()
// The data request can specify multiple data types to return,
// effectively combining multiple data queries into one call.
// This example demonstrates aggregating only one data type.
.aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
// Analogous to a "Group By" in SQL, defines how data should be
// aggregated.
// bucketByTime allows for a time span, whereas bucketBySession allows
// bucketing by <a href="/fit/android/using-sessions">sessions</a>.
.bucketByTime(1, TimeUnit.DAYS)
.setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
.build()
val readCalRequest = DataReadRequest.Builder()
.aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
.bucketByActivityType(1, TimeUnit.SECONDS)
.setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
.build()
Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
.readData(readCalRequest)
.addOnSuccessListener { response ->
// The aggregate query puts datasets into buckets, so flatten into a
// single list of datasets
for (dataSet in response.buckets.flatMap { it.dataSets }) {
dumpDataSet(dataSet)
}
}
.addOnFailureListener { e ->
Log.w(TAG, "There was an error reading data from Google Fit", e)
}
}
private fun dumpDataSet(dataSet: DataSet) {
Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name}")
for (dp in dataSet.dataPoints) {
Log.i(TAG, "Data point:")
Log.i(TAG, "\tType: ${dp.dataType.name}")
Log.i(TAG, "\tStart: ${dp.getStartTimeString()}")
Log.i(TAG, "\tEnd: ${dp.getEndTimeString()}")
for (field in dp.dataType.fields) {
Log.i(TAG, "\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
}
}
}
private fun DataPoint.getStartTimeString() =
Instant.ofEpochSecond(this.getStartTime(TimeUnit.SECONDS))
.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString()
private fun DataPoint.getEndTimeString() =
Instant.ofEpochSecond(this.getEndTime(TimeUnit.SECONDS))
.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString()
private suspend fun findWearDevicesWithApp() {
Log.d(TAG, "findWearDevicesWithApp()")
try {
val capabilityInfo = capabilityClient
.getCapability(CAPABILITY_WEAR_APP, CapabilityClient.FILTER_ALL)
.await()
withContext(Dispatchers.Main) {
Log.d(TAG, "Capability request succeeded.")
wearNodesWithApp = capabilityInfo.nodes
Log.d(TAG, "Capable Nodes: $wearNodesWithApp")
updateUI()
}
} catch (cancellationException: CancellationException) {
// Request was cancelled normally
throw cancellationException
} catch (throwable: Throwable) {
Log.d(TAG, "Capability request failed to return any results.")
}
}
private suspend fun findAllWearDevices() {
Log.d(TAG, "findAllWearDevices()")
try {
val connectedNodes = nodeClient.connectedNodes.await()
withContext(Dispatchers.Main) {
allConnectedNodes = connectedNodes
updateUI()
}
} catch (cancellationException: CancellationException) {
// Request was cancelled normally
} catch (throwable: Throwable) {
Log.d(TAG, "Node request failed to return any results.")
}
}
private fun updateUI() {
Log.d(TAG, "updateUI()")
val wearNodesWithApp = wearNodesWithApp
val allConnectedNodes = allConnectedNodes
when {
wearNodesWithApp == null || allConnectedNodes == null -> {
Log.d(TAG, "Waiting on Results for both connected nodes and nodes with app")
informationTextView.text = getString(R.string.message_checking)
remoteOpenButton.alpha = 0.5f
remoteOpenButton.isEnabled = false
}
allConnectedNodes.isEmpty() -> {
Log.d(TAG, "No devices")
informationTextView.text = getString(R.string.message_checking)
remoteOpenButton.alpha = 0.5f
remoteOpenButton.isEnabled = false
}
wearNodesWithApp.isEmpty() -> {
Log.d(TAG, "Missing on all devices")
informationTextView.text = getString(R.string.message_missing_all)
remoteOpenButton.alpha = 1f
remoteOpenButton.isEnabled = true
}
wearNodesWithApp.size < allConnectedNodes.size -> {
// TODO: Add your code to communicate with the wear app(s) via Wear APIs
// (MessageClient, DataClient, etc.)
Log.d(TAG, "Installed on some devices")
informationTextView.text =
getString(R.string.message_some_installed, wearNodesWithApp.toString())
remoteOpenButton.alpha = 1f
remoteOpenButton.isEnabled = true
}
else -> {
// TODO: Add your code to communicate with the wear app(s) via Wear APIs
// (MessageClient, DataClient, etc.)
Log.d(TAG, "Installed on all devices")
informationTextView.text =
getString(R.string.message_all_installed, wearNodesWithApp.toString())
remoteOpenButton.alpha = 0.5f
remoteOpenButton.isEnabled = false
}
}
}
private fun openPlayStoreOnWearDevicesWithoutApp() {
Log.d(TAG, "openPlayStoreOnWearDevicesWithoutApp()")
val wearNodesWithApp = wearNodesWithApp ?: return
val allConnectedNodes = allConnectedNodes ?: return
// Determine the list of nodes (wear devices) that don't have the app installed yet.
val nodesWithoutApp = allConnectedNodes - wearNodesWithApp
Log.d(TAG, "Number of nodes without app: " + nodesWithoutApp.size)
val intent = Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse(PLAY_STORE_APP_URI))
// In parallel, start remote activity requests for all wear devices that don't have the app installed yet.
nodesWithoutApp.forEach { node ->
lifecycleScope.launch {
try {
remoteActivityHelper
.startRemoteActivity(
targetIntent = intent,
targetNodeId = node.id
)
.await()
Toast.makeText(
this@WearOsActivity,
"The App is Successfully Installed on your wearOs",
Toast.LENGTH_SHORT
).show()
} catch (cancellationException: CancellationException) {
// Request was cancelled normally
} catch (throwable: Throwable) {
Toast.makeText(
this@WearOsActivity,
"Install request failed",
Toast.LENGTH_LONG
).show()
}
}
}
}
companion object {
private const val TAG = "MainMobileActivity"
// Name of capability listed in Wear app's wear.xml.
// IMPORTANT NOTE: This should be named differently than your Phone app's capability.
private const val CAPABILITY_WEAR_APP = "verify_remote_example_wear_app"
// Links to Wear app (Play Store).
// TODO: Replace with your links/packages.
private const val PLAY_STORE_APP_URI =
"market://details?id=com.yewapp"
}
override fun onConnected(p0: Bundle?) {
TODO("Not yet implemented")
}
override fun onConnectionSuspended(p0: Int) {
TODO("Not yet implemented")
}
override fun onConnectionFailed(p0: ConnectionResult) {
TODO("Not yet implemented")
}
}