Ok so i have a recycler view that i add a bunch of text to, the text are in cards with a fixed height and width, when i press a button the text is read out using androids TTS engine.
The problem is if the text exceeds the room in the view I want the recycler view to scroll to the beginning of the text/recycler view, read out the text and scroll the view as it goes while keeping up or slowing down with the android tts engine, so I've set a custom linearlayoutmanager on my recycler view with help from here that allows me to speed up or slow down the scrolling depending on a float variable, I've then tried to accomplish this a few ways to no avail and found that I need to use the UtteranceProgressListener so i created a method that updates the position of the card and tried to pass this to the UtteranceProgressListener like this
public void speakWordsExclusive(String speech, final int position) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
myTTS.speak(speech, TextToSpeech.QUEUE_ADD, params, "");
}
else{
myTTS.speak(speech, TextToSpeech.QUEUE_ADD, map);
}
myTTS.setOnUtteranceProgressListener(new UtteranceProgressListener() {
@Override
public void onStart(final String utteranceId) {
}
@Override
public void onDone(final String utteranceId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//position should print 1,2,3, etc but just gives me the last number
System.out.println("position UtteranceProgress " + position );
//the line below will smooth scroll the view to the end position with
//no care for which cards is being read
SpeakGridDB.recyclerView.getLayoutManager()
.smoothScrollToPosition(SpeakGridDB.recyclerView, null, position );
}
});
}
@Override
public void onError(String utteranceId) {
}
});
}
this doesnt work the position is always just the highest number so if I were expecting 1,2,3,4 i would actually just get 4 like this,
11-07 14:23:09.121 28882-28882/ss.sealstudios.com.socialstories
I/System.out: position UtteranceProgress 4
11-07 14:23:09.741 28882-
28882/ss.sealstudios.com.socialstories
I/System.out: position UtteranceProgress 4
11-07 14:23:10.353 28882-
28882/ss.sealstudios.com.socialstories
I/System.out: position UtteranceProgress 4
11-07 14:23:10.944 28882-
28882/ss.sealstudios.com.socialstories
I/System.out: position UtteranceProgress 4
I give the UtteranceProgressListener the position from the method below
public void speakAndMoveExclusive() {
final ArrayList<String> list = new ArrayList<>();
list.clear();
words = list.toString();
SpeakGridDB.recyclerView.getLayoutManager().scrollToPosition(0);
for (int i = 0; i < SpeakGridDB.cardMakerList.size(); i++) {
list.add(SpeakGridDB.cardMakerList.get(i).getCardSpeech());
words = list.toString();
}
System.out.println(words);
if (words == null) {
speakWords("");
} else if (words.contains(", 's")) {
formatString = words.replaceFirst(", 's", "'s");
} else if (words.contains(", ing")) {
formatString = words.replaceFirst(", ing", "ing");
}else{
formatString = words;
}
List<String> items = Arrays.asList(formatString.split("\\s*,\\s*"));
if (items.contains("[]")){
speakWords("");
}
else{
for(int j = 0; j < items.size();j++){
speakWordsExclusive(items.get(j), j);
//the below line prints the position just fine
System.out.println("position j " + j);
params.putString
(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,"UniqueID" + j);
map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,"UniqueID" +
j);
}
}
}
the position variable ive set in here obviously isnt the position but it is identical and the print out reflects that, like below
11-07 14:23:08.452 28882-28882/ss.sealstudios.com.socialstories
I/System.out: position j 0
11-07 14:23:08.454 28882-28882/ss.sealstudios.com.socialstories
I/System.out: position j 1
11-07 14:23:08.456 28882-28882/ss.sealstudios.com.socialstories
I/System.out: position j 2
11-07 14:23:08.459 28882-28882/ss.sealstudios.com.socialstories
I/System.out: position j 3
if i just give this to a static int and give that to the onUtteranceProgressListener then i get a slightly better result but it is still not in sync with each other
so im wondering whats going on between them I've looked through the docs and cannot seem to make any sense of it I've moved my call to onStart instead of onDone hoping for a better result but nothing changes can anyone help me with this?
ok like @brandall said its best practice to set an UtteranceProgressListener once and preferably from onInit(int initStatus) so ive switched to this model and have it working, my code generally works for scrolling the view, note the code i will paste throws a NPE in onDone where I'm setting the background colour something to do with it not being in view make sure your updating the view from a UI thread
public void onInit(int initStatus) {
if (initStatus == TextToSpeech.SUCCESS) {
myTTS.setLanguage(Locale.UK);
//myExclusiveTTS.setLanguage(Locale.UK);
myExclusiveTTS.setOnUtteranceProgressListener(new
UtteranceProgressListener() {
@Override
public void onDone(String utteranceId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
SpeakGridDB.recyclerView.getLayoutManager()
.getChildAt(cardCountPosition)
.setBackgroundResource(R.drawable.border);
cardCountPosition ++;
if(cardCountPosition ==
SpeakGridDB.cardMakerList.size()){
cardCountPosition = 0;
}
}
});
}
@Override
public void onError(String utteranceId) {
}
@Override
public void onStart(String utteranceId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
moveView();
SpeakGridDB.recyclerView.getLayoutManager()
.getChildAt(cardCountPosition).setBackgroundResource
(R.drawable.selected_blue_border);
}
});
}
});
} else if (initStatus == TextToSpeech.ERROR) {
Toast.makeText(this, "Oops sorry! Text To Speech failed...
SimpleAAC cannot fix this", Toast.LENGTH_LONG).show();
}
}
public void moveView(){
runOnUiThread(new Runnable() {
@Override
public void run() {
if (cardCountPosition < SpeakGridDB.cardMakerList.size()){
SpeakGridDB.recyclerView.getLayoutManager()
.smoothScrollToPosition(SpeakGridDB.recyclerView, null,
cardCountPosition + 1);
}
else{
//this is never called
cardCountPosition = 0;
}
}
});
}