1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16package com.android.tts.compat;
17
18import android.database.Cursor;
19import android.net.Uri;
20import android.speech.tts.SynthesisCallback;
21import android.speech.tts.SynthesisRequest;
22import android.speech.tts.TextToSpeech;
23import android.speech.tts.TextToSpeechService;
24import android.util.Log;
25
26import java.io.File;
27
28public abstract class CompatTtsService extends TextToSpeechService {
29
30    private static final boolean DBG = false;
31    private static final String TAG = "CompatTtsService";
32
33    private SynthProxy mNativeSynth = null;
34
35    protected abstract String getSoFilename();
36
37    @Override
38    public void onCreate() {
39        if (DBG) Log.d(TAG, "onCreate()");
40
41        String soFilename = getSoFilename();
42
43        if (mNativeSynth != null) {
44            mNativeSynth.stopSync();
45            mNativeSynth.shutdown();
46            mNativeSynth = null;
47        }
48
49        // Load the engineConfig from the plugin if it has any special configuration
50        // to be loaded. By convention, if an engine wants the TTS framework to pass
51        // in any configuration, it must put it into its content provider which has the URI:
52        // content://<packageName>.providers.SettingsProvider
53        // That content provider must provide a Cursor which returns the String that
54        // is to be passed back to the native .so file for the plugin when getString(0) is
55        // called on it.
56        // Note that the TTS framework does not care what this String data is: it is something
57        // that comes from the engine plugin and is consumed only by the engine plugin itself.
58        String engineConfig = "";
59        Cursor c = getContentResolver().query(Uri.parse("content://" + getPackageName()
60                + ".providers.SettingsProvider"), null, null, null, null);
61        if (c != null){
62            c.moveToFirst();
63            engineConfig = c.getString(0);
64            c.close();
65        }
66        mNativeSynth = new SynthProxy(soFilename, engineConfig);
67
68        // mNativeSynth is used by TextToSpeechService#onCreate so it must be set prior
69        // to that call.
70        // getContentResolver() is also moved prior to super.onCreate(), and it works
71        // because the super method don't sets a field or value that affects getContentResolver();
72        // (including the content resolver itself).
73        super.onCreate();
74    }
75
76    @Override
77    public void onDestroy() {
78        if (DBG) Log.d(TAG, "onDestroy()");
79        super.onDestroy();
80
81        if (mNativeSynth != null) {
82            mNativeSynth.shutdown();
83        }
84        mNativeSynth = null;
85    }
86
87    @Override
88    protected String[] onGetLanguage() {
89        if (mNativeSynth == null) return null;
90        return mNativeSynth.getLanguage();
91    }
92
93    @Override
94    protected int onIsLanguageAvailable(String lang, String country, String variant) {
95        if (DBG) Log.d(TAG, "onIsLanguageAvailable(" + lang + "," + country + "," + variant + ")");
96        if (mNativeSynth == null) return TextToSpeech.ERROR;
97        return mNativeSynth.isLanguageAvailable(lang, country, variant);
98    }
99
100    @Override
101    protected int onLoadLanguage(String lang, String country, String variant) {
102        if (DBG) Log.d(TAG, "onLoadLanguage(" + lang + "," + country + "," + variant + ")");
103        int result = onIsLanguageAvailable(lang, country, variant);
104        if (result >= TextToSpeech.LANG_AVAILABLE) {
105            mNativeSynth.setLanguage(lang, country, variant);
106        }
107
108        return result;
109    }
110
111    @Override
112    protected void onSynthesizeText(SynthesisRequest request, SynthesisCallback callback) {
113        if (mNativeSynth == null) {
114            callback.error();
115            return;
116        }
117
118        // Set language
119        String lang = request.getLanguage();
120        String country = request.getCountry();
121        String variant = request.getVariant();
122        if (mNativeSynth.setLanguage(lang, country, variant) != TextToSpeech.SUCCESS) {
123            Log.e(TAG, "setLanguage(" + lang + "," + country + "," + variant + ") failed");
124            callback.error();
125            return;
126        }
127
128        // Set speech rate
129        int speechRate = request.getSpeechRate();
130        if (mNativeSynth.setSpeechRate(speechRate) != TextToSpeech.SUCCESS) {
131            Log.e(TAG, "setSpeechRate(" + speechRate + ") failed");
132            callback.error();
133            return;
134        }
135
136        // Set speech
137        int pitch = request.getPitch();
138        if (mNativeSynth.setPitch(pitch) != TextToSpeech.SUCCESS) {
139            Log.e(TAG, "setPitch(" + pitch + ") failed");
140            callback.error();
141            return;
142        }
143
144        // Synthesize
145        if (mNativeSynth.speak(request, callback) != TextToSpeech.SUCCESS) {
146            callback.error();
147            return;
148        }
149    }
150
151    @Override
152    protected void onStop() {
153        if (DBG) Log.d(TAG, "onStop()");
154        if (mNativeSynth == null) return;
155        mNativeSynth.stop();
156    }
157
158}
159