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 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
implementation 'com.github.squti:Android-Wave-Recorder:1.6.0' implementation 'com.github.squti:Android-Wave-Recorder:1.6.0'
implementation("com.squareup.okhttp3:okhttp:4.9.3") 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' implementation 'androidx.activity:activity-compose:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" 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 package ch.mathieubroillet.jarvis.android.nav
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import ch.mathieubroillet.jarvis.android.R 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.ConversationUiState
import ch.mathieubroillet.jarvis.android.chat.Message import ch.mathieubroillet.jarvis.android.chat.Message
import ch.mathieubroillet.jarvis.android.pages.DisplayMainPage import ch.mathieubroillet.jarvis.android.pages.DisplayMainPage
@ -52,7 +54,8 @@ fun MainScreen(navController: NavController) {
stringResource(id = R.string.demo_message_1) 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.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import ch.mathieubroillet.jarvis.android.R 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.ConversationUiState
import ch.mathieubroillet.jarvis.android.chat.Message import ch.mathieubroillet.jarvis.android.chat.Message
import ch.mathieubroillet.jarvis.android.chat.Messages import ch.mathieubroillet.jarvis.android.chat.Messages
import ch.mathieubroillet.jarvis.android.nav.Screen 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.ui.theme.productSansFont
import ch.mathieubroillet.jarvis.android.utils.DefaultBox import ch.mathieubroillet.jarvis.android.utils.DefaultBox
import ch.mathieubroillet.jarvis.android.utils.IconAlertDialogTextField 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". //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 @Composable
fun StartRecordingFAB() { fun StartRecordingFAB(onClick: () -> Unit, isRecording: Boolean) {
//We create a row that we align to the bottom center of the parent box //We create a row that we align to the bottom center of the parent box
Row( Row(
Modifier Modifier
@ -99,13 +98,9 @@ fun StartRecordingFAB() {
verticalAlignment = Alignment.Bottom, verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
var isRecording by remember { mutableStateOf(isRecording()) }
//Microphone floating button to manually start/stop listening //Microphone floating button to manually start/stop listening
FloatingActionButton(onClick = { FloatingActionButton(onClick = onClick, modifier = Modifier.size(70.dp)) {
if (isRecording) stopRecording() else startRecording()
}, modifier = Modifier.size(70.dp)) {
Icon( Icon(
painter = painterResource(id = if (isRecording) R.drawable.ic_baseline_shield_24 else R.drawable.ic_baseline_mic_24), painter = painterResource(id = if (isRecording) R.drawable.ic_baseline_shield_24 else R.drawable.ic_baseline_mic_24),
contentDescription = "microphone" contentDescription = "microphone"
@ -116,9 +111,12 @@ fun StartRecordingFAB() {
@Composable @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. //We create a main box with basic padding to avoid having stuff too close to every side.
DefaultBox { DefaultBox {
@ -140,20 +138,48 @@ fun DisplayMainPage(navController: NavController, uiState: ConversationUiState)
.align(Alignment.BottomCenter) .align(Alignment.BottomCenter)
.padding(bottom = 40.dp) .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 @Composable
fun MainPagePreview() { fun MainPagePreview() {
JarvisComposeTheme { JarvisComposeTheme {
DisplayMainPage( DisplayMainPage(
rememberNavController(), ConversationUiState( rememberNavController(), ConversationUiState(
listOf(Message(true, stringResource(id = R.string.demo_message_1))) 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.File
import java.io.IOException import java.io.IOException
fun contactServerWithFileAudioRecording(file: File) { fun contactServerWithFileAudioRecording(file: File): String {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -17,7 +17,6 @@ fun contactServerWithFileAudioRecording(file: File) {
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response") if (!response.isSuccessful) throw IOException("Unexpected code $response")
return response.body!!.string()
println(response.body!!.string())
} }
} }