• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.applications.assist;
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.provider.Settings;
25 import android.service.voice.VoiceInteractionService;
26 import android.service.voice.VoiceInteractionServiceInfo;
27 import android.speech.RecognitionService;
28 import android.text.TextUtils;
29 import android.util.Log;
30 
31 import com.android.internal.app.AssistUtils;
32 import com.android.internal.logging.nano.MetricsProto;
33 import com.android.settings.R;
34 import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
35 
36 import com.android.settingslib.applications.DefaultAppInfo;
37 import com.android.settingslib.widget.CandidateInfo;
38 
39 import java.util.ArrayList;
40 import java.util.List;
41 
42 public class DefaultAssistPicker extends DefaultAppPickerFragment {
43 
44     private static final String TAG = "DefaultAssistPicker";
45     private static final Intent ASSIST_SERVICE_PROBE =
46             new Intent(VoiceInteractionService.SERVICE_INTERFACE);
47     private static final Intent ASSIST_ACTIVITY_PROBE =
48             new Intent(Intent.ACTION_ASSIST);
49     private final List<Info> mAvailableAssistants = new ArrayList<>();
50 
51     private AssistUtils mAssistUtils;
52 
53     @Override
getMetricsCategory()54     public int getMetricsCategory() {
55         return MetricsProto.MetricsEvent.DEFAULT_ASSIST_PICKER;
56     }
57 
58     @Override
shouldShowItemNone()59     protected boolean shouldShowItemNone() {
60         return true;
61     }
62 
63     @Override
onAttach(Context context)64     public void onAttach(Context context) {
65         super.onAttach(context);
66         mAssistUtils = new AssistUtils(context);
67     }
68 
69     @Override
getPreferenceScreenResId()70     protected int getPreferenceScreenResId() {
71         return R.xml.default_assist_settings;
72     }
73 
74     @Override
getCandidates()75     protected List<DefaultAppInfo> getCandidates() {
76         mAvailableAssistants.clear();
77         addAssistServices();
78         addAssistActivities();
79 
80         final List<String> packages = new ArrayList<>();
81         final List<DefaultAppInfo> candidates = new ArrayList<>();
82         for (Info info : mAvailableAssistants) {
83             final String packageName = info.component.getPackageName();
84             if (packages.contains(packageName)) {
85                 // A service appears before an activity thus overrides it if from the same package.
86                 continue;
87             }
88             packages.add(packageName);
89             candidates.add(new DefaultAppInfo(getContext(), mPm, mUserId, info.component));
90         }
91         return candidates;
92     }
93 
94     @Override
getDefaultKey()95     protected String getDefaultKey() {
96         final ComponentName cn = getCurrentAssist();
97         if (cn != null) {
98             return new DefaultAppInfo(getContext(), mPm, mUserId, cn).getKey();
99         }
100         return null;
101     }
102 
103     @Override
getConfirmationMessage(CandidateInfo appInfo)104     protected String getConfirmationMessage(CandidateInfo appInfo) {
105         if (appInfo == null) {
106             return null;
107         }
108         return getContext().getString(R.string.assistant_security_warning, appInfo.loadLabel());
109     }
110 
111     @Override
setDefaultKey(String key)112     protected boolean setDefaultKey(String key) {
113         if (TextUtils.isEmpty(key)) {
114             setAssistNone();
115             return true;
116         }
117         ComponentName cn = ComponentName.unflattenFromString(key);
118         final Info info = findAssistantByPackageName(cn.getPackageName());
119         if (info == null) {
120             setAssistNone();
121             return true;
122         }
123 
124         if (info.isVoiceInteractionService()) {
125             setAssistService(info);
126         } else {
127             setAssistActivity(info);
128         }
129         return true;
130     }
131 
getCurrentAssist()132     public ComponentName getCurrentAssist() {
133         return mAssistUtils.getAssistComponentForUser(mUserId);
134     }
135 
addAssistServices()136     private void addAssistServices() {
137         final PackageManager pm = mPm.getPackageManager();
138         final List<ResolveInfo> services = pm.queryIntentServices(
139                 ASSIST_SERVICE_PROBE, PackageManager.GET_META_DATA);
140         for (ResolveInfo resolveInfo : services) {
141             VoiceInteractionServiceInfo voiceInteractionServiceInfo =
142                     new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo);
143             if (!voiceInteractionServiceInfo.getSupportsAssist()) {
144                 continue;
145             }
146 
147             mAvailableAssistants.add(new Info(
148                     new ComponentName(resolveInfo.serviceInfo.packageName,
149                             resolveInfo.serviceInfo.name),
150                     voiceInteractionServiceInfo));
151         }
152     }
153 
addAssistActivities()154     private void addAssistActivities() {
155         final PackageManager pm = mPm.getPackageManager();
156         final List<ResolveInfo> activities = pm.queryIntentActivities(
157                 ASSIST_ACTIVITY_PROBE, PackageManager.MATCH_DEFAULT_ONLY);
158         for (ResolveInfo resolveInfo : activities) {
159             mAvailableAssistants.add(new Info(
160                     new ComponentName(resolveInfo.activityInfo.packageName,
161                             resolveInfo.activityInfo.name)));
162         }
163     }
164 
findAssistantByPackageName(String packageName)165     private Info findAssistantByPackageName(String packageName) {
166         for (Info info : mAvailableAssistants) {
167             if (TextUtils.equals(info.component.getPackageName(), packageName)) {
168                 return info;
169             }
170         }
171         return null;
172     }
173 
setAssistNone()174     private void setAssistNone() {
175         Settings.Secure.putString(getContext().getContentResolver(),
176                 Settings.Secure.ASSISTANT, "");
177         Settings.Secure.putString(getContext().getContentResolver(),
178                 Settings.Secure.VOICE_INTERACTION_SERVICE, "");
179         Settings.Secure.putString(getContext().getContentResolver(),
180                 Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
181     }
182 
setAssistService(Info serviceInfo)183     private void setAssistService(Info serviceInfo) {
184         final String serviceComponentName = serviceInfo.component.
185                 flattenToShortString();
186         final String serviceRecognizerName = new ComponentName(
187                 serviceInfo.component.getPackageName(),
188                 serviceInfo.voiceInteractionServiceInfo.getRecognitionService())
189                 .flattenToShortString();
190 
191         Settings.Secure.putString(getContext().getContentResolver(),
192                 Settings.Secure.ASSISTANT, serviceComponentName);
193         Settings.Secure.putString(getContext().getContentResolver(),
194                 Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName);
195         Settings.Secure.putString(getContext().getContentResolver(),
196                 Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName);
197     }
198 
setAssistActivity(Info activityInfo)199     private void setAssistActivity(Info activityInfo) {
200         Settings.Secure.putString(getContext().getContentResolver(),
201                 Settings.Secure.ASSISTANT, activityInfo.component.flattenToShortString());
202         Settings.Secure.putString(getContext().getContentResolver(),
203                 Settings.Secure.VOICE_INTERACTION_SERVICE, "");
204         Settings.Secure.putString(getContext().getContentResolver(),
205                 Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
206     }
207 
getDefaultRecognizer()208     private String getDefaultRecognizer() {
209         final ResolveInfo resolveInfo = mPm.getPackageManager().resolveService(
210                 new Intent(RecognitionService.SERVICE_INTERFACE),
211                 PackageManager.GET_META_DATA);
212         if (resolveInfo == null || resolveInfo.serviceInfo == null) {
213             Log.w(TAG, "Unable to resolve default voice recognition service.");
214             return "";
215         }
216 
217         return new ComponentName(resolveInfo.serviceInfo.packageName,
218                 resolveInfo.serviceInfo.name).flattenToShortString();
219     }
220 
221     static class Info {
222         public final ComponentName component;
223         public final VoiceInteractionServiceInfo voiceInteractionServiceInfo;
224 
Info(ComponentName component)225         Info(ComponentName component) {
226             this.component = component;
227             this.voiceInteractionServiceInfo = null;
228         }
229 
Info(ComponentName component, VoiceInteractionServiceInfo voiceInteractionServiceInfo)230         Info(ComponentName component, VoiceInteractionServiceInfo voiceInteractionServiceInfo) {
231             this.component = component;
232             this.voiceInteractionServiceInfo = voiceInteractionServiceInfo;
233         }
234 
isVoiceInteractionService()235         public boolean isVoiceInteractionService() {
236             return voiceInteractionServiceInfo != null;
237         }
238     }
239 }
240