From 86b81ba744514e6d1bcc62e8e8c6bd56a50f721f Mon Sep 17 00:00:00 2001 From: garan Date: Tue, 23 Sep 2025 12:42:05 +0100 Subject: [PATCH 1/2] Updates remote launch from Wear OS --- app/src/main/res/values/wear.xml | 21 +++++++++ .../transfer/WearDeviceRepository.kt | 6 +-- .../common/WatchFaceInstallationStatus.kt | 2 +- .../wear/common/WearableConstants.kt | 3 +- .../androidify/LaunchOnPhoneActivity.kt | 47 +++++++++++++++++-- 5 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 app/src/main/res/values/wear.xml diff --git a/app/src/main/res/values/wear.xml b/app/src/main/res/values/wear.xml new file mode 100644 index 00000000..b011d0d0 --- /dev/null +++ b/app/src/main/res/values/wear.xml @@ -0,0 +1,21 @@ + + + + + androidify_phone + + \ No newline at end of file diff --git a/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/WearDeviceRepository.kt b/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/WearDeviceRepository.kt index fde3e4da..bc033e45 100644 --- a/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/WearDeviceRepository.kt +++ b/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/WearDeviceRepository.kt @@ -19,7 +19,7 @@ package com.android.developers.androidify.watchface.transfer import android.content.Context import com.android.developers.androidify.wear.common.ConnectedWatch -import com.android.developers.androidify.wear.common.WearableConstants.ANDROIDIFY_INSTALLED +import com.android.developers.androidify.wear.common.WearableConstants.ANDROIDIFY_INSTALLED_WEAR import com.google.android.gms.wearable.CapabilityClient import com.google.android.gms.wearable.Node import com.google.android.gms.wearable.NodeClient @@ -56,7 +56,7 @@ class WearDeviceRepositoryImpl @Inject constructor( val allDevices = nodeClient.connectedNodes.await().toSet() val reachableCapability = capabilityClient.getCapability( - ANDROIDIFY_INSTALLED, + ANDROIDIFY_INSTALLED_WEAR, CapabilityClient.FILTER_REACHABLE, ) .await() @@ -70,7 +70,7 @@ class WearDeviceRepositoryImpl @Inject constructor( trySend(selectConnectedDevice(installedDevicesUpdated, allDevices)) } - capabilityClient.addListener(capabilityListener, ANDROIDIFY_INSTALLED) + capabilityClient.addListener(capabilityListener, ANDROIDIFY_INSTALLED_WEAR) } else { trySend(null) } diff --git a/wear/common/src/main/java/com/android/developers/androidify/wear/common/WatchFaceInstallationStatus.kt b/wear/common/src/main/java/com/android/developers/androidify/wear/common/WatchFaceInstallationStatus.kt index 70025bc4..39837d5d 100644 --- a/wear/common/src/main/java/com/android/developers/androidify/wear/common/WatchFaceInstallationStatus.kt +++ b/wear/common/src/main/java/com/android/developers/androidify/wear/common/WatchFaceInstallationStatus.kt @@ -45,7 +45,7 @@ sealed class WatchFaceInstallationStatus() { val activationStrategy: WatchFaceActivationStrategy, ) : WatchFaceInstallationStatus() - object Preparing: WatchFaceInstallationStatus() + object Preparing : WatchFaceInstallationStatus() object Sending : WatchFaceInstallationStatus() diff --git a/wear/common/src/main/java/com/android/developers/androidify/wear/common/WearableConstants.kt b/wear/common/src/main/java/com/android/developers/androidify/wear/common/WearableConstants.kt index b998b711..a8549f54 100644 --- a/wear/common/src/main/java/com/android/developers/androidify/wear/common/WearableConstants.kt +++ b/wear/common/src/main/java/com/android/developers/androidify/wear/common/WearableConstants.kt @@ -19,7 +19,8 @@ object WearableConstants { const val ANDROIDIFY_INITIATE_TRANSFER_PATH = "/initiate_transfer" const val ANDROIDIFY_FINALIZE_TRANSFER_TEMPLATE = "/finalize_transfer/%s" - const val ANDROIDIFY_INSTALLED = "androidify" + const val ANDROIDIFY_INSTALLED_WEAR = "androidify" + const val ANDROIDIFY_INSTALLED_PHONE = "androidify_phone" const val ANDROIDIFY_TRANSFER_PATH_TEMPLATE = "/transfer_apk/%s" const val SETUP_TIMEOUT_MS = 60_000L diff --git a/wear/src/main/java/com/android/developers/androidify/LaunchOnPhoneActivity.kt b/wear/src/main/java/com/android/developers/androidify/LaunchOnPhoneActivity.kt index 56f7caa1..7d5856b6 100644 --- a/wear/src/main/java/com/android/developers/androidify/LaunchOnPhoneActivity.kt +++ b/wear/src/main/java/com/android/developers/androidify/LaunchOnPhoneActivity.kt @@ -20,11 +20,16 @@ import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.core.net.toUri +import androidx.lifecycle.lifecycleScope import androidx.wear.remote.interactions.RemoteActivityHelper import androidx.wear.widget.ConfirmationOverlay import androidx.wear.widget.ConfirmationOverlay.OPEN_ON_PHONE_ANIMATION +import com.android.developers.androidify.wear.common.WearableConstants.ANDROIDIFY_INSTALLED_PHONE +import com.google.android.gms.wearable.CapabilityClient +import com.google.android.gms.wearable.Wearable import kotlinx.coroutines.guava.await -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.launch +import kotlinx.coroutines.tasks.await /** * A helper activity that launches the phone Androidify app. This Activity is only started from the @@ -34,8 +39,6 @@ class LaunchOnPhoneActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val intent = Intent(Intent.ACTION_VIEW, "androidify://launch".toUri()) - intent.addCategory(Intent.CATEGORY_BROWSABLE) val helper = RemoteActivityHelper(this) val message: CharSequence = getString(R.string.continue_on_phone) @@ -48,7 +51,14 @@ class LaunchOnPhoneActivity : ComponentActivity() { } .showOn(this) - runBlocking { + lifecycleScope.launch { + val phoneNodeId = getConnectedAndroidifyNodeId() + val intent = if (phoneNodeId != null) { + getAndroidifyIntent() + } else { + getPlayIntent() + } + try { helper.startRemoteActivity(intent).await() } catch (e: RemoteActivityHelper.RemoteIntentException) { @@ -57,7 +67,34 @@ class LaunchOnPhoneActivity : ComponentActivity() { } } - fun onAnimationFinished() { + private suspend fun getConnectedAndroidifyNodeId(): String? { + val capabilityClient = Wearable.getCapabilityClient(this) + + val capabilities = capabilityClient.getCapability( + ANDROIDIFY_INSTALLED_PHONE, + CapabilityClient.FILTER_REACHABLE, + ) + .await() + return if (capabilities.nodes.isNotEmpty()) { + capabilities.nodes.first().id + } else { + null + } + } + + private fun getPlayIntent(): Intent { + val intent = Intent(Intent.ACTION_VIEW, "market://details?id=$packageName".toUri()) + intent.addCategory(Intent.CATEGORY_BROWSABLE) + return intent + } + + private fun getAndroidifyIntent(): Intent { + val intent = Intent(Intent.ACTION_VIEW, "androidify://launch".toUri()) + intent.addCategory(Intent.CATEGORY_BROWSABLE) + return intent + } + + private fun onAnimationFinished() { finish() } } From 6447dd45e49cd3e891c1fa17c39a6238ca2c8b70 Mon Sep 17 00:00:00 2001 From: garan Date: Tue, 23 Sep 2025 13:01:01 +0100 Subject: [PATCH 2/2] Addresses comments --- app/src/main/res/values/wear.xml | 2 +- .../androidify/wear/common/WearableConstants.kt | 3 +++ .../developers/androidify/LaunchOnPhoneActivity.kt | 12 +++++------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values/wear.xml b/app/src/main/res/values/wear.xml index b011d0d0..828b9d71 100644 --- a/app/src/main/res/values/wear.xml +++ b/app/src/main/res/values/wear.xml @@ -18,4 +18,4 @@ androidify_phone - \ No newline at end of file + diff --git a/wear/common/src/main/java/com/android/developers/androidify/wear/common/WearableConstants.kt b/wear/common/src/main/java/com/android/developers/androidify/wear/common/WearableConstants.kt index a8549f54..62b5ffc2 100644 --- a/wear/common/src/main/java/com/android/developers/androidify/wear/common/WearableConstants.kt +++ b/wear/common/src/main/java/com/android/developers/androidify/wear/common/WearableConstants.kt @@ -23,6 +23,9 @@ object WearableConstants { const val ANDROIDIFY_INSTALLED_PHONE = "androidify_phone" const val ANDROIDIFY_TRANSFER_PATH_TEMPLATE = "/transfer_apk/%s" + const val ANDROIDIFY_PLAY_URL = "market://details?id=" + const val ANDROIDIFY_LAUNCH_URL = "androidify://launch" + const val SETUP_TIMEOUT_MS = 60_000L const val TRANSFER_TIMEOUT_MS = 60_000L } diff --git a/wear/src/main/java/com/android/developers/androidify/LaunchOnPhoneActivity.kt b/wear/src/main/java/com/android/developers/androidify/LaunchOnPhoneActivity.kt index 7d5856b6..f38370a0 100644 --- a/wear/src/main/java/com/android/developers/androidify/LaunchOnPhoneActivity.kt +++ b/wear/src/main/java/com/android/developers/androidify/LaunchOnPhoneActivity.kt @@ -25,6 +25,8 @@ import androidx.wear.remote.interactions.RemoteActivityHelper import androidx.wear.widget.ConfirmationOverlay import androidx.wear.widget.ConfirmationOverlay.OPEN_ON_PHONE_ANIMATION import com.android.developers.androidify.wear.common.WearableConstants.ANDROIDIFY_INSTALLED_PHONE +import com.android.developers.androidify.wear.common.WearableConstants.ANDROIDIFY_LAUNCH_URL +import com.android.developers.androidify.wear.common.WearableConstants.ANDROIDIFY_PLAY_URL import com.google.android.gms.wearable.CapabilityClient import com.google.android.gms.wearable.Wearable import kotlinx.coroutines.guava.await @@ -75,21 +77,17 @@ class LaunchOnPhoneActivity : ComponentActivity() { CapabilityClient.FILTER_REACHABLE, ) .await() - return if (capabilities.nodes.isNotEmpty()) { - capabilities.nodes.first().id - } else { - null - } + return capabilities.nodes.firstOrNull()?.id } private fun getPlayIntent(): Intent { - val intent = Intent(Intent.ACTION_VIEW, "market://details?id=$packageName".toUri()) + val intent = Intent(Intent.ACTION_VIEW, "$ANDROIDIFY_PLAY_URL$packageName".toUri()) intent.addCategory(Intent.CATEGORY_BROWSABLE) return intent } private fun getAndroidifyIntent(): Intent { - val intent = Intent(Intent.ACTION_VIEW, "androidify://launch".toUri()) + val intent = Intent(Intent.ACTION_VIEW, ANDROIDIFY_LAUNCH_URL.toUri()) intent.addCategory(Intent.CATEGORY_BROWSABLE) return intent }