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