From 8ab5a21a74f3d0952cf8d44768bfa98de2ef2934 Mon Sep 17 00:00:00 2001 From: himalia416 Date: Fri, 10 Oct 2025 15:54:10 +0200 Subject: [PATCH] Smooth transition on restarting session --- .../channelSounding/ChannelSoundingScreen.kt | 69 ++++++++++++++++--- .../res/values/channelSoundingStrings.xml | 2 + .../data/ChannelSoundingServiceData.kt | 3 +- 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/profile/src/main/java/no/nordicsemi/android/toolbox/profile/view/channelSounding/ChannelSoundingScreen.kt b/profile/src/main/java/no/nordicsemi/android/toolbox/profile/view/channelSounding/ChannelSoundingScreen.kt index e01d2bb0..fcce6690 100644 --- a/profile/src/main/java/no/nordicsemi/android/toolbox/profile/view/channelSounding/ChannelSoundingScreen.kt +++ b/profile/src/main/java/no/nordicsemi/android/toolbox/profile/view/channelSounding/ChannelSoundingScreen.kt @@ -24,6 +24,9 @@ import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -41,6 +44,7 @@ import no.nordicsemi.android.toolbox.profile.data.ChannelSoundingServiceData import no.nordicsemi.android.toolbox.profile.data.ConfidenceLevel import no.nordicsemi.android.toolbox.profile.data.CsRangingData import no.nordicsemi.android.toolbox.profile.data.RangingSessionAction +import no.nordicsemi.android.toolbox.profile.data.RangingSessionFailedReason import no.nordicsemi.android.toolbox.profile.data.RangingTechnology import no.nordicsemi.android.toolbox.profile.data.SessionClosedReason import no.nordicsemi.android.toolbox.profile.data.UpdateRate @@ -95,13 +99,14 @@ private fun ChannelSoundingView( channelSoundingState: ChannelSoundingServiceData, onClickEvent: (ChannelSoundingEvent) -> Unit, ) { + var isRestartingSession by rememberSaveable { mutableStateOf(false) } Column( verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxWidth() ) { when (val sessionData = channelSoundingState.rangingSessionAction) { is RangingSessionAction.OnError -> { - SessionError(sessionData) { onClickEvent(it) } + SessionError(sessionData, isRestartingSession) { onClickEvent(it) } } is RangingSessionAction.OnResult -> { @@ -109,18 +114,26 @@ private fun ChannelSoundingView( channelSoundingState.updateRate, sessionData.data, sessionData.previousData, - onClickEvent - ) + ) { + isRestartingSession = true + onClickEvent(it) + } } RangingSessionAction.OnClosed -> { - SessionClosed(onClickEvent) + SessionClosed(isRestartingSession, onClickEvent) } RangingSessionAction.OnStart -> { + isRestartingSession = false InitiatingSession() } + RangingSessionAction.OnRestarting -> { + isRestartingSession = true + RestartingSession() + } + null -> LoadingView() } @@ -149,8 +162,31 @@ private fun InitiatingSession() { } } +@Composable +private fun RestartingSession() { + ScreenSection(modifier = Modifier.padding(0.dp) /* No padding */) { + Column(modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp)) { + SectionTitle( + icon = Icons.Default.SocialDistance, + title = stringResource(R.string.channel_sounding), + ) + } + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + TextWithAnimatedDots( + text = stringResource(R.string.ranging_session_restarting), + ) + } + } +} + @Composable private fun SessionClosed( + isRestartingSession: Boolean, onClickEvent: (ChannelSoundingEvent) -> Unit, ) { Column( @@ -177,10 +213,17 @@ private fun SessionClosed( ) } } - Button( - onClick = { onClickEvent(ChannelSoundingEvent.RestartRangingSession) }, - ) { - Text(text = stringResource(id = R.string.reconnect)) + + if (isRestartingSession) { + Button(onClick = { /* No action */ }) { + Text(text = stringResource(id = R.string.ranging_session_reconnecting)) + } + } else { + Button( + onClick = { onClickEvent(ChannelSoundingEvent.RestartRangingSession) }, + ) { + Text(text = stringResource(id = R.string.reconnect)) + } } } } @@ -189,13 +232,14 @@ private fun SessionClosed( @Composable private fun SessionClosed_Preview() { NordicTheme { - SessionClosed(onClickEvent = {}) + SessionClosed(false, onClickEvent = {}) } } @Composable private fun SessionError( sessionData: RangingSessionAction.OnError, + isRestartingSession: Boolean, onClickEvent: (ChannelSoundingEvent) -> Unit, ) { Column( @@ -222,7 +266,12 @@ private fun SessionError( ) } } - if (sessionData.reason != SessionClosedReason.NOT_SUPPORTED || + + if (isRestartingSession && sessionData.reason == RangingSessionFailedReason.LOCAL_REQUEST) { + Button(onClick = { /* No action */ }) { + Text(text = stringResource(id = R.string.ranging_session_reconnecting)) + } + } else if (sessionData.reason != SessionClosedReason.NOT_SUPPORTED || sessionData.reason != SessionClosedReason.RANGING_NOT_AVAILABLE ) { Button( diff --git a/profile/src/main/res/values/channelSoundingStrings.xml b/profile/src/main/res/values/channelSoundingStrings.xml index 2f35347d..36341407 100644 --- a/profile/src/main/res/values/channelSoundingStrings.xml +++ b/profile/src/main/res/values/channelSoundingStrings.xml @@ -4,7 +4,9 @@ Channel Sounding is not supported on this Android version. Initiating ranging + Reconnecting session Ranging session stopped + Reconnecting… Distance Current measurement diff --git a/profile_data/src/main/java/no/nordicsemi/android/toolbox/profile/data/ChannelSoundingServiceData.kt b/profile_data/src/main/java/no/nordicsemi/android/toolbox/profile/data/ChannelSoundingServiceData.kt index e1e47d3b..65712bbc 100644 --- a/profile_data/src/main/java/no/nordicsemi/android/toolbox/profile/data/ChannelSoundingServiceData.kt +++ b/profile_data/src/main/java/no/nordicsemi/android/toolbox/profile/data/ChannelSoundingServiceData.kt @@ -10,7 +10,7 @@ data class ChannelSoundingServiceData( ) : ProfileServiceData() sealed interface RangingSessionAction { - object OnStart : RangingSessionAction + data object OnStart : RangingSessionAction data class OnResult( val data: CsRangingData, val previousData: List = emptyList() @@ -18,6 +18,7 @@ sealed interface RangingSessionAction { data class OnError(val reason: SessionCloseReasonProvider) : RangingSessionAction object OnClosed : RangingSessionAction + data object OnRestarting : RangingSessionAction } data class CsRangingData(