Home | History | Annotate | Download | only in tts
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.tts;
     18 
     19 import android.content.ActivityNotFoundException;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.os.Bundle;
     25 import android.preference.ListPreference;
     26 import android.preference.Preference;
     27 import android.preference.Preference.OnPreferenceChangeListener;
     28 import android.preference.Preference.OnPreferenceClickListener;
     29 import android.preference.PreferenceScreen;
     30 import android.speech.tts.TextToSpeech;
     31 import android.speech.tts.TtsEngines;
     32 import android.text.TextUtils;
     33 import android.util.Log;
     34 import android.util.Pair;
     35 
     36 import com.android.settings.R;
     37 import com.android.settings.SettingsPreferenceFragment;
     38 
     39 import java.util.ArrayList;
     40 import java.util.Collections;
     41 import java.util.Comparator;
     42 import java.util.Locale;
     43 
     44 
     45 public class TtsEngineSettingsFragment extends SettingsPreferenceFragment implements
     46         OnPreferenceClickListener, OnPreferenceChangeListener {
     47     private static final String TAG = "TtsEngineSettings";
     48     private static final boolean DBG = false;
     49 
     50     private static final String KEY_ENGINE_LOCALE = "tts_default_lang";
     51     private static final String KEY_ENGINE_SETTINGS = "tts_engine_settings";
     52     private static final String KEY_INSTALL_DATA = "tts_install_data";
     53 
     54     private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
     55 
     56     private TtsEngines mEnginesHelper;
     57     private ListPreference mLocalePreference;
     58     private Preference mEngineSettingsPreference;
     59     private Preference mInstallVoicesPreference;
     60     private Intent mEngineSettingsIntent;
     61     private Intent mVoiceDataDetails;
     62 
     63     private TextToSpeech mTts;
     64 
     65     private final TextToSpeech.OnInitListener mTtsInitListener = new TextToSpeech.OnInitListener() {
     66         @Override
     67         public void onInit(int status) {
     68             if (status != TextToSpeech.SUCCESS) {
     69                 finishFragment();
     70             } else {
     71                 getActivity().runOnUiThread(new Runnable() {
     72                     @Override
     73                     public void run() {
     74                         mLocalePreference.setEnabled(true);
     75                     }
     76                 });
     77             }
     78         }
     79     };
     80 
     81     private final BroadcastReceiver mLanguagesChangedReceiver = new BroadcastReceiver() {
     82         @Override
     83         public void onReceive(Context context, Intent intent) {
     84             // Installed or uninstalled some data packs
     85             if (TextToSpeech.Engine.ACTION_TTS_DATA_INSTALLED.equals(intent.getAction())) {
     86                 checkTtsData();
     87             }
     88         }
     89     };
     90 
     91     public TtsEngineSettingsFragment() {
     92         super();
     93     }
     94 
     95     @Override
     96     public void onCreate(Bundle savedInstanceState) {
     97         super.onCreate(savedInstanceState);
     98         addPreferencesFromResource(R.xml.tts_engine_settings);
     99         mEnginesHelper = new TtsEngines(getActivity());
    100 
    101         final PreferenceScreen root = getPreferenceScreen();
    102         mLocalePreference = (ListPreference) root.findPreference(KEY_ENGINE_LOCALE);
    103         mLocalePreference.setOnPreferenceChangeListener(this);
    104         mEngineSettingsPreference = root.findPreference(KEY_ENGINE_SETTINGS);
    105         mEngineSettingsPreference.setOnPreferenceClickListener(this);
    106         mInstallVoicesPreference = root.findPreference(KEY_INSTALL_DATA);
    107         mInstallVoicesPreference.setOnPreferenceClickListener(this);
    108         // Remove this preference unless voices are indeed available to install.
    109         root.removePreference(mInstallVoicesPreference);
    110         // Remove this preference unless locales are indeed available.
    111         root.removePreference(mLocalePreference);
    112 
    113         root.setTitle(getEngineLabel());
    114         root.setKey(getEngineName());
    115         mEngineSettingsPreference.setTitle(getResources().getString(
    116                 R.string.tts_engine_settings_title, getEngineLabel()));
    117 
    118         mEngineSettingsIntent = mEnginesHelper.getSettingsIntent(getEngineName());
    119         if (mEngineSettingsIntent == null) {
    120             mEngineSettingsPreference.setEnabled(false);
    121         }
    122         mInstallVoicesPreference.setEnabled(false);
    123 
    124         mLocalePreference.setEnabled(false);
    125         mLocalePreference.setEntries(new CharSequence[0]);
    126         mLocalePreference.setEntryValues(new CharSequence[0]);
    127 
    128         mVoiceDataDetails = getArguments().getParcelable(TtsEnginePreference.FRAGMENT_ARGS_VOICES);
    129 
    130         mTts = new TextToSpeech(getActivity().getApplicationContext(), mTtsInitListener,
    131                 getEngineName());
    132 
    133 
    134         // Check if data packs changed
    135         checkTtsData();
    136 
    137         getActivity().registerReceiver(mLanguagesChangedReceiver,
    138                 new IntentFilter(TextToSpeech.Engine.ACTION_TTS_DATA_INSTALLED));
    139     }
    140 
    141     @Override
    142     public void onDestroy() {
    143         getActivity().unregisterReceiver(mLanguagesChangedReceiver);
    144         mTts.shutdown();
    145         super.onDestroy();
    146     }
    147 
    148     private final void checkTtsData() {
    149         Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
    150         intent.setPackage(getEngineName());
    151         try {
    152             if (DBG) Log.d(TAG, "Updating engine: Checking voice data: " + intent.toUri(0));
    153             startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
    154         } catch (ActivityNotFoundException ex) {
    155             Log.e(TAG, "Failed to check TTS data, no activity found for " + intent + ")");
    156         }
    157     }
    158 
    159     @Override
    160     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    161         if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
    162             mVoiceDataDetails = data;
    163             updateVoiceDetails();
    164         }
    165     }
    166 
    167     private void updateVoiceDetails() {
    168         if (DBG) Log.d(TAG, "Parsing voice data details, data: " + mVoiceDataDetails.toUri(0));
    169 
    170         final ArrayList<String> available = mVoiceDataDetails.getStringArrayListExtra(
    171                 TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
    172         final ArrayList<String> unavailable = mVoiceDataDetails.getStringArrayListExtra(
    173                 TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
    174 
    175         if (available == null){
    176             Log.e(TAG, "TTS data check failed (available == null).");
    177             mLocalePreference.setEnabled(false);
    178             getPreferenceScreen().removePreference(mLocalePreference);
    179             return;
    180         }
    181 
    182         if (unavailable != null && unavailable.size() > 0) {
    183             mInstallVoicesPreference.setEnabled(true);
    184             getPreferenceScreen().addPreference(mInstallVoicesPreference);
    185         } else {
    186             getPreferenceScreen().removePreference(mInstallVoicesPreference);
    187         }
    188 
    189         if (available.size() > 0) {
    190             mLocalePreference.setEnabled(true);
    191             getPreferenceScreen().addPreference(mLocalePreference);
    192             updateDefaultLocalePref(available);
    193         } else {
    194             mLocalePreference.setEnabled(false);
    195             getPreferenceScreen().removePreference(mLocalePreference);
    196         }
    197     }
    198 
    199     private void updateDefaultLocalePref(ArrayList<String> availableLangs) {
    200         String currentLocale = mEnginesHelper.getLocalePrefForEngine(
    201                 getEngineName());
    202 
    203         ArrayList<Pair<String, String>> entryPairs =
    204                 new ArrayList<Pair<String, String>>(availableLangs.size());
    205         for (int i = 0; i < availableLangs.size(); i++) {
    206             String[] langCountryVariant = availableLangs.get(i).split("-");
    207             Locale loc = null;
    208             if (langCountryVariant.length == 1){
    209                 loc = new Locale(langCountryVariant[0]);
    210             } else if (langCountryVariant.length == 2){
    211                 loc = new Locale(langCountryVariant[0], langCountryVariant[1]);
    212             } else if (langCountryVariant.length == 3){
    213                 loc = new Locale(langCountryVariant[0], langCountryVariant[1],
    214                                  langCountryVariant[2]);
    215             }
    216             if (loc != null){
    217                 entryPairs.add(new Pair<String, String>(
    218                         loc.getDisplayName(), availableLangs.get(i)));
    219             }
    220         }
    221 
    222         // Sort it
    223         Collections.sort(entryPairs, new Comparator<Pair<String, String>>() {
    224             @Override
    225             public int compare(Pair<String, String> lhs, Pair<String, String> rhs) {
    226                 return lhs.first.compareToIgnoreCase(rhs.first);
    227             }
    228         });
    229 
    230         // Get two arrays out of one of pairs
    231         int selectedLanguageIndex = -1;
    232         CharSequence[] entries = new CharSequence[availableLangs.size()];
    233         CharSequence[] entryValues = new CharSequence[availableLangs.size()];
    234         int i = 0;
    235         for (Pair<String, String> entry : entryPairs) {
    236             if (entry.second.equalsIgnoreCase(currentLocale)) {
    237                 selectedLanguageIndex = i;
    238             }
    239             entries[i] = entry.first;
    240             entryValues[i++] = entry.second;
    241         }
    242 
    243         mLocalePreference.setEntries(entries);
    244         mLocalePreference.setEntryValues(entryValues);
    245         if (selectedLanguageIndex > -1) {
    246             mLocalePreference.setValueIndex(selectedLanguageIndex);
    247         } else {
    248             mLocalePreference.setValueIndex(0);
    249             updateLanguageTo(availableLangs.get(0));
    250         }
    251     }
    252 
    253     /**
    254      * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
    255      * so the required TTS files are properly installed.
    256      */
    257     private void installVoiceData() {
    258         if (TextUtils.isEmpty(getEngineName())) return;
    259         Intent intent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
    260         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    261         intent.setPackage(getEngineName());
    262         try {
    263             Log.v(TAG, "Installing voice data: " + intent.toUri(0));
    264             startActivity(intent);
    265         } catch (ActivityNotFoundException ex) {
    266             Log.e(TAG, "Failed to install TTS data, no acitivty found for " + intent + ")");
    267         }
    268     }
    269 
    270     @Override
    271     public boolean onPreferenceClick(Preference preference) {
    272         if (preference == mInstallVoicesPreference) {
    273             installVoiceData();
    274             return true;
    275         } else if (preference == mEngineSettingsPreference) {
    276             startActivity(mEngineSettingsIntent);
    277             return true;
    278         }
    279 
    280         return false;
    281     }
    282 
    283     @Override
    284     public boolean onPreferenceChange(Preference preference, Object newValue) {
    285         if (preference == mLocalePreference) {
    286             updateLanguageTo((String) newValue);
    287             return true;
    288         }
    289 
    290         return false;
    291     }
    292 
    293     private void updateLanguageTo(String locale) {
    294         mEnginesHelper.updateLocalePrefForEngine(getEngineName(), locale);
    295         if (getEngineName().equals(mTts.getCurrentEngine())) {
    296             String[] localeArray = TtsEngines.parseLocalePref(locale);
    297             if (localeArray != null) {
    298                 mTts.setLanguage(new Locale(localeArray[0], localeArray[1], localeArray[2]));
    299             }
    300         }
    301     }
    302 
    303     private String getEngineName() {
    304         return getArguments().getString(TtsEnginePreference.FRAGMENT_ARGS_NAME);
    305     }
    306 
    307     private String getEngineLabel() {
    308         return getArguments().getString(TtsEnginePreference.FRAGMENT_ARGS_LABEL);
    309     }
    310 
    311 }
    312