diff options
Diffstat (limited to 'src/com/android/settings/voice')
3 files changed, 148 insertions, 483 deletions
diff --git a/src/com/android/settings/voice/VoiceInputListPreference.java b/src/com/android/settings/voice/VoiceInputListPreference.java new file mode 100644 index 0000000..a131d21 --- /dev/null +++ b/src/com/android/settings/voice/VoiceInputListPreference.java @@ -0,0 +1,148 @@ +package com.android.settings.voice; + +import android.content.ComponentName; +import android.content.Context; +import android.provider.Settings; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListAdapter; + +import com.android.settings.AppListPreferenceWithSettings; +import com.android.settings.R; + +import java.util.ArrayList; +import java.util.List; + +public class VoiceInputListPreference extends AppListPreferenceWithSettings { + + private VoiceInputHelper mHelper; + + // The assist component name to restrict available voice inputs. + private ComponentName mAssistRestrict; + + private final List<Integer> mAvailableIndexes = new ArrayList<>(); + + public VoiceInputListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setDialogTitle(R.string.choose_voice_input_title); + } + + @Override + protected ListAdapter createListAdapter() { + return new CustomAdapter(getContext(), getEntries()); + } + + @Override + protected boolean persistString(String value) { + for (int i = 0; i < mHelper.mAvailableInteractionInfos.size(); ++i) { + VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i); + if (info.key.equals(value)) { + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE, value); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE, + new ComponentName(info.service.packageName, + info.serviceInfo.getRecognitionService()) + .flattenToShortString()); + setSummary(getEntry()); + setSettingsComponent(info.settings); + return true; + } + } + + for (int i = 0; i < mHelper.mAvailableRecognizerInfos.size(); ++i) { + VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i); + if (info.key.equals(value)) { + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE, ""); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE, value); + setSummary(getEntry()); + setSettingsComponent(info.settings); + return true; + } + } + + setSettingsComponent(null); + return true; + } + + @Override + public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName) { + // Skip since all entries are created from |mHelper|. + } + + public void setAssistRestrict(ComponentName assistRestrict) { + mAssistRestrict = assistRestrict; + } + + public void refreshVoiceInputs() { + mHelper = new VoiceInputHelper(getContext()); + mHelper.buildUi(); + + final String assistKey = + mAssistRestrict == null ? "" : mAssistRestrict.flattenToShortString(); + + mAvailableIndexes.clear(); + List<CharSequence> entries = new ArrayList<>(); + List<CharSequence> values = new ArrayList<>(); + for (int i = 0; i < mHelper.mAvailableInteractionInfos.size(); ++i) { + VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i); + entries.add(info.appLabel); + values.add(info.key); + + if (info.key.contentEquals(assistKey)) { + mAvailableIndexes.add(i); + } + } + + final boolean assitIsService = !mAvailableIndexes.isEmpty(); + final int serviceCount = entries.size(); + + for (int i = 0; i < mHelper.mAvailableRecognizerInfos.size(); ++i) { + VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i); + entries.add(info.label); + values.add(info.key); + if (!assitIsService) { + mAvailableIndexes.add(serviceCount + i); + } + } + setEntries(entries.toArray(new CharSequence[entries.size()])); + setEntryValues(values.toArray(new CharSequence[values.size()])); + + if (mHelper.mCurrentVoiceInteraction != null) { + setValue(mHelper.mCurrentVoiceInteraction.flattenToShortString()); + } else if (mHelper.mCurrentRecognizer != null) { + setValue(mHelper.mCurrentRecognizer.flattenToShortString()); + } else { + setValue(null); + } + } + + private class CustomAdapter extends ArrayAdapter<CharSequence> { + + public CustomAdapter(Context context, CharSequence[] objects) { + super(context, com.android.internal.R.layout.select_dialog_singlechoice_material, + android.R.id.text1, objects); + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return mAvailableIndexes.contains(position); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = super.getView(position, convertView, parent); + view.setEnabled(isEnabled(position)); + return view; + } + } +} diff --git a/src/com/android/settings/voice/VoiceInputPreference.java b/src/com/android/settings/voice/VoiceInputPreference.java deleted file mode 100644 index 38ef0ca..0000000 --- a/src/com/android/settings/voice/VoiceInputPreference.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.voice; - -import android.app.AlertDialog; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.preference.Preference; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Checkable; -import android.widget.CompoundButton; -import android.widget.RadioButton; - - -import com.android.settings.R; -import com.android.settings.Utils; - -public final class VoiceInputPreference extends Preference { - - private static final String TAG = "VoiceInputPreference"; - - private final CharSequence mLabel; - - private final CharSequence mAppLabel; - - private final CharSequence mAlertText; - - private final ComponentName mSettingsComponent; - - /** - * The shared radio button state, which button is checked etc. - */ - private final RadioButtonGroupState mSharedState; - - /** - * When true, the change callbacks on the radio button will not - * fire. - */ - private volatile boolean mPreventRadioButtonCallbacks; - - private View mSettingsIcon; - private RadioButton mRadioButton; - - private final CompoundButton.OnCheckedChangeListener mRadioChangeListener = - new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - onRadioButtonClicked(buttonView, isChecked); - } - }; - - public VoiceInputPreference(Context context, VoiceInputHelper.BaseInfo info, - CharSequence summary, CharSequence alertText, RadioButtonGroupState state) { - super(context); - setLayoutResource(R.layout.preference_tts_engine); - - mSharedState = state; - mLabel = info.label; - mAppLabel = info.appLabel; - mAlertText = alertText; - mSettingsComponent = info.settings; - mPreventRadioButtonCallbacks = false; - - setKey(info.key); - setTitle(info.label); - setSummary(summary); - } - - @Override - public View getView(View convertView, ViewGroup parent) { - if (mSharedState == null) { - throw new IllegalStateException("Call to getView() before a call to" + - "setSharedState()"); - } - - View view = super.getView(convertView, parent); - final RadioButton rb = (RadioButton) view.findViewById(R.id.tts_engine_radiobutton); - rb.setOnCheckedChangeListener(mRadioChangeListener); - - boolean isChecked = getKey().equals(mSharedState.getCurrentKey()); - if (isChecked) { - mSharedState.setCurrentChecked(rb); - } - - mPreventRadioButtonCallbacks = true; - rb.setChecked(isChecked); - mPreventRadioButtonCallbacks = false; - - mRadioButton = rb; - - View textLayout = view.findViewById(R.id.tts_engine_pref_text); - textLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!rb.isChecked()) { - onRadioButtonClicked(rb, true); - } - } - }); - - mSettingsIcon = view.findViewById(R.id.tts_engine_settings); - mSettingsIcon.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setComponent(mSettingsComponent); - getContext().startActivity(new Intent(intent)); - } - }); - updateCheckedState(isChecked); - - return view; - } - - private boolean shouldDisplayAlert() { - return mAlertText != null; - } - - private void displayAlert( - final DialogInterface.OnClickListener positiveOnClickListener, - final DialogInterface.OnClickListener negativeOnClickListener) { - Log.i(TAG, "Displaying data alert for :" + getKey()); - - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - String msg = String.format(getContext().getResources().getConfiguration().locale, - mAlertText.toString(), mAppLabel); - builder.setTitle(android.R.string.dialog_alert_title) - .setMessage(msg) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, positiveOnClickListener) - .setNegativeButton(android.R.string.cancel, negativeOnClickListener) - .setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override public void onCancel(DialogInterface dialog) { - negativeOnClickListener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE); - } - }); - - AlertDialog dialog = builder.create(); - dialog.show(); - } - - public void doClick() { - mRadioButton.performClick(); - } - - void updateCheckedState(boolean isChecked) { - if (mSettingsComponent != null) { - mSettingsIcon.setVisibility(View.VISIBLE); - if (isChecked) { - mSettingsIcon.setEnabled(true); - mSettingsIcon.setAlpha(1); - } else { - mSettingsIcon.setEnabled(false); - mSettingsIcon.setAlpha(Utils.DISABLED_ALPHA); - } - } else { - mSettingsIcon.setVisibility(View.GONE); - } - } - - void onRadioButtonClicked(final CompoundButton buttonView, boolean isChecked) { - if (mPreventRadioButtonCallbacks) { - return; - } - if (mSharedState.getCurrentChecked() == buttonView) { - updateCheckedState(isChecked); - return; - } - - if (isChecked) { - // Should we alert user? if that's true, delay making engine current one. - if (shouldDisplayAlert()) { - displayAlert(new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - makeCurrentChecked(buttonView); - } - }, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // Undo the click. - buttonView.setChecked(false); - } - } - ); - } else { - // Privileged engine, set it current - makeCurrentChecked(buttonView); - } - } else { - updateCheckedState(isChecked); - } - } - - void makeCurrentChecked(Checkable current) { - if (mSharedState.getCurrentChecked() != null) { - mSharedState.getCurrentChecked().setChecked(false); - } - mSharedState.setCurrentChecked(current); - mSharedState.setCurrentKey(getKey()); - updateCheckedState(true); - callChangeListener(mSharedState.getCurrentKey()); - current.setChecked(true); - } - - /** - * Holds all state that is common to this group of radio buttons, such - * as the currently selected key and the currently checked compound button. - * (which corresponds to this key). - */ - public interface RadioButtonGroupState { - String getCurrentKey(); - Checkable getCurrentChecked(); - - void setCurrentKey(String key); - void setCurrentChecked(Checkable current); - } -} diff --git a/src/com/android/settings/voice/VoiceInputSettings.java b/src/com/android/settings/voice/VoiceInputSettings.java deleted file mode 100644 index bac297e..0000000 --- a/src/com/android/settings/voice/VoiceInputSettings.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.voice; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.preference.Preference; -import android.provider.Settings; -import android.service.voice.VoiceInteractionService; -import android.service.voice.VoiceInteractionServiceInfo; -import android.speech.RecognitionService; -import com.android.internal.logging.MetricsLogger; -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settings.search.Indexable; -import com.android.settings.search.SearchIndexableRaw; -import com.android.settings.voice.VoiceInputPreference.RadioButtonGroupState; - -import android.os.Bundle; -import android.preference.PreferenceCategory; -import android.widget.Checkable; - -import java.util.ArrayList; -import java.util.List; - -public class VoiceInputSettings extends SettingsPreferenceFragment implements - Preference.OnPreferenceClickListener, RadioButtonGroupState, Indexable { - - private static final String TAG = "VoiceInputSettings"; - private static final boolean DBG = false; - - /** - * Preference key for the engine selection preference. - */ - private static final String KEY_SERVICE_PREFERENCE_SECTION = - "voice_service_preference_section"; - - private PreferenceCategory mServicePreferenceCategory; - - private CharSequence mInteractorSummary; - private CharSequence mRecognizerSummary; - private CharSequence mInteractorWarning; - - /** - * The currently selected engine. - */ - private String mCurrentKey; - - /** - * The engine checkbox that is currently checked. Saves us a bit of effort - * in deducing the right one from the currently selected engine. - */ - private Checkable mCurrentChecked; - - private VoiceInputHelper mHelper; - - @Override - protected int getMetricsCategory() { - return MetricsLogger.VOICE_INPUT; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.voice_input_settings); - - mServicePreferenceCategory = (PreferenceCategory) findPreference( - KEY_SERVICE_PREFERENCE_SECTION); - - mInteractorSummary = getActivity().getText( - R.string.voice_interactor_preference_summary); - mRecognizerSummary = getActivity().getText( - R.string.voice_recognizer_preference_summary); - mInteractorWarning = getActivity().getText(R.string.voice_interaction_security_warning); - } - - @Override - public void onStart() { - super.onStart(); - initSettings(); - } - - private void initSettings() { - mHelper = new VoiceInputHelper(getActivity()); - mHelper.buildUi(); - - mServicePreferenceCategory.removeAll(); - - if (mHelper.mCurrentVoiceInteraction != null) { - mCurrentKey = mHelper.mCurrentVoiceInteraction.flattenToShortString(); - } else if (mHelper.mCurrentRecognizer != null) { - mCurrentKey = mHelper.mCurrentRecognizer.flattenToShortString(); - } else { - mCurrentKey = null; - } - - for (int i=0; i<mHelper.mAvailableInteractionInfos.size(); i++) { - VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i); - VoiceInputPreference pref = new VoiceInputPreference(getActivity(), info, - mInteractorSummary, mInteractorWarning, this); - mServicePreferenceCategory.addPreference(pref); - } - - for (int i=0; i<mHelper.mAvailableRecognizerInfos.size(); i++) { - VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i); - VoiceInputPreference pref = new VoiceInputPreference(getActivity(), info, - mRecognizerSummary, null, this); - mServicePreferenceCategory.addPreference(pref); - } - } - - @Override - public Checkable getCurrentChecked() { - return mCurrentChecked; - } - - @Override - public String getCurrentKey() { - return mCurrentKey; - } - - @Override - public void setCurrentChecked(Checkable current) { - mCurrentChecked = current; - } - - @Override - public void setCurrentKey(String key) { - mCurrentKey = key; - for (int i=0; i<mHelper.mAvailableInteractionInfos.size(); i++) { - VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i); - if (info.key.equals(key)) { - // Put the new value back into secure settings. - Settings.Secure.putString(getActivity().getContentResolver(), - Settings.Secure.VOICE_INTERACTION_SERVICE, key); - Settings.Secure.putString(getActivity().getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE, - new ComponentName(info.service.packageName, - info.serviceInfo.getRecognitionService()) - .flattenToShortString()); - return; - } - } - - for (int i=0; i<mHelper.mAvailableRecognizerInfos.size(); i++) { - VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i); - if (info.key.equals(key)) { - Settings.Secure.putString(getActivity().getContentResolver(), - Settings.Secure.VOICE_INTERACTION_SERVICE, ""); - Settings.Secure.putString(getActivity().getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE, key); - return; - } - } - } - - @Override - public boolean onPreferenceClick(Preference preference) { - if (preference instanceof VoiceInputPreference) { - ((VoiceInputPreference)preference).doClick(); - } - return true; - } - - // For Search - public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = - new BaseSearchIndexProvider() { - - @Override - public List<SearchIndexableRaw> getRawDataToIndex(Context context, - boolean enabled) { - - List<SearchIndexableRaw> indexables = new ArrayList<>(); - - final String screenTitle = context.getString(R.string.voice_input_settings_title); - - SearchIndexableRaw indexable = new SearchIndexableRaw(context); - indexable.key = "voice_service_preference_section_title"; - indexable.title = context.getString(R.string.voice_service_preference_section_title); - indexable.screenTitle = screenTitle; - indexables.add(indexable); - - final List<ResolveInfo> voiceInteractions = - context.getPackageManager().queryIntentServices( - new Intent(VoiceInteractionService.SERVICE_INTERFACE), - PackageManager.GET_META_DATA); - - final int countInteractions = voiceInteractions.size(); - for (int i = 0; i < countInteractions; i++) { - ResolveInfo info = voiceInteractions.get(i); - VoiceInteractionServiceInfo visInfo = new VoiceInteractionServiceInfo( - context.getPackageManager(), info.serviceInfo); - if (visInfo.getParseError() != null) { - continue; - } - indexables.add(getSearchIndexableRaw(context, info, screenTitle)); - } - - final List<ResolveInfo> recognitions = - context.getPackageManager().queryIntentServices( - new Intent(RecognitionService.SERVICE_INTERFACE), - PackageManager.GET_META_DATA); - - final int countRecognitions = recognitions.size(); - for (int i = 0; i < countRecognitions; i++) { - ResolveInfo info = recognitions.get(i); - indexables.add(getSearchIndexableRaw(context, info, screenTitle)); - } - - return indexables; - } - - private SearchIndexableRaw getSearchIndexableRaw(Context context, - ResolveInfo info, String screenTitle) { - - ServiceInfo serviceInfo = info.serviceInfo; - ComponentName componentName = new ComponentName(serviceInfo.packageName, - serviceInfo.name); - - SearchIndexableRaw indexable = new SearchIndexableRaw(context); - indexable.key = componentName.flattenToString(); - indexable.title = info.loadLabel(context.getPackageManager()).toString(); - indexable.screenTitle = screenTitle; - - return indexable; - } - }; -} |