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 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