mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-19 04:53:36 +00:00
[android] Consider all system langs for TTS
Signed-off-by: gekeleda <git@davidgekeler.eu>
This commit is contained in:
@@ -107,6 +107,7 @@ import app.organicmaps.sdk.routing.RoutingOptions;
|
|||||||
import app.organicmaps.sdk.search.SearchEngine;
|
import app.organicmaps.sdk.search.SearchEngine;
|
||||||
import app.organicmaps.sdk.settings.RoadType;
|
import app.organicmaps.sdk.settings.RoadType;
|
||||||
import app.organicmaps.sdk.settings.UnitLocale;
|
import app.organicmaps.sdk.settings.UnitLocale;
|
||||||
|
import app.organicmaps.sdk.sound.TtsPlayer;
|
||||||
import app.organicmaps.sdk.util.Config;
|
import app.organicmaps.sdk.util.Config;
|
||||||
import app.organicmaps.sdk.util.LocationUtils;
|
import app.organicmaps.sdk.util.LocationUtils;
|
||||||
import app.organicmaps.sdk.util.PowerManagment;
|
import app.organicmaps.sdk.util.PowerManagment;
|
||||||
@@ -132,7 +133,6 @@ import com.google.android.material.appbar.MaterialToolbar;
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.textview.MaterialTextView;
|
import com.google.android.material.textview.MaterialTextView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -1813,6 +1813,18 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void deliverTtsMessage()
|
||||||
|
{
|
||||||
|
if (Config.isTtsMessageDelivered())
|
||||||
|
return;
|
||||||
|
|
||||||
|
String navigationStartMessage = getResources().getString(R.string.navigation_start_tts_message);
|
||||||
|
navigationStartMessage += TtsPlayer.INSTANCE.getLanguageDisplayName();
|
||||||
|
Toast.makeText(this, navigationStartMessage, Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
Config.setTtsMessageDelivered();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean showStartPointNotice()
|
private boolean showStartPointNotice()
|
||||||
{
|
{
|
||||||
final RoutingController controller = RoutingController.get();
|
final RoutingController controller = RoutingController.get();
|
||||||
@@ -2189,6 +2201,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||||||
if (!showRoutingDisclaimer())
|
if (!showRoutingDisclaimer())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
deliverTtsMessage();
|
||||||
|
|
||||||
closeFloatingPanels();
|
closeFloatingPanels();
|
||||||
setFullscreen(false);
|
setFullscreen(false);
|
||||||
RoutingController.get().start();
|
RoutingController.get().start();
|
||||||
|
|||||||
@@ -932,6 +932,7 @@
|
|||||||
<string name="share_track">Share Track</string>
|
<string name="share_track">Share Track</string>
|
||||||
<string name="delete_track_dialog_title">Delete %s?</string>
|
<string name="delete_track_dialog_title">Delete %s?</string>
|
||||||
<string name="pref_tts_no_system_tts_short">No text-to-speech engine found, check the app settings</string>
|
<string name="pref_tts_no_system_tts_short">No text-to-speech engine found, check the app settings</string>
|
||||||
|
<string name="navigation_start_tts_message">"Starting Navigation, voice instruction language: "</string>
|
||||||
<string name="unknown_power_output">unknown</string>
|
<string name="unknown_power_output">unknown</string>
|
||||||
<string name="charge_socket_type2">Type 2 (no cable)</string>
|
<string name="charge_socket_type2">Type 2 (no cable)</string>
|
||||||
<string name="charge_socket_type2_cable">Type 2 (w/ cable)</string>
|
<string name="charge_socket_type2_cable">Type 2 (w/ cable)</string>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package app.organicmaps.sdk.sound;
|
package app.organicmaps.sdk.sound;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -14,6 +15,8 @@ import android.util.Pair;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.os.ConfigurationCompat;
|
||||||
|
import androidx.core.os.LocaleListCompat;
|
||||||
import androidx.media.AudioAttributesCompat;
|
import androidx.media.AudioAttributesCompat;
|
||||||
import androidx.media.AudioFocusRequestCompat;
|
import androidx.media.AudioFocusRequestCompat;
|
||||||
import androidx.media.AudioManagerCompat;
|
import androidx.media.AudioManagerCompat;
|
||||||
@@ -23,6 +26,7 @@ import app.organicmaps.sdk.util.log.Logger;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code TtsPlayer} class manages available TTS voice languages.
|
* {@code TtsPlayer} class manages available TTS voice languages.
|
||||||
@@ -33,9 +37,9 @@ import java.util.Locale;
|
|||||||
* unsupported voices are excluded.
|
* unsupported voices are excluded.
|
||||||
* <p>
|
* <p>
|
||||||
* At startup we check whether currently selected language is in our list of supported voices and its data is
|
* At startup we check whether currently selected language is in our list of supported voices and its data is
|
||||||
* downloaded. If not, we check system default locale. If failed, the same check is made for English language. Finally,
|
* downloaded. If not, we check system default locale. If failed, the same check is made for other system locales.
|
||||||
* if mentioned checks fail we manually disable TTS, so the user must go to the settings and select preferred voice
|
* If those fail too, we check for English language. Then, as a final resort, all installed TTS locales are checked.
|
||||||
* language by hand. <p> If no core supported languages can be used by the system, TTS is locked down and can not be
|
* <p> If no core supported languages can be used by the system, TTS is locked down and can not be
|
||||||
* enabled and used.
|
* enabled and used.
|
||||||
*/
|
*/
|
||||||
public enum TtsPlayer
|
public enum TtsPlayer
|
||||||
@@ -78,6 +82,8 @@ public enum TtsPlayer
|
|||||||
// TTS is locked down due to absence of supported languages
|
// TTS is locked down due to absence of supported languages
|
||||||
private boolean mUnavailable;
|
private boolean mUnavailable;
|
||||||
|
|
||||||
|
private LocaleListCompat mInstalledSystemLocales;
|
||||||
|
|
||||||
TtsPlayer() {}
|
TtsPlayer() {}
|
||||||
|
|
||||||
private static @Nullable LanguageData findSupportedLanguage(String internalCode, List<LanguageData> langs)
|
private static @Nullable LanguageData findSupportedLanguage(String internalCode, List<LanguageData> langs)
|
||||||
@@ -126,28 +132,57 @@ public enum TtsPlayer
|
|||||||
return (lang != null && setLanguageInternal(lang));
|
return (lang != null && setLanguageInternal(lang));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable LanguageData getDefaultLanguage(List<LanguageData> langs)
|
public static @Nullable LanguageData getSelectedLanguage(List<LanguageData> langs)
|
||||||
|
{
|
||||||
|
return findSupportedLanguage(Config.TTS.getLanguage(), langs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable LanguageData getSystemLanguage(List<LanguageData> langs)
|
||||||
{
|
{
|
||||||
LanguageData res;
|
LanguageData res;
|
||||||
|
|
||||||
|
// Try default system locale
|
||||||
Locale defLocale = Locale.getDefault();
|
Locale defLocale = Locale.getDefault();
|
||||||
if (defLocale != null)
|
res = findSupportedLanguage(defLocale, langs);
|
||||||
|
if (res != null && res.downloaded)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
// Try other installed system locales
|
||||||
|
for (int i = 0; i < mInstalledSystemLocales.size(); i++)
|
||||||
{
|
{
|
||||||
res = findSupportedLanguage(defLocale, langs);
|
Locale loc = mInstalledSystemLocales.get(i);
|
||||||
|
res = findSupportedLanguage(loc, langs);
|
||||||
|
if (res != null && res.downloaded)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable LanguageData getTTSLanguage(List<LanguageData> langs)
|
||||||
|
{
|
||||||
|
LanguageData res;
|
||||||
|
|
||||||
|
// Try all TTS installed languages
|
||||||
|
Set<Locale> ttsLocales = mTts.getAvailableLanguages();
|
||||||
|
for (Locale loc : ttsLocales)
|
||||||
|
{
|
||||||
|
res = findSupportedLanguage(loc, langs);
|
||||||
if (res != null && res.downloaded)
|
if (res != null && res.downloaded)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = findSupportedLanguage(DEFAULT_LOCALE, langs);
|
|
||||||
if (res != null && res.downloaded)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable LanguageData getSelectedLanguage(List<LanguageData> langs)
|
private static @Nullable LanguageData getDefaultLanguage(List<LanguageData> langs)
|
||||||
{
|
{
|
||||||
return findSupportedLanguage(Config.TTS.getLanguage(), langs);
|
LanguageData res;
|
||||||
|
|
||||||
|
// Try default app locale (en.US)
|
||||||
|
res = findSupportedLanguage(DEFAULT_LOCALE, langs);
|
||||||
|
if (res != null && res.downloaded)
|
||||||
|
return res;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void lockDown()
|
private void lockDown()
|
||||||
@@ -167,6 +202,10 @@ public enum TtsPlayer
|
|||||||
// TextToSpeech.OnInitListener() can be called from a non-main thread
|
// TextToSpeech.OnInitListener() can be called from a non-main thread
|
||||||
// on LineageOS '20.0-20231127-RELEASE-thyme' 'Xiaomi/thyme/thyme'.
|
// on LineageOS '20.0-20231127-RELEASE-thyme' 'Xiaomi/thyme/thyme'.
|
||||||
// https://github.com/organicmaps/organicmaps/issues/6903
|
// https://github.com/organicmaps/organicmaps/issues/6903
|
||||||
|
|
||||||
|
Configuration config = context.getResources().getConfiguration();
|
||||||
|
mInstalledSystemLocales = ConfigurationCompat.getLocales(config);
|
||||||
|
|
||||||
mTts = new TextToSpeech(context, status -> UiThread.run(() -> {
|
mTts = new TextToSpeech(context, status -> UiThread.run(() -> {
|
||||||
if (status == TextToSpeech.ERROR)
|
if (status == TextToSpeech.ERROR)
|
||||||
{
|
{
|
||||||
@@ -239,6 +278,17 @@ public enum TtsPlayer
|
|||||||
return (INSTANCE.mTts != null && !INSTANCE.mUnavailable && !INSTANCE.mInitializing);
|
return (INSTANCE.mTts != null && !INSTANCE.mUnavailable && !INSTANCE.mInitializing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Locale getVoiceLocale()
|
||||||
|
{
|
||||||
|
return mTts.getVoice().getLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLanguageDisplayName()
|
||||||
|
{
|
||||||
|
Locale locale = getVoiceLocale();
|
||||||
|
return locale.getDisplayName(locale);
|
||||||
|
}
|
||||||
|
|
||||||
public void speak(String textToSpeak)
|
public void speak(String textToSpeak)
|
||||||
{
|
{
|
||||||
if (Config.TTS.isEnabled())
|
if (Config.TTS.isEnabled())
|
||||||
@@ -328,24 +378,49 @@ public enum TtsPlayer
|
|||||||
|
|
||||||
if (outList.isEmpty())
|
if (outList.isEmpty())
|
||||||
{
|
{
|
||||||
// No supported languages found, lock down TTS :(
|
Logger.d("TtsPlayer", "No supported languages found, lock down TTS :( ");
|
||||||
lockDown();
|
lockDown();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
LanguageData res = getSelectedLanguage(outList);
|
LanguageData res = getSelectedLanguage(outList);
|
||||||
if (res == null || !res.downloaded)
|
if (res != null && res.downloaded)
|
||||||
// Selected locale is not available or not downloaded
|
|
||||||
res = getDefaultLanguage(outList);
|
|
||||||
|
|
||||||
if (res == null || !res.downloaded)
|
|
||||||
{
|
{
|
||||||
// Default locale can not be used too
|
Logger.d("TtsPlayer", "Selected locale " + res.internalCode + " will be used for TTS");
|
||||||
Config.TTS.setEnabled(false);
|
return res;
|
||||||
return null;
|
}
|
||||||
|
Logger.d("TtsPlayer", "Selected locale " + Config.TTS.getLanguage()
|
||||||
|
+ " is not available or not downloaded, trying system locales...");
|
||||||
|
|
||||||
|
res = getSystemLanguage(outList);
|
||||||
|
if (res != null && res.downloaded)
|
||||||
|
{
|
||||||
|
Logger.d("TtsPlayer", "System locale " + res.internalCode + " will be used for TTS");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
Logger.d("TtsPlayer",
|
||||||
|
"None of the system locales are available, or they are not downloaded, trying default locale...");
|
||||||
|
|
||||||
|
res = getDefaultLanguage(outList);
|
||||||
|
if (res != null && res.downloaded)
|
||||||
|
{
|
||||||
|
Logger.d("TtsPlayer", "Default locale " + res.internalCode + " will be used for TTS");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
Logger.d("TtsPlayer",
|
||||||
|
"Default locale " + DEFAULT_LOCALE + " can not be used either, trying all installed TTS locales...");
|
||||||
|
|
||||||
|
res = getTTSLanguage(outList);
|
||||||
|
if (res != null && res.downloaded)
|
||||||
|
{
|
||||||
|
Logger.d("TtsPlayer", "TTS locale " + res.internalCode + " will be used for TTS");
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
Logger.d("TtsPlayer",
|
||||||
|
"None of the TTS engine locales are available, or they are not downloaded, disabling TTS :( ");
|
||||||
|
Config.TTS.setEnabled(false);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull List<LanguageData> refreshLanguages()
|
public @NonNull List<LanguageData> refreshLanguages()
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public final class Config
|
|||||||
private static final String KEY_PREF_USE_GS = "UseGoogleServices";
|
private static final String KEY_PREF_USE_GS = "UseGoogleServices";
|
||||||
|
|
||||||
private static final String KEY_MISC_DISCLAIMER_ACCEPTED = "IsDisclaimerApproved";
|
private static final String KEY_MISC_DISCLAIMER_ACCEPTED = "IsDisclaimerApproved";
|
||||||
|
private static final String KEY_MISC_TTS_MESSAGE_DELIVERED = "TtsMessageDelivered";
|
||||||
private static final String KEY_MISC_LOCATION_REQUESTED = "LocationRequested";
|
private static final String KEY_MISC_LOCATION_REQUESTED = "LocationRequested";
|
||||||
private static final String KEY_MISC_USE_MOBILE_DATA = "UseMobileData";
|
private static final String KEY_MISC_USE_MOBILE_DATA = "UseMobileData";
|
||||||
private static final String KEY_MISC_USE_MOBILE_DATA_TIMESTAMP = "UseMobileDataTimestamp";
|
private static final String KEY_MISC_USE_MOBILE_DATA_TIMESTAMP = "UseMobileDataTimestamp";
|
||||||
@@ -237,6 +238,16 @@ public final class Config
|
|||||||
setBool(KEY_MISC_DISCLAIMER_ACCEPTED);
|
setBool(KEY_MISC_DISCLAIMER_ACCEPTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isTtsMessageDelivered()
|
||||||
|
{
|
||||||
|
return getBool(KEY_MISC_TTS_MESSAGE_DELIVERED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setTtsMessageDelivered()
|
||||||
|
{
|
||||||
|
setBool(KEY_MISC_TTS_MESSAGE_DELIVERED);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isLocationRequested()
|
public static boolean isLocationRequested()
|
||||||
{
|
{
|
||||||
return getBool(KEY_MISC_LOCATION_REQUESTED);
|
return getBool(KEY_MISC_LOCATION_REQUESTED);
|
||||||
|
|||||||
Reference in New Issue
Block a user