This is caused by the Voice features being set to null. The fix in Android Marshmallow is to make onGetFeaturesForLanguage return an empty HashSet object. This does not work for eSpeak because: 1. eSpeak was overriding onGetVoices and initializing each Voice's features to null; 2. the bug is still present on Lollipop. Thus, the fix here is two-fold: 1. make onGetVoices use onGetFeaturesForLanguage; 2. make onGetFeaturesForLanguage return an empty HashSet, so that eSpeak does not crash on Lollipop either.master
import com.reecedunn.espeak.Voice; | import com.reecedunn.espeak.Voice; | ||||
import java.util.Locale; | import java.util.Locale; | ||||
import java.util.Set; | |||||
import static com.reecedunn.espeak.test.TtsMatcher.isTtsLangCode; | import static com.reecedunn.espeak.test.TtsMatcher.isTtsLangCode; | ||||
import static org.hamcrest.MatcherAssert.assertThat; | import static org.hamcrest.MatcherAssert.assertThat; | ||||
return super.onLoadLanguage(language, country, variant); | return super.onLoadLanguage(language, country, variant); | ||||
} | } | ||||
public Set<String> onGetFeaturesForLanguage(String language, String country, String variant) { | |||||
return super.onGetFeaturesForLanguage(language, country, variant); | |||||
} | |||||
public Voice getActiveVoice() { | public Voice getActiveVoice() { | ||||
return mMatchingVoice; | return mMatchingVoice; | ||||
} | } | ||||
assertThat(locale.getISO3Language(), is(data.javaLanguage)); | assertThat(locale.getISO3Language(), is(data.javaLanguage)); | ||||
assertThat(locale.getISO3Country(), is(data.javaCountry)); | assertThat(locale.getISO3Country(), is(data.javaCountry)); | ||||
assertThat(locale.getVariant(), is(data.variant)); | assertThat(locale.getVariant(), is(data.variant)); | ||||
Set<String> features = mService.onGetFeaturesForLanguage(data.javaLanguage, data.javaCountry, data.variant); | |||||
assertThat(features, is(notNullValue())); | |||||
assertThat(features.size(), is(0)); | |||||
} | } | ||||
} | } | ||||
} | } |
assertThat(voice.getLocale().getLanguage(), is(data.javaLanguage)); | assertThat(voice.getLocale().getLanguage(), is(data.javaLanguage)); | ||||
assertThat(voice.getLocale().getCountry(), is(data.javaCountry)); | assertThat(voice.getLocale().getCountry(), is(data.javaCountry)); | ||||
assertThat(voice.getLocale().getVariant(), is(data.variant)); | assertThat(voice.getLocale().getVariant(), is(data.variant)); | ||||
assertThat(voice.getFeatures(), is(nullValue())); | |||||
assertThat(voice.getFeatures(), is(notNullValue())); | |||||
assertThat(voice.getFeatures().size(), is(0)); | |||||
assertThat(voice.getLatency(), is(android.speech.tts.Voice.LATENCY_VERY_LOW)); | assertThat(voice.getLatency(), is(android.speech.tts.Voice.LATENCY_VERY_LOW)); | ||||
assertThat(voice.getQuality(), is(android.speech.tts.Voice.QUALITY_NORMAL)); | assertThat(voice.getQuality(), is(android.speech.tts.Voice.QUALITY_NORMAL)); | ||||
assertThat(voice2.getLocale().getLanguage(), is(data.javaLanguage)); | assertThat(voice2.getLocale().getLanguage(), is(data.javaLanguage)); | ||||
assertThat(voice2.getLocale().getCountry(), is(data.javaCountry)); | assertThat(voice2.getLocale().getCountry(), is(data.javaCountry)); | ||||
assertThat(voice2.getLocale().getVariant(), is(data.variant)); | assertThat(voice2.getLocale().getVariant(), is(data.variant)); | ||||
assertThat(voice2.getFeatures(), is(nullValue())); | |||||
assertThat(voice2.getFeatures(), is(notNullValue())); | |||||
assertThat(voice2.getFeatures().size(), is(0)); | |||||
assertThat(voice2.getLatency(), is(android.speech.tts.Voice.LATENCY_VERY_LOW)); | assertThat(voice2.getLatency(), is(android.speech.tts.Voice.LATENCY_VERY_LOW)); | ||||
assertThat(voice2.getQuality(), is(android.speech.tts.Voice.QUALITY_NORMAL)); | assertThat(voice2.getQuality(), is(android.speech.tts.Voice.QUALITY_NORMAL)); | ||||
} | } |
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.HashSet; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Locale; | import java.util.Locale; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | |||||
/** | /** | ||||
* Implements the eSpeak engine as a {@link TextToSpeechService}. | * Implements the eSpeak engine as a {@link TextToSpeechService}. | ||||
return match.second; | return match.second; | ||||
} | } | ||||
@Override | |||||
protected Set<String> onGetFeaturesForLanguage(String lang, String country, String variant) { | |||||
return new HashSet<String>(); | |||||
} | |||||
@Override | @Override | ||||
public String onGetDefaultVoiceNameFor(String language, String country, String variant) { | public String onGetDefaultVoiceNameFor(String language, String country, String variant) { | ||||
final Voice match = getDefaultVoiceFor(language, country, variant).first; | final Voice match = getDefaultVoiceFor(language, country, variant).first; | ||||
for (Voice voice : mAvailableVoices.values()) { | for (Voice voice : mAvailableVoices.values()) { | ||||
int quality = android.speech.tts.Voice.QUALITY_NORMAL; | int quality = android.speech.tts.Voice.QUALITY_NORMAL; | ||||
int latency = android.speech.tts.Voice.LATENCY_VERY_LOW; | int latency = android.speech.tts.Voice.LATENCY_VERY_LOW; | ||||
voices.add(new android.speech.tts.Voice(voice.name, new Locale(voice.locale.getISO3Language(), voice.locale.getISO3Country(), voice.locale.getVariant()), quality, latency, false, null)); | |||||
Locale locale = new Locale(voice.locale.getISO3Language(), voice.locale.getISO3Country(), voice.locale.getVariant()); | |||||
Set<String> features = onGetFeaturesForLanguage(locale.getLanguage(), locale.getCountry(), locale.getVariant()); | |||||
voices.add(new android.speech.tts.Voice(voice.name, locale, quality, latency, false, features)); | |||||
} | } | ||||
return voices; | return voices; | ||||
} | } |