The UI now responds to recording/not recording and messages appears in chat, still some crashes

This commit is contained in:
Mathieu 2022-01-06 16:09:24 +01:00
parent 7baa44a8aa
commit c24d07bb26
6 changed files with 88 additions and 82 deletions

View File

@ -61,6 +61,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
implementation 'com.github.squti:Android-Wave-Recorder:1.6.0'
implementation("com.squareup.okhttp3:okhttp:4.9.3")
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'androidx.activity:activity-compose:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"

View File

@ -0,0 +1,36 @@
package ch.mathieubroillet.jarvis.android.audio
import android.content.Context
import com.github.squti.androidwaverecorder.WaveRecorder
import java.io.File
class AudioRecorder(
private var audioTempFileOutput: String = "",
var waveRecorder: WaveRecorder
) {
fun startRecording() {
waveRecorder.startRecording()
}
fun stopRecording() {
waveRecorder.stopRecording()
}
fun getOutputFile(): File {
return File(audioTempFileOutput)
}
}
fun getAudioRecorder(context: Context): AudioRecorder {
val outputFile = context.filesDir.absolutePath + "/temp_recording.wav"
val waveRecorder: WaveRecorder = WaveRecorder(outputFile)
waveRecorder.noiseSuppressorActive = true
return AudioRecorder(audioTempFileOutput = outputFile, waveRecorder = waveRecorder)
}

View File

@ -1,59 +0,0 @@
package ch.mathieubroillet.jarvis.android.audio
import android.content.Context
import ch.mathieubroillet.jarvis.android.utils.contactServerWithFileAudioRecording
import com.github.squti.androidwaverecorder.RecorderState
import com.github.squti.androidwaverecorder.WaveRecorder
import java.io.File
import kotlin.concurrent.thread
private var audioTempFileOutput: String = ""
private var waveRecorder: WaveRecorder? = null
private var isRecording: Boolean = false
fun startRecording() {
if (waveRecorder != null) {
waveRecorder!!.startRecording()
}
}
fun stopRecording() {
if (waveRecorder != null) {
waveRecorder!!.stopRecording()
}
thread {
contactServerWithFileAudioRecording(getOutputFile())
getOutputFile().delete()
}
}
fun getOutputFile(): File {
return File(audioTempFileOutput)
}
fun isRecording(): Boolean {
return isRecording
}
fun registerRecorder(context: Context) {
if (waveRecorder == null) {
audioTempFileOutput = context.filesDir.absolutePath + "/temp_recording.wav"
waveRecorder = WaveRecorder(audioTempFileOutput)
waveRecorder!!.noiseSuppressorActive = true
waveRecorder!!.onStateChangeListener = {
when (it) {
RecorderState.RECORDING -> {
isRecording = true
}
RecorderState.STOP -> {
isRecording = false
}
else -> {}
}
}
}
}

View File

@ -1,12 +1,14 @@
package ch.mathieubroillet.jarvis.android.nav
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import ch.mathieubroillet.jarvis.android.R
import ch.mathieubroillet.jarvis.android.audio.getAudioRecorder
import ch.mathieubroillet.jarvis.android.chat.ConversationUiState
import ch.mathieubroillet.jarvis.android.chat.Message
import ch.mathieubroillet.jarvis.android.pages.DisplayMainPage
@ -52,7 +54,8 @@ fun MainScreen(navController: NavController) {
stringResource(id = R.string.demo_message_1)
)
)
)
),
getAudioRecorder(LocalContext.current)
)
}

View File

@ -5,26 +5,25 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import ch.mathieubroillet.jarvis.android.R
import ch.mathieubroillet.jarvis.android.audio.*
import ch.mathieubroillet.jarvis.android.audio.AudioRecorder
import ch.mathieubroillet.jarvis.android.chat.ConversationUiState
import ch.mathieubroillet.jarvis.android.chat.Message
import ch.mathieubroillet.jarvis.android.chat.Messages
import ch.mathieubroillet.jarvis.android.nav.Screen
import ch.mathieubroillet.jarvis.android.ui.theme.JarvisComposeTheme
import ch.mathieubroillet.jarvis.android.ui.theme.productSansFont
import ch.mathieubroillet.jarvis.android.utils.DefaultBox
import ch.mathieubroillet.jarvis.android.utils.IconAlertDialogTextField
import com.github.squti.androidwaverecorder.WaveRecorder
import ch.mathieubroillet.jarvis.android.utils.contactServerWithFileAudioRecording
import com.github.squti.androidwaverecorder.RecorderState
import org.json.JSONObject
import kotlin.concurrent.thread
//Draws the base of the main activity, that includes the 3-dots menu and the "hi text".
@ -91,7 +90,7 @@ fun DropDownSettingsMenu(navController: NavController) {
}
@Composable
fun StartRecordingFAB() {
fun StartRecordingFAB(onClick: () -> Unit, isRecording: Boolean) {
//We create a row that we align to the bottom center of the parent box
Row(
Modifier
@ -99,13 +98,9 @@ fun StartRecordingFAB() {
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.Center
) {
var isRecording by remember { mutableStateOf(isRecording()) }
//Microphone floating button to manually start/stop listening
FloatingActionButton(onClick = {
if (isRecording) stopRecording() else startRecording()
}, modifier = Modifier.size(70.dp)) {
FloatingActionButton(onClick = onClick, modifier = Modifier.size(70.dp)) {
Icon(
painter = painterResource(id = if (isRecording) R.drawable.ic_baseline_shield_24 else R.drawable.ic_baseline_mic_24),
contentDescription = "microphone"
@ -116,9 +111,12 @@ fun StartRecordingFAB() {
@Composable
fun DisplayMainPage(navController: NavController, uiState: ConversationUiState) {
fun DisplayMainPage(
navController: NavController,
uiState: ConversationUiState,
audioRecorder: AudioRecorder
) {
registerRecorder(LocalContext.current)
//We create a main box with basic padding to avoid having stuff too close to every side.
DefaultBox {
@ -140,20 +138,48 @@ fun DisplayMainPage(navController: NavController, uiState: ConversationUiState)
.align(Alignment.BottomCenter)
.padding(bottom = 40.dp)
) {
StartRecordingFAB()
var listening: Boolean by remember { mutableStateOf(false) }
audioRecorder.waveRecorder.onStateChangeListener = {
when (it) {
RecorderState.RECORDING -> listening = true
RecorderState.STOP -> {
listening = false
thread {
val requestOutput =
contactServerWithFileAudioRecording(audioRecorder.getOutputFile())
val json: JSONObject = JSONObject(requestOutput)
val sent = JSONObject(requestOutput).getString("sent").replace("\"", "")
.replace("[", "").replace("]", "").replace(",", " ")
sent.replaceFirstChar { sent.first().uppercase() }
uiState.addMessage(Message(false, sent))
Thread.sleep(1000)
uiState.addMessage(Message(true, json.getString("response")))
audioRecorder.getOutputFile().delete()
}
}
else -> {}
}
}
StartRecordingFAB(
onClick = { if (listening) audioRecorder.stopRecording() else audioRecorder.startRecording() },
isRecording = listening
)
}
}
}
@Preview(showBackground = true)
/*@Preview(showBackground = true)
@Composable
fun MainPagePreview() {
JarvisComposeTheme {
DisplayMainPage(
rememberNavController(), ConversationUiState(
listOf(Message(true, stringResource(id = R.string.demo_message_1)))
)
), null
)
}
}
}*/

View File

@ -7,7 +7,7 @@ import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
import java.io.IOException
fun contactServerWithFileAudioRecording(file: File) {
fun contactServerWithFileAudioRecording(file: File): String {
val client = OkHttpClient()
val request = Request.Builder()
@ -17,7 +17,6 @@ fun contactServerWithFileAudioRecording(file: File) {
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
println(response.body!!.string())
return response.body!!.string()
}
}