feat: Add MicrophoneDisconnectedDialog

This commit is contained in:
Myzel394 2023-10-21 20:37:35 +02:00
parent 78453f1c4d
commit 07757f34bb
No known key found for this signature in database
GPG Key ID: 79CC92F37B3E1A2B
3 changed files with 134 additions and 7 deletions

View File

@ -0,0 +1,62 @@
package app.myzel394.alibi.ui.components.AudioRecorder.atoms
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MicOff
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
import app.myzel394.alibi.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MicrophoneDisconnectedDialog(
microphoneName: String,
onClose: () -> Unit,
) {
AlertDialog(
onDismissRequest = onClose,
title = {
Text(
stringResource(
R.string.ui_audioRecorder_error_microphoneDisconnected_title,
),
textAlign = TextAlign.Center,
)
},
text = {
Text(
stringResource(
R.string.ui_audioRecorder_error_microphoneDisconnected_message,
microphoneName,
)
)
},
icon = {
Icon(
Icons.Default.MicOff,
contentDescription = null,
)
},
confirmButton = {
val label = stringResource(R.string.dialog_close_neutral_label)
Button(
modifier = Modifier
.semantics {
contentDescription = label
},
onClick = onClose,
) {
Text(label)
}
}
)
}

View File

@ -24,11 +24,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.DeleteButton
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneDisconnectedDialog
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.PauseResumeButton
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.RealtimeAudioVisualizer
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.RecordingTime
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.SaveButton
import app.myzel394.alibi.ui.components.AudioRecorder.molecules.MicrophoneSelection
import app.myzel394.alibi.ui.effects.rememberPrevious
import app.myzel394.alibi.ui.models.AudioRecorderModel
import app.myzel394.alibi.ui.utils.KeepScreenOn
import app.myzel394.alibi.ui.utils.MicrophoneInfo
@ -137,19 +139,41 @@ fun RecordingStatus(
val microphones = MicrophoneInfo.fetchDeviceMicrophones(context)
val microphoneStatus = audioRecorder.microphoneStatus
val previousStatus = rememberPrevious(microphoneStatus)
var showMicrophoneStatusDialog by remember {
// null = no dialog
// `MicrophoneConnectivityStatus.CONNECTED` = Reconnected dialog
// `MicrophoneConnectivityStatus.DISCONNECTED` = Disconnected dialog
mutableStateOf<AudioRecorderModel.MicrophoneConnectivityStatus?>(null)
}
LaunchedEffect(microphoneStatus) {
println(microphoneStatus)
println(previousStatus)
if (microphoneStatus != previousStatus && showMicrophoneStatusDialog == null && previousStatus != null) {
showMicrophoneStatusDialog = microphoneStatus
}
}
if (showMicrophoneStatusDialog == AudioRecorderModel.MicrophoneConnectivityStatus.DISCONNECTED) {
MicrophoneDisconnectedDialog(
onClose = {
showMicrophoneStatusDialog = null
},
microphoneName = audioRecorder.selectedMicrophone?.name ?: "",
)
}
if (microphones.isNotEmpty()) {
MicrophoneSelection(
microphones = microphones,
selectedMicrophone = audioRecorder.selectedMicrophone,
onSelect = {
audioRecorder.changeMicrophone(it)
if (!audioRecorder.isPaused) {
audioRecorder.recorderService!!.startNewCycle()
}
}
onSelect = audioRecorder::changeMicrophone,
)
} else {
Box {}
}
}
}

View File

@ -0,0 +1,41 @@
package app.myzel394.alibi.ui.effects
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.remember
/**
* Returns a dummy MutableState that does not cause render when setting it
*/
@Composable
private fun <T> rememberRef(): MutableState<T?> {
// for some reason it always recreated the value with vararg keys,
// leaving out the keys as a parameter for remember for now
return remember() {
object : MutableState<T?> {
override var value: T? = null
override fun component1(): T? = value
override fun component2(): (T?) -> Unit = { value = it }
}
}
}
@Composable
fun <T> rememberPrevious(
current: T,
shouldUpdate: (prev: T?, curr: T) -> Boolean = { a: T?, b: T -> a != b },
): T? {
val ref = rememberRef<T>()
// launched after render, so the current render will have the old value anyway
SideEffect {
if (shouldUpdate(ref.value, current)) {
ref.value = current
}
}
return ref.value
}