Skip to content

Commit 29e13c6

Browse files
committed
Initial implementation of the deactivation of mic only during TTS in WalkieTalkie mode, initial improvements of the manual TTS implementation, fixed some language identification bugs, improved handling of meta text in walkieTalkie mode (previously if the result in a language contained meta text (e.g. [speaking italian]) it was considered and processed as normal text for language recognition)
1 parent 6bd483d commit 29e13c6

13 files changed

Lines changed: 406 additions & 177 deletions

File tree

app/src/main/java/nie/translator/rtranslator/tools/ImageActivity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ protected void onStart() {
5353
messages.add(new GuiMessage(new Message(this, new Peer(null, "Carlos11", true), "m", "Je vais bien"), false, true));
5454
messages.add(new GuiMessage(new Message(this, "m", "Moi aussi"), true, true));*/
5555

56-
mAdapter = new MessagesAdapter(messages, this.getApplication(), new MessagesAdapter.Callback() {
56+
mAdapter = new MessagesAdapter(messages, this.getApplication(), -1, new MessagesAdapter.Callback() {
5757
@Override
5858
public void onFirstItemAdded() {
5959
mRecyclerView.setVisibility(View.VISIBLE);
6060
}
6161
@Override
62-
public void onPlayAudio(GuiMessage message, android.widget.ImageView icon) {
62+
public void onTTSButtonClick(GuiMessage message, boolean play) {
6363
// Do nothing for ImageActivity
6464
}
6565
});

app/src/main/java/nie/translator/rtranslator/tools/gui/ButtonMic.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ public void onAnimationEnd() {
281281
private Animator updateVolumeLevel(float volumeLevel, CustomAnimator.EndListener listener){
282282
if(leftLine != null && centerLine != null && rightLine != null && leftLine.getVisibility() == VISIBLE && centerLine.getVisibility() == VISIBLE && rightLine.getVisibility() == VISIBLE && volumeLevel > 0) {
283283
int duration_ms = 70;
284-
Log.d("volume", "volume: " + volumeLevel);
284+
//Log.d("volume", "volume: " + volumeLevel);
285285
float maxLengthLeft = Tools.convertDpToPixels(context, MAX_LENGTH_LEFT_LINE_DP);
286286
float maxLengthCenter = Tools.convertDpToPixels(context, MAX_LENGTH_CENTER_LINE_DP);
287287
float maxLengthRight = Tools.convertDpToPixels(context, MAX_LENGTH_RIGHT_LINE_DP);

app/src/main/java/nie/translator/rtranslator/tools/gui/messages/MessagesAdapter.java

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import android.view.LayoutInflater;
2323
import android.view.View;
2424
import android.view.ViewGroup;
25+
import android.widget.ImageView;
2526
import android.widget.LinearLayout;
2627
import android.widget.TextView;
2728

@@ -32,7 +33,6 @@
3233

3334
import java.util.ArrayList;
3435

35-
import nie.translator.rtranslator.Global;
3636
import nie.translator.rtranslator.R;
3737

3838
/** Is used to connect to the RecycleView, which functions as a ListView, a list of strings, which will be inserted in the ViewHolder layout and this will be inserted in the list**/
@@ -42,11 +42,13 @@ public class MessagesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
4242
//private static final int PREVIEW = 2;
4343
private ArrayList<GuiMessage> mResults = new ArrayList<>();
4444
private Callback callback;
45+
private long playingMessageID = -1;
4546

4647
private static boolean showOriginalTranscriptionMsg;
4748

48-
public MessagesAdapter(ArrayList<GuiMessage> messages, Application application, @NonNull Callback callback) {
49+
public MessagesAdapter(ArrayList<GuiMessage> messages, Application application, long playingMessageID, @NonNull Callback callback) {
4950
this.callback = callback;
51+
this.playingMessageID = playingMessageID;
5052
if (messages != null) {
5153
if (messages.size() > 0) {
5254
callback.onFirstItemAdded();
@@ -74,12 +76,17 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi
7476
if (holder instanceof MessageHolder) {
7577
final GuiMessage message = mResults.get(position);
7678
if (holder instanceof ReceivedHolder && message.getMessage().getSender() != null) {
77-
((ReceivedHolder) holder).text.setVisibility(View.GONE);
79+
((ReceivedHolder) holder).containerNoSender.setVisibility(View.GONE);
7880
((ReceivedHolder) holder).containerSender.setVisibility(View.VISIBLE);
7981
((ReceivedHolder) holder).sender.setText(message.getMessage().getSender().getName());
8082
Log.d("recyclerview", "RecyclerView bind sender");
8183
}
8284
((MessageHolder) holder).setText(message.getMessage().getTextToTranslate(), message.getMessage().getText());
85+
if(message.getMessageID() == playingMessageID){
86+
((MessageHolder) holder).setIsPlayingTTS(true);
87+
}else{
88+
((MessageHolder) holder).setIsPlayingTTS(false);
89+
}
8390
Log.d("recyclerview", "RecyclerView bind text");
8491
//holder.itemView.requestLayout();
8592

@@ -88,7 +95,9 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi
8895
@Override
8996
public void onClick(View v) {
9097
if (callback != null && v instanceof android.widget.ImageView) {
91-
callback.onPlayAudio(message, (android.widget.ImageView) v);
98+
boolean play = false;
99+
if(playingMessageID != message.getMessageID()) play = true;
100+
callback.onTTSButtonClick(message, play);
92101
}
93102
}
94103
};
@@ -178,25 +187,51 @@ public ArrayList<GuiMessage> getMessages() {
178187
return mResults;
179188
}
180189

190+
public long getPlayingMessageID() {
191+
return playingMessageID;
192+
}
193+
194+
public void setPlayingMessageID(long playingMessageID) {
195+
long oldPlayingMessageId = this.playingMessageID;
196+
this.playingMessageID = playingMessageID;
197+
if(oldPlayingMessageId != playingMessageID){
198+
if(oldPlayingMessageId != -1) {
199+
int index = getMessageIndex(oldPlayingMessageId);
200+
notifyItemChanged(index);
201+
}
202+
if(playingMessageID != -1){
203+
int index = getMessageIndex(playingMessageID);
204+
notifyItemChanged(index);
205+
//todo: I can insert a scroll to the playing message here
206+
}
207+
}
208+
}
209+
181210
/** The layout for each item in the RecicleView list*/
182211
private static class ReceivedHolder extends RecyclerView.ViewHolder implements MessageHolder {
183212
TextView originalTextToBeTranslated;
184213
TextView text;
214+
LinearLayout containerNoSender;
185215
LinearLayout containerSender;
186216
TextView textSender;
187217
TextView sender;
218+
ImageView ttsButton;
219+
ImageView ttsButton2;
188220

189221
ReceivedHolder(LayoutInflater inflater, ViewGroup parent) {
190222
super(inflater.inflate(R.layout.component_message_received, parent, false));
191223
originalTextToBeTranslated = itemView.findViewById(R.id.original_text_to_be_translated);
192224
if (!showOriginalTranscriptionMsg) {
193225
originalTextToBeTranslated.setVisibility(View.GONE);
194226
}
227+
containerNoSender = itemView.findViewById(R.id.no_sender_container);
195228
text = itemView.findViewById(R.id.text_content);
229+
ttsButton = itemView.findViewById(R.id.tts_button_content);
196230

197231
containerSender = itemView.findViewById(R.id.sender_container);
198232
textSender = itemView.findViewById(R.id.text_content2);
199233
sender = itemView.findViewById(R.id.text_sender);
234+
ttsButton2 = itemView.findViewById(R.id.tts_button_content2);
200235
}
201236

202237
@Override
@@ -205,13 +240,34 @@ public void setText(String originalTextToBeTranslated, String text) {
205240
this.originalTextToBeTranslated.setText(originalTextToBeTranslated);
206241
this.text.setText(text);
207242
}
243+
244+
@Override
245+
public boolean isPlayingTTS() {
246+
return ((int) ttsButton.getTag() == R.drawable.stop_icon);
247+
}
248+
249+
@Override
250+
public void setIsPlayingTTS(boolean playingTTS) {
251+
if(playingTTS) {
252+
ttsButton.setImageResource(R.drawable.stop_icon);
253+
ttsButton.setTag(R.drawable.stop_icon);
254+
ttsButton2.setImageResource(R.drawable.stop_icon);
255+
ttsButton2.setTag(R.drawable.stop_icon);
256+
}else{
257+
ttsButton.setImageResource(R.drawable.sound_icon);
258+
ttsButton.setTag(R.drawable.sound_icon);
259+
ttsButton2.setImageResource(R.drawable.stop_icon);
260+
ttsButton2.setTag(R.drawable.stop_icon);
261+
}
262+
}
208263
}
209264

210265
/** The layout for each item in the RecicleView list*/
211266
private static class SendHolder extends RecyclerView.ViewHolder implements MessageHolder {
212267
TextView originalTextToBeTranslated;
213268
TextView text;
214269
CardView card;
270+
ImageView ttsButton;
215271

216272
SendHolder(LayoutInflater inflater, ViewGroup parent) {
217273
super(inflater.inflate(R.layout.component_message_send, parent, false));
@@ -221,21 +277,40 @@ private static class SendHolder extends RecyclerView.ViewHolder implements Messa
221277
}
222278
text = itemView.findViewById(R.id.text);
223279
card = itemView.findViewById(R.id.card);
280+
ttsButton = itemView.findViewById(R.id.tts_button);
224281
}
225282

226283
@Override
227284
public void setText(String originalTextToBeTranslated, String text) {
228285
this.originalTextToBeTranslated.setText(originalTextToBeTranslated);
229286
this.text.setText(text);
230287
}
288+
289+
@Override
290+
public boolean isPlayingTTS() {
291+
return ((int) ttsButton.getTag() == R.drawable.stop_icon);
292+
}
293+
294+
@Override
295+
public void setIsPlayingTTS(boolean playingTTS) {
296+
if(playingTTS) {
297+
ttsButton.setImageResource(R.drawable.stop_icon);
298+
ttsButton.setTag(R.drawable.stop_icon);
299+
}else{
300+
ttsButton.setImageResource(R.drawable.sound_icon);
301+
ttsButton.setTag(R.drawable.sound_icon);
302+
}
303+
}
231304
}
232305

233306
interface MessageHolder {
234307
void setText(String originalTextToBeTranslated, String text);
308+
boolean isPlayingTTS();
309+
void setIsPlayingTTS(boolean playingTTS);
235310
}
236311

237312
public interface Callback {
238313
void onFirstItemAdded();
239-
void onPlayAudio(GuiMessage message, android.widget.ImageView icon);
314+
void onTTSButtonClick(GuiMessage message, boolean play);
240315
}
241316
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package nie.translator.rtranslator.voice_translation;
2+
3+
import android.content.Context;
4+
import android.speech.tts.TextToSpeech;
5+
import android.speech.tts.UtteranceProgressListener;
6+
import android.util.Log;
7+
import android.util.Pair;
8+
9+
import java.util.ArrayDeque;
10+
11+
import javax.annotation.Nullable;
12+
13+
import nie.translator.rtranslator.tools.CustomLocale;
14+
import nie.translator.rtranslator.tools.TTS;
15+
import nie.translator.rtranslator.tools.gui.messages.GuiMessage;
16+
17+
public class TTSEngine {
18+
private final ArrayDeque<Pair<GuiMessage, CustomLocale>> messageQueue = new ArrayDeque<>();
19+
@Nullable
20+
private GuiMessage executingMessage = null;
21+
private boolean speaking = false;
22+
@Nullable
23+
private final TTS tts;
24+
private boolean pause = false;
25+
private Context context;
26+
@Nullable
27+
private TTSEngineListener listener;
28+
29+
30+
public TTSEngine(Context context, @Nullable TTSEngineListener listener) {
31+
this.context = context;
32+
this.tts = new TTS(context, new TTS.InitListener() { // tts initialization (to be improved, automatic package installation)
33+
@Override
34+
public void onInit() {
35+
if(TTSEngine.this.tts != null) {
36+
TTSEngine.this.tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
37+
@Override
38+
public void onDone(String utteranceId) {
39+
if (listener != null) {
40+
boolean isLast = messageQueue.isEmpty();
41+
listener.onUtteranceFinished(executingMessage, isLast);
42+
executingMessage = null;
43+
}
44+
speak();
45+
}
46+
47+
@Override
48+
public void onError(String utteranceId) {
49+
stop();
50+
}
51+
52+
@Override
53+
public void onStart(String utteranceId) {
54+
55+
}
56+
});
57+
}
58+
}
59+
60+
@Override
61+
public void onError(int reason) {
62+
63+
}
64+
});
65+
this.listener = listener;
66+
67+
}
68+
69+
70+
public boolean speak(GuiMessage message, CustomLocale language){
71+
messageQueue.addLast(new Pair<>(message, language));
72+
if (!messageQueue.isEmpty() && !speaking && !pause) {
73+
return speak();
74+
}
75+
return false;
76+
}
77+
78+
private boolean speak(){
79+
if(!pause) {
80+
boolean wasSpeaking = speaking;
81+
speaking = true;
82+
Pair<GuiMessage, CustomLocale> pair = messageQueue.pollFirst();
83+
if (pair != null && tts != null) {
84+
if(listener != null) listener.onUtteranceStarting(pair.first, !wasSpeaking);
85+
executingMessage = pair.first;
86+
if (tts.getVoice() != null && pair.second.equals(new CustomLocale(tts.getVoice().getLocale()))) {
87+
tts.speak(pair.first.getMessage().getText(), TextToSpeech.QUEUE_ADD, null, String.valueOf(pair.first.getMessageID()));
88+
} else {
89+
tts.setLanguage(pair.second, context);
90+
tts.speak(pair.first.getMessage().getText(), TextToSpeech.QUEUE_ADD, null, String.valueOf(pair.first.getMessageID()));
91+
}
92+
return true;
93+
}else{
94+
speaking = false;
95+
executingMessage = null;
96+
}
97+
}
98+
return false;
99+
}
100+
101+
public void pause() {
102+
if(!pause) {
103+
Log.i("tts_engine", "tts engine paused");
104+
if(tts != null) tts.stop();
105+
executingMessage = null;
106+
pause = true;
107+
}
108+
}
109+
110+
public void resume() {
111+
if(pause){
112+
Log.i("tts_engine", "tts engine resumed");
113+
pause = false;
114+
speak();
115+
}
116+
}
117+
118+
public boolean isPaused(){
119+
return pause;
120+
}
121+
122+
public boolean isEmpty(){
123+
return messageQueue.isEmpty();
124+
}
125+
126+
public void stop(){
127+
if(tts != null) tts.stop();
128+
boolean notify = listener != null && (executingMessage != null || !messageQueue.isEmpty());
129+
GuiMessage oldExecutingMessage = executingMessage;
130+
executingMessage = null;
131+
messageQueue.clear();
132+
if(notify){
133+
listener.onUtteranceFinished(oldExecutingMessage, true);
134+
}
135+
speaking = false;
136+
}
137+
138+
@Nullable
139+
public GuiMessage getExecutingMessage() {
140+
return executingMessage;
141+
}
142+
143+
@Nullable
144+
public TTS getTts() {
145+
return tts;
146+
}
147+
148+
public void shutdown() {
149+
if(tts != null) tts.shutdown();
150+
}
151+
152+
public boolean isActive() {
153+
if(tts != null) return tts.isActive();
154+
return false;
155+
}
156+
157+
public static abstract class TTSEngineListener {
158+
abstract void onUtteranceStarting(GuiMessage message, boolean first);
159+
abstract void onUtteranceFinished(@Nullable GuiMessage message, boolean last);
160+
abstract void onInitError(int reason);
161+
}
162+
}

0 commit comments

Comments
 (0)