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.preference.Preference;
26 import android.provider.Settings;
27 import android.service.voice.VoiceInteractionService;
28 import android.service.voice.VoiceInteractionServiceInfo;
29 import android.speech.RecognitionService;
30 import com.android.settings.R;
31 import com.android.settings.SettingsPreferenceFragment;
32 import com.android.settings.search.BaseSearchIndexProvider;
33 import com.android.settings.search.Indexable;
34 import com.android.settings.search.SearchIndexableRaw;
35 import com.android.settings.voice.VoiceInputPreference.RadioButtonGroupState;
36 
37 import android.os.Bundle;
38 import android.preference.PreferenceCategory;
39 import android.widget.Checkable;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 
44 public class VoiceInputSettings extends SettingsPreferenceFragment implements
45         Preference.OnPreferenceClickListener, RadioButtonGroupState, Indexable {
46 
47     private static final String TAG = "VoiceInputSettings";
48     private static final boolean DBG = false;
49 
50     /**
51      * Preference key for the engine selection preference.
52      */
53     private static final String KEY_SERVICE_PREFERENCE_SECTION =
54             "voice_service_preference_section";
55 
56     private PreferenceCategory mServicePreferenceCategory;
57 
58     private CharSequence mInteractorSummary;
59     private CharSequence mRecognizerSummary;
60     private CharSequence mInteractorWarning;
61 
62     /**
63      * The currently selected engine.
64      */
65     private String mCurrentKey;
66 
67     /**
68      * The engine checkbox that is currently checked. Saves us a bit of effort
69      * in deducing the right one from the currently selected engine.
70      */
71     private Checkable mCurrentChecked;
72 
73     private VoiceInputHelper mHelper;
74 
75     @Override
onCreate(Bundle savedInstanceState)76     public void onCreate(Bundle savedInstanceState) {
77         super.onCreate(savedInstanceState);
78         addPreferencesFromResource(R.xml.voice_input_settings);
79 
80         mServicePreferenceCategory = (PreferenceCategory) findPreference(
81                 KEY_SERVICE_PREFERENCE_SECTION);
82 
83         mInteractorSummary = getActivity().getText(
84                 R.string.voice_interactor_preference_summary);
85         mRecognizerSummary = getActivity().getText(
86                 R.string.voice_recognizer_preference_summary);
87         mInteractorWarning = getActivity().getText(R.string.voice_interaction_security_warning);
88     }
89 
90     @Override
onStart()91     public void onStart() {
92         super.onStart();
93         initSettings();
94     }
95 
initSettings()96     private void initSettings() {
97         mHelper = new VoiceInputHelper(getActivity());
98         mHelper.buildUi();
99 
100         mServicePreferenceCategory.removeAll();
101 
102         if (mHelper.mCurrentVoiceInteraction != null) {
103             mCurrentKey = mHelper.mCurrentVoiceInteraction.flattenToShortString();
104         } else if (mHelper.mCurrentRecognizer != null) {
105             mCurrentKey = mHelper.mCurrentRecognizer.flattenToShortString();
106         } else {
107             mCurrentKey = null;
108         }
109 
110         for (int i=0; i<mHelper.mAvailableInteractionInfos.size(); i++) {
111             VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i);
112             VoiceInputPreference pref = new VoiceInputPreference(getActivity(), info,
113                     mInteractorSummary, mInteractorWarning, this);
114             mServicePreferenceCategory.addPreference(pref);
115         }
116 
117         for (int i=0; i<mHelper.mAvailableRecognizerInfos.size(); i++) {
118             VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i);
119             VoiceInputPreference pref = new VoiceInputPreference(getActivity(), info,
120                     mRecognizerSummary, null, this);
121             mServicePreferenceCategory.addPreference(pref);
122         }
123     }
124 
125     @Override
getCurrentChecked()126     public Checkable getCurrentChecked() {
127         return mCurrentChecked;
128     }
129 
130     @Override
getCurrentKey()131     public String getCurrentKey() {
132         return mCurrentKey;
133     }
134 
135     @Override
setCurrentChecked(Checkable current)136     public void setCurrentChecked(Checkable current) {
137         mCurrentChecked = current;
138     }
139 
140     @Override
setCurrentKey(String key)141     public void setCurrentKey(String key) {
142         mCurrentKey = key;
143         for (int i=0; i<mHelper.mAvailableInteractionInfos.size(); i++) {
144             VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i);
145             if (info.key.equals(key)) {
146                 // Put the new value back into secure settings.
147                 Settings.Secure.putString(getActivity().getContentResolver(),
148                         Settings.Secure.VOICE_INTERACTION_SERVICE, key);
149                 // Eventually we will require that an interactor always specify a recognizer
150                 if (info.settings != null) {
151                     Settings.Secure.putString(getActivity().getContentResolver(),
152                             Settings.Secure.VOICE_RECOGNITION_SERVICE,
153                             info.settings.flattenToShortString());
154                 }
155                 return;
156             }
157         }
158 
159         for (int i=0; i<mHelper.mAvailableRecognizerInfos.size(); i++) {
160             VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i);
161             if (info.key.equals(key)) {
162                 Settings.Secure.putString(getActivity().getContentResolver(),
163                         Settings.Secure.VOICE_INTERACTION_SERVICE, "");
164                 Settings.Secure.putString(getActivity().getContentResolver(),
165                         Settings.Secure.VOICE_RECOGNITION_SERVICE, key);
166                 return;
167             }
168         }
169     }
170 
171     @Override
onPreferenceClick(Preference preference)172     public boolean onPreferenceClick(Preference preference) {
173         if (preference instanceof VoiceInputPreference) {
174             ((VoiceInputPreference)preference).doClick();
175         }
176         return true;
177     }
178 
179     // For Search
180     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
181         new BaseSearchIndexProvider() {
182 
183             @Override
184             public List<SearchIndexableRaw> getRawDataToIndex(Context context,
185                     boolean enabled) {
186 
187                 List<SearchIndexableRaw> indexables = new ArrayList<>();
188 
189                 final String screenTitle = context.getString(R.string.voice_input_settings_title);
190 
191                 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
192                 indexable.key = "voice_service_preference_section_title";
193                 indexable.title = context.getString(R.string.voice_service_preference_section_title);
194                 indexable.screenTitle = screenTitle;
195                 indexables.add(indexable);
196 
197                 final List<ResolveInfo> voiceInteractions =
198                         context.getPackageManager().queryIntentServices(
199                                 new Intent(VoiceInteractionService.SERVICE_INTERFACE),
200                                 PackageManager.GET_META_DATA);
201 
202                 final int countInteractions = voiceInteractions.size();
203                 for (int i = 0; i < countInteractions; i++) {
204                     ResolveInfo info = voiceInteractions.get(i);
205                     VoiceInteractionServiceInfo visInfo = new VoiceInteractionServiceInfo(
206                             context.getPackageManager(), info.serviceInfo);
207                     if (visInfo.getParseError() != null) {
208                         continue;
209                     }
210                     indexables.add(getSearchIndexableRaw(context, info, screenTitle));
211                 }
212 
213                 final List<ResolveInfo> recognitions =
214                         context.getPackageManager().queryIntentServices(
215                                 new Intent(RecognitionService.SERVICE_INTERFACE),
216                                 PackageManager.GET_META_DATA);
217 
218                 final int countRecognitions = recognitions.size();
219                 for (int i = 0; i < countRecognitions; i++) {
220                     ResolveInfo info = recognitions.get(i);
221                     indexables.add(getSearchIndexableRaw(context, info, screenTitle));
222                 }
223 
224                 return indexables;
225             }
226 
227             private SearchIndexableRaw getSearchIndexableRaw(Context context,
228                     ResolveInfo info, String screenTitle) {
229 
230                 ServiceInfo serviceInfo = info.serviceInfo;
231                 ComponentName componentName = new ComponentName(serviceInfo.packageName,
232                         serviceInfo.name);
233 
234                 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
235                 indexable.key = componentName.flattenToString();
236                 indexable.title = info.loadLabel(context.getPackageManager()).toString();
237                 indexable.screenTitle = screenTitle;
238 
239                 return indexable;
240             }
241         };
242 }
243