blob: 7ff45b1d5aa943c46afded090cfec1605d87e4b0 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.settings.tts;
import android.app.AlertDialog;
import android.content.Context;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TtsEngines;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.car.settings.R;
import com.android.car.settings.common.Logger;
import com.android.car.ui.AlertDialogBuilder;
import java.util.Locale;
/** Handles interactions with TTS playback settings. */
class TtsPlaybackSettingsManager {
private static final Logger LOG = new Logger(TtsPlaybackSettingsManager.class);
/**
* Maximum speech rate value.
*/
public static final int MAX_SPEECH_RATE = 600;
/**
* Minimum speech rate value.
*/
public static final int MIN_SPEECH_RATE = 10;
/**
* Maximum voice pitch value.
*/
public static final int MAX_VOICE_PITCH = 400;
/**
* Minimum voice pitch value.
*/
public static final int MIN_VOICE_PITCH = 25;
/**
* Scaling factor used to convert speech rate and pitch values between {@link Settings.Secure}
* and {@link TextToSpeech}.
*/
public static final float SCALING_FACTOR = 100.0f;
private static final String UTTERANCE_ID = "Sample";
private final Context mContext;
private final TextToSpeech mTts;
private final TtsEngines mEnginesHelper;
TtsPlaybackSettingsManager(Context context, @NonNull TextToSpeech tts,
@NonNull TtsEngines enginesHelper) {
mContext = context;
mTts = tts;
mEnginesHelper = enginesHelper;
}
void updateSpeechRate(int speechRate) {
Settings.Secure.putInt(
mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_RATE, speechRate);
mTts.setSpeechRate(speechRate / SCALING_FACTOR);
LOG.d("TTS default rate changed, now " + speechRate);
}
int getCurrentSpeechRate() {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.TTS_DEFAULT_RATE, TextToSpeech.Engine.DEFAULT_RATE);
}
void resetSpeechRate() {
updateSpeechRate(TextToSpeech.Engine.DEFAULT_RATE);
}
void updateVoicePitch(int pitch) {
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_PITCH,
pitch);
mTts.setPitch(pitch / SCALING_FACTOR);
LOG.d("TTS default pitch changed, now " + pitch);
}
int getCurrentVoicePitch() {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.TTS_DEFAULT_PITCH, TextToSpeech.Engine.DEFAULT_PITCH);
}
void resetVoicePitch() {
updateVoicePitch(TextToSpeech.Engine.DEFAULT_PITCH);
}
/**
* Returns the currently stored locale for the given tts engine. It can return {@code null}, if
* it is configured to use the system default locale.
*/
@Nullable
Locale getStoredTtsLocale() {
Locale currentLocale = null;
if (!mEnginesHelper.isLocaleSetToDefaultForEngine(mTts.getCurrentEngine())) {
currentLocale = mEnginesHelper.getLocalePrefForEngine(mTts.getCurrentEngine());
}
return currentLocale;
}
/**
* Similar to {@link #getStoredTtsLocale()}, but returns the language of the voice registered
* to the actual TTS object. It is possible for the TTS voice to be {@code null} if TTS is not
* yet initialized.
*/
@Nullable
Locale getEffectiveTtsLocale() {
if (mTts.getVoice() == null) {
return null;
}
return mEnginesHelper.parseLocaleString(mTts.getVoice().getLocale().toString());
}
/**
* Attempts to update the default tts locale. Returns {@code true} if successful, false
* otherwise.
*/
boolean updateTtsLocale(Locale newLocale) {
int resultCode = mTts.setLanguage((newLocale != null) ? newLocale : Locale.getDefault());
boolean success = resultCode != TextToSpeech.LANG_NOT_SUPPORTED
&& resultCode != TextToSpeech.LANG_MISSING_DATA;
if (success) {
mEnginesHelper.updateLocalePrefForEngine(mTts.getCurrentEngine(), newLocale);
}
return success;
}
void speakSampleText(String text) {
boolean networkRequired = mTts.getVoice().isNetworkConnectionRequired();
Locale defaultLocale = getEffectiveTtsLocale();
if (!networkRequired || networkRequired && mTts.isLanguageAvailable(defaultLocale)
>= TextToSpeech.LANG_AVAILABLE) {
mTts.speak(text, TextToSpeech.QUEUE_FLUSH, /* params= */ null, UTTERANCE_ID);
} else {
displayNetworkAlert();
}
}
private void displayNetworkAlert() {
AlertDialog dialog = new AlertDialogBuilder(mContext)
.setTitle(android.R.string.dialog_alert_title)
.setMessage(R.string.tts_engine_network_required)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, null).create();
dialog.show();
}
}