Merge pull request #2818 from trashkalmar/tts-crash-guards

[android] fix: Prevent crash if the TTS is corrupted.
This commit is contained in:
Dmitry Yunitsky 2016-04-07 16:09:17 +03:00
commit a99bc4a938
3 changed files with 69 additions and 23 deletions

View file

@ -10,8 +10,8 @@ import java.util.Locale;
*/
public class LanguageData
{
public static class NotAvailableException extends Exception {
public NotAvailableException(Locale locale)
static class NotAvailableException extends Exception {
NotAvailableException(Locale locale)
{
super("Locale \"" + locale + "\" is not supported by current TTS engine");
}
@ -22,7 +22,7 @@ public class LanguageData
public final String internalCode;
public final boolean downloaded;
public LanguageData(String line, String name, TextToSpeech tts) throws NotAvailableException
LanguageData(String line, String name, TextToSpeech tts) throws NotAvailableException, IllegalArgumentException
{
this.name = name;
@ -36,6 +36,7 @@ public class LanguageData
String country = (parts.length > 1 ? parts[1] : "");
locale = new Locale(language, country);
// tts.isLanguageAvailable() may throw IllegalArgumentException if the TTS is corrupted internally.
int status = tts.isLanguageAvailable(locale);
if (status < TextToSpeech.LANG_MISSING_DATA)
throw new NotAvailableException(locale);
@ -43,7 +44,7 @@ public class LanguageData
downloaded = (status >= TextToSpeech.LANG_AVAILABLE);
}
public boolean matchesLocale(Locale locale)
boolean matchesLocale(Locale locale)
{
String lang = locale.getLanguage();
if (!lang.equals(this.locale.getLanguage()))
@ -61,7 +62,7 @@ public class LanguageData
return true;
}
public boolean matchesInternalCode(String internalCode)
boolean matchesInternalCode(String internalCode)
{
return this.internalCode.equals(internalCode);
}

View file

@ -7,15 +7,17 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import com.mapswithme.maps.Framework;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.maps.R;
import com.mapswithme.util.Config;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import com.mapswithme.maps.Framework;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.maps.R;
import com.mapswithme.util.Config;
import com.mapswithme.util.statistics.Statistics;
/**
* {@code TtsPlayer} class manages available TTS voice languages.
* Single TTS language is described by {@link LanguageData} item.
@ -46,6 +48,13 @@ public enum TtsPlayer
TtsPlayer() {}
private static void reportFailure(IllegalArgumentException e, String location)
{
Statistics.INSTANCE.trackEvent(Statistics.EventName.TTS_FAILURE_LOCATION,
Statistics.params().add(Statistics.EventParam.ERR_MSG, e.getMessage())
.add(Statistics.EventParam.FROM, location));
}
private static @Nullable LanguageData findSupportedLanguage(String internalCode, List<LanguageData> langs)
{
if (TextUtils.isEmpty(internalCode))
@ -70,17 +79,27 @@ public enum TtsPlayer
return null;
}
private void setLanguageInternal(LanguageData lang)
private boolean setLanguageInternal(LanguageData lang)
{
mTts.setLanguage(lang.locale);
nativeSetTurnNotificationsLocale(lang.internalCode);
Config.setTtsLanguage(lang.internalCode);
try
{
mTts.setLanguage(lang.locale);
nativeSetTurnNotificationsLocale(lang.internalCode);
Config.setTtsLanguage(lang.internalCode);
return true;
}
catch (IllegalArgumentException e)
{
reportFailure(e, "setLanguageInternal(): " + lang.locale);
lockDown();
return false;
}
}
public void setLanguage(LanguageData lang)
public boolean setLanguage(LanguageData lang)
{
if (lang != null)
setLanguageInternal(lang);
return (lang != null && setLanguageInternal(lang));
}
private static @Nullable LanguageData getDefaultLanguage(List<LanguageData> langs)
@ -147,8 +166,16 @@ public enum TtsPlayer
private void speak(String textToSpeak)
{
if (Config.isTtsEnabled())
//noinspection deprecation
mTts.speak(textToSpeak, TextToSpeech.QUEUE_ADD, null);
try
{
//noinspection deprecation
mTts.speak(textToSpeak, TextToSpeech.QUEUE_ADD, null);
}
catch (IllegalArgumentException e)
{
reportFailure(e, "speak()");
lockDown();
}
}
public void playTurnNotifications()
@ -164,7 +191,15 @@ public enum TtsPlayer
public void stop()
{
if (isReady())
mTts.stop();
try
{
mTts.stop();
}
catch (IllegalArgumentException e)
{
reportFailure(e, "stop()");
lockDown();
}
}
public boolean isEnabled()
@ -178,7 +213,7 @@ public enum TtsPlayer
nativeEnableTurnNotifications(enabled);
}
private void getUsableLanguages(List<LanguageData> outList)
private boolean getUsableLanguages(List<LanguageData> outList)
{
Resources resources = MwmApplication.get().getResources();
String[] codes = resources.getStringArray(R.array.tts_languages_supported);
@ -189,14 +224,23 @@ public enum TtsPlayer
try
{
outList.add(new LanguageData(codes[i], names[i], mTts));
} catch (LanguageData.NotAvailableException ignored)
{}
}
catch (LanguageData.NotAvailableException ignored) {}
catch (IllegalArgumentException e)
{
reportFailure(e, "getUsableLanguages()");
lockDown();
return false;
}
}
return true;
}
private @Nullable LanguageData refreshLanguagesInternal(List<LanguageData> outList)
{
getUsableLanguages(outList);
if (!getUsableLanguages(outList))
return null;
if (outList.isEmpty())
{

View file

@ -98,6 +98,7 @@ public enum Statistics
public static final String DOWNLOAD_COUNTRY_NOTIFICATION_CLICKED = "Download country notification clicked";
public static final String ACTIVE_CONNECTION = "Connection";
public static final String STATISTICS_STATUS_CHANGED = "Statistics status changed";
public static final String TTS_FAILURE_LOCATION = "TTS failure location";
// routing
public static final String ROUTING_BUILD = "Routing. Build";
public static final String ROUTING_START_SUGGEST_REBUILD = "Routing. Suggest rebuild";