[android] TTS Volume config

Signed-off-by: Andrew Shkrob <andrew.shkrob.social@yandex.by>
This commit is contained in:
Andrew Shkrob 2024-01-23 19:41:28 +01:00 committed by Roman Tsisyk
parent a48758501b
commit 2e0d36b584
7 changed files with 518 additions and 518 deletions

View file

@ -3,22 +3,15 @@ package app.organicmaps.settings;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import app.organicmaps.Framework;
import app.organicmaps.R;
@ -28,219 +21,18 @@ import app.organicmaps.editor.ProfileActivity;
import app.organicmaps.help.HelpActivity;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.location.LocationProviderFactory;
import app.organicmaps.sound.LanguageData;
import app.organicmaps.sound.TtsPlayer;
import app.organicmaps.util.Config;
import app.organicmaps.util.NetworkPolicy;
import app.organicmaps.util.PowerManagment;
import app.organicmaps.util.SharedPropertiesUtils;
import app.organicmaps.util.ThemeSwitcher;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils;
import app.organicmaps.util.log.LogsManager;
import app.organicmaps.search.SearchRecents;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SettingsPrefsFragment extends BaseXmlSettingsFragment
{
private static final int REQUEST_INSTALL_DATA = 1;
@Nullable
private Preference mStoragePref;
@Nullable
private TwoStatePreference mTtsPrefEnabled;
@Nullable
private ListPreference mTtsPrefLanguages;
@Nullable
private Preference mTtsLangInfo;
@Nullable
private Preference mTtsVoiceTest;
private List<String> mTtsTestStringArray;
private int mTestStringIndex;
private PreferenceScreen mPreferenceScreen;
@NonNull
private final Map<String, LanguageData> mLanguages = new HashMap<>();
private LanguageData mCurrentLanguage;
private String mSelectedLanguage;
private final Preference.OnPreferenceChangeListener mEnabledListener = new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
final Preference root = getPreference(getString(R.string.pref_tts_screen));
boolean set = (Boolean)newValue;
if (!set)
{
TtsPlayer.setEnabled(false);
if (mTtsPrefLanguages != null)
mTtsPrefLanguages.setEnabled(false);
if (mTtsLangInfo != null)
mTtsLangInfo.setSummary(R.string.prefs_languages_information_off);
if (mTtsVoiceTest != null)
mTtsVoiceTest.setEnabled(false);
root.setSummary(R.string.off);
if (mTtsPrefEnabled != null)
mTtsPrefEnabled.setTitle(R.string.off);
return true;
}
if (mTtsLangInfo != null)
mTtsLangInfo.setSummary(R.string.prefs_languages_information);
root.setSummary(R.string.on);
if (mTtsPrefEnabled != null)
mTtsPrefEnabled.setTitle(R.string.on);
if (mTtsVoiceTest != null)
mTtsVoiceTest.setEnabled(true);
if (mCurrentLanguage != null && mCurrentLanguage.downloaded)
{
setLanguage(mCurrentLanguage);
return true;
}
return false;
}
};
private final Preference.OnPreferenceChangeListener mLangListener = new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
if (newValue == null)
return false;
mSelectedLanguage = (String)newValue;
LanguageData lang = mLanguages.get(mSelectedLanguage);
if (lang == null)
return false;
if (lang.downloaded)
setLanguage(lang);
else
UiUtils.startActivityForResult(SettingsPrefsFragment.this,
new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA), REQUEST_INSTALL_DATA);
return false;
}
};
private void enableListeners(boolean enable)
{
if (mTtsPrefEnabled != null)
mTtsPrefEnabled.setOnPreferenceChangeListener(enable ? mEnabledListener : null);
if (mTtsPrefLanguages != null)
mTtsPrefLanguages.setOnPreferenceChangeListener(enable ? mLangListener : null);
}
private void setLanguage(@NonNull LanguageData lang)
{
Config.setTtsEnabled(true);
TtsPlayer.INSTANCE.setLanguage(lang);
if (mTtsPrefLanguages != null)
mTtsPrefLanguages.setSummary(lang.name);
updateTts();
}
// Use this method only on TTS screen. Prerequisites: mTtsPrefEnabled, mTtsPrefLanguages, mTtsVoiceTest and mTtsLangInfo are not null.
private void updateTts()
{
enableListeners(false);
List<LanguageData> languages = TtsPlayer.INSTANCE.refreshLanguages();
mLanguages.clear();
mCurrentLanguage = null;
mTtsTestStringArray = null;
final Preference root = getPreference(getString(R.string.pref_tts_screen));
if (languages.isEmpty())
{
mTtsPrefEnabled.setChecked(false);
mTtsPrefEnabled.setEnabled(false);
mTtsPrefEnabled.setSummary(R.string.pref_tts_unavailable);
mTtsPrefEnabled.setTitle(R.string.off);
mTtsPrefLanguages.setEnabled(false);
mTtsPrefLanguages.setSummary(null);
mTtsVoiceTest.setEnabled(false);
mTtsLangInfo.setSummary(R.string.prefs_languages_information_off);
root.setSummary(R.string.off);
enableListeners(true);
return;
}
boolean enabled = TtsPlayer.isEnabled();
mTtsPrefEnabled.setChecked(enabled);
mTtsPrefEnabled.setSummary(null);
mTtsPrefEnabled.setTitle(enabled ? R.string.on : R.string.off);
mTtsVoiceTest.setEnabled(enabled);
mTtsLangInfo.setSummary(enabled ? R.string.prefs_languages_information
: R.string.prefs_languages_information_off);
if (root != null)
root.setSummary(enabled ? R.string.on : R.string.off);
final CharSequence[] entries = new CharSequence[languages.size()];
final CharSequence[] values = new CharSequence[languages.size()];
for (int i = 0; i < languages.size(); i++)
{
LanguageData lang = languages.get(i);
entries[i] = lang.name;
values[i] = lang.internalCode;
mLanguages.put(lang.internalCode, lang);
}
mTtsPrefLanguages.setEntries(entries);
mTtsPrefLanguages.setEntryValues(values);
mCurrentLanguage = TtsPlayer.getSelectedLanguage(languages);
boolean available = (mCurrentLanguage != null && mCurrentLanguage.downloaded);
mTtsPrefLanguages.setEnabled(available && TtsPlayer.isEnabled());
mTtsPrefLanguages.setSummary(available ? mCurrentLanguage.name : null);
mTtsPrefLanguages.setValue(available ? mCurrentLanguage.internalCode : null);
mTtsPrefEnabled.setChecked(available && TtsPlayer.isEnabled());
mTtsVoiceTest.setEnabled(enabled && available && TtsPlayer.isEnabled());
if (available)
{
// Update array of TTS test strings. Strings are taken from resources using selected TTS language.
Configuration config = new Configuration(getResources().getConfiguration());
config.setLocale(mCurrentLanguage.locale);
mTtsTestStringArray = Arrays.asList(getContext().createConfigurationContext(config).getResources().getStringArray(R.array.app_tips));
Collections.shuffle(mTtsTestStringArray);
mTestStringIndex = 0;
}
enableListeners(true);
}
private boolean isOnMainScreen()
{
return mPreferenceScreen.getKey() == null;
}
private boolean isOnTtsScreen()
{
String ttsScreenKey = requireActivity().getString(R.string.pref_tts_screen);
return mPreferenceScreen.getKey() != null && mPreferenceScreen.getKey().equals(ttsScreenKey);
}
@Override
protected int getXmlResources()
{
@ -251,81 +43,49 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
mPreferenceScreen = getPreferenceScreen();
if (isOnMainScreen())
{
// Initialize main preferences screen.
mStoragePref = getPreference(getString(R.string.pref_storage));
initStoragePrefCallbacks();
initMeasureUnitsPrefsCallbacks();
initZoomPrefsCallbacks();
initMapStylePrefsCallbacks();
initSpeedCamerasPrefs();
initAutoDownloadPrefsCallbacks();
initLargeFontSizePrefsCallbacks();
initTransliterationPrefsCallbacks();
init3dModePrefsCallbacks();
initPerspectivePrefsCallbacks();
initAutoZoomPrefsCallbacks();
initLoggingEnabledPrefsCallbacks();
initEmulationBadStorage();
initUseMobileDataPrefsCallbacks();
initPowerManagementPrefsCallbacks();
initPlayServicesPrefsCallbacks();
initSearchPrivacyPrefsCallbacks();
initScreenSleepEnabledPrefsCallbacks();
initShowOnLockScreenPrefsCallbacks();
}
else if (isOnTtsScreen())
{
// Initialize TTS preferences screen.
mTtsPrefEnabled = getPreference(getString(R.string.pref_tts_enabled));
mTtsPrefLanguages = getPreference(getString(R.string.pref_tts_language));
mTtsLangInfo = getPreference(getString(R.string.pref_tts_info));
mTtsVoiceTest = getPreference(getString(R.string.pref_tts_test_voice));
initStoragePrefCallbacks();
initMeasureUnitsPrefsCallbacks();
initZoomPrefsCallbacks();
initMapStylePrefsCallbacks();
initSpeedCamerasPrefs();
initAutoDownloadPrefsCallbacks();
initLargeFontSizePrefsCallbacks();
initTransliterationPrefsCallbacks();
init3dModePrefsCallbacks();
initPerspectivePrefsCallbacks();
initAutoZoomPrefsCallbacks();
initLoggingEnabledPrefsCallbacks();
initEmulationBadStorage();
initUseMobileDataPrefsCallbacks();
initPowerManagementPrefsCallbacks();
initPlayServicesPrefsCallbacks();
initSearchPrivacyPrefsCallbacks();
initScreenSleepEnabledPrefsCallbacks();
initShowOnLockScreenPrefsCallbacks();
}
if (mTtsVoiceTest != null)
{
mTtsVoiceTest.setOnPreferenceClickListener(pref -> {
if (mTtsTestStringArray == null)
return false;
Utils.showSnackbar(getView(), getString(R.string.pref_tts_playing_test_voice));
TtsPlayer.INSTANCE.speak(mTtsTestStringArray.get(mTestStringIndex));
mTestStringIndex++;
if (mTestStringIndex >= mTtsTestStringArray.size())
mTestStringIndex = 0;
return true;
});
}
initTtsLangInfoLink();
updateTts();
}
private void updateVoiceInstructionsPrefsSummary()
{
final Preference pref = getPreference(getString(R.string.pref_tts_screen));
pref.setSummary(Config.TTS.isEnabled() ? R.string.on : R.string.off);
}
private void initSpeedCamerasPrefs()
{
String key = getString(R.string.pref_speed_cameras);
final ListPreference pref = getPreference(key);
final ListPreference pref = getPreference(getString(R.string.pref_speed_cameras));
pref.setSummary(pref.getEntry());
pref.setOnPreferenceChangeListener(this::onSpeedCamerasPrefSelected);
}
pref.setOnPreferenceChangeListener((preference, newValue) -> {
final String speedCamModeValue = (String) newValue;
final SpeedCameraMode newCamMode = SpeedCameraMode.valueOf(speedCamModeValue);
final CharSequence summary = pref.getEntries()[newCamMode.ordinal()];
pref.setSummary(summary);
if (pref.getValue().equals(newValue))
return true;
private boolean onSpeedCamerasPrefSelected(@NonNull Preference preference,
@NonNull Object newValue)
{
String speedCamModeValue = (String) newValue;
ListPreference speedCamModeList = (ListPreference) preference;
SpeedCameraMode newCamMode = SpeedCameraMode.valueOf(speedCamModeValue);
CharSequence summary = speedCamModeList.getEntries()[newCamMode.ordinal()];
speedCamModeList.setSummary(summary);
if (speedCamModeList.getValue().equals(newValue))
onSpeedCamerasPrefChanged(newCamMode);
return true;
onSpeedCamerasPrefChanged(newCamMode);
return true;
});
}
private void onSpeedCamerasPrefChanged(@NonNull SpeedCameraMode newCamMode)
@ -338,26 +98,7 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
{
super.onResume();
if (isOnTtsScreen())
updateTts();
}
@Override
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
// Do not check resultCode here as it is always RESULT_CANCELED
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_INSTALL_DATA)
{
if (isOnTtsScreen())
updateTts();
LanguageData lang = mLanguages.get(mSelectedLanguage);
if (lang != null && lang.downloaded)
setLanguage(lang);
}
updateVoiceInstructionsPrefsSummary();
}
@Override
@ -374,42 +115,18 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
return super.onPreferenceTreeClick(preference);
}
// Use this method only on TTS screen.
private void initTtsLangInfoLink()
{
final Preference ttsLangInfoLink = getPreference(getString(R.string.pref_tts_info_link));
final String ttsLinkText = getString(R.string.prefs_languages_information_off_link);
final Spannable link = new SpannableString(ttsLinkText + "");
// Set link color.
link.setSpan(new ForegroundColorSpan(ContextCompat.getColor(requireContext(),
UiUtils.getStyledResourceId(requireContext(), androidx.appcompat.R.attr.colorAccent))),
0, ttsLinkText.length(), 0);
ttsLangInfoLink.setSummary(link);
final String ttsInfoUrl = requireActivity().getString(R.string.tts_info_link);
ttsLangInfoLink.setOnPreferenceClickListener(preference -> {
Utils.openUrl(requireContext(), ttsInfoUrl);
return false;
});
}
private void initLargeFontSizePrefsCallbacks()
{
final Preference pref = getPreference(getString(R.string.pref_large_fonts_size));
((TwoStatePreference)pref).setChecked(Config.isLargeFontsSize());
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
boolean oldVal = Config.isLargeFontsSize();
boolean newVal = (Boolean) newValue;
if (oldVal != newVal)
Config.setLargeFontsSize(newVal);
pref.setOnPreferenceChangeListener((preference, newValue) -> {
final boolean oldVal = Config.isLargeFontsSize();
final boolean newVal = (Boolean) newValue;
if (oldVal != newVal)
Config.setLargeFontsSize(newVal);
return true;
}
return true;
});
}
@ -418,18 +135,13 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
final Preference pref = getPreference(getString(R.string.pref_transliteration));
((TwoStatePreference)pref).setChecked(Config.isTransliteration());
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
boolean oldVal = Config.isTransliteration();
boolean newVal = (Boolean) newValue;
if (oldVal != newVal)
Config.setTransliteration(newVal);
pref.setOnPreferenceChangeListener((preference, newValue) -> {
final boolean oldVal = Config.isTransliteration();
final boolean newVal = (Boolean) newValue;
if (oldVal != newVal)
Config.setTransliteration(newVal);
return true;
}
return true;
});
}
@ -441,16 +153,11 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
if (curValue == NetworkPolicy.Type.NOT_TODAY || curValue == NetworkPolicy.Type.TODAY)
curValue = NetworkPolicy.Type.ASK;
mobilePref.setValue(curValue.name());
mobilePref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
String valueStr = (String)newValue;
NetworkPolicy.Type type = NetworkPolicy.Type.valueOf(valueStr);
Config.setUseMobileDataSettings(type);
return true;
}
mobilePref.setOnPreferenceChangeListener((preference, newValue) -> {
final String valueStr = (String) newValue;
NetworkPolicy.Type type = NetworkPolicy.Type.valueOf(valueStr);
Config.setUseMobileDataSettings(type);
return true;
});
}
@ -484,7 +191,7 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
if (!LogsManager.INSTANCE.setFileLoggingEnabled((Boolean) newValue))
{
// It's a very rare condition when debugging, so we can do without translation.
Utils.showSnackbar(getView(), "ERROR: Can't create a logs folder!");
Utils.showSnackbar(requireView(), "ERROR: Can't create a logs folder!");
return false;
}
return true;
@ -507,14 +214,9 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
boolean autozoomEnabled = Framework.nativeGetAutoZoomEnabled();
pref.setChecked(autozoomEnabled);
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
Framework.nativeSetAutoZoomEnabled((Boolean)newValue);
return true;
}
pref.setOnPreferenceChangeListener((preference, newValue) -> {
Framework.nativeSetAutoZoomEnabled((boolean) newValue);
return true;
});
}
@ -533,7 +235,7 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
{
@SuppressLint("MissingPermission")
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
public boolean onPreferenceChange(@NonNull Preference preference, Object newValue)
{
final LocationHelper locationHelper = LocationHelper.from(requireContext());
boolean oldVal = Config.useGoogleServices();
@ -588,14 +290,9 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
final Integer powerManagementValue = (powerManagementValueStr!=null) ? Integer.parseInt(powerManagementValueStr) : null;
disableOrEnable3DBuildingsForPowerMode(powerManagementValue);
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
Framework.nativeSet3dMode(_3d.enabled, (Boolean)newValue);
return true;
}
pref.setOnPreferenceChangeListener((preference, newValue) -> {
Framework.nativeSet3dMode(_3d.enabled, (Boolean)newValue);
return true;
});
}
@ -632,14 +329,9 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
pref.setChecked(_3d.enabled);
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
Framework.nativeSet3dMode((Boolean) newValue, _3d.buildings);
return true;
}
pref.setOnPreferenceChangeListener((preference, newValue) -> {
Framework.nativeSet3dMode((Boolean) newValue, _3d.buildings);
return true;
});
}
@ -648,19 +340,14 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
final TwoStatePreference pref = getPreference(getString(R.string.pref_autodownload));
pref.setChecked(Config.isAutodownloadEnabled());
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
boolean value = (Boolean)newValue;
Config.setAutodownloadEnabled(value);
pref.setOnPreferenceChangeListener((preference, newValue) -> {
final boolean value = (boolean) newValue;
Config.setAutodownloadEnabled(value);
if (value)
OnmapDownloader.setAutodownloadLocked(false);
if (value)
OnmapDownloader.setAutodownloadLocked(false);
return true;
}
return true;
});
}
@ -671,22 +358,18 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
String curTheme = Config.getUiThemeSettings(requireContext());
pref.setValue(curTheme);
pref.setSummary(pref.getEntry());
pref.setOnPreferenceChangeListener(this::onMapStylePrefChanged);
}
pref.setOnPreferenceChangeListener((preference, newValue) -> {
final String themeName = (String) newValue;
if (!Config.setUiThemeSettings(requireContext(), themeName))
return true;
private boolean onMapStylePrefChanged(@NonNull Preference pref, @NonNull Object newValue)
{
String themeName = (String) newValue;
if (!Config.setUiThemeSettings(requireContext(), themeName))
ThemeSwitcher.INSTANCE.restart(false);
ThemeMode mode = ThemeMode.getInstance(requireContext().getApplicationContext(), themeName);
CharSequence summary = pref.getEntries()[mode.ordinal()];
pref.setSummary(summary);
return true;
ThemeSwitcher.INSTANCE.restart(false);
ListPreference mapStyleModeList = (ListPreference) pref;
ThemeMode mode = ThemeMode.getInstance(requireContext().getApplicationContext(), themeName);
CharSequence summary = mapStyleModeList.getEntries()[mode.ordinal()];
mapStyleModeList.setSummary(summary);
return true;
});
}
private void initZoomPrefsCallbacks()
@ -694,14 +377,9 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
final Preference pref = getPreference(getString(R.string.pref_show_zoom_buttons));
((TwoStatePreference)pref).setChecked(Config.showZoomButtons());
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
Config.setShowZoomButtons((Boolean) newValue);
return true;
}
pref.setOnPreferenceChangeListener((preference, newValue) -> {
Config.setShowZoomButtons((boolean) newValue);
return true;
});
}
@ -710,42 +388,30 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
final Preference pref = getPreference(getString(R.string.pref_munits));
((ListPreference)pref).setValue(String.valueOf(UnitLocale.getUnits()));
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
UnitLocale.setUnits(Integer.parseInt((String) newValue));
return true;
}
pref.setOnPreferenceChangeListener((preference, newValue) -> {
UnitLocale.setUnits(Integer.parseInt((String) newValue));
return true;
});
}
private void initStoragePrefCallbacks()
{
if (mStoragePref == null)
return;
mStoragePref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener()
{
@Override
public boolean onPreferenceClick(Preference preference)
final Preference pref = getPreference(getString(R.string.pref_storage));
pref.setOnPreferenceClickListener(preference -> {
if (MapManager.nativeIsDownloading())
{
if (MapManager.nativeIsDownloading())
{
new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
.setTitle(R.string.downloading_is_active)
.setMessage(R.string.cant_change_this_setting)
.setPositiveButton(R.string.ok, null)
.show();
}
else
{
getSettingsActivity().stackFragment(StoragePathFragment.class, getString(R.string.maps_storage), null);
}
return true;
new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
.setTitle(R.string.downloading_is_active)
.setMessage(R.string.cant_change_this_setting)
.setPositiveButton(R.string.ok, null)
.show();
}
else
{
getSettingsActivity().stackFragment(StoragePathFragment.class, getString(R.string.maps_storage), null);
}
return true;
});
}
@ -755,17 +421,16 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
final boolean isKeepScreenOnEnabled = Config.isKeepScreenOnEnabled();
((TwoStatePreference) pref).setChecked(isKeepScreenOnEnabled);
pref.setOnPreferenceChangeListener(
(preference, newValue) ->
{
boolean newVal = (Boolean) newValue;
if (isKeepScreenOnEnabled != newVal)
{
Config.setKeepScreenOnEnabled(newVal);
// No need to call Utils.keepScreenOn() here, as relevant activities do it when starting / stopping.
}
return true;
});
pref.setOnPreferenceChangeListener((preference, newValue) ->
{
boolean newVal = (Boolean) newValue;
if (isKeepScreenOnEnabled != newVal)
{
Config.setKeepScreenOnEnabled(newVal);
// No need to call Utils.keepScreenOn() here, as relevant activities do it when starting / stopping.
}
return true;
});
}
private void initShowOnLockScreenPrefsCallbacks()
@ -774,17 +439,16 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment
final boolean isShowOnLockScreenEnabled = Config.isShowOnLockScreenEnabled();
((TwoStatePreference) pref).setChecked(isShowOnLockScreenEnabled);
pref.setOnPreferenceChangeListener(
(preference, newValue) ->
{
boolean newVal = (Boolean) newValue;
if (isShowOnLockScreenEnabled != newVal)
{
Config.setShowOnLockScreenEnabled(newVal);
Utils.showOnLockScreen(newVal, requireActivity());
}
return true;
});
pref.setOnPreferenceChangeListener((preference, newValue) ->
{
boolean newVal = (Boolean) newValue;
if (isShowOnLockScreenEnabled != newVal)
{
Config.setShowOnLockScreenEnabled(newVal);
Utils.showOnLockScreen(newVal, requireActivity());
}
return true;
});
}
private void removePreference(@NonNull String categoryKey, @NonNull Preference preference)

View file

@ -0,0 +1,283 @@
package app.organicmaps.settings;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.SeekBarPreference;
import androidx.preference.TwoStatePreference;
import app.organicmaps.R;
import app.organicmaps.sound.LanguageData;
import app.organicmaps.sound.TtsPlayer;
import app.organicmaps.util.Config;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
{
private static final int REQUEST_INSTALL_DATA = 1;
@NonNull
@SuppressWarnings("NotNullFieldNotInitialized")
private TwoStatePreference mTtsPrefEnabled;
@NonNull
@SuppressWarnings("NotNullFieldNotInitialized")
private ListPreference mTtsPrefLanguages;
@NonNull
@SuppressWarnings("NotNullFieldNotInitialized")
private SeekBarPreference mTtsVolume;
@NonNull
@SuppressWarnings("NotNullFieldNotInitialized")
private Preference mTtsLangInfo;
@NonNull
@SuppressWarnings("NotNullFieldNotInitialized")
private Preference mTtsVoiceTest;
private List<String> mTtsTestStringArray;
private int mTestStringIndex;
@NonNull
private final Map<String, LanguageData> mLanguages = new HashMap<>();
private LanguageData mCurrentLanguage;
private String mSelectedLanguage;
private final Preference.OnPreferenceChangeListener mEnabledListener = (preference, newValue) -> {
final boolean set = (Boolean) newValue;
if (!set)
{
TtsPlayer.setEnabled(false);
mTtsPrefLanguages.setEnabled(false);
mTtsVolume.setEnabled(false);
mTtsLangInfo.setSummary(R.string.prefs_languages_information_off);
mTtsVoiceTest.setEnabled(false);
return true;
}
mTtsLangInfo.setSummary(R.string.prefs_languages_information);
mTtsVolume.setEnabled(true);
mTtsVoiceTest.setEnabled(true);
if (mCurrentLanguage != null && mCurrentLanguage.downloaded)
{
setLanguage(mCurrentLanguage);
return true;
}
return false;
};
private final Preference.OnPreferenceChangeListener mLangListener = (preference, newValue) -> {
if (newValue == null)
return false;
mSelectedLanguage = (String) newValue;
final LanguageData lang = mLanguages.get(mSelectedLanguage);
if (lang == null)
return false;
if (lang.downloaded)
setLanguage(lang);
else
UiUtils.startActivityForResult(VoiceInstructionsSettingsFragment.this,
new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA), REQUEST_INSTALL_DATA);
return false;
};
@Override
protected int getXmlResources()
{
return R.xml.prefs_voice_instructions;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
mTtsPrefEnabled = getPreference(getString(R.string.pref_tts_enabled));
mTtsPrefLanguages = getPreference(getString(R.string.pref_tts_language));
mTtsLangInfo = getPreference(getString(R.string.pref_tts_info));
mTtsVoiceTest = getPreference(getString(R.string.pref_tts_test_voice));
mTtsVoiceTest.setOnPreferenceClickListener(pref -> {
if (mTtsTestStringArray == null)
return false;
Utils.showSnackbar(view, getString(R.string.pref_tts_playing_test_voice));
TtsPlayer.INSTANCE.speak(mTtsTestStringArray.get(mTestStringIndex));
mTestStringIndex++;
if (mTestStringIndex >= mTtsTestStringArray.size())
mTestStringIndex = 0;
return true;
});
initVolume();
initTtsLangInfoLink();
updateTts();
}
@Override
public void onResume()
{
super.onResume();
updateTts();
}
@Override
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
// Do not check resultCode here as it is always RESULT_CANCELED
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_INSTALL_DATA)
{
updateTts();
LanguageData lang = mLanguages.get(mSelectedLanguage);
if (lang != null && lang.downloaded)
setLanguage(lang);
}
}
private void enableListeners(boolean enable)
{
mTtsPrefEnabled.setOnPreferenceChangeListener(enable ? mEnabledListener : null);
mTtsPrefLanguages.setOnPreferenceChangeListener(enable ? mLangListener : null);
}
private void setLanguage(@NonNull LanguageData lang)
{
TtsPlayer.setEnabled(true);
TtsPlayer.INSTANCE.setLanguage(lang);
mTtsPrefLanguages.setSummary(lang.name);
updateTts();
}
private void updateTts()
{
enableListeners(false);
final List<LanguageData> languages = TtsPlayer.INSTANCE.refreshLanguages();
mLanguages.clear();
mCurrentLanguage = null;
mTtsTestStringArray = null;
if (languages.isEmpty())
{
mTtsPrefEnabled.setChecked(false);
mTtsPrefEnabled.setEnabled(false);
mTtsPrefEnabled.setSummary(R.string.pref_tts_unavailable);
mTtsPrefLanguages.setEnabled(false);
mTtsPrefLanguages.setSummary(null);
mTtsVolume.setEnabled(false);
mTtsVoiceTest.setEnabled(false);
mTtsLangInfo.setSummary(R.string.prefs_languages_information_off);
enableListeners(true);
return;
}
final boolean enabled = TtsPlayer.isEnabled();
mTtsLangInfo.setSummary(enabled ? R.string.prefs_languages_information
: R.string.prefs_languages_information_off);
final CharSequence[] entries = new CharSequence[languages.size()];
final CharSequence[] values = new CharSequence[languages.size()];
for (int i = 0; i < languages.size(); i++)
{
LanguageData lang = languages.get(i);
entries[i] = lang.name;
values[i] = lang.internalCode;
mLanguages.put(lang.internalCode, lang);
}
mTtsPrefLanguages.setEntries(entries);
mTtsPrefLanguages.setEntryValues(values);
mCurrentLanguage = TtsPlayer.getSelectedLanguage(languages);
final boolean available = (mCurrentLanguage != null && mCurrentLanguage.downloaded);
mTtsPrefEnabled.setChecked(available && TtsPlayer.isEnabled());
mTtsPrefLanguages.setEnabled(available && TtsPlayer.isEnabled());
mTtsPrefLanguages.setSummary(available ? mCurrentLanguage.name : null);
mTtsPrefLanguages.setValue(available ? mCurrentLanguage.internalCode : null);
mTtsVolume.setEnabled(enabled && available && TtsPlayer.isEnabled());
mTtsVoiceTest.setEnabled(enabled && available && TtsPlayer.isEnabled());
if (available)
{
// Update array of TTS test strings. Strings are taken from resources using selected TTS language.
final Configuration config = new Configuration(getResources().getConfiguration());
config.setLocale(mCurrentLanguage.locale);
mTtsTestStringArray = Arrays.asList(requireContext().createConfigurationContext(config).getResources().getStringArray(R.array.app_tips));
Collections.shuffle(mTtsTestStringArray);
mTestStringIndex = 0;
}
enableListeners(true);
}
@SuppressWarnings("ConstantConditions")
private void initVolume()
{
mTtsVolume = getPreference(getString(R.string.pref_tts_volume));
mTtsVolume.setMin((int) (Config.TTS.Defaults.VOLUME_MIN * 100));
mTtsVolume.setMax((int) (Config.TTS.Defaults.VOLUME_MAX * 100));
mTtsVolume.setUpdatesContinuously(true);
mTtsVolume.setOnPreferenceClickListener(preference -> {
setTtsVolume(Config.TTS.Defaults.VOLUME);
return true;
});
mTtsVolume.setOnPreferenceChangeListener((preference, newValue) -> {
setTtsVolume(((float) ((int) newValue)) / 100);
return true;
});
setTtsVolume(TtsPlayer.INSTANCE.getVolume());
}
private void setTtsVolume(final float volume)
{
final int volumeInt = (int) (volume * 100);
mTtsVolume.setValue(volumeInt);
mTtsVolume.setSummary(Integer.toString(volumeInt));
TtsPlayer.INSTANCE.setVolume(volume);
}
private void initTtsLangInfoLink()
{
final Preference ttsLangInfoLink = getPreference(getString(R.string.pref_tts_info_link));
final String ttsLinkText = getString(R.string.prefs_languages_information_off_link);
final Spannable link = new SpannableString(ttsLinkText + "");
// Set link color.
link.setSpan(new ForegroundColorSpan(ContextCompat.getColor(requireContext(),
UiUtils.getStyledResourceId(requireContext(), androidx.appcompat.R.attr.colorAccent))),
0, ttsLinkText.length(), 0);
ttsLangInfoLink.setSummary(link);
final String ttsInfoUrl = requireActivity().getString(R.string.tts_info_link);
ttsLangInfoLink.setOnPreferenceClickListener(preference -> {
Utils.openUrl(requireContext(), ttsInfoUrl);
return false;
});
}
}

View file

@ -2,6 +2,7 @@ package app.organicmaps.sound;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.speech.tts.TextToSpeech;
@ -48,6 +49,8 @@ public enum TtsPlayer
private boolean mInitializing;
private AudioFocusManager mAudioFocusManager;
private final Bundle mParams = new Bundle();
private final Handler delayHandler = new Handler(Looper.getMainLooper());
@SuppressWarnings("NotNullFieldNotInitialized")
@ -89,7 +92,7 @@ public enum TtsPlayer
{
mTts.setLanguage(lang.locale);
nativeSetTurnNotificationsLocale(lang.internalCode);
Config.setTtsLanguage(lang.internalCode);
Config.TTS.setLanguage(lang.internalCode);
return true;
}
@ -126,7 +129,7 @@ public enum TtsPlayer
public static @Nullable LanguageData getSelectedLanguage(List<LanguageData> langs)
{
return findSupportedLanguage(Config.getTtsLanguage(), langs);
return findSupportedLanguage(Config.TTS.getLanguage(), langs);
}
private void lockDown()
@ -149,7 +152,7 @@ public enum TtsPlayer
mTts = new TextToSpeech(context, status -> UiThread.run(() -> {
if (status == TextToSpeech.ERROR)
{
Logger.e(TAG, "Failed to initialize TextToSpeach");
Logger.e(TAG, "Failed to initialize TextToSpeech");
lockDown();
mInitializing = false;
return;
@ -180,6 +183,7 @@ public enum TtsPlayer
}
});
mAudioFocusManager = new AudioFocusManager(context);
mParams.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, Config.TTS.getVolume());
mInitializing = false;
}));
}
@ -191,14 +195,14 @@ public enum TtsPlayer
public void speak(String textToSpeak)
{
if (Config.isTtsEnabled())
if (Config.TTS.isEnabled())
try
{
boolean isMusicActive = mAudioFocusManager.requestAudioFocus();
if (isMusicActive)
delayHandler.postDelayed(() -> mTts.speak(textToSpeak, TextToSpeech.QUEUE_ADD, null, textToSpeak), TTS_SPEAK_DELAY_MILLIS);
delayHandler.postDelayed(() -> mTts.speak(textToSpeak, TextToSpeech.QUEUE_ADD, mParams, textToSpeak), TTS_SPEAK_DELAY_MILLIS);
else
mTts.speak(textToSpeak, TextToSpeech.QUEUE_ADD, null, textToSpeak);
mTts.speak(textToSpeak, TextToSpeech.QUEUE_ADD, mParams, textToSpeak);
}
catch (IllegalArgumentException e)
{
@ -234,10 +238,21 @@ public enum TtsPlayer
public static void setEnabled(boolean enabled)
{
Config.setTtsEnabled(enabled);
Config.TTS.setEnabled(enabled);
nativeEnableTurnNotifications(enabled);
}
public float getVolume()
{
return Config.TTS.getVolume();
}
public void setVolume(final float volume)
{
mParams.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, volume);
Config.TTS.setVolume(volume);
}
private boolean getUsableLanguages(List<LanguageData> outList)
{
Resources resources = MwmApplication.from(mContext).getResources();
@ -284,7 +299,7 @@ public enum TtsPlayer
if (res == null || !res.downloaded)
{
// Default locale can not be used too
Config.setTtsEnabled(false);
Config.TTS.setEnabled(false);
return null;
}
@ -300,7 +315,7 @@ public enum TtsPlayer
LanguageData lang = refreshLanguagesInternal(res);
setLanguage(lang);
setEnabled(Config.isTtsEnabled());
setEnabled(Config.TTS.isEnabled());
return res;
}

View file

@ -15,9 +15,6 @@ public final class Config
{
private static final String KEY_APP_STORAGE = "StoragePath";
private static final String KEY_TTS_ENABLED = "TtsEnabled";
private static final String KEY_TTS_LANGUAGE = "TtsLanguage";
private static final String KEY_DOWNLOADER_AUTO = "AutoDownloadEnabled";
private static final String KEY_PREF_ZOOM_BUTTONS = "ZoomButtonsEnabled";
static final String KEY_PREF_STATISTICS = "StatisticsEnabled";
@ -71,6 +68,11 @@ public final class Config
return nativeGetLong(key, def);
}
private static float getFloat(@NonNull final String key, final float def)
{
return (float) nativeGetDouble(key, def);
}
@NonNull
private static String getString(String key)
{
@ -103,6 +105,11 @@ public final class Config
nativeSetLong(key, value);
}
private static void setFloat(@NonNull final String key, final float value)
{
nativeSetDouble(key, value);
}
private static void setString(String key, String value)
{
nativeSetString(key, value);
@ -128,26 +135,6 @@ public final class Config
setString(KEY_APP_STORAGE, path);
}
public static boolean isTtsEnabled()
{
return getBool(KEY_TTS_ENABLED, true);
}
public static void setTtsEnabled(boolean enabled)
{
setBool(KEY_TTS_ENABLED, enabled);
}
public static String getTtsLanguage()
{
return getString(KEY_TTS_LANGUAGE);
}
public static void setTtsLanguage(String language)
{
setString(KEY_TTS_LANGUAGE, language);
}
public static boolean isAutodownloadEnabled()
{
return getBool(KEY_DOWNLOADER_AUTO, true);
@ -425,6 +412,56 @@ public final class Config
setBool(KEY_PREF_SEARCH_HISTORY, enabled);
}
public static class TTS
{
interface Keys
{
String ENABLED = "TtsEnabled";
String LANGUAGE = "TtsLanguage";
String VOLUME = "TtsVolume";
}
public interface Defaults
{
boolean ENABLED = true;
float VOLUME_MIN = 0.0f;
float VOLUME_MAX = 1.0f;
float VOLUME = VOLUME_MAX;
}
public static boolean isEnabled()
{
return getBool(Keys.ENABLED, Defaults.ENABLED);
}
public static void setEnabled(final boolean enabled)
{
setBool(Keys.ENABLED, enabled);
}
@NonNull
public static String getLanguage()
{
return getString(Keys.LANGUAGE);
}
public static void setLanguage(@NonNull final String language)
{
setString(Keys.LANGUAGE, language);
}
public static float getVolume()
{
return getFloat(Keys.VOLUME, Defaults.VOLUME);
}
public static void setVolume(final float volume)
{
setFloat(Keys.VOLUME, volume);
}
}
private static native boolean nativeHasConfigValue(String name);
private static native boolean nativeDeleteConfigValue(String name);
private static native boolean nativeGetBoolean(String name, boolean defaultValue);

View file

@ -19,6 +19,7 @@
<string name="pref_tts_screen" translatable="false">TtsScreen</string>
<string name="pref_tts_enabled" translatable="false">TtsEnabled</string>
<string name="pref_tts_language" translatable="false">TtsLanguage</string>
<string name="pref_tts_volume" translatable="false">TtsVolume</string>
<string name="pref_tts_test_voice" translatable="false">TtsTestVoice</string>
<string name="pref_tts_info" translatable="false">TtsInfo</string>
<string name="pref_tts_info_link" translatable="false">TtsInfoLink</string>

View file

@ -120,41 +120,14 @@
android:title="@string/pref_map_auto_zoom"
app:singleLineTitle="false"
android:order="3"/>
<androidx.preference.PreferenceScreen
<Preference
android:key="@string/pref_tts_screen"
android:title="@string/pref_tts_enable_title"
app:singleLineTitle="false"
android:persistent="false"
android:order="4">
<SwitchPreferenceCompat
android:key="@string/pref_tts_enabled"
android:title="@string/pref_tts_enable_title"
app:singleLineTitle="false"
android:order="1"/>
<ListPreference
android:key="@string/pref_tts_language"
android:title="@string/pref_tts_language_title"
app:singleLineTitle="false"
android:order="2"/>
<Preference
android:key="@string/pref_tts_test_voice"
android:title="@string/pref_tts_test_voice_title"
android:order="3"/>
<Preference
android:key="@string/pref_tts_info"
android:selectable="false"
android:persistent="false"
android:enabled="false"
android:summary="@string/prefs_languages_information"
android:order="4"/>
<Preference
android:key="@string/pref_tts_info_link"
android:selectable="true"
android:persistent="false"
android:enabled="true"
android:summary="@string/prefs_languages_information_off_link"
android:order="5"/>
</androidx.preference.PreferenceScreen>
android:order="4"
app:fragment="app.organicmaps.settings.VoiceInstructionsSettingsFragment">
</Preference>
<ListPreference
android:key="@string/pref_speed_cameras"
android:title="@string/speedcams_alert_title"

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreferenceCompat
android:key="@string/pref_tts_enabled"
android:title="@string/pref_tts_enable_title" />
<ListPreference
android:key="@string/pref_tts_language"
android:title="@string/pref_tts_language_title" />
<SeekBarPreference
android:key="@string/pref_tts_volume"
android:title="@string/volume" />
<Preference
android:key="@string/pref_tts_test_voice"
android:title="@string/pref_tts_test_voice_title" />
<Preference
android:enabled="false"
android:key="@string/pref_tts_info"
android:persistent="false"
android:selectable="false"
android:summary="@string/prefs_languages_information" />
<Preference
android:enabled="true"
android:key="@string/pref_tts_info_link"
android:persistent="false"
android:selectable="true"
android:summary="@string/prefs_languages_information_off_link" />
</androidx.preference.PreferenceScreen>