diff --git a/shared/src/androidHostTest/kotlin/com/devil/phoenixproject/presentation/manager/BleConnectionManagerTest.kt b/shared/src/androidHostTest/kotlin/com/devil/phoenixproject/presentation/manager/BleConnectionManagerTest.kt index 4ca04425f..f4b0e540e 100644 --- a/shared/src/androidHostTest/kotlin/com/devil/phoenixproject/presentation/manager/BleConnectionManagerTest.kt +++ b/shared/src/androidHostTest/kotlin/com/devil/phoenixproject/presentation/manager/BleConnectionManagerTest.kt @@ -228,11 +228,15 @@ class BleConnectionManagerTest { } } - private class FakeWorkoutStateProvider(var active: Boolean, var midSet: Boolean = false) : WorkoutStateProvider { + private class FakeWorkoutStateProvider( + var active: Boolean, + var midSet: Boolean = false, + var allBodyweight: Boolean = false, // Issue #693 + ) : WorkoutStateProvider { var connectionLostCallbacks = 0 override val isWorkoutActiveForConnectionAlert: Boolean - get() = active + get() = active && !allBodyweight override val isWorkoutMidSet: Boolean get() = midSet @@ -240,4 +244,33 @@ class BleConnectionManagerTest { connectionLostCallbacks++ } } + + @Test + fun `disconnect during bodyweight-only workout does not set connection lost alert`() = runTest { + val managerScope = CoroutineScope(coroutineContext + SupervisorJob()) + try { + val workoutStateProvider = FakeWorkoutStateProvider(active = true, allBodyweight = true) + val settingsManager = + SettingsManager(fakePreferencesManager, fakeProfileRepository, managerScope) + val manager = + BleConnectionManager( + fakeBleRepository, + settingsManager, + workoutStateProvider, + MutableSharedFlow(), + managerScope, + ) + advanceUntilIdle() + + fakeBleRepository.simulateConnect("Vee_Test") + advanceUntilIdle() + fakeBleRepository.simulateDisconnect() + advanceUntilIdle() + + assertFalse(manager.connectionLostDuringWorkout.value) + assertEquals(0, workoutStateProvider.connectionLostCallbacks) + } finally { + managerScope.cancel() + } + } } diff --git a/shared/src/androidHostTest/kotlin/com/devil/phoenixproject/presentation/viewmodel/MainViewModelTest.kt b/shared/src/androidHostTest/kotlin/com/devil/phoenixproject/presentation/viewmodel/MainViewModelTest.kt index e47b7e724..3884ff6a4 100644 --- a/shared/src/androidHostTest/kotlin/com/devil/phoenixproject/presentation/viewmodel/MainViewModelTest.kt +++ b/shared/src/androidHostTest/kotlin/com/devil/phoenixproject/presentation/viewmodel/MainViewModelTest.kt @@ -601,6 +601,44 @@ class MainViewModelTest { assertEquals(2, viewModel.repCount.value.workingReps) } + @Test + fun `disconnect during bodyweight-only workout does not show connection lost alert`() = runTest(testCoroutineRule.dispatcher) { + fakeBleRepository.simulateConnect("Vee_Test", "AA:BB:CC:DD:EE:FF") + advanceUntilIdle() + + val bodyweightRoutine = Routine( + id = "routine-bodyweight-test", + name = "Bodyweight Test", + exercises = listOf( + RoutineExercise( + id = "routine-ex-bw-1", + exercise = Exercise( + id = "pullup", + name = "Pull-Up", + muscleGroup = "Back", + equipment = "", + isBodyweightOverride = true, + ), + orderIndex = 0, + setReps = listOf(10), + weightPerCableKg = 0f, + warmupSets = emptyList(), + ), + ), + ) + viewModel.loadRoutine(bodyweightRoutine) + advanceUntilIdle() + viewModel.enterSetReady(0, 0) + advanceUntilIdle() + viewModel.startWorkout(skipCountdown = true) + advanceUntilIdle() + + fakeBleRepository.simulateDisconnect() + advanceUntilIdle() + + assertFalse(viewModel.connectionLostDuringWorkout.value) + } + @Test fun `disconnect during workout sets connection lost flag`() = runTest(testCoroutineRule.dispatcher) { fakeBleRepository.simulateConnect("Vee_Test", "AA:BB:CC:DD:EE:FF") diff --git a/shared/src/commonMain/kotlin/com/devil/phoenixproject/presentation/manager/DefaultWorkoutSessionManager.kt b/shared/src/commonMain/kotlin/com/devil/phoenixproject/presentation/manager/DefaultWorkoutSessionManager.kt index e63fc8461..e8e7d8fd7 100644 --- a/shared/src/commonMain/kotlin/com/devil/phoenixproject/presentation/manager/DefaultWorkoutSessionManager.kt +++ b/shared/src/commonMain/kotlin/com/devil/phoenixproject/presentation/manager/DefaultWorkoutSessionManager.kt @@ -472,9 +472,24 @@ class DefaultWorkoutSessionManager( // ===== WorkoutStateProvider Implementation ===== override val isWorkoutActiveForConnectionAlert: Boolean - get() = when (coordinator._workoutState.value) { - is WorkoutState.Active, is WorkoutState.Countdown, is WorkoutState.Resting -> true - else -> false + get() { + val state = coordinator._workoutState.value + if (state !is WorkoutState.Active && + state !is WorkoutState.Countdown && + state !is WorkoutState.Resting + ) { + return false + } + // Issue #693: Suppress connection alerts for bodyweight-only workouts. + // When all exercises in the loaded routine are bodyweight, the Vitruvian + // trainer is not needed and its auto-power-off should not interrupt the user. + val routine = coordinator._loadedRoutine.value + if (routine != null && routine.exercises.isNotEmpty() && + routine.exercises.all { it.exercise.isBodyweight } + ) { + return false + } + return true } override val isWorkoutMidSet: Boolean