The UI now responds to recording/not recording and messages appears in chat, still some crashes
This commit is contained in:
parent
7baa44a8aa
commit
c24d07bb26
@ -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"
|
||||||
|
@ -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)
|
||||||
|
}
|
@ -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 -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}*/
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user