Merge "Disable ring, notification and touch vibration in silent mode"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a2adeaf..10f93cb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5463,6 +5463,8 @@
     <string name="accessibility_vibration_settings_title">Vibration &amp; haptics</string>
     <!-- Summary for preference screen for configuring vibrations. [CHAR LIMIT=NONE] -->
     <string name="accessibility_vibration_settings_summary">Control the vibration strength for different usages</string>
+    <!-- Summary for vibration preference shown when it is disabled because the device is in silent mode. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_vibration_setting_disabled_for_silent_mode_summary">Setting disabled because device is set to silent</string>
     <!-- Title for the category of preferences to configure device vibrations related to calls. [CHAR LIMIT=NONE] -->
     <string name="accessibility_call_vibration_category_title">Calls</string>
     <!-- Title for the category of preferences to configure device vibrations related to notifications and alarms. [CHAR LIMIT=NONE] -->
diff --git a/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceController.java b/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceController.java
index 98fd5f2..05dc784 100644
--- a/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceController.java
+++ b/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceController.java
@@ -38,6 +38,12 @@
         }
 
         @Override
+        public boolean isRestrictedByRingerModeSilent() {
+            // Touch feedback is disabled when the phone is in silent mode.
+            return true;
+        }
+
+        @Override
         public int readIntensity() {
             final int hapticFeedbackEnabled = Settings.System.getInt(mContentResolver,
                     Settings.System.HAPTIC_FEEDBACK_ENABLED, ON);
diff --git a/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceController.java b/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceController.java
index cee45f6..951d322 100644
--- a/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceController.java
+++ b/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceController.java
@@ -32,6 +32,12 @@
             super(context, Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                     VibrationAttributes.USAGE_NOTIFICATION);
         }
+
+        @Override
+        public boolean isRestrictedByRingerModeSilent() {
+            // Notifications never vibrate when the phone is in silent mode.
+            return true;
+        }
     }
 
     public NotificationVibrationIntensityPreferenceController(Context context,
diff --git a/src/com/android/settings/accessibility/RingVibrationPreferenceConfig.java b/src/com/android/settings/accessibility/RingVibrationPreferenceConfig.java
index da446d7..f4ec747 100644
--- a/src/com/android/settings/accessibility/RingVibrationPreferenceConfig.java
+++ b/src/com/android/settings/accessibility/RingVibrationPreferenceConfig.java
@@ -36,6 +36,12 @@
     }
 
     @Override
+    public boolean isRestrictedByRingerModeSilent() {
+        // Incoming calls never vibrate when the phone is in silent mode.
+        return true;
+    }
+
+    @Override
     public int readIntensity() {
         final int vibrateWhenRinging = Settings.System.getInt(mContentResolver,
                 Settings.System.VIBRATE_WHEN_RINGING, ON);
diff --git a/src/com/android/settings/accessibility/VibrationIntensityPreferenceController.java b/src/com/android/settings/accessibility/VibrationIntensityPreferenceController.java
index b3e8168..6441eeb 100644
--- a/src/com/android/settings/accessibility/VibrationIntensityPreferenceController.java
+++ b/src/com/android/settings/accessibility/VibrationIntensityPreferenceController.java
@@ -58,12 +58,12 @@
 
     @Override
     public void onStart() {
-        mSettingsContentObserver.register(mContext.getContentResolver());
+        mSettingsContentObserver.register(mContext);
     }
 
     @Override
     public void onStop() {
-        mSettingsContentObserver.unregister(mContext.getContentResolver());
+        mSettingsContentObserver.unregister(mContext);
     }
 
     @Override
@@ -72,6 +72,7 @@
         final SeekBarPreference preference = screen.findPreference(getPreferenceKey());
         mSettingsContentObserver.onDisplayPreference(this, preference);
         preference.setEnabled(mPreferenceConfig.isPreferenceEnabled());
+        preference.setSummaryProvider(unused -> mPreferenceConfig.getSummary());
         // TODO: remove setContinuousUpdates and replace with a different way to play the haptic
         // preview without relying on the setting being propagated to the service.
         preference.setContinuousUpdates(true);
diff --git a/src/com/android/settings/accessibility/VibrationPreferenceConfig.java b/src/com/android/settings/accessibility/VibrationPreferenceConfig.java
index 1b0b163..9208b18 100644
--- a/src/com/android/settings/accessibility/VibrationPreferenceConfig.java
+++ b/src/com/android/settings/accessibility/VibrationPreferenceConfig.java
@@ -18,9 +18,13 @@
 
 import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
 
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.database.ContentObserver;
+import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.VibrationAttributes;
@@ -28,8 +32,10 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 
+import androidx.annotation.Nullable;
 import androidx.preference.Preference;
 
+import com.android.settings.R;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 /**
@@ -45,8 +51,10 @@
     public static final String MAIN_SWITCH_SETTING_KEY = Settings.System.VIBRATE_ON;
 
     protected final ContentResolver mContentResolver;
+    private final AudioManager mAudioManager;
     private final Vibrator mVibrator;
     private final String mSettingKey;
+    private final String mRingerModeSilentSummary;
     private final int mDefaultIntensity;
     private final VibrationAttributes mVibrationAttributes;
 
@@ -58,6 +66,9 @@
     public VibrationPreferenceConfig(Context context, String settingKey, int vibrationUsage) {
         mContentResolver = context.getContentResolver();
         mVibrator = context.getSystemService(Vibrator.class);
+        mAudioManager = context.getSystemService(AudioManager.class);
+        mRingerModeSilentSummary = context.getString(
+                R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary);
         mSettingKey = settingKey;
         mDefaultIntensity = mVibrator.getDefaultVibrationIntensity(vibrationUsage);
         mVibrationAttributes = new VibrationAttributes.Builder()
@@ -70,9 +81,24 @@
         return mSettingKey;
     }
 
+    /** Returns the summary string for this setting preference. */
+    @Nullable
+    public CharSequence getSummary() {
+        return isRestrictedByRingerModeSilent() && isRingerModeSilent()
+                ? mRingerModeSilentSummary : null;
+    }
+
     /** Returns true if this setting preference is enabled for user update. */
     public boolean isPreferenceEnabled() {
-        return isMainVibrationSwitchEnabled(mContentResolver);
+        return isMainVibrationSwitchEnabled(mContentResolver)
+                && (!isRestrictedByRingerModeSilent() || !isRingerModeSilent());
+    }
+
+    /**
+     * Returns true if this setting preference should be disabled when the device is in silent mode.
+     */
+    public boolean isRestrictedByRingerModeSilent() {
+        return false;
     }
 
     /** Returns the default intensity to be displayed when the setting value is not set. */
@@ -96,12 +122,23 @@
                 mVibrationAttributes);
     }
 
+    private boolean isRingerModeSilent() {
+        // AudioManager.isSilentMode() also returns true when ringer mode is VIBRATE.
+        // The vibration preferences are only disabled when the ringer mode is SILENT.
+        return mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT;
+    }
+
     /** {@link ContentObserver} for a setting described by a {@link VibrationPreferenceConfig}. */
     public static final class SettingObserver extends ContentObserver {
         private static final Uri MAIN_SWITCH_SETTING_URI =
                 Settings.System.getUriFor(MAIN_SWITCH_SETTING_KEY);
+        private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
+                new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
 
         private final Uri mUri;
+        @Nullable
+        private final BroadcastReceiver mRingerModeChangeReceiver;
+
         private AbstractPreferenceController mPreferenceController;
         private Preference mPreference;
 
@@ -109,35 +146,63 @@
         public SettingObserver(VibrationPreferenceConfig preferenceConfig) {
             super(new Handler(/* async= */ true));
             mUri = Settings.System.getUriFor(preferenceConfig.getSettingKey());
+
+            if (preferenceConfig.isRestrictedByRingerModeSilent()) {
+                // If this preference is restricted by AudioManager.getRingerModeInternal() result
+                // for the device mode, then listen to changes in that value using the broadcast
+                // intent action INTERNAL_RINGER_MODE_CHANGED_ACTION.
+                mRingerModeChangeReceiver = new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        final String action = intent.getAction();
+                        if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
+                            notifyChange();
+                        }
+                    }
+                };
+            } else {
+                // No need to register a receiver if this preference is not affected by ringer mode.
+                mRingerModeChangeReceiver = null;
+            }
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            if (mPreferenceController == null || mPreference == null) {
-                // onDisplayPreference not triggered yet, nothing to update.
-                return;
-            }
             if (mUri.equals(uri) || MAIN_SWITCH_SETTING_URI.equals(uri)) {
+                notifyChange();
+            }
+        }
+
+        private void notifyChange() {
+            if (mPreferenceController != null && mPreference != null) {
                 mPreferenceController.updateState(mPreference);
             }
         }
 
         /**
-         * Register this observer to given {@link ContentResolver}, to be called from lifecycle
+         * Register this observer to given {@link Context}, to be called from lifecycle
          * {@code onStart} method.
          */
-        public void register(ContentResolver contentResolver) {
-            contentResolver.registerContentObserver(mUri, /* notifyForDescendants= */ false, this);
-            contentResolver.registerContentObserver(MAIN_SWITCH_SETTING_URI,
-                    /* notifyForDescendants= */ false, this);
+        public void register(Context context) {
+            if (mRingerModeChangeReceiver != null) {
+                context.registerReceiver(mRingerModeChangeReceiver,
+                        INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
+            }
+            context.getContentResolver().registerContentObserver(
+                    mUri, /* notifyForDescendants= */ false, this);
+            context.getContentResolver().registerContentObserver(
+                    MAIN_SWITCH_SETTING_URI, /* notifyForDescendants= */ false, this);
         }
 
         /**
-         * Unregister this observer from given {@link ContentResolver}, to be called from lifecycle
+         * Unregister this observer from given {@link Context}, to be called from lifecycle
          * {@code onStop} method.
          */
-        public void unregister(ContentResolver contentResolver) {
-            contentResolver.unregisterContentObserver(this);
+        public void unregister(Context context) {
+            if (mRingerModeChangeReceiver != null) {
+                context.unregisterReceiver(mRingerModeChangeReceiver);
+            }
+            context.getContentResolver().unregisterContentObserver(this);
         }
 
         /**
diff --git a/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceController.java b/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceController.java
index 37a0257..8d1b43e 100644
--- a/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceController.java
+++ b/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceController.java
@@ -93,7 +93,7 @@
 
     @Override
     public void onStart() {
-        mRingSettingObserver.register(mContext.getContentResolver());
+        mRingSettingObserver.register(mContext);
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER),
                 /* notifyForDescendants= */ false,
@@ -102,7 +102,7 @@
 
     @Override
     public void onStop() {
-        mRingSettingObserver.unregister(mContext.getContentResolver());
+        mRingSettingObserver.unregister(mContext);
         mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
     }
 
diff --git a/src/com/android/settings/accessibility/VibrationTogglePreferenceController.java b/src/com/android/settings/accessibility/VibrationTogglePreferenceController.java
index 8f158cc..c60158d 100644
--- a/src/com/android/settings/accessibility/VibrationTogglePreferenceController.java
+++ b/src/com/android/settings/accessibility/VibrationTogglePreferenceController.java
@@ -45,12 +45,12 @@
 
     @Override
     public void onStart() {
-        mSettingsContentObserver.register(mContext.getContentResolver());
+        mSettingsContentObserver.register(mContext);
     }
 
     @Override
     public void onStop() {
-        mSettingsContentObserver.unregister(mContext.getContentResolver());
+        mSettingsContentObserver.unregister(mContext);
     }
 
     @Override
@@ -66,6 +66,7 @@
         super.updateState(preference);
         if (preference != null) {
             preference.setEnabled(mPreferenceConfig.isPreferenceEnabled());
+            preference.setSummary(mPreferenceConfig.getSummary());
         }
     }
 
diff --git a/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java
index 2273a3e..9d87c93 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java
@@ -18,9 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -48,6 +50,7 @@
     private static final int ON = 1;
 
     @Mock private PreferenceScreen mScreen;
+    @Mock private AudioManager mAudioManager;
 
     private Lifecycle mLifecycle;
     private Context mContext;
@@ -59,7 +62,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mLifecycle = new Lifecycle(() -> mLifecycle);
-        mContext = ApplicationProvider.getApplicationContext();
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         mVibrator = mContext.getSystemService(Vibrator.class);
         mController = new HapticFeedbackIntensityPreferenceController(mContext, PREFERENCE_KEY,
                 Vibrator.VIBRATION_INTENSITY_HIGH);
@@ -89,6 +94,54 @@
     }
 
     @Test
+    public void updateState_ringerModeUpdates_shouldPreserveSettingAndDisplaySummary() {
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+        // TODO(b/136805769): summary is broken in SeekBarPreference, enable this once fixed
+//        assertThat(mPreference.getSummary()).isNotNull();
+//        assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
+//                R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
+        assertThat(mPreference.isEnabled()).isFalse();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void updateState_hapticFeedbackDisabled_shouldDisplayAlwaysOff() {
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, OFF);
+
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+    }
+
+    @Test
     public void updateState_shouldDisplayIntensityInSliderPosition() {
         updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
         mController.updateState(mPreference);
diff --git a/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackTogglePreferenceControllerTest.java
index f992b0e..3e8aeac 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackTogglePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackTogglePreferenceControllerTest.java
@@ -18,9 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -29,6 +31,7 @@
 import androidx.preference.SwitchPreference;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
@@ -48,6 +51,7 @@
     private static final int ON = 1;
 
     @Mock private PreferenceScreen mScreen;
+    @Mock AudioManager mAudioManager;
 
     private Lifecycle mLifecycle;
     private Context mContext;
@@ -59,7 +63,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mLifecycle = new Lifecycle(() -> mLifecycle);
-        mContext = ApplicationProvider.getApplicationContext();
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         mVibrator = mContext.getSystemService(Vibrator.class);
         mController = new HapticFeedbackTogglePreferenceController(mContext, PREFERENCE_KEY);
         mLifecycle.addObserver(mController);
@@ -84,6 +90,54 @@
         assertThat(mPreference.isChecked()).isTrue();
     }
 
+
+    @Test
+    public void updateState_ringerModeUpdates_shouldPreserveSettingAndDisplaySummary() {
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isTrue();
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+        assertThat(mPreference.getSummary()).isNotNull();
+        assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
+                R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
+        assertThat(mPreference.isEnabled()).isFalse();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isTrue();
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void updateState_hapticFeedbackDisabled_shouldDisplayAlwaysOff() {
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, OFF);
+
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+
+        updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
     @Test
     public void updateState_shouldDisplayOnOffState() {
         updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
diff --git a/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java
index d0be8b5..9533c53 100644
--- a/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java
@@ -18,9 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -46,6 +48,7 @@
     private static final String PREFERENCE_KEY = "preference_key";
 
     @Mock private PreferenceScreen mScreen;
+    @Mock private AudioManager mAudioManager;
 
     private Lifecycle mLifecycle;
     private Context mContext;
@@ -57,7 +60,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mLifecycle = new Lifecycle(() -> mLifecycle);
-        mContext = ApplicationProvider.getApplicationContext();
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         mVibrator = mContext.getSystemService(Vibrator.class);
         mController = new NotificationVibrationIntensityPreferenceController(mContext,
                 PREFERENCE_KEY, Vibrator.VIBRATION_INTENSITY_HIGH);
@@ -86,6 +91,34 @@
                 mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION));
     }
 
+
+    @Test
+    public void updateState_ringerModeUpdates_shouldPreserveSettingAndDisplaySummary() {
+        updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_LOW);
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+        // TODO(b/136805769): summary is broken in SeekBarPreference, enable this once fixed
+//        assertThat(mPreference.getSummary()).isNotNull();
+//        assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
+//                R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
+        assertThat(mPreference.isEnabled()).isFalse();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
     @Test
     public void updateState_shouldDisplayIntensityInSliderPosition() {
         updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
diff --git a/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationTogglePreferenceControllerTest.java
index adf9436..4b6f686 100644
--- a/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationTogglePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationTogglePreferenceControllerTest.java
@@ -18,9 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -29,6 +31,7 @@
 import androidx.preference.SwitchPreference;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
@@ -46,6 +49,7 @@
     private static final String PREFERENCE_KEY = "preference_key";
 
     @Mock private PreferenceScreen mScreen;
+    @Mock private AudioManager mAudioManager;
 
     private Lifecycle mLifecycle;
     private Context mContext;
@@ -57,7 +61,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mLifecycle = new Lifecycle(() -> mLifecycle);
-        mContext = ApplicationProvider.getApplicationContext();
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         mVibrator = mContext.getSystemService(Vibrator.class);
         mController = new NotificationVibrationTogglePreferenceController(mContext, PREFERENCE_KEY);
         mLifecycle.addObserver(mController);
@@ -83,6 +89,32 @@
     }
 
     @Test
+    public void updateState_ringerModeUpdates_shouldPreserveSettingAndDisplaySummary() {
+        updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_LOW);
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isTrue();
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+        assertThat(mPreference.getSummary()).isNotNull();
+        assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
+                R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
+        assertThat(mPreference.isEnabled()).isFalse();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isTrue();
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
     public void updateState_shouldDisplayOnOffState() {
         updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                 Vibrator.VIBRATION_INTENSITY_HIGH);
diff --git a/tests/robotests/src/com/android/settings/accessibility/RingVibrationIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/RingVibrationIntensityPreferenceControllerTest.java
index d891926..2bffaf4 100644
--- a/tests/robotests/src/com/android/settings/accessibility/RingVibrationIntensityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/RingVibrationIntensityPreferenceControllerTest.java
@@ -18,9 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -49,6 +51,7 @@
     private static final int ON = 1;
 
     @Mock private PreferenceScreen mScreen;
+    @Mock private AudioManager mAudioManager;
 
     private Lifecycle mLifecycle;
     private Context mContext;
@@ -60,7 +63,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mLifecycle = new Lifecycle(() -> mLifecycle);
-        mContext = ApplicationProvider.getApplicationContext();
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         mVibrator = mContext.getSystemService(Vibrator.class);
         mController = new RingVibrationIntensityPreferenceController(mContext, PREFERENCE_KEY,
                 Vibrator.VIBRATION_INTENSITY_HIGH);
@@ -90,6 +95,55 @@
     }
 
     @Test
+    public void updateState_ringerModeUpdates_shouldPreserveSettingAndDisplaySummary() {
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+        // TODO(b/136805769): summary is broken in SeekBarPreference, enable this once fixed
+//        assertThat(mPreference.getSummary()).isNotNull();
+//        assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
+//                R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
+        assertThat(mPreference.isEnabled()).isFalse();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void updateState_vibrateWhenRingingAndRampingRingerOff_shouldDisplayAlwaysOff() {
+        when(mAudioManager.isRampingRingerEnabled()).thenReturn(false);
+        updateSetting(Settings.System.VIBRATE_WHEN_RINGING, OFF);
+
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+        mController.updateState(mPreference);
+        assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
+    }
+
+    @Test
     public void updateState_shouldDisplayIntensityInSliderPosition() {
         updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
         mController.updateState(mPreference);
@@ -109,7 +163,6 @@
         assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
     }
 
-
     @Test
     @Ignore
     public void setProgress_updatesIntensityAndDependentSettings() throws Exception {
diff --git a/tests/robotests/src/com/android/settings/accessibility/RingVibrationTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/RingVibrationTogglePreferenceControllerTest.java
index 8e85c86..08ad1cd 100644
--- a/tests/robotests/src/com/android/settings/accessibility/RingVibrationTogglePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/RingVibrationTogglePreferenceControllerTest.java
@@ -18,9 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -29,6 +31,7 @@
 import androidx.preference.SwitchPreference;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
@@ -48,6 +51,7 @@
     private static final int ON = 1;
 
     @Mock private PreferenceScreen mScreen;
+    @Mock private AudioManager mAudioManager;
 
     private Lifecycle mLifecycle;
     private Context mContext;
@@ -59,7 +63,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mLifecycle = new Lifecycle(() -> mLifecycle);
-        mContext = ApplicationProvider.getApplicationContext();
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         mVibrator = mContext.getSystemService(Vibrator.class);
         mController = new RingVibrationTogglePreferenceController(mContext, PREFERENCE_KEY);
         mLifecycle.addObserver(mController);
@@ -85,6 +91,54 @@
     }
 
     @Test
+    public void updateState_ringerModeUpdates_shouldPreserveSettingAndDisplaySummary() {
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isTrue();
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+        assertThat(mPreference.getSummary()).isNotNull();
+        assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
+                R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
+        assertThat(mPreference.isEnabled()).isFalse();
+
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isTrue();
+        assertThat(mPreference.getSummary()).isNull();
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void updateState_vibrateWhenRingingAndRampingRingerOff_shouldDisplayAlwaysOff() {
+        when(mAudioManager.isRampingRingerEnabled()).thenReturn(false);
+        updateSetting(Settings.System.VIBRATE_WHEN_RINGING, OFF);
+
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+
+        updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+        mController.updateState(mPreference);
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
     public void updateState_shouldDisplayOnOffState() {
         updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
         mController.updateState(mPreference);
diff --git a/tests/robotests/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceControllerTest.java
index 1f2c0d9..9e31130 100644
--- a/tests/robotests/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceControllerTest.java
@@ -70,6 +70,7 @@
         mContext = spy(ApplicationProvider.getApplicationContext());
         when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
         when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         mController = new VibrationRampingRingerTogglePreferenceController(mContext,
                 PREFERENCE_KEY, mDeviceConfigProvider);
         mLifecycle.addObserver(mController);