1 /* 2 * Copyright (C) 2014 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.voice; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.content.pm.ResolveInfo; 24 import android.content.pm.ServiceInfo; 25 import android.content.res.Resources; 26 import android.content.res.TypedArray; 27 import android.content.res.XmlResourceParser; 28 import android.provider.Settings; 29 import android.service.voice.VoiceInteractionService; 30 import android.service.voice.VoiceInteractionServiceInfo; 31 import android.speech.RecognitionService; 32 import android.util.ArraySet; 33 import android.util.AttributeSet; 34 import android.util.Log; 35 import android.util.Xml; 36 import org.xmlpull.v1.XmlPullParser; 37 import org.xmlpull.v1.XmlPullParserException; 38 39 import java.io.IOException; 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.List; 43 44 public final class VoiceInputHelper { 45 static final String TAG = "VoiceInputHelper"; 46 final Context mContext; 47 48 final List<ResolveInfo> mAvailableVoiceInteractions; 49 final List<ResolveInfo> mAvailableRecognition; 50 51 static public class BaseInfo implements Comparable { 52 public final ServiceInfo service; 53 public final ComponentName componentName; 54 public final String key; 55 public final ComponentName settings; 56 public final CharSequence label; 57 public final String labelStr; 58 public final CharSequence appLabel; 59 BaseInfo(PackageManager pm, ServiceInfo _service, String _settings)60 public BaseInfo(PackageManager pm, ServiceInfo _service, String _settings) { 61 service = _service; 62 componentName = new ComponentName(_service.packageName, _service.name); 63 key = componentName.flattenToShortString(); 64 settings = _settings != null 65 ? new ComponentName(_service.packageName, _settings) : null; 66 label = _service.loadLabel(pm); 67 labelStr = label.toString(); 68 appLabel = _service.applicationInfo.loadLabel(pm); 69 } 70 71 @Override compareTo(Object another)72 public int compareTo(Object another) { 73 return labelStr.compareTo(((BaseInfo)another).labelStr); 74 } 75 } 76 77 static public class InteractionInfo extends BaseInfo { 78 public final VoiceInteractionServiceInfo serviceInfo; 79 InteractionInfo(PackageManager pm, VoiceInteractionServiceInfo _service)80 public InteractionInfo(PackageManager pm, VoiceInteractionServiceInfo _service) { 81 super(pm, _service.getServiceInfo(), _service.getSettingsActivity()); 82 serviceInfo = _service; 83 } 84 } 85 86 static public class RecognizerInfo extends BaseInfo { RecognizerInfo(PackageManager pm, ServiceInfo _service, String _settings)87 public RecognizerInfo(PackageManager pm, ServiceInfo _service, String _settings) { 88 super(pm, _service, _settings); 89 } 90 } 91 92 final ArrayList<InteractionInfo> mAvailableInteractionInfos = new ArrayList<>(); 93 final ArrayList<RecognizerInfo> mAvailableRecognizerInfos = new ArrayList<>(); 94 95 ComponentName mCurrentVoiceInteraction; 96 ComponentName mCurrentRecognizer; 97 VoiceInputHelper(Context context)98 public VoiceInputHelper(Context context) { 99 mContext = context; 100 101 mAvailableVoiceInteractions = mContext.getPackageManager().queryIntentServices( 102 new Intent(VoiceInteractionService.SERVICE_INTERFACE), 103 PackageManager.GET_META_DATA); 104 mAvailableRecognition = mContext.getPackageManager().queryIntentServices( 105 new Intent(RecognitionService.SERVICE_INTERFACE), 106 PackageManager.GET_META_DATA); 107 } 108 hasItems()109 public boolean hasItems() { 110 return mAvailableVoiceInteractions.size() > 0 || mAvailableRecognition.size() > 0; 111 } 112 buildUi()113 public void buildUi() { 114 // Get the currently selected interactor from the secure setting. 115 String currentSetting = Settings.Secure.getString( 116 mContext.getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE); 117 if (currentSetting != null && !currentSetting.isEmpty()) { 118 mCurrentVoiceInteraction = ComponentName.unflattenFromString(currentSetting); 119 } else { 120 mCurrentVoiceInteraction = null; 121 } 122 123 ArraySet<ComponentName> interactorRecognizers = new ArraySet<>(); 124 125 // Iterate through all the available interactors and load up their info to show 126 // in the preference. 127 int size = mAvailableVoiceInteractions.size(); 128 for (int i = 0; i < size; i++) { 129 ResolveInfo resolveInfo = mAvailableVoiceInteractions.get(i); 130 VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo( 131 mContext.getPackageManager(), resolveInfo.serviceInfo); 132 if (info.getParseError() != null) { 133 Log.w("VoiceInteractionService", "Error in VoiceInteractionService " 134 + resolveInfo.serviceInfo.packageName + "/" 135 + resolveInfo.serviceInfo.name + ": " + info.getParseError()); 136 continue; 137 } 138 mAvailableInteractionInfos.add(new InteractionInfo(mContext.getPackageManager(), info)); 139 if (info.getRecognitionService() != null) { 140 interactorRecognizers.add(new ComponentName(resolveInfo.serviceInfo.packageName, 141 info.getRecognitionService())); 142 } 143 } 144 Collections.sort(mAvailableInteractionInfos); 145 146 // Get the currently selected recognizer from the secure setting. 147 currentSetting = Settings.Secure.getString( 148 mContext.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE); 149 if (currentSetting != null && !currentSetting.isEmpty()) { 150 mCurrentRecognizer = ComponentName.unflattenFromString(currentSetting); 151 } else { 152 mCurrentRecognizer = null; 153 } 154 155 // Iterate through all the available recognizers and load up their info to show 156 // in the preference. 157 size = mAvailableRecognition.size(); 158 for (int i = 0; i < size; i++) { 159 ResolveInfo resolveInfo = mAvailableRecognition.get(i); 160 ComponentName comp = new ComponentName(resolveInfo.serviceInfo.packageName, 161 resolveInfo.serviceInfo.name); 162 if (interactorRecognizers.contains(comp)) { 163 //continue; 164 } 165 ServiceInfo si = resolveInfo.serviceInfo; 166 XmlResourceParser parser = null; 167 String settingsActivity = null; 168 try { 169 parser = si.loadXmlMetaData(mContext.getPackageManager(), 170 RecognitionService.SERVICE_META_DATA); 171 if (parser == null) { 172 throw new XmlPullParserException("No " + RecognitionService.SERVICE_META_DATA + 173 " meta-data for " + si.packageName); 174 } 175 176 Resources res = mContext.getPackageManager().getResourcesForApplication( 177 si.applicationInfo); 178 179 AttributeSet attrs = Xml.asAttributeSet(parser); 180 181 int type; 182 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 183 && type != XmlPullParser.START_TAG) { 184 } 185 186 String nodeName = parser.getName(); 187 if (!"recognition-service".equals(nodeName)) { 188 throw new XmlPullParserException( 189 "Meta-data does not start with recognition-service tag"); 190 } 191 192 TypedArray array = res.obtainAttributes(attrs, 193 com.android.internal.R.styleable.RecognitionService); 194 settingsActivity = array.getString( 195 com.android.internal.R.styleable.RecognitionService_settingsActivity); 196 array.recycle(); 197 } catch (XmlPullParserException e) { 198 Log.e(TAG, "error parsing recognition service meta-data", e); 199 } catch (IOException e) { 200 Log.e(TAG, "error parsing recognition service meta-data", e); 201 } catch (PackageManager.NameNotFoundException e) { 202 Log.e(TAG, "error parsing recognition service meta-data", e); 203 } finally { 204 if (parser != null) parser.close(); 205 } 206 mAvailableRecognizerInfos.add(new RecognizerInfo(mContext.getPackageManager(), 207 resolveInfo.serviceInfo, settingsActivity)); 208 } 209 Collections.sort(mAvailableRecognizerInfos); 210 } 211 } 212