UX: seconds flash interval UI, hidden active controls, back-to-setup flow
This commit is contained in:
@@ -4,6 +4,7 @@ import android.app.Activity
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.background
|
||||
@@ -41,6 +42,7 @@ import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
@@ -72,6 +74,9 @@ import com.mindmachine.mvp.session.FlashColor
|
||||
import com.mindmachine.mvp.session.MainViewModel
|
||||
import com.mindmachine.mvp.session.SplitFlashFrame
|
||||
import com.mindmachine.mvp.session.computeSplitFlashFrame
|
||||
import com.mindmachine.mvp.session.flashIntervalMsToSeconds
|
||||
import com.mindmachine.mvp.session.flashIntervalSecondsToMs
|
||||
import com.mindmachine.mvp.session.shouldShowActiveControlsByDefault
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private val MindMachineColors = darkColorScheme(
|
||||
@@ -174,7 +179,11 @@ fun App(vm: MainViewModel = viewModel()) {
|
||||
}, onHolder = { nav.navigate("holder") })
|
||||
}
|
||||
composable("active") {
|
||||
ActiveSessionScreen(vm = vm, onFinish = { nav.navigate("complete") { popUpTo("setup") } })
|
||||
ActiveSessionScreen(
|
||||
vm = vm,
|
||||
onFinish = { nav.navigate("complete") { popUpTo("setup") } },
|
||||
onBackToSetup = { nav.popBackStack("setup", inclusive = false) }
|
||||
)
|
||||
}
|
||||
composable("complete") {
|
||||
SimpleScreen(
|
||||
@@ -250,13 +259,13 @@ fun SetupScreen(vm: MainViewModel, onStart: () -> Unit, onHolder: () -> Unit) {
|
||||
)
|
||||
}
|
||||
}
|
||||
Text("Flash interval: ${cfg.flashIntervalMs} ms", color = MaterialTheme.colorScheme.onBackground)
|
||||
Text("Flash interval: ${"%.2f".format(flashIntervalMsToSeconds(cfg.flashIntervalMs))} s", color = MaterialTheme.colorScheme.onBackground)
|
||||
Slider(
|
||||
value = cfg.flashIntervalMs.toFloat(),
|
||||
onValueChange = { vm.setFlashIntervalMs(it.roundToInt()) },
|
||||
valueRange = 50f..2000f
|
||||
value = flashIntervalMsToSeconds(cfg.flashIntervalMs),
|
||||
onValueChange = { vm.setFlashIntervalMs(flashIntervalSecondsToMs(it)) },
|
||||
valueRange = 0.05f..2.0f
|
||||
)
|
||||
Text("Controls how long each frame is shown (red/green/black pattern).", color = MaterialTheme.colorScheme.onBackground)
|
||||
Text("Controls how long each frame is shown in the red/green/black pattern.", color = MaterialTheme.colorScheme.onBackground)
|
||||
Text("Carrier ${cfg.carrierFrequencyHz.roundToInt()} Hz", color = MaterialTheme.colorScheme.onBackground)
|
||||
Slider(value = cfg.carrierFrequencyHz, onValueChange = vm::setCarrier, valueRange = 80f..400f)
|
||||
Text("Difference ${cfg.binauralDifferenceHz} Hz", color = MaterialTheme.colorScheme.onBackground)
|
||||
@@ -269,9 +278,14 @@ fun SetupScreen(vm: MainViewModel, onStart: () -> Unit, onHolder: () -> Unit) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ActiveSessionScreen(vm: MainViewModel, onFinish: () -> Unit) {
|
||||
fun ActiveSessionScreen(vm: MainViewModel, onFinish: () -> Unit, onBackToSetup: () -> Unit) {
|
||||
val ui by vm.ui.collectAsStateWithLifecycle()
|
||||
if (ui.runtimeState == RuntimeState.COMPLETED || ui.runtimeState == RuntimeState.STOPPED) onFinish()
|
||||
if (ui.runtimeState == RuntimeState.COMPLETED) onFinish()
|
||||
|
||||
BackHandler {
|
||||
vm.stop()
|
||||
onBackToSetup()
|
||||
}
|
||||
|
||||
val isVisualMode = ui.config.mode != SessionMode.AUDIO_ONLY
|
||||
val context = LocalContext.current
|
||||
@@ -288,7 +302,10 @@ fun ActiveSessionScreen(vm: MainViewModel, onFinish: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
var showOverlay by remember { mutableStateOf(true) }
|
||||
var showOverlay by remember(ui.runtimeState) { mutableStateOf(shouldShowActiveControlsByDefault(ui.runtimeState)) }
|
||||
LaunchedEffect(ui.runtimeState) {
|
||||
showOverlay = shouldShowActiveControlsByDefault(ui.runtimeState)
|
||||
}
|
||||
val flashFrame by rememberSplitFlashFrame(
|
||||
isRunning = ui.runtimeState == RuntimeState.RUNNING,
|
||||
mode = ui.config.mode,
|
||||
@@ -340,7 +357,7 @@ fun ActiveSessionScreen(vm: MainViewModel, onFinish: () -> Unit) {
|
||||
} else {
|
||||
Button(onClick = { vm.resume() }, modifier = Modifier.fillMaxWidth()) { Text("Resume") }
|
||||
}
|
||||
OutlinedButton(onClick = { vm.stop() }, modifier = Modifier.fillMaxWidth()) { Text("Stop") }
|
||||
OutlinedButton(onClick = { vm.stop(); onFinish() }, modifier = Modifier.fillMaxWidth()) { Text("Stop") }
|
||||
if (ui.interruptionReason != null) {
|
||||
Text(ui.interruptionReason!!, color = Color.White)
|
||||
if (ui.runtimeState == RuntimeState.INTERRUPTED) {
|
||||
|
||||
Reference in New Issue
Block a user