Merge "Import translations. DO NOT MERGE"
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 99f6664..c025a94 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3279,27 +3279,6 @@
     <!-- Used to show an amount of time in the form "m minutes, s seconds" in BatteryHistory -->
     <string name="battery_history_minutes_no_seconds"><xliff:g id="minutes">%1$d</xliff:g>m</string>
 
-    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "d days" in BatteryHistory -->
-    <string name="battery_history_days_only"><xliff:g id="days">%1$d</xliff:g>d</string>
-
-    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "d days, h hours" in BatteryHistory -->
-    <string name="battery_history_days_and_hours"><xliff:g id="days">%1$d</xliff:g>d
-        <xliff:g id="hours">%2$d</xliff:g>h</string>
-
-    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "h hours" in BatteryHistory -->
-    <string name="battery_history_hours_only"><xliff:g id="hours">%1$d</xliff:g>h</string>
-
-    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "h hours, m minutes" in BatteryHistory -->
-    <string name="battery_history_hours_and_minutes"><xliff:g id="hours">%1$d</xliff:g>h
-        <xliff:g id="minutes">%2$d</xliff:g>m</string>
-
-    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "m minutes" in BatteryHistory -->
-    <string name="battery_history_minutes_only"><xliff:g id="minutes">%1$d</xliff:g>m</string>
-
-    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "m minutes, s seconds" in BatteryHistory -->
-    <string name="battery_history_minutes_and_seconds"><xliff:g id="minutes">%1$d</xliff:g>m
-        <xliff:g id="seconds">%2$d</xliff:g>s</string>
-
     <!-- XXX remove? Strings used for displaying usage statistics -->
     <string name="usage_stats_label">Usage statistics</string>
 
@@ -3590,6 +3569,18 @@
     <string name="power_discharge_remaining"><xliff:g id="remain">%1$s</xliff:g> remaining</string>
     <!-- Display time remaining until battery is charged [CHAR_LIMIT=60] -->
     <string name="power_charge_remaining"><xliff:g id="until_charged">%1$s</xliff:g> to charge</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging -->
+    <string name="power_discharging"><xliff:g id="level">%1$d</xliff:g>%%</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+    <string name="power_discharging_duration"><xliff:g id="level">%1$d</xliff:g>%%
+            - approx. <xliff:g id="time">%2$s</xliff:g> left</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
+    <string name="power_charging"><xliff:g id="level">%1$d</xliff:g>%% -
+            <xliff:g id="state">%2$s</xliff:g></string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration"><xliff:g id="level">%1$d</xliff:g>%% -
+            <xliff:g id="state">%2$s</xliff:g>
+            (<xliff:g id="time">%3$s</xliff:g> until full)</string>
     <!-- Battery usage since unplugged -->
     <string name="battery_since_unplugged">Battery use since unplugged</string>
     <!-- Battery usage since user reset the stats -->
@@ -3833,6 +3824,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 -->
@@ -5145,6 +5147,9 @@
     <!-- [CHAR LIMIT=20] Zen mode settings: Master switch option title, on -->
     <string name="zen_mode_option_on">On</string>
 
+     <!-- [CHAR LIMIT=30] Zen mode settings: Exit condition selection dialog, default option -->
+    <string name="zen_mode_default_option">Until you turn this off</string>
+
     <!-- [CHAR LIMIT=40] Zen mode settings: General category text -->
     <string name="zen_mode_general_category">Block all interruptions except</string>
 
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/Utils.java b/src/com/android/settings/Utils.java
index 8abe30d..e3335b8 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -444,9 +444,13 @@
     }
 
     public static String getBatteryPercentage(Intent batteryChangedIntent) {
+        return String.valueOf(getBatteryLevel(batteryChangedIntent)) + "%";
+    }
+
+    public static int getBatteryLevel(Intent batteryChangedIntent) {
         int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
         int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
-        return String.valueOf(level * 100 / scale) + "%";
+        return (level * 100) / scale;
     }
 
     public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
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/fuelgauge/BatteryHistoryChart.java b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
index 58471a3..da6729c 100644
--- a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
+++ b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.os.BatteryManager;
+import android.text.format.Formatter;
 import com.android.settings.R;
 
 import android.content.Context;
@@ -413,24 +414,36 @@
         //mDurationString = getContext().getString(R.string.battery_stats_on_battery,
         //        durationString);
         mDurationString = Utils.formatElapsedTime(getContext(), mHistEnd - mHistStart, true);
-        mDrainString = com.android.settings.Utils.getBatteryPercentage(mBatteryBroadcast);
-        mChargeLabelString = com.android.settings.Utils.getBatteryStatus(getResources(),
-                mBatteryBroadcast);
-        final long drainTime = mStats.computeBatteryTimeRemaining(elapsedRealtimeUs);
-        final long chargeTime = mStats.computeChargeTimeRemaining(elapsedRealtimeUs);
+        int batteryLevel = com.android.settings.Utils.getBatteryLevel(mBatteryBroadcast);
         final int status = mBatteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
                 BatteryManager.BATTERY_STATUS_UNKNOWN);
-        if (drainTime > 0) {
-            String timeString = Utils.formatShortElapsedTime(getContext(),drainTime / 1000);
-            mChargeDurationString = getContext().getResources().getString(
-                    R.string.power_discharge_remaining, timeString);
-        } else if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
-            String timeString = Utils.formatShortElapsedTime(getContext(), chargeTime / 1000);
-            mChargeDurationString = getContext().getResources().getString(
-                    R.string.power_charge_remaining, timeString);
+        if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+            final long drainTime = mStats.computeBatteryTimeRemaining(elapsedRealtimeUs);
+            if (drainTime > 0) {
+                String timeString = Formatter.formatShortElapsedTime(getContext(),
+                        drainTime / 1000);
+                mChargeLabelString = getContext().getResources().getString(
+                        R.string.power_discharging_duration, batteryLevel, timeString);
+            } else {
+                mChargeLabelString = getContext().getResources().getString(
+                        R.string.power_discharging, batteryLevel);
+            }
         } else {
-            mChargeDurationString = "";
+            final long chargeTime = mStats.computeChargeTimeRemaining(elapsedRealtimeUs);
+            final String statusLabel = com.android.settings.Utils.getBatteryStatus(getResources(),
+                    mBatteryBroadcast);
+            if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
+                String timeString = Formatter.formatShortElapsedTime(getContext(),
+                        chargeTime / 1000);
+                mChargeLabelString = getContext().getResources().getString(
+                        R.string.power_charging_duration, batteryLevel, statusLabel, timeString);
+            } else {
+                mChargeLabelString = getContext().getResources().getString(
+                        R.string.power_charging, batteryLevel, statusLabel);
+            }
         }
+        mDrainString = "";
+        mChargeDurationString = "";
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/Utils.java b/src/com/android/settings/fuelgauge/Utils.java
index c88b0d2..9a06c9f 100644
--- a/src/com/android/settings/fuelgauge/Utils.java
+++ b/src/com/android/settings/fuelgauge/Utils.java
@@ -81,45 +81,4 @@
         }
         return sb.toString();
     }
-
-    /**
-     * Returns elapsed time for the given millis, in the following format:
-     * 2d 5h; will include at most two units, can go down to seconds precision.
-     * @param context the application context
-     * @param millis the elapsed time in milli seconds
-     * @return the formatted elapsed time
-     */
-    public static String formatShortElapsedTime(Context context, double millis) {
-        int seconds = (int) Math.floor(millis / 1000);
-
-        int days = 0, hours = 0, minutes = 0;
-        if (seconds >= SECONDS_PER_DAY) {
-            days = seconds / SECONDS_PER_DAY;
-            seconds -= days * SECONDS_PER_DAY;
-        }
-        if (seconds >= SECONDS_PER_HOUR) {
-            hours = seconds / SECONDS_PER_HOUR;
-            seconds -= hours * SECONDS_PER_HOUR;
-        }
-        if (seconds >= SECONDS_PER_MINUTE) {
-            minutes = seconds / SECONDS_PER_MINUTE;
-            seconds -= minutes * SECONDS_PER_MINUTE;
-        }
-        if (days >= 4) {
-            return context.getString(R.string.battery_history_days_only, days);
-        } else if (days > 0) {
-            return context.getString(R.string.battery_history_days_and_hours, days, hours);
-        } else if (hours >= 12) {
-            return context.getString(R.string.battery_history_hours_only, hours);
-        } else if (hours > 0) {
-            return context.getString(R.string.battery_history_hours_and_minutes, hours, minutes);
-        } else if (minutes >= 10) {
-            return context.getString(R.string.battery_history_minutes_only, minutes);
-        } else if (minutes > 0) {
-            return context.getString(R.string.battery_history_minutes_and_seconds, minutes,
-                    seconds);
-        } else {
-            return context.getString(R.string.battery_history_seconds, seconds);
-        }
-    }
 }
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();
diff --git a/src/com/android/settings/notification/ZenModeConditionSelection.java b/src/com/android/settings/notification/ZenModeConditionSelection.java
new file mode 100644
index 0000000..031d785
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeConditionSelection.java
@@ -0,0 +1,133 @@
+/*
+ * 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.notification;
+
+import android.animation.LayoutTransition;
+import android.app.INotificationManager;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.Condition;
+import android.service.notification.IConditionListener;
+import android.util.Log;
+import android.widget.CompoundButton;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+
+import com.android.settings.R;
+
+public class ZenModeConditionSelection extends RadioGroup {
+    private static final String TAG = "ZenModeConditionSelection";
+    private static final boolean DEBUG = true;
+
+    private final INotificationManager mNoMan;
+    private final H mHandler = new H();
+    private final Context mContext;
+
+    public ZenModeConditionSelection(Context context) {
+        super(context);
+        mContext = context;
+        setLayoutTransition(new LayoutTransition());
+        final int p = mContext.getResources().getDimensionPixelSize(R.dimen.content_margin_left);
+        setPadding(p, p, p, 0);
+        mNoMan = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        final RadioButton b = newRadioButton(null);
+        b.setText(R.string.zen_mode_default_option);
+        b.setChecked(true);
+    }
+
+    private RadioButton newRadioButton(Object tag) {
+        final RadioButton button = new RadioButton(mContext);
+        button.setTag(tag);
+        button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    handleSubscribe((Uri)button.getTag());
+                }
+            }
+        });
+        addView(button);
+        return button;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        requestZenModeConditions(true);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        requestZenModeConditions(false);
+    }
+
+    protected void requestZenModeConditions(boolean requested) {
+        if (DEBUG) Log.d(TAG, "requestZenModeConditions " + requested);
+        try {
+            mNoMan.requestZenModeConditions(mListener, requested);
+        } catch (RemoteException e) {
+            // noop
+        }
+    }
+
+    protected void handleConditions(Condition[] conditions) {
+        for (final Condition c : conditions) {
+            RadioButton v = (RadioButton) findViewWithTag(c.id);
+            if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_UNKNOWN) {
+                if (v == null) {
+                    v = newRadioButton(c.id);
+                }
+            }
+            if (v != null) {
+                v.setText(c.caption);
+                v.setEnabled(c.state == Condition.STATE_FALSE);
+            }
+        }
+    }
+
+    protected void handleSubscribe(Uri id) {
+        if (DEBUG) Log.d(TAG, "handleSubscribe " + id);
+        try {
+            mNoMan.setZenModeCondition(id);
+        } catch (RemoteException e) {
+            // noop
+        }
+    }
+
+    private final IConditionListener mListener = new IConditionListener.Stub() {
+        @Override
+        public void onConditionsReceived(Condition[] conditions) {
+            if (conditions == null || conditions.length == 0) return;
+            mHandler.obtainMessage(H.CONDITIONS, conditions).sendToTarget();
+        }
+    };
+
+    private final class H extends Handler {
+        private static final int CONDITIONS = 1;
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == CONDITIONS) handleConditions((Condition[])msg.obj);
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java
index 26fa539..a5c720f 100644
--- a/src/com/android/settings/notification/ZenModeSettings.java
+++ b/src/com/android/settings/notification/ZenModeSettings.java
@@ -16,12 +16,14 @@
 
 package com.android.settings.notification;
 
+import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
 import android.app.FragmentManager;
 import android.app.INotificationManager;
 import android.app.TimePickerDialog;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Typeface;
@@ -80,6 +82,7 @@
     private DropDownPreference mWhen;
     private TimePickerPreference mStart;
     private TimePickerPreference mEnd;
+    private AlertDialog mDialog;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -292,6 +295,46 @@
         }
     }
 
+    protected void putZenModeSetting(int value) {
+        Global.putInt(getContentResolver(), Global.ZEN_MODE, value);
+    }
+
+    protected ZenModeConditionSelection newConditionSelection() {
+        return new ZenModeConditionSelection(mContext);
+    }
+
+    private final Runnable mHideDialog = new Runnable() {
+        @Override
+        public void run() {
+            if (mDialog != null) {
+                mDialog.dismiss();
+                mDialog = null;
+            }
+        }
+    };
+
+    private final Runnable mShowDialog = new Runnable() {
+        @Override
+        public void run() {
+            mDialog = new AlertDialog.Builder(mContext)
+                    .setTitle(R.string.zen_mode_settings_title)
+                    .setView(newConditionSelection())
+                    .setNegativeButton(R.string.dlg_cancel, new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            putZenModeSetting(Global.ZEN_MODE_OFF);
+                        }
+                    })
+                    .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            // noop
+                        }
+                    })
+                    .show();
+        }
+    };
+
     private final OnPreferenceChangeListener mSwitchListener = new OnPreferenceChangeListener() {
         @Override
         public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -300,7 +343,8 @@
                 @Override
                 public void run() {
                     final int v = isChecked ? Global.ZEN_MODE_ON : Global.ZEN_MODE_OFF;
-                    Global.putInt(getContentResolver(), Global.ZEN_MODE, v);
+                    putZenModeSetting(v);
+                    mHandler.post(isChecked ? mShowDialog : mHideDialog);
                 }
             });
             return true;