[android] Load TTS languages from core

Signed-off-by: Andrei Shkrob <github@shkrob.dev>
This commit is contained in:
Andrei Shkrob
2025-07-19 00:12:29 +02:00
committed by Konstantin Pastbin
parent 0a96a23ca0
commit ebc5370052
4 changed files with 95 additions and 139 deletions

View File

@@ -1,7 +1,6 @@
package app.organicmaps.sdk.sound;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Bundle;
import android.os.Handler;
@@ -10,9 +9,9 @@ import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.text.TextUtils;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.organicmaps.R;
import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.concurrency.UiThread;
import app.organicmaps.sdk.util.log.Logger;
@@ -43,6 +42,9 @@ public enum TtsPlayer
private static final float SPEECH_RATE = 1.0f;
private static final int TTS_SPEAK_DELAY_MILLIS = 50;
@Nullable
private static List<Pair<String, String>> sSupportedLanguages = null;
public static Runnable sOnReloadCallback = null;
private ContentObserver mTtsEngineObserver;
@@ -286,19 +288,15 @@ public enum TtsPlayer
private boolean getUsableLanguages(List<LanguageData> outList)
{
Resources resources = mContext.getResources();
String[] codes = resources.getStringArray(R.array.tts_languages_supported);
String[] names = resources.getStringArray(R.array.tts_language_names);
for (int i = 0; i < codes.length; i++)
for (final Pair<String, String> langNamePair : getSupportedLanguages())
{
try
{
outList.add(new LanguageData(codes[i], names[i], mTts));
outList.add(new LanguageData(langNamePair.first, langNamePair.second, mTts));
}
catch (LanguageData.NotAvailableException ignored)
catch (LanguageData.NotAvailableException ex)
{
Logger.w(TAG, "Failed to get usable languages " + ignored.getMessage());
Logger.w(TAG, "Failed to get usable languages " + ex.getMessage());
}
catch (IllegalArgumentException e)
{
@@ -351,8 +349,20 @@ public enum TtsPlayer
return res;
}
@NonNull
private List<Pair<String, String>> getSupportedLanguages()
{
if (sSupportedLanguages == null)
{
sSupportedLanguages = nativeGetSupportedLanguages();
}
return sSupportedLanguages;
}
private native static void nativeEnableTurnNotifications(boolean enable);
private native static boolean nativeAreTurnNotificationsEnabled();
private native static void nativeSetTurnNotificationsLocale(String code);
private native static String nativeGetTurnNotificationsLocale();
@NonNull
private native static List<Pair<String, String>> nativeGetSupportedLanguages();
}

View File

@@ -1,106 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Supported voice languages. Should have language[-country][:code] format.
Where:
language - two-letter ISO 639-1 language code.
country - two-letter ISO 3166-1 (alpha-2) country code.
code - internal language code to be set in the core.
If not specified, language is used.
Chinese is an exception. Matching is based on country code:
zh_TW, zh_MO and zh_HK correspond to zh-Hant.
Otherwise we consider that this is zh-Hans.
TODO: Move language list to core, now sync manually with platform/languages.hpp
languages should be added alphabetically in tts_language_names
-->
<string-array name="tts_languages_supported"
translatable="false">
<item>en</item>
<item>id</item>
<item>ca</item>
<item>da</item>
<item>de</item>
<item>es-ES:es</item>
<item>es-MX:es-MX</item>
<item>eu</item>
<item>fr</item>
<item>hr</item>
<item>it</item>
<item>sw</item>
<item>hu</item>
<item>nl</item>
<item>nb</item>
<item>pl</item>
<item>pt-PT:pt</item>
<item>pt-BR:pt-BR</item>
<item>ro</item>
<item>sk</item>
<item>fi</item>
<item>sv</item>
<item>vi</item>
<item>tr</item>
<item>cs</item>
<item>el</item>
<item>be</item>
<item>bg</item>
<item>ru</item>
<item>sr</item>
<item>uk</item>
<item>ar</item>
<item>fa</item>
<item>mr</item>
<item>hi</item>
<item>th</item>
<item>zh-CN:zh-Hans</item>
<item>zh-TW:zh-Hant</item>
<item>ja</item>
<item>ko</item>
</string-array>
<string-array name="tts_language_names"
translatable="false">
<item>English</item>
<item>Bahasa Indonesia</item>
<item>Català</item>
<item>Dansk</item>
<item>Deutsch</item>
<item>Español</item>
<item>Español (México)</item>
<item>Euskara</item>
<item>Français</item>
<item>Hrvatski</item>
<item>Italiano</item>
<item>Kiswahili</item>
<item>Magyar</item>
<item>Nederlands</item>
<item>Norsk Bokmål</item>
<item>Polski</item>
<item>Português</item>
<item>Português (Brasil)</item>
<item>Română</item>
<item>Slovenčina</item>
<item>Suomi</item>
<item>Svenska</item>
<item>Tiếng Việt</item>
<item>Türkçe</item>
<item>Čeština</item>
<item>Ελληνικά</item>
<item>Беларуская</item>
<item>Български</item>
<item>Русский</item>
<item>Српски</item>
<item>Українська</item>
<item>العربية</item>
<item>فارسی</item>
<item>मराठी</item>
<item>हिन्दी</item>
<item>ไทย</item>
<item>中文简体</item>
<item>中文繁體</item>
<item>日本語</item>
<item>한국어</item>
</string-array>
</resources>

View File

@@ -1,30 +1,51 @@
#include "app/organicmaps/sdk/Framework.hpp"
#include "app/organicmaps/sdk/core/jni_helper.hpp"
#include "app/organicmaps/sdk/core/jni_java_methods.hpp"
#include "platform/languages.hpp"
extern "C"
{
JNIEXPORT void JNICALL
Java_app_organicmaps_sdk_sound_TtsPlayer_nativeEnableTurnNotifications(JNIEnv *, jclass, jboolean enable)
{
return frm()->GetRoutingManager().EnableTurnNotifications(static_cast<bool>(enable));
}
JNIEXPORT void JNICALL
Java_app_organicmaps_sdk_sound_TtsPlayer_nativeEnableTurnNotifications(JNIEnv *, jclass, jboolean enable)
{
return frm()->GetRoutingManager().EnableTurnNotifications(static_cast<bool>(enable));
}
JNIEXPORT jboolean JNICALL
Java_app_organicmaps_sdk_sound_TtsPlayer_nativeAreTurnNotificationsEnabled(JNIEnv *, jclass)
{
return static_cast<jboolean>(frm()->GetRoutingManager().AreTurnNotificationsEnabled());
}
JNIEXPORT jboolean JNICALL
Java_app_organicmaps_sdk_sound_TtsPlayer_nativeAreTurnNotificationsEnabled(JNIEnv *, jclass)
{
return static_cast<jboolean>(frm()->GetRoutingManager().AreTurnNotificationsEnabled());
}
JNIEXPORT void JNICALL
Java_app_organicmaps_sdk_sound_TtsPlayer_nativeSetTurnNotificationsLocale(JNIEnv * env, jclass, jstring jLocale)
{
frm()->GetRoutingManager().SetTurnNotificationsLocale(jni::ToNativeString(env, jLocale));
}
JNIEXPORT void JNICALL
Java_app_organicmaps_sdk_sound_TtsPlayer_nativeSetTurnNotificationsLocale(JNIEnv * env, jclass, jstring jLocale)
{
frm()->GetRoutingManager().SetTurnNotificationsLocale(jni::ToNativeString(env, jLocale));
}
JNIEXPORT jstring JNICALL
Java_app_organicmaps_sdk_sound_TtsPlayer_nativeGetTurnNotificationsLocale(JNIEnv * env, jclass)
JNIEXPORT jstring JNICALL
Java_app_organicmaps_sdk_sound_TtsPlayer_nativeGetTurnNotificationsLocale(JNIEnv * env, jclass)
{
return jni::ToJavaString(env, frm()->GetRoutingManager().GetTurnNotificationsLocale());
}
JNIEXPORT jobject JNICALL
Java_app_organicmaps_sdk_sound_TtsPlayer_nativeGetSupportedLanguages(JNIEnv * env, jclass)
{
auto const & supportedLanguages = routing::turns::sound::kLanguageList;
auto const & listBuilder = jni::ListBuilder::Instance(env);
jobject const list = listBuilder.CreateArray(env, supportedLanguages.size());
for (auto const & [lang, name] : supportedLanguages)
{
return jni::ToJavaString(env, frm()->GetRoutingManager().GetTurnNotificationsLocale());
jni::TScopedLocalRef const jLangString(env, jni::ToJavaString(env, lang));
jni::TScopedLocalRef const jNameString(env, jni::ToJavaString(env, name));
jni::TScopedLocalRef const pair(env,
jni::PairBuilder::Instance(env).Create(env, jLangString.get(), jNameString.get()));
env->CallBooleanMethod(list, listBuilder.m_add, pair.get());
}
} // extern "C"
return list;
}
} // extern "C"

View File

@@ -4,13 +4,29 @@
#include <string>
#include <utility>
// The list of languages which can be used by TTS.
// It shall be included in Android (jni) and iOS parts to get the languages list.
// TODO: Now it is used only on iOS.
// Manual sync with android/res/values/strings-tts.xml is needed.
#include "std/target_os.hpp"
namespace routing::turns::sound
{
/**
* @brief The list of languages which can be used by TTS (Text-To-Speech).
*
* Supported language identifiers follow the format:
* @code
* language[-COUNTRY][:internal_code]
* @endcode
*
* Where:
* - `language`: a two-letter ISO 639-1 language code (e.g., "en", "fr", "zh").
* - `COUNTRY`: optional two-letter ISO 3166-1 alpha-2 country code (e.g., "US", "CN", "TW").
* - `internal_code`: optional internal language code used by the TTS core.
* If not specified, `language` is used as the default.
*
* @note Special handling for Chinese:
* - `zh_TW`, `zh_MO`, and `zh_HK` are treated as `zh-Hant` (Traditional Chinese).
* - All other variants default to `zh-Hans` (Simplified Chinese).
*
*/
std::array<std::pair<std::string_view, std::string_view>, 40> constexpr kLanguageList =
{{
{"en", "English"},
@@ -18,8 +34,13 @@ std::array<std::pair<std::string_view, std::string_view>, 40> constexpr kLanguag
{"ca", "Català"},
{"da", "Dansk"},
{"de", "Deutsch"},
#ifdef OMIM_OS_ANDROID
{"es-ES:es", "Español"},
{"es-MX:es-MX", "Español (México)"},
#else
{"es", "Español"},
{"es-MX", "Español (México)"},
#endif
{"eu", "Euskara"},
{"fr", "Français"},
{"hr", "Hrvatski"},
@@ -29,8 +50,13 @@ std::array<std::pair<std::string_view, std::string_view>, 40> constexpr kLanguag
{"nl", "Nederlands"},
{"nb", "Norsk Bokmål"},
{"pl", "Polski"},
#ifdef OMIM_OS_ANDROID
{"pt-PT:pt", "Português"},
{"pt-BR:pt-BR", "Português (Brasil)"},
#else
{"pt", "Português"},
{"pt-BR", "Português (Brasil)"},
#endif
{"ro", "Română"},
{"sk", "Slovenčina"},
{"fi", "Suomi"},
@@ -49,8 +75,13 @@ std::array<std::pair<std::string_view, std::string_view>, 40> constexpr kLanguag
{"mr", "मराठी"},
{"hi", "हिंदी"},
{"th", "ไทย"},
#ifdef OMIM_OS_ANDROID
{"zh-CN:zh-Hans", "中文简体"},
{"zh-TW:zh-Hant", "中文繁體"},
#else
{"zh-Hans", "中文简体"},
{"zh-Hant", "中文繁體"},
#endif
{"ja", "日本語"},
{"ko", "한국어"},
}};