Dynamically populate a list of available recognition services in voice settings,

only showing the option to choose if there is more than one to choose from. Use
the new settingsActivity meta-data to target the appropriate settings activity
for the chosen recognizer.
This commit is contained in:
Mike LeBeau
2010-02-11 22:57:12 -08:00
parent eadeebd1df
commit 766a19b278
3 changed files with 184 additions and 36 deletions

View File

@@ -2073,6 +2073,14 @@ found in the list of installed applications.</string>
<string name="voice_search_settings_title">Voice search</string>
<!-- Title of keyboard settings list item within voice input/output settings -->
<string name="keyboard_settings_title">Android keyboard</string>
<!-- Title for the 'voice input' category of voice input/output settings -->
<string name="voice_input_category">Voice input</string>
<!-- Title for the 'voice output' category of voice input/output settings -->
<string name="voice_output_category">Voice output</string>
<!-- Title for the voice recognizer setting in voice input/output settings -->
<string name="recognizer_title">Voice recognizer</string>
<!-- Title for the link to settings for the chosen voice recognizer in voice input/output settings -->
<string name="recognizer_settings_title">Voice recognizer settings</string>
<!-- Text-To-Speech (TTS) settings --><skip />
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings related to the text-to-speech functionality -->

View File

@@ -18,18 +18,21 @@
android:title="@string/voice_input_output_settings_title"
android:key="parent">
<PreferenceScreen android:key="voice_search_settings"
android:title="@string/voice_search_settings_title">
<intent android:action="android.speech.action.VOICE_SEARCH_SETTINGS"/>
</PreferenceScreen>
<PreferenceCategory android:key="voice_input_category"
android:title="@string/voice_input_category" />
<!-- TODO: Link to a specific voice settings activity within LatinIME when it exists. -->
<PreferenceScreen android:key="keyboard_settings"
android:title="@string/keyboard_settings_title">
<intent android:action="android.intent.action.MAIN"
android:targetPackage="com.android.inputmethod.latin"
android:targetClass="com.android.inputmethod.latin.LatinIMESettings" />
</PreferenceScreen>
<!-- entries, entryValues, and defaultValue will be populated programmatically. -->
<ListPreference
android:key="recognizer"
android:title="@string/recognizer_title"
android:dialogTitle="@string/recognizer_title" />
<!-- An intent for this preference will be populated programmatically. -->
<PreferenceScreen android:key="recognizer_settings"
android:title="@string/recognizer_settings_title" />
<PreferenceCategory android:key="voice_output_category"
android:title="@string/voice_output_category" />
<PreferenceScreen android:key="tts_settings"
android:title="@string/tts_settings_title">

View File

@@ -16,24 +16,53 @@
package com.android.settings;
import android.content.Context;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceChangeListener;
import android.provider.Settings;
import android.speech.RecognitionService;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
/**
* Settings screen for voice input/output.
*/
public class VoiceInputOutputSettings extends PreferenceActivity {
public class VoiceInputOutputSettings extends PreferenceActivity
implements OnPreferenceChangeListener {
private static final String TAG = "VoiceInputOutputSettings";
private static final String KEY_PARENT = "parent";
private static final String KEY_VOICE_SEARCH_SETTINGS = "voice_search_settings";
private static final String KEY_KEYBOARD_SETTINGS = "keyboard_settings";
private static final String KEY_VOICE_INPUT_CATEGORY = "voice_input_category";
private static final String KEY_RECOGNIZER = "recognizer";
private static final String KEY_RECOGNIZER_SETTINGS = "recognizer_settings";
private PreferenceGroup mParent;
private PreferenceCategory mVoiceInputCategory;
private ListPreference mRecognizerPref;
private PreferenceScreen mSettingsPref;
private HashMap<String, ResolveInfo> mAvailableRecognizersMap;
@Override
protected void onCreate(Bundle icicle) {
@@ -41,30 +70,138 @@ public class VoiceInputOutputSettings extends PreferenceActivity {
addPreferencesFromResource(R.xml.voice_input_output_settings);
removePreferenceIfNecessary(KEY_VOICE_SEARCH_SETTINGS);
removePreferenceIfNecessary(KEY_KEYBOARD_SETTINGS);
mParent = (PreferenceGroup) findPreference(KEY_PARENT);
mVoiceInputCategory = (PreferenceCategory) mParent.findPreference(KEY_VOICE_INPUT_CATEGORY);
mRecognizerPref = (ListPreference) mParent.findPreference(KEY_RECOGNIZER);
mRecognizerPref.setOnPreferenceChangeListener(this);
mSettingsPref = (PreferenceScreen) mParent.findPreference(KEY_RECOGNIZER_SETTINGS);
mAvailableRecognizersMap = new HashMap<String, ResolveInfo>();
populateOrRemoveRecognizerPreference();
}
/**
* Removes a preference if there is no activity to handle its intent.
*/
private void removePreferenceIfNecessary(String preferenceKey) {
PreferenceGroup parent = (PreferenceGroup) findPreference(KEY_PARENT);
private void populateOrRemoveRecognizerPreference() {
List<ResolveInfo> availableRecognitionServices = getPackageManager().queryIntentServices(
new Intent(RecognitionService.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
int numAvailable = availableRecognitionServices.size();
Preference preference = parent.findPreference(preferenceKey);
if (preference == null) {
return;
}
Intent intent = preference.getIntent();
if (intent != null) {
PackageManager pm = getPackageManager();
if (!pm.queryIntentActivities(intent, 0).isEmpty()) {
return;
if (numAvailable == 0) {
// No recognizer available - remove all related preferences.
removePreference(mVoiceInputCategory);
removePreference(mRecognizerPref);
removePreference(mSettingsPref);
} else if (numAvailable == 1) {
// Only one recognizer available, so don't show the list of choices.
removePreference(mRecognizerPref);
} else {
// Multiple recognizers available, so show the full list of choices.
populateRecognizerPreference(availableRecognitionServices);
}
}
// Did not find a matching activity, so remove the preference.
parent.removePreference(preference);
private void removePreference(Preference pref) {
if (pref != null) {
mParent.removePreference(pref);
}
}
private void populateRecognizerPreference(List<ResolveInfo> recognizers) {
int size = recognizers.size();
CharSequence[] entries = new CharSequence[size];
CharSequence[] values = new CharSequence[size];
// Get the current value from the secure setting.
String currentSetting = Settings.Secure.getString(
getContentResolver(), Settings.Secure.VOICE_RECOGNITION_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 = recognizers.get(i);
String recognizerComponent =
new ComponentName(resolveInfo.serviceInfo.packageName,
resolveInfo.serviceInfo.name).flattenToString();
mAvailableRecognizersMap.put(recognizerComponent, resolveInfo);
entries[i] = resolveInfo.loadLabel(getPackageManager());
values[i] = recognizerComponent;
}
mRecognizerPref.setEntries(entries);
mRecognizerPref.setEntryValues(values);
mRecognizerPref.setDefaultValue(currentSetting);
mRecognizerPref.setValue(currentSetting);
updateSettingsLink(currentSetting);
}
private void updateSettingsLink(String currentSetting) {
ResolveInfo currentRecognizer = mAvailableRecognizersMap.get(currentSetting);
ServiceInfo si = currentRecognizer.serviceInfo;
XmlResourceParser parser = null;
String settingsActivity = null;
try {
parser = si.loadXmlMetaData(getPackageManager(), RecognitionService.SERVICE_META_DATA);
if (parser == null) {
throw new XmlPullParserException("No " + RecognitionService.SERVICE_META_DATA +
" meta-data for " + si.packageName);
}
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
}
String nodeName = parser.getName();
if (!"recognition-service".equals(nodeName)) {
throw new XmlPullParserException(
"Meta-data does not start with recognition-service tag");
}
TypedArray array = getResources().obtainAttributes(attrs,
com.android.internal.R.styleable.RecognitionService);
settingsActivity = array.getString(
com.android.internal.R.styleable.RecognitionService_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);
} finally {
if (parser != null) parser.close();
}
if (settingsActivity == null) {
// No settings preference available - hide the preference.
Log.w(TAG, "no recognizer settings available for " + si.packageName);
mSettingsPref.setIntent(null);
mParent.removePreference(mSettingsPref);
} else {
Intent i = new Intent(Intent.ACTION_MAIN);
i.setComponent(new ComponentName(si.packageName, settingsActivity));
mSettingsPref.setIntent(i);
}
}
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == mRecognizerPref) {
String setting = (String) newValue;
// Put the new value back into secure settings.
Settings.Secure.putString(
getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE,
setting);
// Update the settings item so it points to the right settings.
updateSettingsLink(setting);
}
return true;
}
}