Implement default assist app setting

- Add "None" support to AppListPreference
- Add DefaultAssistPreference to manage assist;
- Add AppListPreferenceWithSettings to show a settings icon;
- Implement DefaultAssistPreference based on AppListPreferenceWithSettings;
- Move voice input settings into ManageAssist and implement it
  based on AppListPreferenceWithSettings;

Bug:20210110
Change-Id: If283b8b55a46b428ecfa6e45dc2123292b1d4302
This commit is contained in:
Xiyuan Xia
2015-06-02 14:55:32 -07:00
parent 275e6f7520
commit 86a554091d
19 changed files with 629 additions and 563 deletions

View File

@@ -559,10 +559,10 @@
android:value="true" /> android:value="true" />
</activity> </activity>
<activity android:name="Settings$VoiceInputSettingsActivity" <activity android:name="Settings$ManageAssistActivity"
android:label="@string/voice_input_settings_title" android:label="@string/assist_and_voice_input_title"
android:taskAffinity="com.android.settings" android:taskAffinity="com.android.settings"
android:parentActivityName="Settings$InputMethodAndLanguageSettingsActivity"> android:parentActivityName="Settings$ManageApplicationsActivity">
<intent-filter android:priority="1"> <intent-filter android:priority="1">
<action android:name="android.settings.VOICE_INPUT_SETTINGS" /> <action android:name="android.settings.VOICE_INPUT_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
@@ -572,9 +572,9 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS" <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.voice.VoiceInputSettings" /> android:value="com.android.settings.applications.ManageAssist" />
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID" <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/language_settings" /> android:resource="@id/application_settings" />
</activity> </activity>
<activity android:name="Settings$KeyboardLayoutPickerActivity" <activity android:name="Settings$KeyboardLayoutPickerActivity"

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="48dp"
android:width="48dp"
android:tint="#db4437"
android:viewportHeight="48"
android:viewportWidth="48" >
<path android:fillColor="@android:color/white"
android:pathData="M24,4C12.95,4,4,12.95,4,24
s8.95,20,20,20,20-8.95,20-20
S35.05,4,24,4zm10,22H14v-4h20v4z"/>
</vector>

View File

@@ -6557,11 +6557,37 @@
<!-- Title for Default Apps settings [CHAR LIMIT=30] --> <!-- Title for Default Apps settings [CHAR LIMIT=30] -->
<string name="default_apps_title">Default Apps</string> <string name="default_apps_title">Default Apps</string>
<!-- Title for Assist and voice input settings [CHAR LIMIT=30] -->
<string name="assist_and_voice_input_title">Assist &amp; voice input</string>
<!-- Title for Default Assist settings [CHAR LIMIT=30] --> <!-- Title for Default Assist settings [CHAR LIMIT=30] -->
<string name="default_assist_title">Assist</string> <string name="default_assist_title">Assist app</string>
<!-- Summary for No Default Assist settings [CHAR LIMIT=45] --> <!-- Summary for No Default Assist settings [CHAR LIMIT=45] -->
<string name="default_assist_none">No default Assist</string> <string name="default_assist_none">None</string>
<!-- Title for Choose Assist dialog [CHAR LIMIT=30] -->
<string name="choose_assist_title">Choose Assist</string>
<!-- [CHAR_LIMIT=45] Title of the security warning dialog for setting an assitant -->
<string name="assistant_security_warning_title">
Make <xliff:g id="assistant_app_name">%s</xliff:g> your assistant?
</string>
<!-- [CHAR_LIMIT=NONE] Warning message about security implications of setting an assistant,
displayed as a dialog message when the user selects an assistant. -->
<string name="assistant_security_warning"><xliff:g id="assistant_app_name">%s</xliff:g> will
be able to read information about apps in use on your system, including information
visible on your screen or accessible within the apps.</string>
<!-- Label for the button to acknowledge assistant security implications. [CHAR LIMIT=30] -->
<string name="assistant_security_warning_agree">Agree</string>
<!-- Label for the button to bail out assistant security implications. [CHAR LIMIT=30] -->
<string name="assistant_security_warning_disagree">Disagree</string>
<!-- Title for Choose voice input dialog [CHAR LIMIT=30] -->
<string name="choose_voice_input_title">Choose voice input</string>
<!-- Title for Default Browser settings [CHAR LIMIT=30] --> <!-- Title for Default Browser settings [CHAR LIMIT=30] -->
<string name="default_browser_title">Browser app</string> <string name="default_browser_title">Browser app</string>
@@ -6779,4 +6805,7 @@
<!-- Instructions for state when SD card is unmounted [CHAR LIMIT=NONE] --> <!-- Instructions for state when SD card is unmounted [CHAR LIMIT=NONE] -->
<string name="sdcard_unmounted_description">The SD card is safely ejected, but still available in the SD card slot for this device.\n\nTo use this card, you have to mount it first.</string> <string name="sdcard_unmounted_description">The SD card is safely ejected, but still available in the SD card slot for this device.\n\nTo use this card, you have to mount it first.</string>
<!-- Label for None item in AppListPreference [CHAR LIMIT=40] -->
<string name="app_list_preference_none">None</string>
</resources> </resources>

View File

@@ -20,9 +20,9 @@
android:key="default_apps"> android:key="default_apps">
<Preference <Preference
android:key="default_assist" android:key="assist_and_voice_input"
android:fragment="com.android.settings.applications.ManageAssist" android:fragment="com.android.settings.applications.ManageAssist"
android:title="@string/default_assist_title" android:title="@string/assist_and_voice_input_title"
/> />
<com.android.settings.applications.DefaultBrowserPreference <com.android.settings.applications.DefaultBrowserPreference

View File

@@ -63,12 +63,6 @@
android:key="voice_category" android:key="voice_category"
android:title="@string/voice_category" > android:title="@string/voice_category" >
<PreferenceScreen
android:key="voice_input_settings"
android:title="@string/voice_input_settings_title"
android:fragment="com.android.settings.voice.VoiceInputSettings"
/>
<PreferenceScreen <PreferenceScreen
android:key="tts_settings" android:key="tts_settings"
android:title="@string/tts_settings_title" android:title="@string/tts_settings_title"

View File

@@ -19,12 +19,23 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:key="manage_assist"> android:key="manage_assist">
<com.android.settings.applications.DefaultAssistPreference
android:key="default_assist"
android:title="@string/default_assist_title"
android:summary="@string/default_assist_none"
/>
<SwitchPreference <SwitchPreference
android:key="context" android:key="context"
android:title="@string/assist_access_context_title" android:title="@string/assist_access_context_title"
android:summary="@string/assist_access_context_summary" android:summary="@string/assist_access_context_summary"
android:persistent="false"/> android:persistent="false"/>
<com.android.settings.voice.VoiceInputListPreference
android:key="voice_input_settings"
android:title="@string/voice_input_settings_title"
/>
<Preference <Preference
android:summary="@string/assist_footer" android:summary="@string/assist_footer"
android:selectable="false"/> android:selectable="false"/>

View File

@@ -35,13 +35,20 @@ import android.widget.ImageView;
import android.widget.ListAdapter; import android.widget.ListAdapter;
import android.widget.TextView; import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/** /**
* Extends ListPreference to allow us to show the icons for a given list of applications. We do this * Extends ListPreference to allow us to show the icons for a given list of applications. We do this
* because the names of applications are very similar and the user may not be able to determine what * because the names of applications are very similar and the user may not be able to determine what
* app they are selecting without an icon. * app they are selecting without an icon.
*/ */
public class AppListPreference extends ListPreference { public class AppListPreference extends ListPreference {
public static final String ITEM_NONE_VALUE = "";
private Drawable[] mEntryDrawables; private Drawable[] mEntryDrawables;
private boolean mShowItemNone = false;
public class AppArrayAdapter extends ArrayAdapter<CharSequence> { public class AppArrayAdapter extends ArrayAdapter<CharSequence> {
private Drawable[] mImageDrawables = null; private Drawable[] mImageDrawables = null;
@@ -78,38 +85,45 @@ public class AppListPreference extends ListPreference {
super(context, attrs); super(context, attrs);
} }
public void setShowItemNone(boolean showItemNone) {
mShowItemNone = showItemNone;
}
public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName) { public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName) {
// Look up all package names in PackageManager. Skip ones we can't find. // Look up all package names in PackageManager. Skip ones we can't find.
int foundPackages = 0;
PackageManager pm = getContext().getPackageManager(); PackageManager pm = getContext().getPackageManager();
ApplicationInfo[] appInfos = new ApplicationInfo[packageNames.length]; final int entryCount = packageNames.length + (mShowItemNone ? 1 : 0);
List<CharSequence> applicationNames = new ArrayList<>(entryCount);
List<CharSequence> validatedPackageNames = new ArrayList<>(entryCount);
List<Drawable> entryDrawables = new ArrayList<>(entryCount);
int selectedIndex = -1;
for (int i = 0; i < packageNames.length; i++) { for (int i = 0; i < packageNames.length; i++) {
try { try {
appInfos[i] = pm.getApplicationInfo(packageNames[i].toString(), 0); ApplicationInfo appInfo = pm.getApplicationInfo(packageNames[i].toString(), 0);
foundPackages++; applicationNames.add(appInfo.loadLabel(pm));
validatedPackageNames.add(appInfo.packageName);
entryDrawables.add(appInfo.loadIcon(pm));
if (defaultPackageName != null &&
appInfo.packageName.contentEquals(defaultPackageName)) {
selectedIndex = i;
}
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
// Leave appInfos[i] uninitialized; it will be skipped in the list. // Skip unknown packages.
} }
} }
// Show the label and icon for each application package. if (mShowItemNone) {
CharSequence[] applicationNames = new CharSequence[foundPackages]; applicationNames.add(
mEntryDrawables = new Drawable[foundPackages]; getContext().getResources().getText(R.string.app_list_preference_none));
int index = 0; validatedPackageNames.add(ITEM_NONE_VALUE);
int selectedIndex = -1; entryDrawables.add(getContext().getDrawable(R.drawable.ic_remove_circle));
for (ApplicationInfo appInfo : appInfos) {
if (appInfo != null) {
applicationNames[index] = appInfo.loadLabel(pm);
mEntryDrawables[index] = appInfo.loadIcon(pm);
if (defaultPackageName != null &&
appInfo.packageName.contentEquals(defaultPackageName)) {
selectedIndex = index;
} }
index++;
} setEntries(applicationNames.toArray(new CharSequence[applicationNames.size()]));
} setEntryValues(
setEntries(applicationNames); validatedPackageNames.toArray(new CharSequence[validatedPackageNames.size()]));
setEntryValues(packageNames); mEntryDrawables = entryDrawables.toArray(new Drawable[entryDrawables.size()]);
if (selectedIndex != -1) { if (selectedIndex != -1) {
setValueIndex(selectedIndex); setValueIndex(selectedIndex);
} else { } else {
@@ -117,25 +131,32 @@ public class AppListPreference extends ListPreference {
} }
} }
protected ListAdapter createListAdapter() {
final String selectedValue = getValue();
final boolean selectedNone = selectedValue == null ||
(mShowItemNone && selectedValue.contentEquals(ITEM_NONE_VALUE));
int selectedIndex = selectedNone ? -1 : findIndexOfValue(selectedValue);
return new AppArrayAdapter(getContext(),
R.layout.app_preference_item, getEntries(), mEntryDrawables, selectedIndex);
}
@Override @Override
protected void onPrepareDialogBuilder(Builder builder) { protected void onPrepareDialogBuilder(Builder builder) {
int selectedIndex = findIndexOfValue(getValue()); builder.setAdapter(createListAdapter(), this);
ListAdapter adapter = new AppArrayAdapter(getContext(),
R.layout.app_preference_item, getEntries(), mEntryDrawables, selectedIndex);
builder.setAdapter(adapter, this);
super.onPrepareDialogBuilder(builder); super.onPrepareDialogBuilder(builder);
} }
@Override @Override
protected Parcelable onSaveInstanceState() { protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState(); Parcelable superState = super.onSaveInstanceState();
return new SavedState(getEntryValues(), getValue(), superState); return new SavedState(getEntryValues(), getValue(), mShowItemNone, superState);
} }
@Override @Override
protected void onRestoreInstanceState(Parcelable state) { protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) { if (state instanceof SavedState) {
SavedState savedState = (SavedState) state; SavedState savedState = (SavedState) state;
mShowItemNone = savedState.showItemNone;
setPackageNames(savedState.entryValues, savedState.value); setPackageNames(savedState.entryValues, savedState.value);
super.onRestoreInstanceState(savedState.superState); super.onRestoreInstanceState(savedState.superState);
} else { } else {
@@ -147,11 +168,14 @@ public class AppListPreference extends ListPreference {
public final CharSequence[] entryValues; public final CharSequence[] entryValues;
public final CharSequence value; public final CharSequence value;
public final boolean showItemNone;
public final Parcelable superState; public final Parcelable superState;
public SavedState(CharSequence[] entryValues, CharSequence value, Parcelable superState) { public SavedState(CharSequence[] entryValues, CharSequence value, boolean showItemNone,
Parcelable superState) {
this.entryValues = entryValues; this.entryValues = entryValues;
this.value = value; this.value = value;
this.showItemNone = showItemNone;
this.superState = superState; this.superState = superState;
} }
@@ -164,6 +188,7 @@ public class AppListPreference extends ListPreference {
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequenceArray(entryValues); dest.writeCharSequenceArray(entryValues);
dest.writeCharSequence(value); dest.writeCharSequence(value);
dest.writeInt(showItemNone ? 1 : 0);
dest.writeParcelable(superState, flags); dest.writeParcelable(superState, flags);
} }
@@ -172,8 +197,9 @@ public class AppListPreference extends ListPreference {
public SavedState createFromParcel(Parcel source) { public SavedState createFromParcel(Parcel source) {
CharSequence[] entryValues = source.readCharSequenceArray(); CharSequence[] entryValues = source.readCharSequenceArray();
CharSequence value = source.readCharSequence(); CharSequence value = source.readCharSequence();
boolean showItemNone = source.readInt() != 0;
Parcelable superState = source.readParcelable(getClass().getClassLoader()); Parcelable superState = source.readParcelable(getClass().getClassLoader());
return new SavedState(entryValues, value, superState); return new SavedState(entryValues, value, showItemNone, superState);
} }
@Override @Override

View File

@@ -0,0 +1,59 @@
package com.android.settings;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* An AppListPreference with optional settings button.
*/
public class AppListPreferenceWithSettings extends AppListPreference {
private View mSettingsIcon;
private ComponentName mSettingsComponent;
public AppListPreferenceWithSettings(Context context, AttributeSet attrs) {
super(context, attrs);
setWidgetLayoutResource(R.layout.preference_widget_settings);
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
mSettingsIcon = view.findViewById(R.id.settings_button);
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));
}
});
ViewGroup container = (ViewGroup) mSettingsIcon.getParent();
container.setPaddingRelative(0, 0, 0, 0);
updateSettingsVisibility();
}
private void updateSettingsVisibility() {
if (mSettingsIcon == null) {
return;
}
if (mSettingsComponent == null) {
mSettingsIcon.setVisibility(View.GONE);
} else {
mSettingsIcon.setVisibility(View.VISIBLE);
}
}
protected void setSettingsComponent(ComponentName settings) {
mSettingsComponent = settings;
updateSettingsVisibility();
}
}

View File

@@ -41,7 +41,6 @@ public class Settings extends SettingsActivity {
public static class InputMethodAndLanguageSettingsActivity extends SettingsActivity { /* empty */ } public static class InputMethodAndLanguageSettingsActivity extends SettingsActivity { /* empty */ }
public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ } public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ }
public static class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { /* empty */ } public static class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { /* empty */ }
public static class VoiceInputSettingsActivity extends SettingsActivity { /* empty */ }
public static class SpellCheckersSettingsActivity extends SettingsActivity { /* empty */ } public static class SpellCheckersSettingsActivity extends SettingsActivity { /* empty */ }
public static class LocalePickerActivity extends SettingsActivity { /* empty */ } public static class LocalePickerActivity extends SettingsActivity { /* empty */ }
public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ } public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ }
@@ -50,6 +49,7 @@ public class Settings extends SettingsActivity {
public static class DeviceInfoSettingsActivity extends SettingsActivity { /* empty */ } public static class DeviceInfoSettingsActivity extends SettingsActivity { /* empty */ }
public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ } public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ }
public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ } public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ }
public static class ManageAssistActivity extends SettingsActivity { /* empty */ }
public static class AllApplicationsActivity extends SettingsActivity { /* empty */ } public static class AllApplicationsActivity extends SettingsActivity { /* empty */ }
public static class HighPowerApplicationsActivity extends SettingsActivity { /* empty */ } public static class HighPowerApplicationsActivity extends SettingsActivity { /* empty */ }
public static class AppOpsSummaryActivity extends SettingsActivity { public static class AppOpsSummaryActivity extends SettingsActivity {

View File

@@ -72,6 +72,7 @@ import com.android.settings.accounts.AccountSettings;
import com.android.settings.accounts.AccountSyncSettings; import com.android.settings.accounts.AccountSyncSettings;
import com.android.settings.applications.InstalledAppDetails; import com.android.settings.applications.InstalledAppDetails;
import com.android.settings.applications.ManageApplications; import com.android.settings.applications.ManageApplications;
import com.android.settings.applications.ManageAssist;
import com.android.settings.applications.ProcessStatsUi; import com.android.settings.applications.ProcessStatsUi;
import com.android.settings.applications.UsageAccessDetails; import com.android.settings.applications.UsageAccessDetails;
import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.bluetooth.BluetoothSettings;
@@ -112,7 +113,6 @@ import com.android.settings.search.Index;
import com.android.settings.sim.SimSettings; import com.android.settings.sim.SimSettings;
import com.android.settings.tts.TextToSpeechSettings; import com.android.settings.tts.TextToSpeechSettings;
import com.android.settings.users.UserSettings; import com.android.settings.users.UserSettings;
import com.android.settings.voice.VoiceInputSettings;
import com.android.settings.vpn2.VpnSettings; import com.android.settings.vpn2.VpnSettings;
import com.android.settings.wfd.WifiDisplaySettings; import com.android.settings.wfd.WifiDisplaySettings;
import com.android.settings.widget.SwitchBar; import com.android.settings.widget.SwitchBar;
@@ -290,7 +290,6 @@ public class SettingsActivity extends Activity
DateTimeSettings.class.getName(), DateTimeSettings.class.getName(),
LocalePicker.class.getName(), LocalePicker.class.getName(),
InputMethodAndLanguageSettings.class.getName(), InputMethodAndLanguageSettings.class.getName(),
VoiceInputSettings.class.getName(),
SpellCheckersSettings.class.getName(), SpellCheckersSettings.class.getName(),
UserDictionaryList.class.getName(), UserDictionaryList.class.getName(),
UserDictionarySettings.class.getName(), UserDictionarySettings.class.getName(),
@@ -298,6 +297,7 @@ public class SettingsActivity extends Activity
DisplaySettings.class.getName(), DisplaySettings.class.getName(),
DeviceInfoSettings.class.getName(), DeviceInfoSettings.class.getName(),
ManageApplications.class.getName(), ManageApplications.class.getName(),
ManageAssist.class.getName(),
ProcessStatsUi.class.getName(), ProcessStatsUi.class.getName(),
NotificationStation.class.getName(), NotificationStation.class.getName(),
LocationSettings.class.getName(), LocationSettings.class.getName(),

View File

@@ -31,7 +31,6 @@ public class VoiceInputOutputSettings {
private static final String TAG = "VoiceInputOutputSettings"; private static final String TAG = "VoiceInputOutputSettings";
private static final String KEY_VOICE_CATEGORY = "voice_category"; private static final String KEY_VOICE_CATEGORY = "voice_category";
private static final String KEY_VOICE_INPUT_SETTINGS = "voice_input_settings";
private static final String KEY_TTS_SETTINGS = "tts_settings"; private static final String KEY_TTS_SETTINGS = "tts_settings";
private PreferenceGroup mParent; private PreferenceGroup mParent;
@@ -47,19 +46,16 @@ public class VoiceInputOutputSettings {
} }
public void onCreate() { public void onCreate() {
mParent = mFragment.getPreferenceScreen(); mParent = mFragment.getPreferenceScreen();
mVoiceCategory = (PreferenceCategory) mParent.findPreference(KEY_VOICE_CATEGORY); mVoiceCategory = (PreferenceCategory) mParent.findPreference(KEY_VOICE_CATEGORY);
mVoiceInputSettingsPref = mVoiceCategory.findPreference(KEY_VOICE_INPUT_SETTINGS);
mTtsSettingsPref = mVoiceCategory.findPreference(KEY_TTS_SETTINGS); mTtsSettingsPref = mVoiceCategory.findPreference(KEY_TTS_SETTINGS);
populateOrRemovePreferences(); populateOrRemovePreferences();
} }
private void populateOrRemovePreferences() { private void populateOrRemovePreferences() {
boolean hasVoiceInputPrefs = populateOrRemoveVoiceInputPrefs();
boolean hasTtsPrefs = populateOrRemoveTtsPrefs(); boolean hasTtsPrefs = populateOrRemoveTtsPrefs();
if (!hasVoiceInputPrefs && !hasTtsPrefs) { if (!hasTtsPrefs) {
// There were no TTS settings and no recognizer settings, // There were no TTS settings and no recognizer settings,
// so it should be safe to hide the preference category // so it should be safe to hide the preference category
// entirely. // entirely.
@@ -67,16 +63,6 @@ public class VoiceInputOutputSettings {
} }
} }
private boolean populateOrRemoveVoiceInputPrefs() {
VoiceInputHelper helper = new VoiceInputHelper(mFragment.getActivity());
if (!helper.hasItems()) {
mVoiceCategory.removePreference(mVoiceInputSettingsPref);
return false;
}
return true;
}
private boolean populateOrRemoveTtsPrefs() { private boolean populateOrRemoveTtsPrefs() {
if (mTtsEngines.getEngines().isEmpty()) { if (mTtsEngines.getEngines().isEmpty()) {
mVoiceCategory.removePreference(mTtsSettingsPref); mVoiceCategory.removePreference(mTtsSettingsPref);

View File

@@ -0,0 +1,215 @@
/*
* Copyright (C) 2015 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.applications;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.provider.Settings;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
import android.speech.RecognitionService;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.android.settings.AppListPreferenceWithSettings;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
public class DefaultAssistPreference extends AppListPreferenceWithSettings {
private static final String TAG = DefaultAssistPreference.class.getSimpleName();
private final List<Info> mAvailableAssistants = new ArrayList<>();
public DefaultAssistPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setShowItemNone(true);
setDialogTitle(R.string.choose_assist_title);
}
@Override
protected boolean persistString(String value) {
final Info info = findAssistantByPackageName(value);
if (info == null) {
setAssistNone();
return true;
}
if (info.isVoiceInteractionService()) {
setAssistService(info);
} else {
setAssistActivity(info);
}
return true;
}
private void setAssistNone() {
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.ASSISTANT, ITEM_NONE_VALUE);
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, "");
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
setSummary(getContext().getText(R.string.default_assist_none));
setSettingsComponent(null);
}
private void setAssistService(Info serviceInfo) {
final String serviceComponentName = serviceInfo.component.flattenToShortString();
final String serviceRecognizerName = new ComponentName(
serviceInfo.component.getPackageName(),
serviceInfo.voiceInteractionServiceInfo.getRecognitionService())
.flattenToShortString();
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.ASSISTANT, serviceComponentName);
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName);
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName);
setSummary(getEntry());
final String settingsActivity =
serviceInfo.voiceInteractionServiceInfo.getSettingsActivity();
setSettingsComponent(settingsActivity == null ?
null :
new ComponentName(serviceInfo.component.getPackageName(), settingsActivity));
}
private void setAssistActivity(Info activityInfo) {
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.ASSISTANT, activityInfo.component.flattenToShortString());
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, "");
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
setSummary(getEntry());
setSettingsComponent(null);
}
private String getDefaultRecognizer() {
ResolveInfo resolveInfo = getContext().getPackageManager().resolveService(
new Intent(RecognitionService.SERVICE_INTERFACE),
PackageManager.GET_META_DATA);
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
Log.w(TAG, "Unable to resolve default voice recognition service.");
return "";
}
return new ComponentName(resolveInfo.serviceInfo.packageName,
resolveInfo.serviceInfo.name).flattenToShortString();
}
private Info findAssistantByPackageName(String packageName) {
for (int i = 0; i < mAvailableAssistants.size(); ++i) {
Info info = mAvailableAssistants.get(i);
if (info.component.getPackageName().equals(packageName)) {
return info;
}
}
return null;
}
private void addAssistServices() {
PackageManager pm = getContext().getPackageManager();
List<ResolveInfo> services = pm.queryIntentServices(
new Intent(VoiceInteractionService.SERVICE_INTERFACE),
PackageManager.GET_META_DATA);
for (int i = 0; i < services.size(); ++i) {
ResolveInfo resolveInfo = services.get(i);
VoiceInteractionServiceInfo voiceInteractionServiceInfo =
new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo);
if (!voiceInteractionServiceInfo.getSupportsAssist()) {
continue;
}
mAvailableAssistants.add(new Info(
new ComponentName(resolveInfo.serviceInfo.packageName,
resolveInfo.serviceInfo.name),
voiceInteractionServiceInfo));
}
}
private void addAssistActivities() {
PackageManager pm = getContext().getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(
new Intent(Intent.ACTION_ASSIST),
PackageManager.MATCH_DEFAULT_ONLY);
for (int i = 0; i < activities.size(); ++i) {
ResolveInfo resolveInfo = activities.get(i);
mAvailableAssistants.add(new Info(
new ComponentName(resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name)));
}
}
public ComponentName getCurrentAssist() {
String currentSetting = Settings.Secure.getString(getContext().getContentResolver(),
Settings.Secure.ASSISTANT);
return currentSetting == null ? null : ComponentName.unflattenFromString(currentSetting);
}
public void refreshAssistApps() {
mAvailableAssistants.clear();
addAssistServices();
addAssistActivities();
List<String> packages = new ArrayList<>();
for (int i = 0; i < mAvailableAssistants.size(); ++i) {
String packageName = mAvailableAssistants.get(i).component.getPackageName();
if (packages.contains(packageName)) {
// A service appears before an activity thus overrides it if from the same package.
continue;
}
packages.add(packageName);
}
ComponentName currentAssist = getCurrentAssist();
setPackageNames(packages.toArray(new String[packages.size()]),
currentAssist == null ? null : currentAssist.getPackageName());
}
private static class Info {
public final ComponentName component;
public final VoiceInteractionServiceInfo voiceInteractionServiceInfo;
Info(ComponentName component) {
this.component = component;
this.voiceInteractionServiceInfo = null;
}
Info(ComponentName component, VoiceInteractionServiceInfo voiceInteractionServiceInfo) {
this.component = component;
this.voiceInteractionServiceInfo = voiceInteractionServiceInfo;
}
public boolean isVoiceInteractionService() {
return voiceInteractionServiceInfo != null;
}
}
}

View File

@@ -16,6 +16,9 @@
package com.android.settings.applications; package com.android.settings.applications;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.preference.Preference; import android.preference.Preference;
import android.preference.SwitchPreference; import android.preference.SwitchPreference;
@@ -25,6 +28,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.settings.InstrumentedFragment; import com.android.settings.InstrumentedFragment;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.voice.VoiceInputListPreference;
/** /**
* Settings screen to manage everything about assist. * Settings screen to manage everything about assist.
@@ -32,18 +36,29 @@ import com.android.settings.SettingsPreferenceFragment;
public class ManageAssist extends SettingsPreferenceFragment public class ManageAssist extends SettingsPreferenceFragment
implements Preference.OnPreferenceChangeListener { implements Preference.OnPreferenceChangeListener {
private static final String KEY_DEFAULT_ASSIST = "default_assist";
private static final String KEY_CONTEXT = "context"; private static final String KEY_CONTEXT = "context";
private static final String KEY_VOICE_INPUT = "voice_input_settings";
private DefaultAssistPreference mDefaultAssitPref;
private SwitchPreference mContextPref; private SwitchPreference mContextPref;
private VoiceInputListPreference mVoiceInputPref;
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
super.onCreate(icicle); super.onCreate(icicle);
addPreferencesFromResource(R.xml.manage_assist); addPreferencesFromResource(R.xml.manage_assist);
mDefaultAssitPref = (DefaultAssistPreference) findPreference(KEY_DEFAULT_ASSIST);
mDefaultAssitPref.setOnPreferenceChangeListener(this);
mContextPref = (SwitchPreference) findPreference(KEY_CONTEXT); mContextPref = (SwitchPreference) findPreference(KEY_CONTEXT);
mContextPref.setChecked(Settings.Secure.getInt(getContentResolver(), mContextPref.setChecked(Settings.Secure.getInt(getContentResolver(),
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0); Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0);
mContextPref.setOnPreferenceChangeListener(this); mContextPref.setOnPreferenceChangeListener(this);
mVoiceInputPref = (VoiceInputListPreference) findPreference(KEY_VOICE_INPUT);
updateUi();
} }
@Override @Override
@@ -58,6 +73,64 @@ public class ManageAssist extends SettingsPreferenceFragment
(boolean) newValue ? 1 : 0); (boolean) newValue ? 1 : 0);
return true; return true;
} }
if (preference == mDefaultAssitPref) {
String newAssitPackage = (String)newValue;
if (newAssitPackage == null ||
newAssitPackage.contentEquals(DefaultAssistPreference.ITEM_NONE_VALUE)) {
setDefaultAssist(DefaultAssistPreference.ITEM_NONE_VALUE);
return false; return false;
} }
final String currentPackage = mDefaultAssitPref.getValue();
if (currentPackage == null || !newAssitPackage.contentEquals(currentPackage)) {
confirmNewAssist(newAssitPackage);
}
return false;
}
return false;
}
private void updateUi() {
mDefaultAssitPref.refreshAssistApps();
final ComponentName currentAssist = mDefaultAssitPref.getCurrentAssist();
final boolean hasAssistant = currentAssist != null;
if (hasAssistant) {
getPreferenceScreen().addPreference(mContextPref);
} else {
getPreferenceScreen().removePreference(mContextPref);
}
mVoiceInputPref.setAssistRestrict(currentAssist);
mVoiceInputPref.refreshVoiceInputs();
}
private void confirmNewAssist(final String newAssitPackage) {
final int selected = mDefaultAssitPref.findIndexOfValue(newAssitPackage);
final CharSequence appLabel = mDefaultAssitPref.getEntries()[selected];
final String title = getString(R.string.assistant_security_warning_title, appLabel);
final String message = getString(R.string.assistant_security_warning, appLabel);
final DialogInterface.OnClickListener onAgree = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
setDefaultAssist(newAssitPackage);
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(title)
.setMessage(message)
.setCancelable(true)
.setPositiveButton(R.string.assistant_security_warning_agree, onAgree)
.setNegativeButton(R.string.assistant_security_warning_disagree, null);
AlertDialog dialog = builder.create();
dialog.show();
}
private void setDefaultAssist(String assistPackage) {
mDefaultAssitPref.setValue(assistPackage);
updateUi();
}
} }

View File

@@ -790,14 +790,6 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
indexables.add(indexable); indexables.add(indexable);
} }
// Voice input
indexable = new SearchIndexableRaw(context);
indexable.key = "voice_input_settings";
indexable.title = context.getString(R.string.voice_input_settings);
indexable.screenTitle = screenTitle;
indexable.keywords = context.getString(R.string.keywords_voice_input);
indexables.add(indexable);
// Text-to-speech. // Text-to-speech.
TtsEngines ttsEngines = new TtsEngines(context); TtsEngines ttsEngines = new TtsEngines(context);
if (!ttsEngines.getEngines().isEmpty()) { if (!ttsEngines.getEngines().isEmpty()) {

View File

@@ -49,7 +49,6 @@ import com.android.settings.notification.ZenModeSettings;
import com.android.settings.print.PrintSettingsFragment; import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.sim.SimSettings; import com.android.settings.sim.SimSettings;
import com.android.settings.users.UserSettings; import com.android.settings.users.UserSettings;
import com.android.settings.voice.VoiceInputSettings;
import com.android.settings.wifi.AdvancedWifiSettings; import com.android.settings.wifi.AdvancedWifiSettings;
import com.android.settings.wifi.SavedAccessPointsWifiSettings; import com.android.settings.wifi.SavedAccessPointsWifiSettings;
import com.android.settings.wifi.WifiSettings; import com.android.settings.wifi.WifiSettings;
@@ -154,7 +153,6 @@ public final class Ranking {
// IMEs // IMEs
sRankMap.put(InputMethodAndLanguageSettings.class.getName(), RANK_IME); sRankMap.put(InputMethodAndLanguageSettings.class.getName(), RANK_IME);
sRankMap.put(VoiceInputSettings.class.getName(), RANK_IME);
// Privacy // Privacy
sRankMap.put(PrivacySettings.class.getName(), RANK_PRIVACY); sRankMap.put(PrivacySettings.class.getName(), RANK_PRIVACY);

View File

@@ -50,7 +50,6 @@ import com.android.settings.notification.ZenModeSettings;
import com.android.settings.print.PrintSettingsFragment; import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.sim.SimSettings; import com.android.settings.sim.SimSettings;
import com.android.settings.users.UserSettings; import com.android.settings.users.UserSettings;
import com.android.settings.voice.VoiceInputSettings;
import com.android.settings.wifi.AdvancedWifiSettings; import com.android.settings.wifi.AdvancedWifiSettings;
import com.android.settings.wifi.SavedAccessPointsWifiSettings; import com.android.settings.wifi.SavedAccessPointsWifiSettings;
import com.android.settings.wifi.WifiSettings; import com.android.settings.wifi.WifiSettings;
@@ -248,13 +247,6 @@ public final class SearchIndexableResources {
InputMethodAndLanguageSettings.class.getName(), InputMethodAndLanguageSettings.class.getName(),
R.drawable.ic_settings_language)); R.drawable.ic_settings_language));
sResMap.put(VoiceInputSettings.class.getName(),
new SearchIndexableResource(
Ranking.getRankForClassName(VoiceInputSettings.class.getName()),
NO_DATA_RES_ID,
VoiceInputSettings.class.getName(),
R.drawable.ic_settings_language));
sResMap.put(PrivacySettings.class.getName(), sResMap.put(PrivacySettings.class.getName(),
new SearchIndexableResource( new SearchIndexableResource(
Ranking.getRankForClassName(PrivacySettings.class.getName()), Ranking.getRankForClassName(PrivacySettings.class.getName()),

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
};
}