From c094f4840b4e597e1887e9133fbe4dd13115832a Mon Sep 17 00:00:00 2001 From: "Reece H. Dunn" Date: Sun, 11 Oct 2015 09:16:21 +0100 Subject: [PATCH] Fix a Parcel `collection == null` exception. 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. --- .../espeak/test/TextToSpeechServiceTest.java | 9 +++++++++ .../com/reecedunn/espeak/test/TextToSpeechTest.java | 6 ++++-- android/src/com/reecedunn/espeak/TtsService.java | 11 ++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/android/eSpeakTests/src/com/reecedunn/espeak/test/TextToSpeechServiceTest.java b/android/eSpeakTests/src/com/reecedunn/espeak/test/TextToSpeechServiceTest.java index df6f4fa9..40b32723 100644 --- a/android/eSpeakTests/src/com/reecedunn/espeak/test/TextToSpeechServiceTest.java +++ b/android/eSpeakTests/src/com/reecedunn/espeak/test/TextToSpeechServiceTest.java @@ -26,6 +26,7 @@ import com.reecedunn.espeak.TtsService; import com.reecedunn.espeak.Voice; import java.util.Locale; +import java.util.Set; import static com.reecedunn.espeak.test.TtsMatcher.isTtsLangCode; import static org.hamcrest.MatcherAssert.assertThat; @@ -52,6 +53,10 @@ public class TextToSpeechServiceTest extends AndroidTestCase return super.onLoadLanguage(language, country, variant); } + public Set onGetFeaturesForLanguage(String language, String country, String variant) { + return super.onGetFeaturesForLanguage(language, country, variant); + } + public Voice getActiveVoice() { return mMatchingVoice; } @@ -242,6 +247,10 @@ public class TextToSpeechServiceTest extends AndroidTestCase assertThat(locale.getISO3Language(), is(data.javaLanguage)); assertThat(locale.getISO3Country(), is(data.javaCountry)); assertThat(locale.getVariant(), is(data.variant)); + + Set features = mService.onGetFeaturesForLanguage(data.javaLanguage, data.javaCountry, data.variant); + assertThat(features, is(notNullValue())); + assertThat(features.size(), is(0)); } } } diff --git a/android/eSpeakTests/src/com/reecedunn/espeak/test/TextToSpeechTest.java b/android/eSpeakTests/src/com/reecedunn/espeak/test/TextToSpeechTest.java index 6800394a..c0fb4580 100644 --- a/android/eSpeakTests/src/com/reecedunn/espeak/test/TextToSpeechTest.java +++ b/android/eSpeakTests/src/com/reecedunn/espeak/test/TextToSpeechTest.java @@ -126,7 +126,8 @@ public class TextToSpeechTest extends TextToSpeechTestCase assertThat(voice.getLocale().getLanguage(), is(data.javaLanguage)); assertThat(voice.getLocale().getCountry(), is(data.javaCountry)); 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.getQuality(), is(android.speech.tts.Voice.QUALITY_NORMAL)); @@ -141,7 +142,8 @@ public class TextToSpeechTest extends TextToSpeechTestCase assertThat(voice2.getLocale().getLanguage(), is(data.javaLanguage)); assertThat(voice2.getLocale().getCountry(), is(data.javaCountry)); 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.getQuality(), is(android.speech.tts.Voice.QUALITY_NORMAL)); } diff --git a/android/src/com/reecedunn/espeak/TtsService.java b/android/src/com/reecedunn/espeak/TtsService.java index 4124badc..c417332a 100644 --- a/android/src/com/reecedunn/espeak/TtsService.java +++ b/android/src/com/reecedunn/espeak/TtsService.java @@ -44,9 +44,11 @@ import com.reecedunn.espeak.SpeechSynthesis.SynthReadyCallback; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; /** * Implements the eSpeak engine as a {@link TextToSpeechService}. @@ -199,6 +201,11 @@ public class TtsService extends TextToSpeechService { return match.second; } + @Override + protected Set onGetFeaturesForLanguage(String lang, String country, String variant) { + return new HashSet(); + } + @Override public String onGetDefaultVoiceNameFor(String language, String country, String variant) { final Voice match = getDefaultVoiceFor(language, country, variant).first; @@ -211,7 +218,9 @@ public class TtsService extends TextToSpeechService { for (Voice voice : mAvailableVoices.values()) { int quality = android.speech.tts.Voice.QUALITY_NORMAL; 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 features = onGetFeaturesForLanguage(locale.getLanguage(), locale.getCountry(), locale.getVariant()); + voices.add(new android.speech.tts.Voice(voice.name, locale, quality, latency, false, features)); } return voices; }