Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions app/src/main/res/values/wear.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2025 The Android Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@array/android_wear_capabilities">
<string-array name="android_wear_capabilities">
<item>androidify_phone</item>
</string-array>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ sealed class WatchFaceInstallationStatus() {
val activationStrategy: WatchFaceActivationStrategy,
) : WatchFaceInstallationStatus()

object Preparing: WatchFaceInstallationStatus()
object Preparing : WatchFaceInstallationStatus()

object Sending : WatchFaceInstallationStatus()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ 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 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
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,18 @@ 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.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
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
Expand All @@ -34,8 +41,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)

Expand All @@ -48,7 +53,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) {
Expand All @@ -57,7 +69,30 @@ 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 capabilities.nodes.firstOrNull()?.id
}

private fun getPlayIntent(): Intent {
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_URL.toUri())
intent.addCategory(Intent.CATEGORY_BROWSABLE)
return intent
}

private fun onAnimationFinished() {
finish()
}
}