From d519f30df47e192e42340a41d28febecc5e54d67 Mon Sep 17 00:00:00 2001 From: garan Date: Mon, 22 Sep 2025 09:35:21 +0100 Subject: [PATCH 1/5] Updates watch send to avoid initial perceived delay --- .../androidify/results/ResultsScreenTest.kt | 1 - .../customize/CustomizeExportViewModel.kt | 10 ++++---- ...Panel.kt => TransferringWatchFacePanel.kt} | 24 ++++++++++++++++--- .../developers/androidify/customize/Utils.kt | 2 +- .../customize/WatchFaceModalSheet.kt | 10 +++++++- .../results/src/main/res/values/strings.xml | 1 + .../EmptyWatchFaceInstallationRepository.kt | 2 ++ .../WatchFaceInstallationRepository.kt | 6 +++++ .../common/WatchFaceInstallationStatus.kt | 3 +++ 9 files changed, 49 insertions(+), 10 deletions(-) rename feature/results/src/main/java/com/android/developers/androidify/customize/{SendingWatchFacePanel.kt => TransferringWatchFacePanel.kt} (82%) diff --git a/feature/results/src/androidTest/java/com/android/developers/androidify/results/ResultsScreenTest.kt b/feature/results/src/androidTest/java/com/android/developers/androidify/results/ResultsScreenTest.kt index 79307dad..70dd20eb 100644 --- a/feature/results/src/androidTest/java/com/android/developers/androidify/results/ResultsScreenTest.kt +++ b/feature/results/src/androidTest/java/com/android/developers/androidify/results/ResultsScreenTest.kt @@ -190,7 +190,6 @@ class ResultsScreenTest { val configProvider = ConfigProvider(TestRemoteConfigDataSource(false)) val viewModel = ResultsViewModel(testUri, originalImageUrl = testUri, null, configProvider) - composeTestRule.setContent { // Disable animation SharedElementContextPreview { 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 c8444670..579de08f 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 @@ -34,6 +34,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed @@ -367,10 +368,11 @@ class CustomizeExportViewModel @AssistedInject constructor( fun installWatchFace() { val watchFaceToInstall = _state.value.watchFaceSelectionState.selectedWatchFace ?: return - transferJob = viewModelScope.launch { - val bitmap = state.value.exportImageCanvas.imageBitmap - val watch = state.value.connectedWatch - if (watch != null && bitmap != null) { + val bitmap = state.value.exportImageCanvas.imageBitmap + val watch = state.value.connectedWatch + if (watch != null && bitmap != null) { + transferJob = viewModelScope.launch(Dispatchers.Default) { + watchfaceInstallationRepository.prepareForTransfer() val wfBitmap = imageGenerationRepository.removeBackground(bitmap) val response = watchfaceInstallationRepository .createAndTransferWatchFace(watch, watchFaceToInstall, wfBitmap) diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/SendingWatchFacePanel.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/TransferringWatchFacePanel.kt similarity index 82% rename from feature/results/src/main/java/com/android/developers/androidify/customize/SendingWatchFacePanel.kt rename to feature/results/src/main/java/com/android/developers/androidify/customize/TransferringWatchFacePanel.kt index f474b005..32972260 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/SendingWatchFacePanel.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/TransferringWatchFacePanel.kt @@ -37,8 +37,9 @@ import com.android.developers.androidify.theme.AndroidifyTheme import com.android.developers.androidify.watchface.WatchFaceAsset @Composable -fun SendingWatchFacePanel( +fun TransferringWatchFacePanel( modifier: Modifier = Modifier, + transferLabel: String, selectedWatchFace: WatchFaceAsset?, ) { Column( @@ -62,7 +63,7 @@ fun SendingWatchFacePanel( Spacer(modifier = Modifier.height(24.dp)) WatchFacePanelButton( modifier = modifier.padding(horizontal = 16.dp), - buttonText = stringResource(R.string.sending_to_watch), + buttonText = transferLabel, isSending = true, colors = ButtonDefaults.buttonColors( contentColor = MaterialTheme.colorScheme.onSurface, @@ -81,8 +82,25 @@ private fun SendingWatchFacePanelPreview() { previewPath = R.drawable.watch_face_preview, ) AndroidifyTheme { - SendingWatchFacePanel( + TransferringWatchFacePanel( selectedWatchFace = watchFace1, + transferLabel = stringResource(R.string.sending_to_watch), + ) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Preview(showBackground = true) +@Composable +private fun PreparingWatchFacePanelPreview() { + val watchFace1 = WatchFaceAsset( + id = "watch_face_1", + previewPath = R.drawable.watch_face_preview, + ) + AndroidifyTheme { + TransferringWatchFacePanel( + selectedWatchFace = watchFace1, + transferLabel = stringResource(R.string.preparing_to_send), ) } } diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/Utils.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/Utils.kt index 4e662541..f5bacf91 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/Utils.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/Utils.kt @@ -32,4 +32,4 @@ fun getPlaceholderBotUri(): Uri = @Composable fun getPlaceholderBotBitmap(): Bitmap = - ImageBitmap.imageResource(id = R.drawable.placeholderbot).asAndroidBitmap() \ No newline at end of file + ImageBitmap.imageResource(id = R.drawable.placeholderbot).asAndroidBitmap() diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/WatchFaceModalSheet.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/WatchFaceModalSheet.kt index c6046646..2c18d0fa 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/WatchFaceModalSheet.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/WatchFaceModalSheet.kt @@ -164,9 +164,17 @@ fun WatchFaceModalSheet( } } + is WatchFaceInstallationStatus.Preparing -> { + TransferringWatchFacePanel( + selectedWatchFace = watchFaceSelectionState.selectedWatchFace, + transferLabel = stringResource(R.string.preparing_to_send), + ) + } + is WatchFaceInstallationStatus.Sending -> { - SendingWatchFacePanel( + TransferringWatchFacePanel( selectedWatchFace = watchFaceSelectionState.selectedWatchFace, + transferLabel = stringResource(R.string.sending_to_watch), ) } diff --git a/feature/results/src/main/res/values/strings.xml b/feature/results/src/main/res/values/strings.xml index 0f6e36f5..6f3d9532 100644 --- a/feature/results/src/main/res/values/strings.xml +++ b/feature/results/src/main/res/values/strings.xml @@ -42,6 +42,7 @@ Hey good looking! Send to watch + Preparing… Sending to watch… Watch face sent %s detected. 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 c908fbe2..d77cdd31 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 @@ -46,4 +46,6 @@ class EmptyWatchFaceInstallationRepositoryImpl @Inject constructor() : WatchFace } override suspend fun resetInstallationStatus() { } + + override suspend fun prepareForTransfer() { } } 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 f2f5a8af..98da3c70 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 @@ -68,6 +68,8 @@ interface WatchFaceInstallationRepository { suspend fun getAvailableWatchFaces(): Result> suspend fun resetInstallationStatus() + + suspend fun prepareForTransfer() } class WatchFaceInstallationRepositoryImpl @Inject constructor( @@ -127,4 +129,8 @@ class WatchFaceInstallationRepositoryImpl @Inject constructor( wearAssetTransmitter.resetTransferId() manualStatusUpdates.tryEmit(WatchFaceInstallationStatus.NotStarted) } + + override suspend fun prepareForTransfer() { + manualStatusUpdates.tryEmit(WatchFaceInstallationStatus.Preparing) + } } 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 5f1143da..70025bc4 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 @@ -44,6 +44,9 @@ sealed class WatchFaceInstallationStatus() { val validationToken: String, val activationStrategy: WatchFaceActivationStrategy, ) : WatchFaceInstallationStatus() + + object Preparing: WatchFaceInstallationStatus() + object Sending : WatchFaceInstallationStatus() @Serializable From ac291d02754ef256eddbd01f0b3c50faec7b6708 Mon Sep 17 00:00:00 2001 From: garan Date: Mon, 22 Sep 2025 10:02:06 +0100 Subject: [PATCH 2/5] Addresses comments --- .../customize/{ => watchface}/TransferringWatchFacePanel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename feature/results/src/main/java/com/android/developers/androidify/customize/{ => watchface}/TransferringWatchFacePanel.kt (98%) diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/TransferringWatchFacePanel.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/TransferringWatchFacePanel.kt similarity index 98% rename from feature/results/src/main/java/com/android/developers/androidify/customize/TransferringWatchFacePanel.kt rename to feature/results/src/main/java/com/android/developers/androidify/customize/watchface/TransferringWatchFacePanel.kt index e2730d0a..777df80f 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/TransferringWatchFacePanel.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/TransferringWatchFacePanel.kt @@ -62,7 +62,7 @@ fun TransferringWatchFacePanel( } Spacer(modifier = Modifier.height(24.dp)) WatchFacePanelButton( - modifier = modifier.padding(horizontal = 16.dp), + modifier = Modifier.padding(horizontal = 16.dp), buttonText = transferLabel, isSending = true, colors = ButtonDefaults.buttonColors( From 449764ca83169137b383f29612aa7567d8667276 Mon Sep 17 00:00:00 2001 From: garan Date: Mon, 22 Sep 2025 10:09:17 +0100 Subject: [PATCH 3/5] Actually remove SendingWatchFacePanel.kt this time --- .../androidify/customize/watchface/SendingWatchFacePanel.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 feature/results/src/main/java/com/android/developers/androidify/customize/watchface/SendingWatchFacePanel.kt diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/SendingWatchFacePanel.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/watchface/SendingWatchFacePanel.kt deleted file mode 100644 index e69de29b..00000000 From 45cf909fadd68c011cf6f6698a0b3fa467759ba7 Mon Sep 17 00:00:00 2001 From: garan Date: Mon, 22 Sep 2025 10:31:03 +0100 Subject: [PATCH 4/5] Fixes use of enumeration on watch side --- .../watchface/WatchFaceModalSheet.kt | 26 +++++++++++++------ .../watchface/di/WatchFaceModule.kt | 2 +- .../ui/WatchFaceOnboardingScreen.kt | 1 + 3 files changed, 20 insertions(+), 9 deletions(-) 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 942f9968..5a2af3fb 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 @@ -17,9 +17,12 @@ package com.android.developers.androidify.customize.watchface import androidx.compose.animation.AnimatedContent import androidx.compose.animation.ContentTransform +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -108,14 +111,21 @@ fun WatchFaceModalSheet( AnimatedContent( targetState = installationStatus, transitionSpec = { - ContentTransform( - targetContentEnter = fadeIn( - animationSpec = tween(durationMillis = 500), - ), - initialContentExit = fadeOut( - animationSpec = tween(durationMillis = 500), - ), - ) + if ( + initialState is WatchFaceInstallationStatus.Preparing && + targetState is WatchFaceInstallationStatus.Sending + ) { + EnterTransition.None togetherWith ExitTransition.None + } else { + ContentTransform( + targetContentEnter = fadeIn( + animationSpec = tween(durationMillis = 500), + ), + initialContentExit = fadeOut( + animationSpec = tween(durationMillis = 500), + ), + ) + } }, ) { installationStatus -> when (installationStatus) { diff --git a/watchface/src/main/java/com/android/developers/androidify/watchface/di/WatchFaceModule.kt b/watchface/src/main/java/com/android/developers/androidify/watchface/di/WatchFaceModule.kt index 32118f32..f2733334 100644 --- a/watchface/src/main/java/com/android/developers/androidify/watchface/di/WatchFaceModule.kt +++ b/watchface/src/main/java/com/android/developers/androidify/watchface/di/WatchFaceModule.kt @@ -70,7 +70,7 @@ class WatchFaceModule { remoteConfigDataSource: RemoteConfigDataSource, ): WatchFaceInstallationRepository { val watchFacesEnabled = remoteConfigDataSource.watchfaceFeatureEnabled() - return if (Build.VERSION.SDK_INT >= MIN_WATCH_FACE_SDK_VERSION && watchFacesEnabled) { + return if (Build.VERSION.SDK_INT >= MIN_WATCH_FACE_SDK_VERSION && watchFacesEnabled || true) { supportedImpl } else { noSupportImpl diff --git a/wear/src/main/java/com/android/developers/androidify/ui/WatchFaceOnboardingScreen.kt b/wear/src/main/java/com/android/developers/androidify/ui/WatchFaceOnboardingScreen.kt index 8f3140fc..afe188da 100644 --- a/wear/src/main/java/com/android/developers/androidify/ui/WatchFaceOnboardingScreen.kt +++ b/wear/src/main/java/com/android/developers/androidify/ui/WatchFaceOnboardingScreen.kt @@ -49,6 +49,7 @@ fun WatchFaceOnboardingScreen( when (state) { is WatchFaceInstallationStatus.Receiving, + is WatchFaceInstallationStatus.Preparing, is WatchFaceInstallationStatus.Sending, -> { TransmissionScreen() From 2b316b6bab3dd6d7355e46c1e1e443a0814a1423 Mon Sep 17 00:00:00 2001 From: garan Date: Mon, 22 Sep 2025 10:41:56 +0100 Subject: [PATCH 5/5] Fixes tests --- .../repository/FakeWatchFaceInstallationRepository.kt | 5 +++++ 1 file changed, 5 insertions(+) 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 ae9c81aa..6be2a15c 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 @@ -75,6 +75,11 @@ class FakeWatchFaceInstallationRepository : WatchFaceInstallationRepository { _watchFaceInstallationStatus.value = WatchFaceInstallationStatus.NotStarted } + override suspend fun prepareForTransfer() { + transferId = generateTransferId() + _watchFaceInstallationStatus.value = WatchFaceInstallationStatus.Preparing + } + private fun generateTransferId() = UUID.randomUUID().toString().take(8) public fun setWatchAsConnected() {