From df7e99b733d8251217d633319928516a6d0009ff Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 17 Apr 2014 18:35:29 -0700 Subject: [PATCH] New preference for selecting new voice interaction service. This is currently in addition to the existing Voice Search preference in this UI. Eventually it will replace that one, as a superset of its functionality. Change-Id: I26f1220fa129870cad3ec4152a569a9c494971fb --- res/values/strings.xml | 11 ++ res/xml/language_settings.xml | 10 ++ .../android/settings/VoiceInputOutputSettings.java | 184 +++++++++++++++++++-- 3 files changed, 188 insertions(+), 17 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 99f6664bc7..be924f034a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3833,6 +3833,17 @@ Android keyboard Speech + + Voice input + + Voice input + + Settings for + \'%s\' + + None Voice recognizer diff --git a/res/xml/language_settings.xml b/res/xml/language_settings.xml index 3e7fdb5223..c210312571 100644 --- a/res/xml/language_settings.xml +++ b/res/xml/language_settings.xml @@ -73,6 +73,16 @@ + + + + + + diff --git a/src/com/android/settings/VoiceInputOutputSettings.java b/src/com/android/settings/VoiceInputOutputSettings.java index b499deda1f..64f8a09d6f 100644 --- a/src/com/android/settings/VoiceInputOutputSettings.java +++ b/src/com/android/settings/VoiceInputOutputSettings.java @@ -16,6 +16,7 @@ package com.android.settings; +import android.Manifest; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; @@ -32,6 +33,7 @@ import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.preference.Preference.OnPreferenceChangeListener; import android.provider.Settings; +import android.service.voice.VoiceInteractionService; import android.speech.RecognitionService; import android.speech.tts.TtsEngines; import android.util.AttributeSet; @@ -53,19 +55,24 @@ public class VoiceInputOutputSettings implements OnPreferenceChangeListener { private static final String TAG = "VoiceInputOutputSettings"; private static final String KEY_VOICE_CATEGORY = "voice_category"; + private static final String KEY_VOICE_INTERACTOR = "voice_interactor"; + private static final String KEY_VOICE_INTERACTOR_SETTINGS = "voice_interactor_settings"; private static final String KEY_RECOGNIZER = "recognizer"; private static final String KEY_RECOGNIZER_SETTINGS = "recognizer_settings"; private static final String KEY_TTS_SETTINGS = "tts_settings"; private PreferenceGroup mParent; + private ListPreference mVoiceInteractionPref; + private PreferenceScreen mVoiceInteractionSettingsPref; private PreferenceCategory mVoiceCategory; private ListPreference mRecognizerPref; - private Preference mRecognizerSettingsPref; + private PreferenceScreen mRecognizerSettingsPref; private Preference mTtsSettingsPref; - private PreferenceScreen mSettingsPref; private final SettingsPreferenceFragment mFragment; private final TtsEngines mTtsEngines; + private HashMap mAvailableVoiceInteractionsMap; + private HashMap mAvailableRecognizersMap; public VoiceInputOutputSettings(SettingsPreferenceFragment fragment) { @@ -77,22 +84,28 @@ public class VoiceInputOutputSettings implements OnPreferenceChangeListener { mParent = mFragment.getPreferenceScreen(); mVoiceCategory = (PreferenceCategory) mParent.findPreference(KEY_VOICE_CATEGORY); + mVoiceInteractionPref = (ListPreference) mVoiceCategory.findPreference( + KEY_VOICE_INTERACTOR); + mVoiceInteractionPref.setOnPreferenceChangeListener(this); + mVoiceInteractionSettingsPref = (PreferenceScreen)mVoiceCategory.findPreference( + KEY_VOICE_INTERACTOR_SETTINGS); mRecognizerPref = (ListPreference) mVoiceCategory.findPreference(KEY_RECOGNIZER); - mRecognizerSettingsPref = mVoiceCategory.findPreference(KEY_RECOGNIZER_SETTINGS); - mTtsSettingsPref = mVoiceCategory.findPreference(KEY_TTS_SETTINGS); - mRecognizerPref.setOnPreferenceChangeListener(this); - mSettingsPref = (PreferenceScreen) + mRecognizerSettingsPref = (PreferenceScreen) mVoiceCategory.findPreference(KEY_RECOGNIZER_SETTINGS); + mRecognizerPref.setOnPreferenceChangeListener(this); + mTtsSettingsPref = mVoiceCategory.findPreference(KEY_TTS_SETTINGS); + mAvailableVoiceInteractionsMap = new HashMap(); mAvailableRecognizersMap = new HashMap(); populateOrRemovePreferences(); } private void populateOrRemovePreferences() { + boolean hasVoiceInteractionPrefs = populateOrRemoveVoiceInteractionPrefs(); boolean hasRecognizerPrefs = populateOrRemoveRecognizerPrefs(); boolean hasTtsPrefs = populateOrRemoveTtsPrefs(); - if (!hasRecognizerPrefs && !hasTtsPrefs) { + if (!hasVoiceInteractionPrefs && !hasRecognizerPrefs && !hasTtsPrefs) { // There were no TTS settings and no recognizer settings, // so it should be safe to hide the preference category // entirely. @@ -100,6 +113,32 @@ public class VoiceInputOutputSettings implements OnPreferenceChangeListener { } } + private boolean populateOrRemoveVoiceInteractionPrefs() { + List availableVoiceServices = + mFragment.getPackageManager().queryIntentServices( + new Intent(VoiceInteractionService.SERVICE_INTERFACE), + PackageManager.GET_META_DATA); + for (int i=0; i availableRecognitionServices = mFragment.getPackageManager().queryIntentServices( @@ -128,7 +167,7 @@ public class VoiceInputOutputSettings implements OnPreferenceChangeListener { String currentSetting = Settings.Secure.getString( mFragment.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE); - updateSettingsLink(currentSetting); + updateRecognizerSettingsLink(currentSetting); } else { // Multiple recognizers available, so show the full list of choices. populateRecognizerPreference(availableRecognitionServices); @@ -148,6 +187,106 @@ public class VoiceInputOutputSettings implements OnPreferenceChangeListener { return true; } + private void populateVoiceInteractionPreference(List voiceInteractors) { + int size = voiceInteractors.size(); + CharSequence[] entries = new CharSequence[size+1]; + CharSequence[] values = new CharSequence[size+1]; + + // Get the current value from the secure setting. + String currentSetting = Settings.Secure.getString( + mFragment.getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE); + + // Iterate through all the available recognizers and load up their info to show + // in the preference. Also build up a map of recognizer component names to their + // ResolveInfos - we'll need that a little later. + for (int i = 0; i < size; i++) { + ResolveInfo resolveInfo = voiceInteractors.get(i); + String recognizerComponent = + new ComponentName(resolveInfo.serviceInfo.packageName, + resolveInfo.serviceInfo.name).flattenToShortString(); + + mAvailableVoiceInteractionsMap.put(recognizerComponent, resolveInfo); + + entries[i] = resolveInfo.loadLabel(mFragment.getPackageManager()); + values[i] = recognizerComponent; + } + + entries[size] = mFragment.getString(R.string.no_voice_interactor); + values[size] = ""; + + mVoiceInteractionPref.setEntries(entries); + mVoiceInteractionPref.setEntryValues(values); + + mVoiceInteractionPref.setDefaultValue(currentSetting); + mVoiceInteractionPref.setValue(currentSetting); + + updateVoiceInteractionSettingsLink(currentSetting); + } + + private void updateVoiceInteractionSettingsLink(String currentSetting) { + ResolveInfo currentRecognizer = mAvailableVoiceInteractionsMap.get(currentSetting); + if (currentRecognizer == null) { + mVoiceInteractionPref.setSummary(mFragment.getString(R.string.no_voice_interactor)); + mVoiceInteractionPref.setValue(""); + return; + } + + ServiceInfo si = currentRecognizer.serviceInfo; + XmlResourceParser parser = null; + String settingsActivity = null; + try { + parser = si.loadXmlMetaData(mFragment.getPackageManager(), + VoiceInteractionService.SERVICE_META_DATA); + if (parser == null) { + throw new XmlPullParserException("No " + VoiceInteractionService.SERVICE_META_DATA + + " meta-data for " + si.packageName); + } + + Resources res = mFragment.getPackageManager().getResourcesForApplication( + si.applicationInfo); + + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + + String nodeName = parser.getName(); + if (!"voice-interaction-service".equals(nodeName)) { + throw new XmlPullParserException( + "Meta-data does not start with voice-interaction-service tag"); + } + + TypedArray array = res.obtainAttributes(attrs, + com.android.internal.R.styleable.VoiceInteractionService); + settingsActivity = array.getString( + com.android.internal.R.styleable.VoiceInteractionService_settingsActivity); + array.recycle(); + } catch (XmlPullParserException e) { + Log.e(TAG, "error parsing recognition service meta-data", e); + } catch (IOException e) { + Log.e(TAG, "error parsing recognition service meta-data", e); + } catch (NameNotFoundException e) { + Log.e(TAG, "error parsing recognition service meta-data", e); + } finally { + if (parser != null) parser.close(); + } + + mVoiceInteractionPref.setSummary(currentRecognizer.loadLabel( + mFragment.getPackageManager())); + mVoiceInteractionPref.setValue(currentSetting); + + if (settingsActivity == null) { + // No settings preference available - hide the preference. + Log.w(TAG, "no recognizer settings available for " + si.packageName); + } else { + Intent i = new Intent(Intent.ACTION_MAIN); + i.setComponent(new ComponentName(si.packageName, settingsActivity)); + mVoiceInteractionSettingsPref.setIntent(i); + } + } + private void populateRecognizerPreference(List recognizers) { int size = recognizers.size(); CharSequence[] entries = new CharSequence[size]; @@ -178,10 +317,10 @@ public class VoiceInputOutputSettings implements OnPreferenceChangeListener { mRecognizerPref.setDefaultValue(currentSetting); mRecognizerPref.setValue(currentSetting); - updateSettingsLink(currentSetting); + updateRecognizerSettingsLink(currentSetting); } - - private void updateSettingsLink(String currentSetting) { + + private void updateRecognizerSettingsLink(String currentSetting) { ResolveInfo currentRecognizer = mAvailableRecognizersMap.get(currentSetting); if (currentRecognizer == null) return; @@ -230,18 +369,29 @@ public class VoiceInputOutputSettings implements OnPreferenceChangeListener { if (settingsActivity == null) { // No settings preference available - hide the preference. Log.w(TAG, "no recognizer settings available for " + si.packageName); - mSettingsPref.setIntent(null); - mVoiceCategory.removePreference(mSettingsPref); + mRecognizerSettingsPref.setIntent(null); + mVoiceCategory.removePreference(mRecognizerSettingsPref); } else { Intent i = new Intent(Intent.ACTION_MAIN); i.setComponent(new ComponentName(si.packageName, settingsActivity)); - mSettingsPref.setIntent(i); + mRecognizerSettingsPref.setIntent(i); mRecognizerPref.setSummary(currentRecognizer.loadLabel(mFragment.getPackageManager())); } } - + public boolean onPreferenceChange(Preference preference, Object newValue) { - if (preference == mRecognizerPref) { + if (preference == mVoiceInteractionPref) { + String setting = (String) newValue; + + // Put the new value back into secure settings. + Settings.Secure.putString(mFragment.getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE, + setting); + + // Update the settings item so it points to the right settings. + updateVoiceInteractionSettingsLink(setting); + + } else if (preference == mRecognizerPref) { String setting = (String) newValue; // Put the new value back into secure settings. @@ -250,7 +400,7 @@ public class VoiceInputOutputSettings implements OnPreferenceChangeListener { setting); // Update the settings item so it points to the right settings. - updateSettingsLink(setting); + updateRecognizerSettingsLink(setting); } return true; } -- 2.11.0