Merge "Muting ring volume slider disables notification" into tm-qpr-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d2871ea..ecf378d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8828,6 +8828,9 @@
     <!-- Sound: Title for the option managing notification volume. [CHAR LIMIT=30] -->
     <string name="notification_volume_option_title">Notification volume</string>
 
+    <!-- Sound: Summary for when notification volume is disabled. [CHAR LIMIT=100] -->
+    <string name="notification_volume_disabled_summary">Unavailable because ring is muted</string>
+
     <!-- Sound: Title for the option defining the phone ringtone. [CHAR LIMIT=30] -->
     <string name="ringtone_title">Phone ringtone</string>
 
diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml
index a84b0ae..7181e80 100644
--- a/res/xml/sound_settings.xml
+++ b/res/xml/sound_settings.xml
@@ -86,8 +86,8 @@
         android:icon="@drawable/ic_notifications"
         android:title="@string/notification_volume_option_title"
         android:order="-150"
-        settings:controller=
-            "com.android.settings.notification.NotificationVolumePreferenceController"/>
+        settings:controller="com.android.settings.notification.NotificationVolumePreferenceController"
+        settings:unavailableSliceSubtitle="@string/notification_volume_disabled_summary"/>
 
     <!-- Alarm volume -->
     <com.android.settings.notification.VolumeSeekBarPreference
diff --git a/src/com/android/settings/notification/NotificationVolumePreferenceController.java b/src/com/android/settings/notification/NotificationVolumePreferenceController.java
index 4fd2341..54d7854 100644
--- a/src/com/android/settings/notification/NotificationVolumePreferenceController.java
+++ b/src/com/android/settings/notification/NotificationVolumePreferenceController.java
@@ -51,7 +51,6 @@
     private final RingReceiver mReceiver = new RingReceiver();
     private final H mHandler = new H();
 
-
     public NotificationVolumePreferenceController(Context context) {
         this(context, KEY_NOTIFICATION_VOLUME);
     }
@@ -63,7 +62,9 @@
         mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;
         mSilentIconId = R.drawable.ic_notifications_off_24dp;
 
-        updateRingerMode();
+        if (updateRingerMode()) {
+            updateEnabledState();
+        }
     }
 
     /**
@@ -77,12 +78,10 @@
         if (mPreference == null) {
             setupVolPreference(screen);
         }
-        mSeparateNotification = isSeparateNotificationConfigEnabled();
-        if (mPreference != null) {
-            mPreference.setVisible(getAvailabilityStatus() == AVAILABLE);
-        }
+
         updateEffectsSuppressor();
         selectPreferenceIconState();
+        updateEnabledState();
     }
 
     /**
@@ -95,15 +94,19 @@
             boolean newVal = isSeparateNotificationConfigEnabled();
             if (newVal != mSeparateNotification) {
                 mSeparateNotification = newVal;
-                // manually hiding the preference because being unavailable does not do the job
+                // Update UI if config change happens when Sound Settings page is on the foreground
                 if (mPreference != null) {
-                    mPreference.setVisible(getAvailabilityStatus() == AVAILABLE);
+                    int status = getAvailabilityStatus();
+                    mPreference.setVisible(status == AVAILABLE
+                            || status == DISABLED_DEPENDENT_SETTING);
+                    if (status == DISABLED_DEPENDENT_SETTING) {
+                        mPreference.setEnabled(false);
+                    }
                 }
             }
         }
     }
 
-
     @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
     @Override
     public void onResume() {
@@ -126,10 +129,11 @@
     @Override
     public int getAvailabilityStatus() {
         boolean separateNotification = isSeparateNotificationConfigEnabled();
-
         return mContext.getResources().getBoolean(R.bool.config_show_notification_volume)
                 && !mHelper.isSingleVolume() && separateNotification
-                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+                ? (mRingerMode == AudioManager.RINGER_MODE_NORMAL
+                    ? AVAILABLE : DISABLED_DEPENDENT_SETTING)
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
@@ -158,7 +162,6 @@
             if (mVibrator != null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
                 mMuteIcon = mVibrateIconId;
                 mPreference.showIcon(mVibrateIconId);
-
             } else if (mRingerMode == AudioManager.RINGER_MODE_SILENT
                     || mVibrator == null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
                 mMuteIcon = mSilentIconId;
@@ -175,6 +178,12 @@
         }
     }
 
+    private void updateEnabledState() {
+        if (mPreference != null) {
+            mPreference.setEnabled(mRingerMode == AudioManager.RINGER_MODE_NORMAL);
+        }
+    }
+
     private final class H extends Handler {
         private static final int UPDATE_EFFECTS_SUPPRESSOR = 1;
         private static final int UPDATE_RINGER_MODE = 2;
@@ -191,10 +200,13 @@
                     updateEffectsSuppressor();
                     break;
                 case UPDATE_RINGER_MODE:
-                    updateRingerMode();
+                    if (updateRingerMode()) {
+                        updateEnabledState();
+                    }
                     break;
                 case NOTIFICATION_VOLUME_CHANGED:
                     selectPreferenceIconState();
+                    updateEnabledState();
                     break;
             }
         }
@@ -239,5 +251,4 @@
             }
         }
     }
-
 }
diff --git a/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java b/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java
index 255fe2f..ec619b4 100644
--- a/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java
+++ b/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java
@@ -140,11 +140,18 @@
         return valueUpdated;
     }
 
-    protected void updateRingerMode() {
+    /**
+     * Updates UI Icon in response to ringer mode changes.
+     * @return whether the ringer mode has changed.
+     */
+    protected boolean updateRingerMode() {
         final int ringerMode = mHelper.getRingerModeInternal();
-        if (mRingerMode == ringerMode) return;
+        if (mRingerMode == ringerMode) {
+            return false;
+        }
         mRingerMode = ringerMode;
         selectPreferenceIconState();
+        return true;
     }
 
     /**
diff --git a/src/com/android/settings/slices/VolumeSliceHelper.java b/src/com/android/settings/slices/VolumeSliceHelper.java
index 4861482..1ba1778 100644
--- a/src/com/android/settings/slices/VolumeSliceHelper.java
+++ b/src/com/android/settings/slices/VolumeSliceHelper.java
@@ -93,8 +93,9 @@
 
         if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
             handleVolumeChanged(context, intent);
-        } else if (AudioManager.STREAM_MUTE_CHANGED_ACTION.equals(action)
-                || AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
+        } else if (AudioManager.STREAM_MUTE_CHANGED_ACTION.equals(action)) {
+            handleMuteChanged(context, intent);
+        } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
             handleStreamChanged(context, intent);
         } else {
             notifyAllStreamsChanged(context);
@@ -109,8 +110,29 @@
         }
     }
 
+    /**
+     *  When mute is changed, notifyChange on relevant Volume Slice ContentResolvers to mark them
+     *  as needing update.
+     *
+     * In addition to the matching stream, we always notifyChange for the Notification stream
+     * when Ring events are issued. This is to make sure that Notification always gets updated
+     * for RingerMode changes, even if Notification's volume is zero and therefore it would not
+     * get its own AudioManager.VOLUME_CHANGED_ACTION.
+     */
+    private static void handleMuteChanged(Context context, Intent intent) {
+        final int inputType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+        handleStreamChanged(context, inputType);
+        if (inputType == AudioManager.STREAM_RING) {
+            handleStreamChanged(context, AudioManager.STREAM_NOTIFICATION);
+        }
+    }
+
     private static void handleStreamChanged(Context context, Intent intent) {
         final int inputType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+        handleStreamChanged(context, inputType);
+    }
+
+    private static void handleStreamChanged(Context context, int inputType) {
         synchronized (sRegisteredUri) {
             for (Map.Entry<Uri, Integer> entry : sRegisteredUri.entrySet()) {
                 if (entry.getValue() == inputType) {
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java
index 7e7ad10..594ef62 100644
--- a/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java
@@ -198,6 +198,7 @@
                 com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
         // block the alternative condition to enable controller
         when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
 
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
@@ -217,8 +218,8 @@
                 SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, Boolean.toString(true),
                 false);
 
-        assertThat(controller.getAvailabilityStatus()
-                == BasePreferenceController.AVAILABLE).isTrue();
+        assertThat(controller.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.AVAILABLE);
     }
 
     @Test
@@ -233,9 +234,10 @@
         // block the alternative condition to enable controller
         when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
 
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
-
         NotificationVolumePreferenceController controller =
                 new NotificationVolumePreferenceController(mContext);
 
@@ -254,4 +256,19 @@
                 == BasePreferenceController.UNSUPPORTED_ON_DEVICE).isTrue();
     }
 
+    @Test
+    public void ringerModeSilent_unaliased_getAvailability_returnsDisabled() {
+        when(mResources.getBoolean(
+                com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
+        when(mHelper.isSingleVolume()).thenReturn(false);
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
+
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.DISABLED_DEPENDENT_SETTING);
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/slices/VolumeSliceHelperTest.java b/tests/robotests/src/com/android/settings/slices/VolumeSliceHelperTest.java
index 2ceeb25..b4abd8c 100644
--- a/tests/robotests/src/com/android/settings/slices/VolumeSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/slices/VolumeSliceHelperTest.java
@@ -34,6 +34,7 @@
 import android.net.Uri;
 
 import com.android.settings.notification.MediaVolumePreferenceController;
+import com.android.settings.notification.NotificationVolumePreferenceController;
 import com.android.settings.notification.RingVolumePreferenceController;
 import com.android.settings.notification.SeparateRingVolumePreferenceController;
 import com.android.settings.notification.VolumeSeekBarPreferenceController;
@@ -64,6 +65,7 @@
     private VolumeSeekBarPreferenceController mMediaController;
     private VolumeSeekBarPreferenceController mRingController;
     private VolumeSeekBarPreferenceController mSeparateRingController;
+    private VolumeSeekBarPreferenceController mNotificationController;
 
     @Before
     public void setUp() {
@@ -72,8 +74,9 @@
         when(mContext.getContentResolver()).thenReturn(mResolver);
 
         mMediaController = new MediaVolumePreferenceController(mContext);
-        mSeparateRingController = new SeparateRingVolumePreferenceController(mContext);
         mRingController = new RingVolumePreferenceController(mContext);
+        mSeparateRingController = new SeparateRingVolumePreferenceController(mContext);
+        mNotificationController = new NotificationVolumePreferenceController(mContext);
 
         mIntent = createIntent(AudioManager.VOLUME_CHANGED_ACTION)
                 .putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 1)
@@ -238,6 +241,40 @@
         verify(mResolver).notifyChange(mMediaController.getSliceUri(), null);
     }
 
+    /**
+     * Without this test passing, when notification is separated from ring and its value is already
+     * zero, setting ringermode to silent would not disable notification slider.
+     * Note: the above scenario happens only in volume panel where controllers do not get to
+     * register for events such as RINGER_MODE_CHANGE.
+     */
+    @Test
+    public void onReceive_ringVolumeMuted_shouldNotifyChangeNotificationSlice() {
+        final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION)
+                .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream());
+        registerIntentToUri(mRingController);
+        registerIntentToUri(mNotificationController);
+
+        VolumeSliceHelper.onReceive(mContext, intent);
+
+        verify(mResolver).notifyChange(mNotificationController.getSliceUri(), null);
+    }
+
+    /**
+     * Notifying notification slice on ring mute does not mean it should not notify ring slice.
+     * Rather, it should notify both slices.
+     */
+    @Test
+    public void onReceive_ringVolumeMuted_shouldNotifyChangeRingSlice() {
+        final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION)
+                .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream());
+        registerIntentToUri(mRingController);
+        registerIntentToUri(mNotificationController);
+
+        VolumeSliceHelper.onReceive(mContext, intent);
+
+        verify(mResolver).notifyChange(mRingController.getSliceUri(), null);
+    }
+
     @Test
     public void onReceive_streamDevicesChanged_shouldNotifyChange() {
         final Intent intent = createIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)