diff --git a/app/src/main/java/com/mindmachine/mvp/MainActivity.kt b/app/src/main/java/com/mindmachine/mvp/MainActivity.kt index 5eee3a9..b7e390e 100644 --- a/app/src/main/java/com/mindmachine/mvp/MainActivity.kt +++ b/app/src/main/java/com/mindmachine/mvp/MainActivity.kt @@ -48,6 +48,7 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.Image import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.Settings @@ -597,6 +598,8 @@ fun ActiveSessionScreen(vm: MainViewModel, onFinish: () -> Unit, onBackToEntry: } var brightnessOverlayVisible by remember { mutableStateOf(false) } var brightnessOverlayToken by remember { mutableIntStateOf(0) } + var stopButtonVisible by remember { mutableStateOf(false) } + var stopButtonToken by remember { mutableIntStateOf(0) } val timingLogTag = "MindMachineTiming" LaunchedEffect(brightnessOverlayToken) { @@ -642,12 +645,24 @@ fun ActiveSessionScreen(vm: MainViewModel, onFinish: () -> Unit, onBackToEntry: detectTapGestures { brightnessOverlayVisible = true brightnessOverlayToken++ + if (ui.settings.showImmersiveStopButton && immersiveFullscreen) { + stopButtonVisible = true + stopButtonToken++ + } if (!immersiveFullscreen) { showOverlay = !showOverlay } } } ) { + // Auto-hide stop button after 3 seconds of visibility + LaunchedEffect(stopButtonToken) { + if (stopButtonVisible) { + delay(3000) + stopButtonVisible = false + } + } + if (isVisualMode) { AndroidView( modifier = Modifier.fillMaxSize(), @@ -715,6 +730,26 @@ fun ActiveSessionScreen(vm: MainViewModel, onFinish: () -> Unit, onBackToEntry: } } + // Stop button in immersive mode (top-right) + if (stopButtonVisible && ui.settings.showImmersiveStopButton) { + IconButton( + onClick = { + vm.stop() + onFinish() + }, + modifier = Modifier + .align(Alignment.TopEnd) + .padding(top = 16.dp, end = 16.dp) + ) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = "Stop", + tint = Color.White, + modifier = Modifier.size(28.dp) + ) + } + } + if (showOverlay) { Column( Modifier @@ -1233,6 +1268,18 @@ fun SettingsScreen(vm: MainViewModel, onRemoveAds: () -> Unit) { Text("Show immersive bottom progress bar", color = MaterialTheme.colorScheme.onBackground) } + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(checked = ui.settings.showImmersiveStopButton, onCheckedChange = vm::updateShowImmersiveStopButton) + Column { + Text("Show stop button in immersive mode", color = MaterialTheme.colorScheme.onBackground) + Text( + "If off, swipe from edge to display back button", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.7f) + ) + } + } + Row(verticalAlignment = Alignment.CenterVertically) { Checkbox(checked = ui.settings.preferLowestRefreshRate, onCheckedChange = vm::updatePreferLowestRefreshRate) Text("Prefer lowest display refresh rate during sessions", color = MaterialTheme.colorScheme.onBackground) diff --git a/app/src/main/java/com/mindmachine/mvp/data/SettingsRepository.kt b/app/src/main/java/com/mindmachine/mvp/data/SettingsRepository.kt index 3870bec..0bf885d 100644 --- a/app/src/main/java/com/mindmachine/mvp/data/SettingsRepository.kt +++ b/app/src/main/java/com/mindmachine/mvp/data/SettingsRepository.kt @@ -20,6 +20,7 @@ class SettingsRepository(private val context: Context) { val countdownSeconds = intPreferencesKey("countdown_seconds") val forceAudioWithoutHeadphones = booleanPreferencesKey("force_audio_without_headphones") val showImmersiveProgressBar = booleanPreferencesKey("show_immersive_progress_bar") + val showImmersiveStopButton = booleanPreferencesKey("show_immersive_stop_button") val preferLowestRefreshRate = booleanPreferencesKey("prefer_lowest_refresh_rate") val monochromeFlashMode = booleanPreferencesKey("monochrome_flash_mode") val immersiveBrightnessPercent = intPreferencesKey("immersive_brightness_percent") @@ -36,6 +37,7 @@ class SettingsRepository(private val context: Context) { ?: 5, forceAudioWithoutHeadphones = p[Keys.forceAudioWithoutHeadphones] ?: false, showImmersiveProgressBar = p[Keys.showImmersiveProgressBar] ?: true, + showImmersiveStopButton = p[Keys.showImmersiveStopButton] ?: true, preferLowestRefreshRate = p[Keys.preferLowestRefreshRate] ?: true, monochromeFlashMode = p[Keys.monochromeFlashMode] ?: false, immersiveBrightnessPercent = (p[Keys.immersiveBrightnessPercent] ?: 50).coerceIn(5, 100), @@ -58,6 +60,9 @@ class SettingsRepository(private val context: Context) { if (prefs[Keys.preferLowestRefreshRate] == null) { prefs[Keys.preferLowestRefreshRate] = true } + if (prefs[Keys.showImmersiveStopButton] == null) { + prefs[Keys.showImmersiveStopButton] = true + } } } @@ -80,6 +85,10 @@ class SettingsRepository(private val context: Context) { it[Keys.showImmersiveProgressBar] = value } + suspend fun updateShowImmersiveStopButton(value: Boolean) = context.dataStore.edit { + it[Keys.showImmersiveStopButton] = value + } + suspend fun updatePreferLowestRefreshRate(value: Boolean) = context.dataStore.edit { it[Keys.preferLowestRefreshRate] = value } diff --git a/app/src/main/java/com/mindmachine/mvp/domain/Models.kt b/app/src/main/java/com/mindmachine/mvp/domain/Models.kt index dad234d..2a13e4c 100644 --- a/app/src/main/java/com/mindmachine/mvp/domain/Models.kt +++ b/app/src/main/java/com/mindmachine/mvp/domain/Models.kt @@ -42,6 +42,7 @@ data class AppSettings( val countdownSeconds: Int = 5, val forceAudioWithoutHeadphones: Boolean = false, val showImmersiveProgressBar: Boolean = true, + val showImmersiveStopButton: Boolean = true, val preferLowestRefreshRate: Boolean = true, val monochromeFlashMode: Boolean = false, // Default used only before DataStore emits (and as a fallback). diff --git a/app/src/main/java/com/mindmachine/mvp/session/MainViewModel.kt b/app/src/main/java/com/mindmachine/mvp/session/MainViewModel.kt index 06b895e..6aa81e2 100644 --- a/app/src/main/java/com/mindmachine/mvp/session/MainViewModel.kt +++ b/app/src/main/java/com/mindmachine/mvp/session/MainViewModel.kt @@ -304,6 +304,7 @@ class MainViewModel( settingsRepository.updateForceAudioWithoutHeadphones(value) } fun updateShowImmersiveProgressBar(value: Boolean) = viewModelScope.launch { settingsRepository.updateShowImmersiveProgressBar(value) } + fun updateShowImmersiveStopButton(value: Boolean) = viewModelScope.launch { settingsRepository.updateShowImmersiveStopButton(value) } fun updatePreferLowestRefreshRate(value: Boolean) = viewModelScope.launch { settingsRepository.updatePreferLowestRefreshRate(value) } fun updateMonochromeFlashMode(value: Boolean) = viewModelScope.launch { settingsRepository.updateMonochromeFlashMode(value) } fun updateImmersiveBrightnessPercent(value: Int) = viewModelScope.launch {