|
|
|
|
@@ -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) {
|
|
|
|
|
|