diff --git a/app/build.gradle b/app/build.gradle index b59c6e0..c354733 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" diff --git a/app/src/main/java/ch/mathieubroillet/jarvis/android/audio/AudioRecorder.kt b/app/src/main/java/ch/mathieubroillet/jarvis/android/audio/AudioRecorder.kt new file mode 100644 index 0000000..8ba3fce --- /dev/null +++ b/app/src/main/java/ch/mathieubroillet/jarvis/android/audio/AudioRecorder.kt @@ -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) +} diff --git a/app/src/main/java/ch/mathieubroillet/jarvis/android/audio/AudioUtils.kt b/app/src/main/java/ch/mathieubroillet/jarvis/android/audio/AudioUtils.kt deleted file mode 100644 index e8a12b0..0000000 --- a/app/src/main/java/ch/mathieubroillet/jarvis/android/audio/AudioUtils.kt +++ /dev/null @@ -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 -> {} - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/ch/mathieubroillet/jarvis/android/nav/Navigation.kt b/app/src/main/java/ch/mathieubroillet/jarvis/android/nav/Navigation.kt index e43c9a7..a94a73a 100644 --- a/app/src/main/java/ch/mathieubroillet/jarvis/android/nav/Navigation.kt +++ b/app/src/main/java/ch/mathieubroillet/jarvis/android/nav/Navigation.kt @@ -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) ) } diff --git a/app/src/main/java/ch/mathieubroillet/jarvis/android/pages/MainPage.kt b/app/src/main/java/ch/mathieubroillet/jarvis/android/pages/MainPage.kt index 6de39ba..dcdaf70 100644 --- a/app/src/main/java/ch/mathieubroillet/jarvis/android/pages/MainPage.kt +++ b/app/src/main/java/ch/mathieubroillet/jarvis/android/pages/MainPage.kt @@ -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 ) } -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/app/src/main/java/ch/mathieubroillet/jarvis/android/utils/RequestsUtils.kt b/app/src/main/java/ch/mathieubroillet/jarvis/android/utils/RequestsUtils.kt index fa61355..d059402 100644 --- a/app/src/main/java/ch/mathieubroillet/jarvis/android/utils/RequestsUtils.kt +++ b/app/src/main/java/ch/mathieubroillet/jarvis/android/utils/RequestsUtils.kt @@ -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() } } \ No newline at end of file