From 0ea46b29b24beddf371cb648f2ec29a22b02c739 Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Wed, 30 Dec 2020 11:37:46 -0600 Subject: [PATCH] Partially implement callbacks. Unfinished due to lazy_static inconsistencies. --- Makefile.toml | 6 ++ .../app/src/main/java/rs/tts/Bridge.java | 13 ++- examples/android/src/lib.rs | 4 +- src/backends/android.rs | 101 +++++++++++++++++- src/lib.rs | 2 + 5 files changed, 120 insertions(+), 6 deletions(-) diff --git a/Makefile.toml b/Makefile.toml index 7673a80..db3c4d3 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -1,3 +1,9 @@ +[tasks.run-android] +script = [ + "cd examples/android", + "./gradlew runDebug", +] + [tasks.log-android] command = "adb" args = ["logcat", "RustStdoutStderr:D", "*:S"] \ No newline at end of file diff --git a/examples/android/app/src/main/java/rs/tts/Bridge.java b/examples/android/app/src/main/java/rs/tts/Bridge.java index 4850c25..e6b81a4 100644 --- a/examples/android/app/src/main/java/rs/tts/Bridge.java +++ b/examples/android/app/src/main/java/rs/tts/Bridge.java @@ -1,15 +1,24 @@ package rs.tts; import android.speech.tts.TextToSpeech; +import android.speech.tts.UtteranceProgressListener; @androidx.annotation.Keep -public class Bridge implements TextToSpeech.OnInitListener { +public class Bridge extends UtteranceProgressListener implements TextToSpeech.OnInitListener { public int backendId; public Bridge(int backendId) { this.backendId = backendId; } - public native void onInit(int status); + public native void onInit(int status); + + public native void onStart(String utteranceId); + + public native void onStop(String utteranceId, Boolean interrupted); + + public native void onDone(String utteranceId); + + public native void onError(String utteranceId) ; } \ No newline at end of file diff --git a/examples/android/src/lib.rs b/examples/android/src/lib.rs index 8a256d6..08a156d 100644 --- a/examples/android/src/lib.rs +++ b/examples/android/src/lib.rs @@ -7,9 +7,9 @@ fn run() -> Result<(), Error> { .. } = tts.supported_features(); if utterance_callbacks { - tts.on_utterance_begin(Some(Box::new(|utterance| { + /*tts.on_utterance_begin(Some(Box::new(|utterance| { println!("Started speaking {:?}", utterance) - })))?; + })))?;*/ tts.on_utterance_end(Some(Box::new(|utterance| { println!("Finished speaking {:?}", utterance) })))?; diff --git a/src/backends/android.rs b/src/backends/android.rs index a1c5d07..16a1212 100644 --- a/src/backends/android.rs +++ b/src/backends/android.rs @@ -1,11 +1,12 @@ #[cfg(target_os = "android")] use std::collections::HashSet; +use std::ffi::{CStr, CString}; use std::os::raw::c_void; use std::sync::{Mutex, RwLock}; use std::thread; use std::time::Duration; -use jni::objects::{GlobalRef, JObject}; +use jni::objects::{GlobalRef, JObject, JString}; use jni::sys::{jfloat, jint, JNI_VERSION_1_6}; use jni::{JNIEnv, JavaVM}; use lazy_static::lazy_static; @@ -50,6 +51,96 @@ pub unsafe extern "C" fn Java_rs_tts_Bridge_onInit(env: JNIEnv, obj: JObject, st } } +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_rs_tts_Bridge_onStart( + env: JNIEnv, + obj: JObject, + utterance_id: JString, +) { + let backend_id = env + .get_field(obj, "backendId", "I") + .expect("Failed to get backend ID") + .i() + .expect("Failed to cast to int") as u64; + let backend_id = BackendId::Android(backend_id); + let utterance_id = CString::from(CStr::from_ptr( + env.get_string(utterance_id).unwrap().as_ptr(), + )) + .into_string() + .unwrap(); + let utterance_id = utterance_id.parse::().unwrap(); + let utterance_id = UtteranceId::Android(utterance_id); + println!("Retrieving callbacks for {:?}", backend_id); + let mut callbacks = CALLBACKS.lock().unwrap(); + println!("Callback keys: {:?}", callbacks.keys()); + if let Some(cb) = callbacks.get_mut(&backend_id) { + if let Some(f) = cb.utterance_begin.as_mut() { + f(utterance_id); + } + } +} + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_rs_tts_Bridge_onStop( + env: JNIEnv, + obj: JObject, + utterance_id: JString, +) { + let id = env + .get_field(obj, "backendId", "I") + .expect("Failed to get backend ID") + .i() + .expect("Failed to cast to int") as u64; + let utterance_id = CString::from(CStr::from_ptr( + env.get_string(utterance_id).unwrap().as_ptr(), + )) + .into_string() + .unwrap(); + //println!("Call stop for {}", utterance_id); +} + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_rs_tts_Bridge_onDone( + env: JNIEnv, + obj: JObject, + utterance_id: JString, +) { + let id = env + .get_field(obj, "backendId", "I") + .expect("Failed to get backend ID") + .i() + .expect("Failed to cast to int") as u64; + let utterance_id = CString::from(CStr::from_ptr( + env.get_string(utterance_id).unwrap().as_ptr(), + )) + .into_string() + .unwrap(); + //println!("Call done for {}", utterance_id); +} + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_rs_tts_Bridge_onError( + env: JNIEnv, + obj: JObject, + utterance_id: JString, +) { + let id = env + .get_field(obj, "backendId", "I") + .expect("Failed to get backend ID") + .i() + .expect("Failed to cast to int") as u64; + let utterance_id = CString::from(CStr::from_ptr( + env.get_string(utterance_id).unwrap().as_ptr(), + )) + .into_string() + .unwrap(); + //println!("Call error for {}", utterance_id); +} + #[derive(Clone)] pub(crate) struct Android { id: BackendId, @@ -77,6 +168,12 @@ impl Android { "(Landroid/content/Context;Landroid/speech/tts/TextToSpeech$OnInitListener;)V", &[native_activity.activity().into(), bridge.into()], )?; + env.call_method( + tts, + "setOnUtteranceProgressListener", + "(Landroid/speech/tts/UtteranceProgressListener;)I", + &[bridge.into()], + )?; { let mut pending = PENDING_INITIALIZATIONS.write().unwrap(); (*pending).insert(bid); @@ -122,7 +219,7 @@ impl Backend for Android { pitch: true, volume: false, is_speaking: true, - utterance_callbacks: false, + utterance_callbacks: true, } } diff --git a/src/lib.rs b/src/lib.rs index 2e53b5e..15021d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -213,7 +213,9 @@ impl TTS { if let Ok(backend) = backend { if let Some(id) = backend.0.id() { let mut callbacks = CALLBACKS.lock().unwrap(); + println!("Initializing callbacks for {:?}", id); callbacks.insert(id, Callbacks::default()); + println!("Keys after: {:?}", callbacks.keys()); } Ok(backend) } else {