diff --git a/core/testing/src/main/java/com/android/developers/testing/repository/FakeWatchFaceInstallationRepository.kt b/core/testing/src/main/java/com/android/developers/testing/repository/FakeWatchFaceInstallationRepository.kt
index 6be2a15c..527f47a9 100644
--- a/core/testing/src/main/java/com/android/developers/testing/repository/FakeWatchFaceInstallationRepository.kt
+++ b/core/testing/src/main/java/com/android/developers/testing/repository/FakeWatchFaceInstallationRepository.kt
@@ -80,6 +80,8 @@ class FakeWatchFaceInstallationRepository : WatchFaceInstallationRepository {
_watchFaceInstallationStatus.value = WatchFaceInstallationStatus.Preparing
}
+ override suspend fun installAndroidify(nodeId: String) { }
+
private fun generateTransferId() = UUID.randomUUID().toString().take(8)
public fun setWatchAsConnected() {
diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportScreen.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportScreen.kt
index 51a58c2a..46213581 100644
--- a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportScreen.kt
+++ b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportScreen.kt
@@ -130,6 +130,9 @@ fun CustomizeAndExportScreen(
onInstallWatchFaceClicked = {
viewModel.installWatchFace()
},
+ onInstallAndroidifyClicked = {
+ viewModel.launchPlayInstallOnWatch()
+ },
onResetWatchFaceSend = {
viewModel.resetWatchFaceSend()
},
@@ -151,6 +154,7 @@ private fun CustomizeExportContents(
onToolSelected: (CustomizeTool) -> Unit,
onSelectedToolStateChanged: (ToolState) -> Unit,
onInstallWatchFaceClicked: () -> Unit,
+ onInstallAndroidifyClicked: suspend () -> Boolean,
onResetWatchFaceSend: () -> Unit,
layoutType: CustomizeExportLayoutType,
snackbarHostState: SnackbarHostState,
@@ -263,6 +267,9 @@ private fun CustomizeExportContents(
onWatchFaceInstallClick = {
onInstallWatchFaceClicked()
},
+ onAndroidifyInstallClick = {
+ onInstallAndroidifyClicked()
+ },
onLoad = loadWatchFaces,
watchFaceSelectionState = state.watchFaceSelectionState,
onWatchFaceSelect = onWatchFaceSelect,
@@ -599,6 +606,7 @@ fun CustomizeExportPreview() {
layoutType = CustomizeExportLayoutType.Compact,
onSelectedToolStateChanged = {},
onInstallWatchFaceClicked = {},
+ onInstallAndroidifyClicked = { true },
onResetWatchFaceSend = {},
loadWatchFaces = {},
onWatchFaceSelect = {},
@@ -640,6 +648,7 @@ fun CustomizeExportPreviewLarge() {
layoutType = CustomizeExportLayoutType.Medium,
onSelectedToolStateChanged = {},
onInstallWatchFaceClicked = {},
+ onInstallAndroidifyClicked = { true },
onResetWatchFaceSend = {},
loadWatchFaces = {},
onWatchFaceSelect = {},
diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt
index 3112cca8..23eb4cbf 100644
--- a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt
+++ b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt
@@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.SnackbarHostState
import androidx.compose.ui.Modifier
import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.application
import androidx.lifecycle.viewModelScope
import com.android.developers.androidify.RemoteConfigDataSource
import com.android.developers.androidify.customize.watchface.WatchFaceSelectionState
@@ -367,6 +368,19 @@ class CustomizeExportViewModel @AssistedInject constructor(
}
}
+ suspend fun launchPlayInstallOnWatch(): Boolean {
+ try {
+ val watch = state.value.connectedWatch
+ watch?.let {
+ watchfaceInstallationRepository.installAndroidify(it.nodeId)
+ }
+ return true
+ } catch (e: Exception) {
+ Timber.e(e, "Failed to open Play Store on watch")
+ }
+ return false
+ }
+
fun installWatchFace() {
val watchFaceToInstall = _state.value.watchFaceSelectionState.selectedWatchFace ?: return
val bitmap = state.value.exportImageCanvas.imageBitmap
diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/InstallAndroidifyPanel.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/InstallAndroidifyPanel.kt
index 1a9af1cf..6308d28e 100644
--- a/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/InstallAndroidifyPanel.kt
+++ b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/InstallAndroidifyPanel.kt
@@ -15,7 +15,6 @@
*/
package com.android.developers.androidify.customize.watchface
-import android.content.Intent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -24,24 +23,33 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.core.net.toUri
import com.android.developers.androidify.results.R
import com.android.developers.androidify.theme.AndroidifyTheme
import com.android.developers.androidify.watchface.WatchFaceAsset
+import kotlinx.coroutines.launch
@Composable
fun InstallAndroidifyPanel(
+ onInstallClick: suspend () -> Boolean,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
+ var isPlayLaunched by remember { mutableStateOf(false) }
val placeholderWatchFace = WatchFaceAsset(
id = "watch_face_1",
previewPath = R.drawable.watch_app_placeholder,
@@ -60,21 +68,47 @@ fun InstallAndroidifyPanel(
) {
WatchFacePreviewItem(
watchFace = placeholderWatchFace,
- isSelected = false,
+ isSelected = true,
onClick = { },
)
}
Spacer(modifier = Modifier.height(24.dp))
+
+ val buttonText = if (isPlayLaunched) {
+ stringResource(R.string.continue_on_watch)
+ } else {
+ stringResource(R.string.install_androidify)
+ }
+ val launchedColors = ButtonDefaults.buttonColors(
+ contentColor = MaterialTheme.colorScheme.onSurface,
+ containerColor = MaterialTheme.colorScheme.secondaryContainer,
+ )
+ val installColors = ButtonDefaults.buttonColors(
+ contentColor = MaterialTheme.colorScheme.surface,
+ containerColor = MaterialTheme.colorScheme.onSurface,
+ )
+ val scope = rememberCoroutineScope()
WatchFacePanelButton(
modifier = modifier.padding(horizontal = 16.dp),
- buttonText = stringResource(R.string.install_androidify),
+ buttonText = buttonText,
iconResId = R.drawable.watch_arrow_24,
onClick = {
- val uri = "market://details?id=${context.packageName}".toUri()
- val intent = Intent(Intent.ACTION_VIEW, uri)
- context.startActivity(intent)
+ if (!isPlayLaunched) {
+ scope.launch {
+ val launchResult = onInstallClick()
+ if (launchResult) {
+ isPlayLaunched = true
+ }
+ }
+ }
+ },
+ colors = if (isPlayLaunched) {
+ launchedColors
+ } else {
+ installColors
},
+ isInProgress = isPlayLaunched,
)
}
}
@@ -84,6 +118,6 @@ fun InstallAndroidifyPanel(
@Composable
private fun InstallAndroidifyPanelPreview() {
AndroidifyTheme {
- InstallAndroidifyPanel()
+ InstallAndroidifyPanel({ true })
}
}
diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/TransferringWatchFacePanel.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/TransferringWatchFacePanel.kt
index 777df80f..804861cc 100644
--- a/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/TransferringWatchFacePanel.kt
+++ b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/TransferringWatchFacePanel.kt
@@ -64,7 +64,7 @@ fun TransferringWatchFacePanel(
WatchFacePanelButton(
modifier = Modifier.padding(horizontal = 16.dp),
buttonText = transferLabel,
- isSending = true,
+ isInProgress = true,
colors = ButtonDefaults.buttonColors(
contentColor = MaterialTheme.colorScheme.onSurface,
containerColor = MaterialTheme.colorScheme.secondaryContainer,
diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/WatchFaceModalSheet.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/WatchFaceModalSheet.kt
index 5a2af3fb..027907d1 100644
--- a/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/WatchFaceModalSheet.kt
+++ b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/WatchFaceModalSheet.kt
@@ -64,6 +64,7 @@ import com.android.developers.androidify.wear.common.WatchFaceInstallationStatus
fun WatchFaceModalSheet(
connectedWatch: ConnectedWatch,
onWatchFaceInstallClick: (String) -> Unit,
+ onAndroidifyInstallClick: suspend () -> Boolean,
installationStatus: WatchFaceInstallationStatus,
sheetState: SheetState,
watchFaceSelectionState: WatchFaceSelectionState,
@@ -202,7 +203,9 @@ fun WatchFaceModalSheet(
}
else -> {
- InstallAndroidifyPanel()
+ InstallAndroidifyPanel(
+ onInstallClick = onAndroidifyInstallClick,
+ )
}
}
}
@@ -243,6 +246,7 @@ private fun WatchFaceModalSheetPreview() {
onLoad = {},
onDismiss = {},
onWatchFaceInstallClick = {},
+ onAndroidifyInstallClick = { true },
sheetState = sheetState,
)
}
diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/WatchFacePanelButton.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/WatchFacePanelButton.kt
index 899a7b84..ade05f60 100644
--- a/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/WatchFacePanelButton.kt
+++ b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/WatchFacePanelButton.kt
@@ -48,7 +48,7 @@ fun WatchFacePanelButton(
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
buttonText: String,
- isSending: Boolean = false,
+ isInProgress: Boolean = false,
iconResId: Int? = null,
colors: ButtonColors = ButtonDefaults.buttonColors(
contentColor = MaterialTheme.colorScheme.surface,
@@ -67,7 +67,7 @@ fun WatchFacePanelButton(
Row(
verticalAlignment = Alignment.CenterVertically,
) {
- if (isSending) {
+ if (isInProgress) {
ContainedLoadingIndicator(
modifier = Modifier.size(24.dp),
containerColor = colors.containerColor,
@@ -92,7 +92,7 @@ private fun WatchFaceInstallButtonPreview() {
WatchFacePanelButton(
onClick = { },
buttonText = stringResource(R.string.send_to_watch),
- isSending = false,
+ isInProgress = false,
iconResId = R.drawable.watch_arrow_24,
)
}
@@ -105,7 +105,7 @@ private fun WatchFaceInstalledButtonPreview() {
WatchFacePanelButton(
onClick = { },
buttonText = stringResource(R.string.watch_face_sent),
- isSending = false,
+ isInProgress = false,
iconResId = R.drawable.check_24,
colors = ButtonDefaults.buttonColors(
contentColor = MaterialTheme.colorScheme.onSurface,
@@ -122,7 +122,7 @@ private fun WatchFaceInstallingButtonPreview() {
WatchFacePanelButton(
onClick = { },
buttonText = stringResource(R.string.sending_to_watch),
- isSending = true,
+ isInProgress = true,
colors = ButtonDefaults.buttonColors(
contentColor = MaterialTheme.colorScheme.onSurface,
containerColor = MaterialTheme.colorScheme.secondaryContainer,
diff --git a/feature/results/src/main/res/values/strings.xml b/feature/results/src/main/res/values/strings.xml
index 6f3d9532..2b270080 100644
--- a/feature/results/src/main/res/values/strings.xml
+++ b/feature/results/src/main/res/values/strings.xml
@@ -59,4 +59,5 @@
Check your watch is connected and try again
"No available watch faces"
OK
+ Continue on watch
diff --git a/watchface/build.gradle.kts b/watchface/build.gradle.kts
index ad85d46e..60305601 100644
--- a/watchface/build.gradle.kts
+++ b/watchface/build.gradle.kts
@@ -61,6 +61,7 @@ dependencies {
implementation(libs.validator.push.android) {
exclude(group = "com.google.guava", "listenablefuture")
}
+ implementation(libs.androidx.wear.remote.interactions)
implementation(libs.bcpkix.jdk18on)
implementation(libs.play.services.wearable)
implementation(libs.kotlinx.coroutines.play.services)
diff --git a/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/EmptyWatchFaceInstallationRepository.kt b/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/EmptyWatchFaceInstallationRepository.kt
index d77cdd31..7838c0cf 100644
--- a/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/EmptyWatchFaceInstallationRepository.kt
+++ b/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/EmptyWatchFaceInstallationRepository.kt
@@ -48,4 +48,6 @@ class EmptyWatchFaceInstallationRepositoryImpl @Inject constructor() : WatchFace
override suspend fun resetInstallationStatus() { }
override suspend fun prepareForTransfer() { }
+
+ override suspend fun installAndroidify(nodeId: String) { }
}
diff --git a/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/WatchFaceInstallationRepository.kt b/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/WatchFaceInstallationRepository.kt
index 98da3c70..be5843f2 100644
--- a/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/WatchFaceInstallationRepository.kt
+++ b/watchface/src/main/java/com/android/developers/androidify/watchface/transfer/WatchFaceInstallationRepository.kt
@@ -16,7 +16,11 @@
package com.android.developers.androidify.watchface.transfer
import android.content.Context
+import android.content.Intent
import android.graphics.Bitmap
+import android.net.Uri
+import androidx.concurrent.futures.await
+import androidx.wear.remote.interactions.RemoteActivityHelper
import com.android.developers.androidify.watchface.WatchFaceAsset
import com.android.developers.androidify.watchface.creator.WatchFaceCreator
import com.android.developers.androidify.wear.common.ConnectedWatch
@@ -24,6 +28,7 @@ import com.android.developers.androidify.wear.common.WatchFaceInstallError
import com.android.developers.androidify.wear.common.WatchFaceInstallationStatus
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -70,6 +75,8 @@ interface WatchFaceInstallationRepository {
suspend fun resetInstallationStatus()
suspend fun prepareForTransfer()
+
+ suspend fun installAndroidify(nodeId: String)
}
class WatchFaceInstallationRepositoryImpl @Inject constructor(
@@ -133,4 +140,18 @@ class WatchFaceInstallationRepositoryImpl @Inject constructor(
override suspend fun prepareForTransfer() {
manualStatusUpdates.tryEmit(WatchFaceInstallationStatus.Preparing)
}
+
+ override suspend fun installAndroidify(nodeId: String) {
+ val backgroundExecutor = Dispatchers.IO.asExecutor()
+ val remoteActivityHelper = RemoteActivityHelper(context, backgroundExecutor)
+
+ remoteActivityHelper.startRemoteActivity(
+ Intent(Intent.ACTION_VIEW)
+ .setData(
+ Uri.parse("market://details?id=${context.packageName}"),
+ )
+ .addCategory(Intent.CATEGORY_BROWSABLE),
+ nodeId,
+ ).await()
+ }
}