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