Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2010 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;
     18 
     19 import android.content.ComponentName;
     20 import android.content.Intent;
     21 import android.content.pm.PackageManager;
     22 import android.content.pm.ResolveInfo;
     23 import android.content.pm.ServiceInfo;
     24 import android.content.pm.PackageManager.NameNotFoundException;
     25 import android.content.res.Resources;
     26 import android.content.res.TypedArray;
     27 import android.content.res.XmlResourceParser;
     28 import android.preference.ListPreference;
     29 import android.preference.Preference;
     30 import android.preference.PreferenceCategory;
     31 import android.preference.PreferenceGroup;
     32 import android.preference.PreferenceScreen;
     33 import android.preference.Preference.OnPreferenceChangeListener;
     34 import android.provider.Settings;
     35 import android.speech.RecognitionService;
     36 import android.speech.tts.TtsEngines;
     37 import android.util.AttributeSet;
     38 import android.util.Log;
     39 import android.util.Xml;
     40 
     41 import java.io.IOException;
     42 import java.util.HashMap;
     43 import java.util.List;
     44 
     45 import org.xmlpull.v1.XmlPullParser;
     46 import org.xmlpull.v1.XmlPullParserException;
     47 
     48 /**
     49  * Settings screen for voice input/output.
     50  */
     51 public class VoiceInputOutputSettings implements OnPreferenceChangeListener {
     52 
     53     private static final String TAG = "VoiceInputOutputSettings";
     54 
     55     private static final String KEY_VOICE_CATEGORY = "voice_category";
     56     private static final String KEY_RECOGNIZER = "recognizer";
     57     private static final String KEY_RECOGNIZER_SETTINGS = "recognizer_settings";
     58     private static final String KEY_TTS_SETTINGS = "tts_settings";
     59 
     60     private PreferenceGroup mParent;
     61     private PreferenceCategory mVoiceCategory;
     62     private ListPreference mRecognizerPref;
     63     private Preference mRecognizerSettingsPref;
     64     private Preference mTtsSettingsPref;
     65     private PreferenceScreen mSettingsPref;
     66     private final SettingsPreferenceFragment mFragment;
     67     private final TtsEngines mTtsEngines;
     68 
     69     private HashMap<String, ResolveInfo> mAvailableRecognizersMap;
     70 
     71     public VoiceInputOutputSettings(SettingsPreferenceFragment fragment) {
     72         mFragment = fragment;
     73         mTtsEngines = new TtsEngines(fragment.getPreferenceScreen().getContext());
     74     }
     75 
     76     public void onCreate() {
     77 
     78         mParent = mFragment.getPreferenceScreen();
     79         mVoiceCategory = (PreferenceCategory) mParent.findPreference(KEY_VOICE_CATEGORY);
     80         mRecognizerPref = (ListPreference) mVoiceCategory.findPreference(KEY_RECOGNIZER);
     81         mRecognizerSettingsPref = mVoiceCategory.findPreference(KEY_RECOGNIZER_SETTINGS);
     82         mTtsSettingsPref = mVoiceCategory.findPreference(KEY_TTS_SETTINGS);
     83         mRecognizerPref.setOnPreferenceChangeListener(this);
     84         mSettingsPref = (PreferenceScreen)
     85                 mVoiceCategory.findPreference(KEY_RECOGNIZER_SETTINGS);
     86 
     87         mAvailableRecognizersMap = new HashMap<String, ResolveInfo>();
     88 
     89         populateOrRemovePreferences();
     90     }
     91 
     92     private void populateOrRemovePreferences() {
     93         boolean hasRecognizerPrefs = populateOrRemoveRecognizerPrefs();
     94         boolean hasTtsPrefs = populateOrRemoveTtsPrefs();
     95         if (!hasRecognizerPrefs && !hasTtsPrefs) {
     96             // There were no TTS settings and no recognizer settings,
     97             // so it should be safe to hide the preference category
     98             // entirely.
     99             mFragment.getPreferenceScreen().removePreference(mVoiceCategory);
    100         }
    101     }
    102 
    103     private boolean populateOrRemoveRecognizerPrefs() {
    104         List<ResolveInfo> availableRecognitionServices =
    105                 mFragment.getPackageManager().queryIntentServices(
    106                         new Intent(RecognitionService.SERVICE_INTERFACE),
    107                         PackageManager.GET_META_DATA);
    108         int numAvailable = availableRecognitionServices.size();
    109 
    110         if (numAvailable == 0) {
    111             mVoiceCategory.removePreference(mRecognizerPref);
    112             mVoiceCategory.removePreference(mRecognizerSettingsPref);
    113             return false;
    114         }
    115 
    116         if (numAvailable == 1) {
    117             // Only one recognizer available, so don't show the list of choices, but do
    118             // set up the link to settings for the available recognizer.
    119             mVoiceCategory.removePreference(mRecognizerPref);
    120 
    121             // But first set up the available recognizers map with just the one recognizer.
    122             ResolveInfo resolveInfo = availableRecognitionServices.get(0);
    123             String recognizerComponent =
    124                 new ComponentName(resolveInfo.serviceInfo.packageName,
    125                         resolveInfo.serviceInfo.name).flattenToShortString();
    126 
    127             mAvailableRecognizersMap.put(recognizerComponent, resolveInfo);
    128 
    129             String currentSetting = Settings.Secure.getString(
    130                     mFragment.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
    131             updateSettingsLink(currentSetting);
    132         } else {
    133             // Multiple recognizers available, so show the full list of choices.
    134             populateRecognizerPreference(availableRecognitionServices);
    135         }
    136 
    137         // In this case, there was at least one available recognizer so
    138         // we populated the settings.
    139         return true;
    140     }
    141 
    142     private boolean populateOrRemoveTtsPrefs() {
    143         if (mTtsEngines.getEngines().isEmpty()) {
    144             mVoiceCategory.removePreference(mTtsSettingsPref);
    145             return false;
    146         }
    147 
    148         return true;
    149     }
    150 
    151     private void populateRecognizerPreference(List<ResolveInfo> recognizers) {
    152         int size = recognizers.size();
    153         CharSequence[] entries = new CharSequence[size];
    154         CharSequence[] values = new CharSequence[size];
    155 
    156         // Get the current value from the secure setting.
    157         String currentSetting = Settings.Secure.getString(
    158                 mFragment.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
    159 
    160         // Iterate through all the available recognizers and load up their info to show
    161         // in the preference. Also build up a map of recognizer component names to their
    162         // ResolveInfos - we'll need that a little later.
    163         for (int i = 0; i < size; i++) {
    164             ResolveInfo resolveInfo = recognizers.get(i);
    165             String recognizerComponent =
    166                     new ComponentName(resolveInfo.serviceInfo.packageName,
    167                             resolveInfo.serviceInfo.name).flattenToShortString();
    168 
    169             mAvailableRecognizersMap.put(recognizerComponent, resolveInfo);
    170 
    171             entries[i] = resolveInfo.loadLabel(mFragment.getPackageManager());
    172             values[i] = recognizerComponent;
    173         }
    174 
    175         mRecognizerPref.setEntries(entries);
    176         mRecognizerPref.setEntryValues(values);
    177 
    178         mRecognizerPref.setDefaultValue(currentSetting);
    179         mRecognizerPref.setValue(currentSetting);
    180 
    181         updateSettingsLink(currentSetting);
    182     }
    183 
    184     private void updateSettingsLink(String currentSetting) {
    185         ResolveInfo currentRecognizer = mAvailableRecognizersMap.get(currentSetting);
    186         if (currentRecognizer == null) return;
    187 
    188         ServiceInfo si = currentRecognizer.serviceInfo;
    189         XmlResourceParser parser = null;
    190         String settingsActivity = null;
    191         try {
    192             parser = si.loadXmlMetaData(mFragment.getPackageManager(),
    193                     RecognitionService.SERVICE_META_DATA);
    194             if (parser == null) {
    195                 throw new XmlPullParserException("No " + RecognitionService.SERVICE_META_DATA +
    196                         " meta-data for " + si.packageName);
    197             }
    198 
    199             Resources res = mFragment.getPackageManager().getResourcesForApplication(
    200                     si.applicationInfo);
    201 
    202             AttributeSet attrs = Xml.asAttributeSet(parser);
    203 
    204             int type;
    205             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
    206                     && type != XmlPullParser.START_TAG) {
    207             }
    208 
    209             String nodeName = parser.getName();
    210             if (!"recognition-service".equals(nodeName)) {
    211                 throw new XmlPullParserException(
    212                         "Meta-data does not start with recognition-service tag");
    213             }
    214 
    215             TypedArray array = res.obtainAttributes(attrs,
    216                     com.android.internal.R.styleable.RecognitionService);
    217             settingsActivity = array.getString(
    218                     com.android.internal.R.styleable.RecognitionService_settingsActivity);
    219             array.recycle();
    220         } catch (XmlPullParserException e) {
    221             Log.e(TAG, "error parsing recognition service meta-data", e);
    222         } catch (IOException e) {
    223             Log.e(TAG, "error parsing recognition service meta-data", e);
    224         } catch (NameNotFoundException e) {
    225             Log.e(TAG, "error parsing recognition service meta-data", e);
    226         } finally {
    227             if (parser != null) parser.close();
    228         }
    229 
    230         if (settingsActivity == null) {
    231             // No settings preference available - hide the preference.
    232             Log.w(TAG, "no recognizer settings available for " + si.packageName);
    233             mSettingsPref.setIntent(null);
    234             mVoiceCategory.removePreference(mSettingsPref);
    235         } else {
    236             Intent i = new Intent(Intent.ACTION_MAIN);
    237             i.setComponent(new ComponentName(si.packageName, settingsActivity));
    238             mSettingsPref.setIntent(i);
    239             mRecognizerPref.setSummary(currentRecognizer.loadLabel(mFragment.getPackageManager()));
    240         }
    241     }
    242 
    243     public boolean onPreferenceChange(Preference preference, Object newValue) {
    244         if (preference == mRecognizerPref) {
    245             String setting = (String) newValue;
    246 
    247             // Put the new value back into secure settings.
    248             Settings.Secure.putString(mFragment.getContentResolver(),
    249                     Settings.Secure.VOICE_RECOGNITION_SERVICE,
    250                     setting);
    251 
    252             // Update the settings item so it points to the right settings.
    253             updateSettingsLink(setting);
    254         }
    255         return true;
    256     }
    257 }
    258