Merge "Add zen-mode exit condition dialog to settings."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 807d011..faa20b4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1727,7 +1727,8 @@
         <activity android:name="RegulatoryInfoDisplayActivity"
                   android:label="@string/regulatory_information"
                   android:taskAffinity=""
-                  android:excludeFromRecents="true">
+                  android:excludeFromRecents="true"
+                  android:enabled="@bool/config_show_regulatory_info">
             <intent-filter>
                 <action android:name="android.settings.SHOW_REGULATORY_INFO" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a21fcf4..1eae3ae 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3833,6 +3833,17 @@
     <string name="keyboard_settings_title">Android keyboard</string>
     <!-- Title for the 'voice input' category of voice input/output settings -->
     <string name="voice_category">Speech</string>
+    <!-- Title for the voice interactor setting in voice input/output settings -->
+    <string name="voice_interactor_title">Voice input</string>
+    <!-- Title for the link to settings for the chosen voice interactor in voice input/output
+         settings -->
+    <string name="voice_interactor_settings_title">Voice input</string>
+    <!-- Summary for the link to settings for the chosen voice interactor in voice input/output
+         settings.  Would say something like, e.g., "Settings for 'Google'". -->
+    <string name="voice_interactor_settings_summary">Settings for
+        \'<xliff:g id="interactor_name">%s</xliff:g>\'</string>
+    <!-- Label to show for no voice interactor selector -->
+    <string name="no_voice_interactor">None</string>
     <!-- Title for the voice recognizer setting in voice input/output settings -->
     <string name="recognizer_title">Voice recognizer</string>
     <!-- Title for the link to settings for the chosen voice recognizer in voice input/output settings -->
diff --git a/res/xml/language_settings.xml b/res/xml/language_settings.xml
index 3e7fdb5..c210312 100644
--- a/res/xml/language_settings.xml
+++ b/res/xml/language_settings.xml
@@ -73,6 +73,16 @@
 
         <!-- entries, entryValues, and defaultValue will be populated programmatically. -->
         <ListPreference
+                android:key="voice_interactor"
+                android:title="@string/voice_interactor_title"
+                android:dialogTitle="@string/voice_interactor_title" />
+
+        <!-- An intent for this preference will be populated programmatically. -->
+        <PreferenceScreen android:key="voice_interactor_settings"
+                android:title="@string/voice_interactor_settings_title" />
+
+        <!-- entries, entryValues, and defaultValue will be populated programmatically. -->
+        <ListPreference
                 android:key="recognizer"
                 android:title="@string/recognizer_title"
                 android:dialogTitle="@string/recognizer_title" />
diff --git a/src/com/android/settings/ActiveNetworkScorerDialog.java b/src/com/android/settings/ActiveNetworkScorerDialog.java
index 66c9f3f..35d35d1 100644
--- a/src/com/android/settings/ActiveNetworkScorerDialog.java
+++ b/src/com/android/settings/ActiveNetworkScorerDialog.java
@@ -94,7 +94,7 @@
             }
             newName = pm.getApplicationInfo(mNewPackageName, 0).loadLabel(pm);
         } catch (NameNotFoundException e) {
-            Log.e(TAG, "Unable to look up package info for scorers", e);
+            Log.e(TAG, "Unable to find package info for scorers", e);
             return false;
         }
 
diff --git a/src/com/android/settings/DeviceInfoSettings.java b/src/com/android/settings/DeviceInfoSettings.java
index 7e94741..1e3b9a8 100644
--- a/src/com/android/settings/DeviceInfoSettings.java
+++ b/src/com/android/settings/DeviceInfoSettings.java
@@ -28,6 +28,7 @@
 import android.preference.Preference;
 import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
+import android.provider.Settings;
 import android.util.Log;
 import android.widget.Toast;
 
@@ -152,9 +153,14 @@
         removePreferenceIfBoolFalse(KEY_UPDATE_SETTING,
                 R.bool.config_additional_system_update_setting_enable);
 
-        // Remove regulatory information if not enabled.
-        removePreferenceIfBoolFalse(KEY_REGULATORY_INFO,
-                R.bool.config_show_regulatory_info);
+        // Remove regulatory information if none present.
+        final Intent intent = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);
+        if (getPackageManager().queryIntentActivities(intent, 0).isEmpty()) {
+            Preference pref = findPreference(KEY_REGULATORY_INFO);
+            if (pref != null) {
+                getPreferenceScreen().removePreference(pref);
+            }
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/VoiceInputOutputSettings.java b/src/com/android/settings/VoiceInputOutputSettings.java
index b499deda..64f8a09 100644
--- a/src/com/android/settings/VoiceInputOutputSettings.java
+++ b/src/com/android/settings/VoiceInputOutputSettings.java
@@ -16,6 +16,7 @@
 
 package com.android.settings;
 
+import android.Manifest;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -32,6 +33,7 @@
 import android.preference.PreferenceScreen;
 import android.preference.Preference.OnPreferenceChangeListener;
 import android.provider.Settings;
+import android.service.voice.VoiceInteractionService;
 import android.speech.RecognitionService;
 import android.speech.tts.TtsEngines;
 import android.util.AttributeSet;
@@ -53,19 +55,24 @@
     private static final String TAG = "VoiceInputOutputSettings";
 
     private static final String KEY_VOICE_CATEGORY = "voice_category";
+    private static final String KEY_VOICE_INTERACTOR = "voice_interactor";
+    private static final String KEY_VOICE_INTERACTOR_SETTINGS = "voice_interactor_settings";
     private static final String KEY_RECOGNIZER = "recognizer";
     private static final String KEY_RECOGNIZER_SETTINGS = "recognizer_settings";
     private static final String KEY_TTS_SETTINGS = "tts_settings";
 
     private PreferenceGroup mParent;
+    private ListPreference mVoiceInteractionPref;
+    private PreferenceScreen mVoiceInteractionSettingsPref;
     private PreferenceCategory mVoiceCategory;
     private ListPreference mRecognizerPref;
-    private Preference mRecognizerSettingsPref;
+    private PreferenceScreen mRecognizerSettingsPref;
     private Preference mTtsSettingsPref;
-    private PreferenceScreen mSettingsPref;
     private final SettingsPreferenceFragment mFragment;
     private final TtsEngines mTtsEngines;
 
+    private HashMap<String, ResolveInfo> mAvailableVoiceInteractionsMap;
+
     private HashMap<String, ResolveInfo> mAvailableRecognizersMap;
 
     public VoiceInputOutputSettings(SettingsPreferenceFragment fragment) {
@@ -77,22 +84,28 @@
 
         mParent = mFragment.getPreferenceScreen();
         mVoiceCategory = (PreferenceCategory) mParent.findPreference(KEY_VOICE_CATEGORY);
+        mVoiceInteractionPref = (ListPreference) mVoiceCategory.findPreference(
+                KEY_VOICE_INTERACTOR);
+        mVoiceInteractionPref.setOnPreferenceChangeListener(this);
+        mVoiceInteractionSettingsPref = (PreferenceScreen)mVoiceCategory.findPreference(
+                KEY_VOICE_INTERACTOR_SETTINGS);
         mRecognizerPref = (ListPreference) mVoiceCategory.findPreference(KEY_RECOGNIZER);
-        mRecognizerSettingsPref = mVoiceCategory.findPreference(KEY_RECOGNIZER_SETTINGS);
-        mTtsSettingsPref = mVoiceCategory.findPreference(KEY_TTS_SETTINGS);
-        mRecognizerPref.setOnPreferenceChangeListener(this);
-        mSettingsPref = (PreferenceScreen)
+        mRecognizerSettingsPref = (PreferenceScreen)
                 mVoiceCategory.findPreference(KEY_RECOGNIZER_SETTINGS);
+        mRecognizerPref.setOnPreferenceChangeListener(this);
+        mTtsSettingsPref = mVoiceCategory.findPreference(KEY_TTS_SETTINGS);
 
+        mAvailableVoiceInteractionsMap = new HashMap<String, ResolveInfo>();
         mAvailableRecognizersMap = new HashMap<String, ResolveInfo>();
 
         populateOrRemovePreferences();
     }
 
     private void populateOrRemovePreferences() {
+        boolean hasVoiceInteractionPrefs = populateOrRemoveVoiceInteractionPrefs();
         boolean hasRecognizerPrefs = populateOrRemoveRecognizerPrefs();
         boolean hasTtsPrefs = populateOrRemoveTtsPrefs();
-        if (!hasRecognizerPrefs && !hasTtsPrefs) {
+        if (!hasVoiceInteractionPrefs && !hasRecognizerPrefs && !hasTtsPrefs) {
             // There were no TTS settings and no recognizer settings,
             // so it should be safe to hide the preference category
             // entirely.
@@ -100,6 +113,32 @@
         }
     }
 
+    private boolean populateOrRemoveVoiceInteractionPrefs() {
+        List<ResolveInfo> availableVoiceServices =
+                mFragment.getPackageManager().queryIntentServices(
+                        new Intent(VoiceInteractionService.SERVICE_INTERFACE),
+                        PackageManager.GET_META_DATA);
+        for (int i=0; i<availableVoiceServices.size(); i++) {
+            ResolveInfo ri = availableVoiceServices.get(i);
+            if (!Manifest.permission.BIND_VOICE_INTERACTION.equals(ri.serviceInfo.permission)) {
+                availableVoiceServices.remove(i);
+            }
+        }
+        int numAvailable = availableVoiceServices.size();
+
+        if (numAvailable == 0) {
+            mVoiceCategory.removePreference(mVoiceInteractionPref);
+            mVoiceCategory.removePreference(mVoiceInteractionSettingsPref);
+            return false;
+        }
+
+        populateVoiceInteractionPreference(availableVoiceServices);
+
+        // In this case, there was at least one available recognizer so
+        // we populated the settings.
+        return true;
+    }
+
     private boolean populateOrRemoveRecognizerPrefs() {
         List<ResolveInfo> availableRecognitionServices =
                 mFragment.getPackageManager().queryIntentServices(
@@ -128,7 +167,7 @@
 
             String currentSetting = Settings.Secure.getString(
                     mFragment.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
-            updateSettingsLink(currentSetting);
+            updateRecognizerSettingsLink(currentSetting);
         } else {
             // Multiple recognizers available, so show the full list of choices.
             populateRecognizerPreference(availableRecognitionServices);
@@ -148,6 +187,106 @@
         return true;
     }
 
+    private void populateVoiceInteractionPreference(List<ResolveInfo> voiceInteractors) {
+        int size = voiceInteractors.size();
+        CharSequence[] entries = new CharSequence[size+1];
+        CharSequence[] values = new CharSequence[size+1];
+
+        // Get the current value from the secure setting.
+        String currentSetting = Settings.Secure.getString(
+                mFragment.getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE);
+
+        // Iterate through all the available recognizers and load up their info to show
+        // in the preference. Also build up a map of recognizer component names to their
+        // ResolveInfos - we'll need that a little later.
+        for (int i = 0; i < size; i++) {
+            ResolveInfo resolveInfo = voiceInteractors.get(i);
+            String recognizerComponent =
+                    new ComponentName(resolveInfo.serviceInfo.packageName,
+                            resolveInfo.serviceInfo.name).flattenToShortString();
+
+            mAvailableVoiceInteractionsMap.put(recognizerComponent, resolveInfo);
+
+            entries[i] = resolveInfo.loadLabel(mFragment.getPackageManager());
+            values[i] = recognizerComponent;
+        }
+
+        entries[size] = mFragment.getString(R.string.no_voice_interactor);
+        values[size] = "";
+
+        mVoiceInteractionPref.setEntries(entries);
+        mVoiceInteractionPref.setEntryValues(values);
+
+        mVoiceInteractionPref.setDefaultValue(currentSetting);
+        mVoiceInteractionPref.setValue(currentSetting);
+
+        updateVoiceInteractionSettingsLink(currentSetting);
+    }
+
+    private void updateVoiceInteractionSettingsLink(String currentSetting) {
+        ResolveInfo currentRecognizer = mAvailableVoiceInteractionsMap.get(currentSetting);
+        if (currentRecognizer == null) {
+            mVoiceInteractionPref.setSummary(mFragment.getString(R.string.no_voice_interactor));
+            mVoiceInteractionPref.setValue("");
+            return;
+        }
+
+        ServiceInfo si = currentRecognizer.serviceInfo;
+        XmlResourceParser parser = null;
+        String settingsActivity = null;
+        try {
+            parser = si.loadXmlMetaData(mFragment.getPackageManager(),
+                    VoiceInteractionService.SERVICE_META_DATA);
+            if (parser == null) {
+                throw new XmlPullParserException("No " + VoiceInteractionService.SERVICE_META_DATA +
+                        " meta-data for " + si.packageName);
+            }
+
+            Resources res = mFragment.getPackageManager().getResourcesForApplication(
+                    si.applicationInfo);
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+
+            String nodeName = parser.getName();
+            if (!"voice-interaction-service".equals(nodeName)) {
+                throw new XmlPullParserException(
+                        "Meta-data does not start with voice-interaction-service tag");
+            }
+
+            TypedArray array = res.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.VoiceInteractionService);
+            settingsActivity = array.getString(
+                    com.android.internal.R.styleable.VoiceInteractionService_settingsActivity);
+            array.recycle();
+        } catch (XmlPullParserException e) {
+            Log.e(TAG, "error parsing recognition service meta-data", e);
+        } catch (IOException e) {
+            Log.e(TAG, "error parsing recognition service meta-data", e);
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "error parsing recognition service meta-data", e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+
+        mVoiceInteractionPref.setSummary(currentRecognizer.loadLabel(
+                mFragment.getPackageManager()));
+        mVoiceInteractionPref.setValue(currentSetting);
+
+        if (settingsActivity == null) {
+            // No settings preference available - hide the preference.
+            Log.w(TAG, "no recognizer settings available for " + si.packageName);
+        } else {
+            Intent i = new Intent(Intent.ACTION_MAIN);
+            i.setComponent(new ComponentName(si.packageName, settingsActivity));
+            mVoiceInteractionSettingsPref.setIntent(i);
+        }
+    }
+
     private void populateRecognizerPreference(List<ResolveInfo> recognizers) {
         int size = recognizers.size();
         CharSequence[] entries = new CharSequence[size];
@@ -178,10 +317,10 @@
         mRecognizerPref.setDefaultValue(currentSetting);
         mRecognizerPref.setValue(currentSetting);
         
-        updateSettingsLink(currentSetting);
+        updateRecognizerSettingsLink(currentSetting);
     }
-    
-    private void updateSettingsLink(String currentSetting) {
+
+    private void updateRecognizerSettingsLink(String currentSetting) {
         ResolveInfo currentRecognizer = mAvailableRecognizersMap.get(currentSetting);
         if (currentRecognizer == null) return;
 
@@ -230,18 +369,29 @@
         if (settingsActivity == null) {
             // No settings preference available - hide the preference.
             Log.w(TAG, "no recognizer settings available for " + si.packageName);
-            mSettingsPref.setIntent(null);
-            mVoiceCategory.removePreference(mSettingsPref);
+            mRecognizerSettingsPref.setIntent(null);
+            mVoiceCategory.removePreference(mRecognizerSettingsPref);
         } else {
             Intent i = new Intent(Intent.ACTION_MAIN);
             i.setComponent(new ComponentName(si.packageName, settingsActivity));
-            mSettingsPref.setIntent(i);
+            mRecognizerSettingsPref.setIntent(i);
             mRecognizerPref.setSummary(currentRecognizer.loadLabel(mFragment.getPackageManager()));
         }
     }
-    
+
     public boolean onPreferenceChange(Preference preference, Object newValue) {
-        if (preference == mRecognizerPref) {
+        if (preference == mVoiceInteractionPref) {
+            String setting = (String) newValue;
+
+            // Put the new value back into secure settings.
+            Settings.Secure.putString(mFragment.getContentResolver(),
+                    Settings.Secure.VOICE_INTERACTION_SERVICE,
+                    setting);
+
+            // Update the settings item so it points to the right settings.
+            updateVoiceInteractionSettingsLink(setting);
+
+        } else  if (preference == mRecognizerPref) {
             String setting = (String) newValue;
 
             // Put the new value back into secure settings.
@@ -250,7 +400,7 @@
                     setting);
 
             // Update the settings item so it points to the right settings.
-            updateSettingsLink(setting);
+            updateRecognizerSettingsLink(setting);
         }
         return true;
     }
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index 172557d..c63ae63 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -114,6 +114,12 @@
     }
 
     @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mListViewState = null;  // you're dead to me
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
         loadAppsList();