android 12系统加上TTS引擎

文章讨论了在MTK设备的系统层,特别是在SettingsProvider和TextToSpeechSettings中关于文本转语音(TTS)的默认语言设置过程,涉及`def_tts`字符串和Locale的获取与处理。

系统层修改:

1.frameworks/base/packages/SettingsProvider/res/values/defaults.xml

<string name="def_tts"></string>

2.frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java

   loadStringSetting(stmt, Settings.Secure.TTS_DEFAULT_SYNTH,
                        R.string.def_tts);

3.vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/tts/TextToSpeechSettings.java

private void checkDefaultLocale() {
        Locale defaultLocale = mTts.getDefaultLanguage();
        if (defaultLocale == null) {
            Log.e(TAG, "Failed to get default language from engine " + mCurrentEngine);
            updateWidgetState(false);
            return;
            //Log.e(TAG, "Failed to get default language from engine " + mCurrentEngine);
            //updateWidgetState(false);
            //return;
			defaultLocale =Locale.getDefault();
        }

        // ISO-3166 alpha 3 country codes are out of spec. If we won't normalize,
        }
        mLocalePreference.setSummary(mLocalePreference.getEntries()[selectedLocaleIndex]);
        mSelectedLocaleIndex = selectedLocaleIndex;
		
		if(mTts==null){
            final TextToSpeechViewModel ttsViewModel =ViewModelProviders.of(this).get(TextToSpeechViewModel.class);
            final Pair<TextToSpeech, Boolean> ttsAndNew = ttsViewModel.getTtsAndWhetherNew(mInitListener);
            mTts = ttsAndNew.first;
        }

更多了解请连续作者!

 

 

package com.example.ys1; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.speech.tts.TextToSpeech; import android.speech.tts.UtteranceProgressListener; import android.util.Log; import android.text.TextUtils; import java.util.List; import java.util.Locale; public class SpeakManager { private static final String TAG = "SpeakManager"; private TextToSpeech tts; private Context context; private Handler mainHandler = new Handler(Looper.getMainLooper()); private OnWordSpokenListener wordSpokenListener; // 文本相关 private List<String> charList; private int resumeIndex = 0; // 🔁 手动管理语速(因为 getSpeechRate() 在 API < 23 不可用) private float speechRate = 1.0f; // 默认正常语速 // 🔁 是否已注册监听器 private boolean isSpeaking = false; public SpeakManager(Context context) { this.context = context; initTts(); } private void initTts() { tts = new TextToSpeech(context, status -> { if (status == TextToSpeech.SUCCESS) { int result = tts.setLanguage(Locale.CHINESE); if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) { Log.e(TAG, "❌ 不支持中文"); } else { Log.d(TAG, "✅ TTS 初始化成功"); setupTtsListener(); } } else { Log.e(TAG, "❌ TTS 初始化失败"); } }); } private void setupTtsListener() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { try { tts.setOnUtteranceProgressListener(new UtteranceProgressListener() { private boolean rangeStartCalled = false; @Override public void onStart(String utteranceId) { Log.d(TAG, "🗣️ 开始朗读: " + utteranceId); rangeStartCalled = false; } @Override public void onRangeStart(String utteranceId, int start, int end, int frame) { rangeStartCalled = true; int globalStart = resumeIndex + start; int globalEnd = resumeIndex + end; for (int i = globalStart; i < globalEnd; i++) { final int index = i; mainHandler.post(() -> notifyWordSpoken(index)); } } @Override public void onDone(String utteranceId) { Log.d(TAG, "✅ 朗读完成"); } @Override public void onError(String utteranceId) { Log.e(TAG, "❌ 朗读错误: " + utteranceId); } @Override public void onStop(String utteranceId, boolean interrupted) { Log.d(TAG, "⏹️ 停止朗读: " + utteranceId); } }); } catch (Exception e) { Log.w(TAG, "⚠️ 设置 OnUtteranceProgressListener 失败: ", e); } } // 即使不支持 onRangeStart,也准备 fallback // 我们将在 speak 后启动 fallback 监控 } /** * 外部可设置语速(模拟 setSpeechRate) * 1.0f 正常,0.5f 慢,2.0f 快 */ public void setSpeechRate(float rate) { this.speechRate = rate > 0 ? rate : 1.0f; } /** * 获取当前引擎(加 API 判断和异常捕获) */ @SuppressWarnings("deprecation") public String getCurrentEngine() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { try { return tts.getCurrentEngine(); } catch (Exception e) { Log.e(TAG, "❌ 调用 getCurrentEngine() 出错", e); return ""; } } else { // API < 21 没有这个方法 return ""; } } /** * 是否是 Google TTS */ public boolean isGoogleTTSEngine() { String engine = getCurrentEngine(); return "com.google.android.tts".equals(engine); } /** * 启动或恢复朗读 */ public void startOrResume(List<String> chars, int fromIndex, OnWordSpokenListener listener) { this.charList = chars; this.resumeIndex = Math.max(0, Math.min(fromIndex, chars.size())); this.wordSpokenListener = listener; if (charList == null || charList.isEmpty()) { Log.w(TAG, "⚠️ 字符列表为空"); return; } if (resumeIndex >= charList.size()) { Log.i(TAG, "🔚 所有文字已朗读完毕"); return; } String textToSpeak = TextUtils.join("", charList.subList(resumeIndex, charList.size())); if (textToSpeak.isEmpty()) return; Bundle params = new Bundle(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "utterance_" + System.currentTimeMillis()); } int result = tts.speak( textToSpeak, TextToSpeech.QUEUE_FLUSH, params ); if (result == TextToSpeech.SUCCESS) { isSpeaking = true; Log.d(TAG, "🔊 已发送朗读请求"); // 🔁 启动 fallback 监控:500ms 内无 onRangeStart 就走定时高亮 mainHandler.removeCallbacks(fallbackRunnable); mainHandler.postDelayed(fallbackRunnable, 500); } else { Log.e(TAG, "❌ speak 失败,尝试 fallback"); fallbackScheduleHighlight(); // 强制 fallback } } private Runnable fallbackRunnable = this::fallbackScheduleHighlight; /** * 降级方案:基于时间估算逐字高亮 */ private void fallbackScheduleHighlight() { mainHandler.removeCallbacksAndMessages(null); // 清理旧任务 if (charList == null || !isSpeaking) return; // 使用我们自己维护的 speechRate long delayPerChar = Math.round(150 / speechRate); // ms/字符 for (int i = resumeIndex; i < charList.size(); i++) { final int index = i; long delay = (i - resumeIndex) * delayPerChar; mainHandler.postDelayed(() -> { if (isSpeaking && tts.isSpeaking()) { notifyWordSpoken(index); } }, delay); } } /** * 触发回调给外部 */ private void notifyWordSpoken(int index) { if (wordSpokenListener != null) { wordSpokenListener.onWordSpoken(index); } } /** * 暂停 */ public void pause() { if (tts.isSpeaking()) { tts.stop(); isSpeaking = false; Log.d(TAG, "⏸️ 暂停朗读,下次从 index=" + resumeIndex + " 继续"); } } /** * 停止并重置 */ public void stop() { if (tts != null) { tts.stop(); isSpeaking = false; resumeIndex = 0; mainHandler.removeCallbacksAndMessages(null); Log.d(TAG, "⏹️ 停止并重置"); } } /** * 销毁资源 */ public void destroy() { if (tts != null) { tts.shutdown(); mainHandler.removeCallbacksAndMessages(null); } } // 回调接口 public interface OnWordSpokenListener { void onWordSpoken(int index); } } 我的项目minisdk都改为api23 了,怎么还提示无法解析 'TextToSpeech' 中的方法 'getCurrentEngine' 和'android.speech.tts.TextToSpeech' 中的 'speak(java.lang.String, int, java.util.HashMap<java.lang.String,java.lang.String>)' 无法应用于 '(java.lang.String, int, android.os.Bundle)'
最新发布
10-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值