mirror of
https://github.com/jahruz67/Bible-Daily.git
synced 2026-08-08 18:14:05 +00:00
Merge pull request #1 from jahruz67/refactor/mistral-tts-support
Add Mistral API TTS support with provider selection
This commit is contained in:
@@ -86,8 +86,9 @@ public class MainActivity extends Activity {
|
||||
private static final int RANK_FEAST = 2;
|
||||
private static final int RANK_SOLEMNITY = 3;
|
||||
|
||||
private static final String TTS_SPEECH_URL = "https://ttsapi.host2.gleeze.com/v1/audio/speech";
|
||||
private static final String TTS_MODEL = "kokoro";
|
||||
private static final String TTS_SPEECH_URL_GLEEZE = "https://ttsapi.host2.gleeze.com/v1/audio/speech";
|
||||
private static final String TTS_MODEL_GLEEZE = "kokoro";
|
||||
private static final String TTS_SPEECH_URL_MISTRAL = "https://api.mistral.ai/v1/text-to-speech";
|
||||
private static final String READING_CACHE_DIR = "reading-cache";
|
||||
private static final String PREFETCH_MONTH_PREFIX = "prefetch_month_";
|
||||
private static final int NEXT_MONTH_PREFETCH_DAY = 26;
|
||||
@@ -1490,7 +1491,16 @@ public class MainActivity extends Activity {
|
||||
}
|
||||
|
||||
private File downloadTtsAudio(String text) throws Exception {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(TTS_SPEECH_URL).openConnection();
|
||||
String provider = UpdateManager.getTtsProvider(this);
|
||||
if (provider.equals(UpdateManager.TTS_PROVIDER_MISTRAL)) {
|
||||
return downloadMistralTtsAudio(text);
|
||||
} else {
|
||||
return downloadGleezTtsAudio(text);
|
||||
}
|
||||
}
|
||||
|
||||
private File downloadGleezTtsAudio(String text) throws Exception {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(TTS_SPEECH_URL_GLEEZE).openConnection();
|
||||
connection.setConnectTimeout(15000);
|
||||
connection.setReadTimeout(120000);
|
||||
connection.setRequestMethod("POST");
|
||||
@@ -1500,8 +1510,8 @@ public class MainActivity extends Activity {
|
||||
connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
|
||||
|
||||
JSONObject body = new JSONObject();
|
||||
body.put("model", TTS_MODEL);
|
||||
body.put("voice", UpdateManager.getTtsVoice(this, isEnglish));
|
||||
body.put("model", TTS_MODEL_GLEEZE);
|
||||
body.put("voice", UpdateManager.getTtsVoice(this, isEnglish, UpdateManager.TTS_PROVIDER_GLEEZE));
|
||||
body.put("input", text);
|
||||
body.put("response_format", "mp3");
|
||||
body.put("speed", 1.0);
|
||||
@@ -1539,6 +1549,59 @@ public class MainActivity extends Activity {
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
private File downloadMistralTtsAudio(String text) throws Exception {
|
||||
String apiKey = UpdateManager.getMistralApiKey(this);
|
||||
if (apiKey == null || apiKey.trim().isEmpty()) {
|
||||
throw new IOException(isEnglish ? "Mistral API key is not configured. Please set it in Settings." : "La clave API de Mistral no está configurada. Configúrela en Ajustes.");
|
||||
}
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(TTS_SPEECH_URL_MISTRAL).openConnection();
|
||||
connection.setConnectTimeout(15000);
|
||||
connection.setReadTimeout(120000);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestProperty("User-Agent", "BibliaDiaria/1.0 Android");
|
||||
connection.setRequestProperty("Accept", "audio/mpeg");
|
||||
connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
|
||||
connection.setRequestProperty("Authorization", "Bearer " + apiKey);
|
||||
|
||||
JSONObject body = new JSONObject();
|
||||
body.put("text", text);
|
||||
body.put("voice", UpdateManager.getTtsVoice(this, isEnglish, UpdateManager.TTS_PROVIDER_MISTRAL));
|
||||
|
||||
byte[] payload = body.toString().getBytes(StandardCharsets.UTF_8);
|
||||
connection.setFixedLengthStreamingMode(payload.length);
|
||||
try (OutputStream stream = connection.getOutputStream()) {
|
||||
stream.write(payload);
|
||||
}
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode < 200 || responseCode >= 300) {
|
||||
String details = "";
|
||||
InputStream errorStream = connection.getErrorStream();
|
||||
if (errorStream != null) {
|
||||
details = readStream(errorStream);
|
||||
}
|
||||
connection.disconnect();
|
||||
throw new IOException((isEnglish ? "Mistral TTS API responded with code " : "La API TTS de Mistral respondió con código ")
|
||||
+ responseCode
|
||||
+ (details.isEmpty() ? "." : ". " + details));
|
||||
}
|
||||
|
||||
File outputFile = File.createTempFile("daily-reading-tts-", ".mp3", getCacheDir());
|
||||
try (InputStream input = connection.getInputStream();
|
||||
FileOutputStream output = new FileOutputStream(outputFile)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
while ((read = input.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, read);
|
||||
}
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
private void showTtsAudioReady(File audioFile) {
|
||||
try {
|
||||
releaseTtsPlayer();
|
||||
|
||||
@@ -7,9 +7,11 @@ import android.graphics.Typeface;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
@@ -45,6 +47,8 @@ public class SettingsActivity extends Activity {
|
||||
private TextView downloadButton;
|
||||
private TextView voiceSubtitleText;
|
||||
private TextView voiceButton;
|
||||
private TextView ttsProviderButton;
|
||||
private TextView mistralApiKeyButton;
|
||||
private UpdateManager.UpdateInfo latestInfo;
|
||||
private boolean isEnglish;
|
||||
|
||||
@@ -100,7 +104,9 @@ public class SettingsActivity extends Activity {
|
||||
content.addView(title, titleParams);
|
||||
|
||||
content.addView(createLanguageCard());
|
||||
content.addView(createTtsProviderCard());
|
||||
content.addView(createVoiceCard());
|
||||
content.addView(createMistralApiKeyCard());
|
||||
content.addView(createVersionCard());
|
||||
|
||||
checkButton = actionButton(isEnglish ? "Check now" : "Comprobar", COLOR_ACCENT, Color.WHITE);
|
||||
@@ -200,6 +206,8 @@ public class SettingsActivity extends Activity {
|
||||
boolean current = UpdateManager.isEnglish(this);
|
||||
UpdateManager.setEnglish(this, !current);
|
||||
langToggle.setText(!current ? "English" : "Español");
|
||||
isEnglish = !current;
|
||||
recreate();
|
||||
updateVoiceCard();
|
||||
});
|
||||
card.addView(langToggle, new LinearLayout.LayoutParams(dp(100), dp(40)));
|
||||
@@ -207,6 +215,39 @@ public class SettingsActivity extends Activity {
|
||||
return card;
|
||||
}
|
||||
|
||||
private View createTtsProviderCard() {
|
||||
LinearLayout card = createCard();
|
||||
card.setOrientation(LinearLayout.HORIZONTAL);
|
||||
card.setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
LinearLayout copy = new LinearLayout(this);
|
||||
copy.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
TextView title = textView("TTS Provider", 20, ink(), Typeface.BOLD);
|
||||
title.setIncludeFontPadding(false);
|
||||
copy.addView(title);
|
||||
|
||||
TextView subtitle = textView("Choose between Gleez and Mistral TTS", 14, muted(), Typeface.NORMAL);
|
||||
LinearLayout.LayoutParams subtitleParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
subtitleParams.setMargins(0, dp(6), 0, 0);
|
||||
copy.addView(subtitle, subtitleParams);
|
||||
|
||||
card.addView(copy, new LinearLayout.LayoutParams(
|
||||
0,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
1f
|
||||
));
|
||||
|
||||
ttsProviderButton = actionButton(getTtsProviderLabel(), COLOR_ACCENT, Color.WHITE);
|
||||
ttsProviderButton.setOnClickListener(view -> showTtsProviderPicker());
|
||||
card.addView(ttsProviderButton, new LinearLayout.LayoutParams(dp(140), dp(40)));
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
private View createVoiceCard() {
|
||||
LinearLayout card = createCard();
|
||||
card.setOrientation(LinearLayout.VERTICAL);
|
||||
@@ -244,6 +285,42 @@ public class SettingsActivity extends Activity {
|
||||
return card;
|
||||
}
|
||||
|
||||
private View createMistralApiKeyCard() {
|
||||
LinearLayout card = createCard();
|
||||
card.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
LinearLayout copy = new LinearLayout(this);
|
||||
copy.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
TextView title = textView("Mistral API Key", 20, ink(), Typeface.BOLD);
|
||||
title.setIncludeFontPadding(false);
|
||||
copy.addView(title);
|
||||
|
||||
TextView subtitle = textView("Required for Mistral TTS (get from mistral.ai)", 14, muted(), Typeface.NORMAL);
|
||||
LinearLayout.LayoutParams subtitleParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
subtitleParams.setMargins(0, dp(6), 0, 0);
|
||||
copy.addView(subtitle, subtitleParams);
|
||||
|
||||
card.addView(copy, new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
mistralApiKeyButton = actionButton(getMistralApiKeyLabel(), COLOR_ACCENT, Color.WHITE);
|
||||
mistralApiKeyButton.setOnClickListener(view -> showMistralApiKeyDialog());
|
||||
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
dp(42)
|
||||
);
|
||||
buttonParams.setMargins(0, dp(12), 0, 0);
|
||||
card.addView(mistralApiKeyButton, buttonParams);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
private View createVersionCard() {
|
||||
LinearLayout card = createCard();
|
||||
card.setOrientation(LinearLayout.VERTICAL);
|
||||
@@ -319,7 +396,8 @@ public class SettingsActivity extends Activity {
|
||||
|
||||
private void showVoicePicker() {
|
||||
boolean english = UpdateManager.isEnglish(this);
|
||||
UpdateManager.VoiceOption[] options = UpdateManager.getTtsVoiceOptions(english);
|
||||
String provider = UpdateManager.getTtsProvider(this);
|
||||
UpdateManager.VoiceOption[] options = UpdateManager.getTtsVoiceOptions(english, provider);
|
||||
String[] labels = new String[options.length];
|
||||
for (int index = 0; index < options.length; index++) {
|
||||
labels[index] = options[index].label;
|
||||
@@ -327,13 +405,14 @@ public class SettingsActivity extends Activity {
|
||||
|
||||
int selectedIndex = UpdateManager.getTtsVoiceIndex(
|
||||
english,
|
||||
UpdateManager.getTtsVoice(this, english)
|
||||
provider,
|
||||
UpdateManager.getTtsVoice(this, english, provider)
|
||||
);
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(english ? "English voice" : "Spanish voice")
|
||||
.setSingleChoiceItems(labels, selectedIndex, (dialog, which) -> {
|
||||
UpdateManager.setTtsVoice(this, english, options[which].id);
|
||||
UpdateManager.setTtsVoice(this, english, provider, options[which].id);
|
||||
updateVoiceCard();
|
||||
dialog.dismiss();
|
||||
})
|
||||
@@ -341,17 +420,94 @@ public class SettingsActivity extends Activity {
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showTtsProviderPicker() {
|
||||
String[] providers = {"Gleez", "Mistral"};
|
||||
String currentProvider = UpdateManager.getTtsProvider(this);
|
||||
int selectedIndex = currentProvider.equals(UpdateManager.TTS_PROVIDER_MISTRAL) ? 1 : 0;
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Select TTS Provider")
|
||||
.setSingleChoiceItems(providers, selectedIndex, (dialog, which) -> {
|
||||
String provider = which == 1 ? UpdateManager.TTS_PROVIDER_MISTRAL : UpdateManager.TTS_PROVIDER_GLEEZE;
|
||||
UpdateManager.setTtsProvider(this, provider);
|
||||
ttsProviderButton.setText(getTtsProviderLabel());
|
||||
updateVoiceCard();
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showMistralApiKeyDialog() {
|
||||
String currentKey = UpdateManager.getMistralApiKey(this);
|
||||
String displayKey = currentKey.isEmpty() ? "" : "••••••••";
|
||||
|
||||
EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
input.setHint("Enter your Mistral API key");
|
||||
input.setText(currentKey);
|
||||
input.setSelection(currentKey.length());
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setOrientation(LinearLayout.VERTICAL);
|
||||
layout.setPadding(dp(20), dp(20), dp(20), dp(20));
|
||||
layout.addView(input, new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Mistral API Key")
|
||||
.setView(layout)
|
||||
.setPositiveButton("Save", (dialog, which) -> {
|
||||
UpdateManager.setMistralApiKey(this, input.getText().toString().trim());
|
||||
mistralApiKeyButton.setText(getMistralApiKeyLabel());
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private String getTtsProviderLabel() {
|
||||
String provider = UpdateManager.getTtsProvider(this);
|
||||
return provider.equals(UpdateManager.TTS_PROVIDER_MISTRAL) ? "Mistral" : "Gleez";
|
||||
}
|
||||
|
||||
private String getMistralApiKeyLabel() {
|
||||
String apiKey = UpdateManager.getMistralApiKey(this);
|
||||
if (apiKey.isEmpty()) {
|
||||
return "Not configured";
|
||||
}
|
||||
return "••••••••";
|
||||
}
|
||||
|
||||
private void updateVoiceCard() {
|
||||
if (voiceSubtitleText == null || voiceButton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean english = UpdateManager.isEnglish(this);
|
||||
String provider = UpdateManager.getTtsProvider(this);
|
||||
String voiceId = UpdateManager.getTtsVoice(this, english);
|
||||
voiceSubtitleText.setText(english
|
||||
? "English voices are shown while English is selected"
|
||||
: "Las voces en español se muestran mientras el español esté seleccionado");
|
||||
voiceButton.setText(UpdateManager.getTtsVoiceLabel(english, voiceId));
|
||||
|
||||
// Update subtitle based on provider
|
||||
if (provider.equals(UpdateManager.TTS_PROVIDER_MISTRAL)) {
|
||||
voiceSubtitleText.setText(english
|
||||
? "Mistral voices (requires API key)"
|
||||
: "Voces de Mistral (requiere clave API)");
|
||||
} else {
|
||||
voiceSubtitleText.setText(english
|
||||
? "Gleez voices are shown while English is selected"
|
||||
: "Las voces de Gleez se muestran mientras el idioma esta seleccionado");
|
||||
}
|
||||
|
||||
voiceButton.setText(UpdateManager.getTtsVoiceLabel(english, provider, voiceId));
|
||||
|
||||
// If Mistral is selected but API key is not configured, show a warning
|
||||
if (provider.equals(UpdateManager.TTS_PROVIDER_MISTRAL) && !UpdateManager.isMistralConfigured(this)) {
|
||||
voiceSubtitleText.setText(english
|
||||
? "Mistral voices (requires API key - not configured!)"
|
||||
: "Voces de Mistral (requiere clave API - ¡no configurada!)");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateInstalledVersionText() {
|
||||
|
||||
@@ -29,12 +29,19 @@ final class UpdateManager {
|
||||
static final String KEY_TTS_VOICE_SPANISH = "tts_voice_spanish";
|
||||
static final String DEFAULT_TTS_VOICE_ENGLISH = "af_heart";
|
||||
static final String DEFAULT_TTS_VOICE_SPANISH = "ef_dora";
|
||||
static final String DEFAULT_TTS_VOICE_ENGLISH_MISTRAL = "v2/en_us-florence";
|
||||
static final String DEFAULT_TTS_VOICE_SPANISH_MISTRAL = "v2/es_es-carlota";
|
||||
static final String KEY_TTS_PROVIDER = "tts_provider";
|
||||
static final String KEY_MISTRAL_API_KEY = "mistral_api_key";
|
||||
static final String TTS_PROVIDER_GLEEZE = "gleeze";
|
||||
static final String TTS_PROVIDER_MISTRAL = "mistral";
|
||||
static final String MISTRAL_TTS_URL = "https://api.mistral.ai/v1/text-to-speech";
|
||||
static final String LATEST_JSON_URL =
|
||||
"https://github.com/jahruz67/Bible-Daily/releases/download/latest/latest.json";
|
||||
static final String APK_URL =
|
||||
"https://github.com/jahruz67/Bible-Daily/releases/download/latest/app-debug.apk";
|
||||
|
||||
private static final VoiceOption[] ENGLISH_VOICES = {
|
||||
private static final VoiceOption[] ENGLISH_VOICES_GLEEZE = {
|
||||
new VoiceOption("af_heart", "Heart (US female)"),
|
||||
new VoiceOption("af_alloy", "Alloy (US female)"),
|
||||
new VoiceOption("af_aoede", "Aoede (US female)"),
|
||||
@@ -65,12 +72,27 @@ final class UpdateManager {
|
||||
new VoiceOption("bm_lewis", "Lewis (UK male)")
|
||||
};
|
||||
|
||||
private static final VoiceOption[] SPANISH_VOICES = {
|
||||
private static final VoiceOption[] ENGLISH_VOICES_MISTRAL = {
|
||||
new VoiceOption("v2/en_us-florence", "Florence (US female)"),
|
||||
new VoiceOption("v2/en_us-dave", "Dave (US male)"),
|
||||
new VoiceOption("v2/en_us-libby", "Libby (US female)"),
|
||||
new VoiceOption("v2/en_us-matt", "Matt (US male)"),
|
||||
new VoiceOption("v2/en_us-serena", "Serena (US female)"),
|
||||
new VoiceOption("v2/en_us-andrew", "Andrew (US male)")
|
||||
};
|
||||
|
||||
private static final VoiceOption[] SPANISH_VOICES_GLEEZE = {
|
||||
new VoiceOption("ef_dora", "Dora (Spanish female)"),
|
||||
new VoiceOption("em_alex", "Alex (Spanish male)"),
|
||||
new VoiceOption("em_santa", "Santa (Spanish male)")
|
||||
};
|
||||
|
||||
private static final VoiceOption[] SPANISH_VOICES_MISTRAL = {
|
||||
new VoiceOption("v2/es_es-alvaro", "Alvaro (Spanish male)"),
|
||||
new VoiceOption("v2/es_es-carlota", "Carlota (Spanish female)"),
|
||||
new VoiceOption("v2/es_es-gerardo", "Gerardo (Spanish male)")
|
||||
};
|
||||
|
||||
private UpdateManager() {
|
||||
}
|
||||
|
||||
@@ -98,35 +120,76 @@ final class UpdateManager {
|
||||
preferences(context).edit().putBoolean(KEY_DARK_MODE, enabled).apply();
|
||||
}
|
||||
|
||||
static VoiceOption[] getTtsVoiceOptions(boolean isEnglish) {
|
||||
return isEnglish ? ENGLISH_VOICES : SPANISH_VOICES;
|
||||
static VoiceOption[] getTtsVoiceOptions(boolean isEnglish, String provider) {
|
||||
if (provider == null || provider.equals(TTS_PROVIDER_GLEEZE)) {
|
||||
return isEnglish ? ENGLISH_VOICES_GLEEZE : SPANISH_VOICES_GLEEZE;
|
||||
} else {
|
||||
return isEnglish ? ENGLISH_VOICES_MISTRAL : SPANISH_VOICES_MISTRAL;
|
||||
}
|
||||
}
|
||||
|
||||
static String getTtsVoice(Context context, boolean isEnglish) {
|
||||
String defaultVoice = getDefaultTtsVoice(isEnglish);
|
||||
String voice = preferences(context).getString(getTtsVoiceKey(isEnglish), defaultVoice);
|
||||
if (isValidTtsVoice(isEnglish, voice)) {
|
||||
static VoiceOption[] getTtsVoiceOptions(boolean isEnglish) {
|
||||
return getTtsVoiceOptions(isEnglish, TTS_PROVIDER_GLEEZE);
|
||||
}
|
||||
|
||||
static String getTtsVoice(Context context, boolean isEnglish, String provider) {
|
||||
String defaultVoice = getDefaultTtsVoice(isEnglish, provider);
|
||||
String voice = preferences(context).getString(getTtsVoiceKey(isEnglish, provider), defaultVoice);
|
||||
if (isValidTtsVoice(isEnglish, provider, voice)) {
|
||||
return voice;
|
||||
}
|
||||
return defaultVoice;
|
||||
}
|
||||
|
||||
static void setTtsVoice(Context context, boolean isEnglish, String voiceId) {
|
||||
String voice = isValidTtsVoice(isEnglish, voiceId) ? voiceId : getDefaultTtsVoice(isEnglish);
|
||||
preferences(context).edit().putString(getTtsVoiceKey(isEnglish), voice).apply();
|
||||
static String getTtsVoice(Context context, boolean isEnglish) {
|
||||
return getTtsVoice(context, isEnglish, TTS_PROVIDER_GLEEZE);
|
||||
}
|
||||
|
||||
static String getTtsVoiceLabel(boolean isEnglish, String voiceId) {
|
||||
for (VoiceOption option : getTtsVoiceOptions(isEnglish)) {
|
||||
static void setTtsVoice(Context context, boolean isEnglish, String provider, String voiceId) {
|
||||
String voice = isValidTtsVoice(isEnglish, provider, voiceId) ? voiceId : getDefaultTtsVoice(isEnglish, provider);
|
||||
preferences(context).edit().putString(getTtsVoiceKey(isEnglish, provider), voice).apply();
|
||||
}
|
||||
|
||||
static void setTtsVoice(Context context, boolean isEnglish, String voiceId) {
|
||||
setTtsVoice(context, isEnglish, TTS_PROVIDER_GLEEZE, voiceId);
|
||||
}
|
||||
|
||||
static String getTtsProvider(Context context) {
|
||||
return preferences(context).getString(KEY_TTS_PROVIDER, TTS_PROVIDER_GLEEZE);
|
||||
}
|
||||
|
||||
static void setTtsProvider(Context context, String provider) {
|
||||
preferences(context).edit().putString(KEY_TTS_PROVIDER, provider).apply();
|
||||
}
|
||||
|
||||
static String getMistralApiKey(Context context) {
|
||||
return preferences(context).getString(KEY_MISTRAL_API_KEY, "");
|
||||
}
|
||||
|
||||
static void setMistralApiKey(Context context, String apiKey) {
|
||||
preferences(context).edit().putString(KEY_MISTRAL_API_KEY, apiKey).apply();
|
||||
}
|
||||
|
||||
static boolean isMistralConfigured(Context context) {
|
||||
String apiKey = getMistralApiKey(context);
|
||||
return apiKey != null && !apiKey.trim().isEmpty();
|
||||
}
|
||||
|
||||
static String getTtsVoiceLabel(boolean isEnglish, String provider, String voiceId) {
|
||||
for (VoiceOption option : getTtsVoiceOptions(isEnglish, provider)) {
|
||||
if (option.id.equals(voiceId)) {
|
||||
return option.label;
|
||||
}
|
||||
}
|
||||
return getTtsVoiceLabel(isEnglish, getDefaultTtsVoice(isEnglish));
|
||||
return getTtsVoiceLabel(isEnglish, provider, getDefaultTtsVoice(isEnglish));
|
||||
}
|
||||
|
||||
static int getTtsVoiceIndex(boolean isEnglish, String voiceId) {
|
||||
VoiceOption[] options = getTtsVoiceOptions(isEnglish);
|
||||
static String getTtsVoiceLabel(boolean isEnglish, String voiceId) {
|
||||
return getTtsVoiceLabel(isEnglish, TTS_PROVIDER_GLEEZE, voiceId);
|
||||
}
|
||||
|
||||
static int getTtsVoiceIndex(boolean isEnglish, String provider, String voiceId) {
|
||||
VoiceOption[] options = getTtsVoiceOptions(isEnglish, provider);
|
||||
for (int index = 0; index < options.length; index++) {
|
||||
if (options[index].id.equals(voiceId)) {
|
||||
return index;
|
||||
@@ -135,19 +198,39 @@ final class UpdateManager {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getTtsVoiceIndex(boolean isEnglish, String voiceId) {
|
||||
return getTtsVoiceIndex(isEnglish, TTS_PROVIDER_GLEEZE, voiceId);
|
||||
}
|
||||
|
||||
private static String getDefaultTtsVoice(boolean isEnglish, String provider) {
|
||||
if (provider == null || provider.equals(TTS_PROVIDER_GLEEZE)) {
|
||||
return isEnglish ? DEFAULT_TTS_VOICE_ENGLISH : DEFAULT_TTS_VOICE_SPANISH;
|
||||
} else {
|
||||
return isEnglish ? DEFAULT_TTS_VOICE_ENGLISH_MISTRAL : DEFAULT_TTS_VOICE_SPANISH_MISTRAL;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getDefaultTtsVoice(boolean isEnglish) {
|
||||
return isEnglish ? DEFAULT_TTS_VOICE_ENGLISH : DEFAULT_TTS_VOICE_SPANISH;
|
||||
return getDefaultTtsVoice(isEnglish, TTS_PROVIDER_GLEEZE);
|
||||
}
|
||||
|
||||
private static String getTtsVoiceKey(boolean isEnglish, String provider) {
|
||||
if (provider == null || provider.equals(TTS_PROVIDER_GLEEZE)) {
|
||||
return isEnglish ? KEY_TTS_VOICE_ENGLISH : KEY_TTS_VOICE_SPANISH;
|
||||
} else {
|
||||
return isEnglish ? KEY_TTS_VOICE_ENGLISH + "_mistral" : KEY_TTS_VOICE_SPANISH + "_mistral";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getTtsVoiceKey(boolean isEnglish) {
|
||||
return isEnglish ? KEY_TTS_VOICE_ENGLISH : KEY_TTS_VOICE_SPANISH;
|
||||
return getTtsVoiceKey(isEnglish, TTS_PROVIDER_GLEEZE);
|
||||
}
|
||||
|
||||
private static boolean isValidTtsVoice(boolean isEnglish, String voiceId) {
|
||||
private static boolean isValidTtsVoice(boolean isEnglish, String provider, String voiceId) {
|
||||
if (voiceId == null) {
|
||||
return false;
|
||||
}
|
||||
for (VoiceOption option : getTtsVoiceOptions(isEnglish)) {
|
||||
for (VoiceOption option : getTtsVoiceOptions(isEnglish, provider)) {
|
||||
if (option.id.equals(voiceId)) {
|
||||
return true;
|
||||
}
|
||||
@@ -155,6 +238,10 @@ final class UpdateManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isValidTtsVoice(boolean isEnglish, String voiceId) {
|
||||
return isValidTtsVoice(isEnglish, TTS_PROVIDER_GLEEZE, voiceId);
|
||||
}
|
||||
|
||||
static UpdateInfo fetchLatestUpdate(Context context) throws Exception {
|
||||
String jsonText = download(LATEST_JSON_URL, "application/json");
|
||||
JSONObject json = new JSONObject(jsonText);
|
||||
|
||||
Reference in New Issue
Block a user