1/* 2 * Copyright (C) 2015 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 17package com.android.settings.applications; 18 19import android.content.ComponentName; 20import android.content.Context; 21import android.content.Intent; 22import android.content.pm.PackageManager; 23import android.content.pm.ResolveInfo; 24import android.os.UserHandle; 25import android.provider.Settings; 26import android.service.voice.VoiceInteractionService; 27import android.service.voice.VoiceInteractionServiceInfo; 28import android.speech.RecognitionService; 29import android.util.AttributeSet; 30import android.util.Log; 31 32import com.android.internal.app.AssistUtils; 33import com.android.settings.AppListPreferenceWithSettings; 34import com.android.settings.R; 35 36import java.util.ArrayList; 37import java.util.List; 38 39public class DefaultAssistPreference extends AppListPreferenceWithSettings { 40 41 private static final String TAG = DefaultAssistPreference.class.getSimpleName(); 42 43 private final List<Info> mAvailableAssistants = new ArrayList<>(); 44 45 private final AssistUtils mAssistUtils; 46 47 public DefaultAssistPreference(Context context, AttributeSet attrs) { 48 super(context, attrs); 49 setShowItemNone(true); 50 setDialogTitle(R.string.choose_assist_title); 51 mAssistUtils = new AssistUtils(context); 52 } 53 54 @Override 55 protected boolean persistString(String value) { 56 final Info info = findAssistantByPackageName(value); 57 if (info == null) { 58 setAssistNone(); 59 return true; 60 } 61 62 if (info.isVoiceInteractionService()) { 63 setAssistService(info); 64 } else { 65 setAssistActivity(info); 66 } 67 return true; 68 } 69 70 private void setAssistNone() { 71 Settings.Secure.putString(getContext().getContentResolver(), 72 Settings.Secure.ASSISTANT, ITEM_NONE_VALUE); 73 Settings.Secure.putString(getContext().getContentResolver(), 74 Settings.Secure.VOICE_INTERACTION_SERVICE, ""); 75 Settings.Secure.putString(getContext().getContentResolver(), 76 Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer()); 77 78 setSummary(getContext().getText(R.string.default_assist_none)); 79 setSettingsComponent(null); 80 } 81 82 private void setAssistService(Info serviceInfo) { 83 final String serviceComponentName = serviceInfo.component.flattenToShortString(); 84 final String serviceRecognizerName = new ComponentName( 85 serviceInfo.component.getPackageName(), 86 serviceInfo.voiceInteractionServiceInfo.getRecognitionService()) 87 .flattenToShortString(); 88 89 Settings.Secure.putString(getContext().getContentResolver(), 90 Settings.Secure.ASSISTANT, serviceComponentName); 91 Settings.Secure.putString(getContext().getContentResolver(), 92 Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName); 93 Settings.Secure.putString(getContext().getContentResolver(), 94 Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName); 95 96 setSummary(getEntry()); 97 final String settingsActivity = 98 serviceInfo.voiceInteractionServiceInfo.getSettingsActivity(); 99 setSettingsComponent(settingsActivity == null ? 100 null : 101 new ComponentName(serviceInfo.component.getPackageName(), settingsActivity)); 102 } 103 104 private void setAssistActivity(Info activityInfo) { 105 Settings.Secure.putString(getContext().getContentResolver(), 106 Settings.Secure.ASSISTANT, activityInfo.component.flattenToShortString()); 107 Settings.Secure.putString(getContext().getContentResolver(), 108 Settings.Secure.VOICE_INTERACTION_SERVICE, ""); 109 Settings.Secure.putString(getContext().getContentResolver(), 110 Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer()); 111 112 setSummary(getEntry()); 113 setSettingsComponent(null); 114 } 115 116 private String getDefaultRecognizer() { 117 ResolveInfo resolveInfo = getContext().getPackageManager().resolveService( 118 new Intent(RecognitionService.SERVICE_INTERFACE), 119 PackageManager.GET_META_DATA); 120 if (resolveInfo == null || resolveInfo.serviceInfo == null) { 121 Log.w(TAG, "Unable to resolve default voice recognition service."); 122 return ""; 123 } 124 125 return new ComponentName(resolveInfo.serviceInfo.packageName, 126 resolveInfo.serviceInfo.name).flattenToShortString(); 127 } 128 129 private Info findAssistantByPackageName(String packageName) { 130 for (int i = 0; i < mAvailableAssistants.size(); ++i) { 131 Info info = mAvailableAssistants.get(i); 132 if (info.component.getPackageName().equals(packageName)) { 133 return info; 134 } 135 } 136 return null; 137 } 138 139 private void addAssistServices() { 140 PackageManager pm = getContext().getPackageManager(); 141 142 List<ResolveInfo> services = pm.queryIntentServices( 143 new Intent(VoiceInteractionService.SERVICE_INTERFACE), 144 PackageManager.GET_META_DATA); 145 for (int i = 0; i < services.size(); ++i) { 146 ResolveInfo resolveInfo = services.get(i); 147 VoiceInteractionServiceInfo voiceInteractionServiceInfo = 148 new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo); 149 if (!voiceInteractionServiceInfo.getSupportsAssist()) { 150 continue; 151 } 152 153 mAvailableAssistants.add(new Info( 154 new ComponentName(resolveInfo.serviceInfo.packageName, 155 resolveInfo.serviceInfo.name), 156 voiceInteractionServiceInfo)); 157 } 158 } 159 160 private void addAssistActivities() { 161 PackageManager pm = getContext().getPackageManager(); 162 163 List<ResolveInfo> activities = pm.queryIntentActivities( 164 new Intent(Intent.ACTION_ASSIST), 165 PackageManager.MATCH_DEFAULT_ONLY); 166 for (int i = 0; i < activities.size(); ++i) { 167 ResolveInfo resolveInfo = activities.get(i); 168 mAvailableAssistants.add(new Info( 169 new ComponentName(resolveInfo.activityInfo.packageName, 170 resolveInfo.activityInfo.name))); 171 } 172 } 173 174 public ComponentName getCurrentAssist() { 175 return mAssistUtils.getAssistComponentForUser(UserHandle.myUserId()); 176 } 177 178 public void refreshAssistApps() { 179 mAvailableAssistants.clear(); 180 addAssistServices(); 181 addAssistActivities(); 182 183 List<String> packages = new ArrayList<>(); 184 for (int i = 0; i < mAvailableAssistants.size(); ++i) { 185 String packageName = mAvailableAssistants.get(i).component.getPackageName(); 186 if (packages.contains(packageName)) { 187 // A service appears before an activity thus overrides it if from the same package. 188 continue; 189 } 190 packages.add(packageName); 191 } 192 193 ComponentName currentAssist = getCurrentAssist(); 194 setPackageNames(packages.toArray(new String[packages.size()]), 195 currentAssist == null ? null : currentAssist.getPackageName()); 196 } 197 198 private static class Info { 199 public final ComponentName component; 200 public final VoiceInteractionServiceInfo voiceInteractionServiceInfo; 201 202 Info(ComponentName component) { 203 this.component = component; 204 this.voiceInteractionServiceInfo = null; 205 } 206 207 Info(ComponentName component, VoiceInteractionServiceInfo voiceInteractionServiceInfo) { 208 this.component = component; 209 this.voiceInteractionServiceInfo = voiceInteractionServiceInfo; 210 } 211 212 public boolean isVoiceInteractionService() { 213 return voiceInteractionServiceInfo != null; 214 } 215 } 216} 217