Add items in Other Sound directly into Sound settings.

- In new IA, Other Sound no longer is a separate preference screen, but
individual preferences are put directly under SoundSettings.

- Add controllers for each other sounds preference.

- Remove Cast settings from Soundsettings in new IA.

Change-Id: I75d771674ffabfecbd66079bc86844b2ff098b71
Fix: 33944294
Test: make RunSettingsRoboTests
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 73c7e3b..f302701 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6161,6 +6161,11 @@
     <!-- Work Sound: Message for dialog shown when enabling sync with personal sounds. [CHAR LIMIT=none] -->
     <string name="work_sync_dialog_message">Your current work profile sounds will be replaced with your personal profile sounds</string>
 
+    <!-- Ringtones preference category: Title for the Ringotnes preference categories. [CHAR LIMIT=none] -->
+    <string name="ringtones_category_preference_title">Ringtones</string>
+
+    <!-- Other sounds and vibrations preference category: Title for the Other sounds and vibrations preference categories. [CHAR LIMIT=none] -->
+    <string name="other_sound_category_preference_title">Other sounds and vibrations</string>
 
     <!-- Configure Notifications Settings title. [CHAR LIMIT=30] -->
     <string name="configure_notification_settings">Notification preferences</string>
diff --git a/res/xml/ia_sound_settings.xml b/res/xml/ia_sound_settings.xml
new file mode 100644
index 0000000..e63db0d
--- /dev/null
+++ b/res/xml/ia_sound_settings.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+                  xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+          android:title="@string/sound_settings"
+          android:key="sound_settings"
+          settings:keywords="@string/keywords_sounds">
+
+        <!-- Media volume -->
+        <com.android.settings.notification.VolumeSeekBarPreference
+                android:key="media_volume"
+                android:icon="@*android:drawable/ic_audio_media"
+                android:title="@string/media_volume_option_title" />
+
+        <!-- Alarm volume -->
+        <com.android.settings.notification.VolumeSeekBarPreference
+                android:key="alarm_volume"
+                android:icon="@*android:drawable/ic_audio_alarm"
+                android:title="@string/alarm_volume_option_title" />
+
+        <!-- Ring volume -->
+        <com.android.settings.notification.VolumeSeekBarPreference
+                android:key="ring_volume"
+                android:icon="@*android:drawable/ic_audio_ring_notif"
+                android:title="@string/ring_volume_option_title" />
+
+        <!-- Notification volume -->
+        <com.android.settings.notification.VolumeSeekBarPreference
+                android:key="notification_volume"
+                android:icon="@*android:drawable/ic_audio_ring_notif"
+                android:title="@string/notification_volume_option_title" />
+
+        <!-- Also vibrate for calls -->
+        <SwitchPreference
+                android:key="vibrate_when_ringing"
+                android:title="@string/vibrate_when_ringing_title" />
+
+
+        <!-- Interruptions -->
+        <com.android.settingslib.RestrictedPreference
+                android:key="zen_mode"
+                android:title="@string/zen_mode_settings_title"
+                settings:useAdminDisabledSummary="true"
+                settings:keywords="@string/keywords_sounds_and_notifications_interruptions"
+                android:fragment="com.android.settings.notification.ZenModeSettings" />
+
+        <PreferenceCategory
+          android:key="ringtones_preferecence_category"
+          android:title="@string/ringtones_category_preference_title" />
+
+        <!-- Phone ringtone -->
+        <com.android.settings.DefaultRingtonePreference
+                android:key="ringtone"
+                android:title="@string/ringtone_title"
+                android:dialogTitle="@string/ringtone_title"
+                android:summary="@string/ringtone_summary"
+                android:ringtoneType="ringtone" />
+
+        <!-- Default notification ringtone -->
+        <com.android.settings.DefaultRingtonePreference
+                android:key="notification_ringtone"
+                android:title="@string/notification_ringtone_title"
+                android:dialogTitle="@string/notification_ringtone_title"
+                android:summary="@string/ringtone_summary"
+                android:ringtoneType="notification" />
+
+        <!-- Default alarm ringtone -->
+        <com.android.settings.DefaultRingtonePreference
+                android:key="alarm_ringtone"
+                android:title="@string/alarm_ringtone_title"
+                android:dialogTitle="@string/alarm_ringtone_title"
+                android:summary="@string/ringtone_summary"
+                android:persistent="false"
+                android:ringtoneType="alarm" />
+
+        <!-- Other sounds -->
+        <PreferenceCategory
+          android:key="other_sound_preferecence_category"
+          android:title="@string/other_sound_category_preference_title" />
+
+        <!-- Dial pad tones -->
+        <SwitchPreference
+          android:key="dial_pad_tones"
+          android:title="@string/dial_pad_tones_title" />
+
+        <!-- Screen locking sounds -->
+        <SwitchPreference
+          android:key="screen_locking_sounds"
+          android:title="@string/screen_locking_sounds_title" />
+
+        <!-- Charging sounds -->
+        <SwitchPreference
+          android:key="charging_sounds"
+          android:title="@string/charging_sounds_title" />
+
+        <!-- Docking sounds -->
+        <SwitchPreference
+          android:key="docking_sounds"
+          android:title="@string/docking_sounds_title" />
+
+        <!-- Touch sounds -->
+        <SwitchPreference
+          android:key="touch_sounds"
+          android:title="@string/touch_sounds_title" />
+
+        <!-- Vibrate on touch -->
+        <SwitchPreference
+          android:key="vibrate_on_touch"
+          android:title="@string/vibrate_on_touch_title" />
+
+        <!-- Dock speaker plays -->
+        <DropDownPreference
+          android:key="dock_audio_media"
+          android:title="@string/dock_audio_media_title"
+          android:summary="%s" />
+
+        <!-- Boot sounds -->
+        <SwitchPreference
+          android:key="boot_sounds"
+          android:title="@string/boot_sounds_title" />
+
+        <!-- Emergency tone -->
+        <DropDownPreference
+          android:key="emergency_tone"
+          android:title="@string/emergency_tone_title"
+          android:summary="%s" />
+
+        <com.android.settingslib.RestrictedPreference
+          android:key="cell_broadcast_settings"
+          android:title="@string/cell_broadcast_settings"
+          settings:useAdminDisabledSummary="true">
+                <intent
+                  android:action="android.intent.action.MAIN"
+                  android:targetPackage="com.android.cellbroadcastreceiver"
+                  android:targetClass="com.android.cellbroadcastreceiver.CellBroadcastSettings" />
+        </com.android.settingslib.RestrictedPreference>
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/notification/BootSoundPreferenceController.java b/src/com/android/settings/notification/BootSoundPreferenceController.java
new file mode 100644
index 0000000..b644ee9
--- /dev/null
+++ b/src/com/android/settings/notification/BootSoundPreferenceController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.content.Context;
+import android.os.SystemProperties;
+import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import com.android.settings.core.PreferenceController;
+
+public class BootSoundPreferenceController extends PreferenceController {
+
+    // Boot Sounds needs to be a system property so it can be accessed during boot.
+    private static final String KEY_BOOT_SOUNDS = "boot_sounds";
+    @VisibleForTesting
+    static final String PROPERTY_BOOT_SOUNDS = "persist.sys.bootanim.play_sound";
+
+    public BootSoundPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        if (isAvailable()) {
+            SwitchPreference preference = (SwitchPreference) screen.findPreference(KEY_BOOT_SOUNDS);
+            preference.setChecked(SystemProperties.getBoolean(PROPERTY_BOOT_SOUNDS, true));
+        }
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (KEY_BOOT_SOUNDS.equals(preference.getKey())) {
+            SwitchPreference switchPreference = (SwitchPreference) preference;
+            SystemProperties.set(PROPERTY_BOOT_SOUNDS, switchPreference.isChecked() ? "1" : "0");
+        }
+        return false;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BOOT_SOUNDS;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mContext.getResources().getBoolean(com.android.settings.R.bool.has_boot_sounds);
+    }
+
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/ChargingSoundPreferenceController.java b/src/com/android/settings/notification/ChargingSoundPreferenceController.java
new file mode 100644
index 0000000..1114b4a
--- /dev/null
+++ b/src/com/android/settings/notification/ChargingSoundPreferenceController.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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 static com.android.settings.notification.SettingPref.TYPE_GLOBAL;
+
+import android.content.Context;
+
+import android.provider.Settings.Global;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+public class ChargingSoundPreferenceController extends SettingPrefController {
+
+    private static final String KEY_CHARGING_SOUNDS = "charging_sounds";
+
+    public ChargingSoundPreferenceController(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+        super(context, parent, lifecycle);
+        mPreference = new SettingPref(
+            TYPE_GLOBAL, KEY_CHARGING_SOUNDS, Global.CHARGING_SOUNDS_ENABLED, DEFAULT_ON);
+
+    }
+
+}
diff --git a/src/com/android/settings/notification/DialPadTonePreferenceController.java b/src/com/android/settings/notification/DialPadTonePreferenceController.java
new file mode 100644
index 0000000..08e1a7d
--- /dev/null
+++ b/src/com/android/settings/notification/DialPadTonePreferenceController.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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 static com.android.settings.notification.SettingPref.TYPE_SYSTEM;
+
+import android.content.Context;
+
+import android.provider.Settings.System;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+public class DialPadTonePreferenceController extends SettingPrefController {
+
+    private static final String KEY_DIAL_PAD_TONES = "dial_pad_tones";
+
+    public DialPadTonePreferenceController(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+        super(context, parent, lifecycle);
+        mPreference = new SettingPref(
+            TYPE_SYSTEM, KEY_DIAL_PAD_TONES, System.DTMF_TONE_WHEN_DIALING, DEFAULT_ON) {
+            @Override
+            public boolean isApplicable(Context context) {
+                return Utils.isVoiceCapable(context);
+            }
+        };
+    }
+
+}
diff --git a/src/com/android/settings/notification/DockAudioMediaPreferenceController.java b/src/com/android/settings/notification/DockAudioMediaPreferenceController.java
new file mode 100644
index 0000000..20c20b4
--- /dev/null
+++ b/src/com/android/settings/notification/DockAudioMediaPreferenceController.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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 static com.android.settings.notification.SettingPref.TYPE_GLOBAL;
+
+import android.content.Context;
+
+import android.content.res.Resources;
+import android.provider.Settings.Global;
+import android.telephony.TelephonyManager;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+public class DockAudioMediaPreferenceController extends SettingPrefController {
+
+    private static final String KEY_DOCK_AUDIO_MEDIA = "dock_audio_media";
+
+    private static final int DOCK_AUDIO_MEDIA_DISABLED = 0;
+    private static final int DOCK_AUDIO_MEDIA_ENABLED = 1;
+    private static final int DEFAULT_DOCK_AUDIO_MEDIA = DOCK_AUDIO_MEDIA_DISABLED;
+
+    public DockAudioMediaPreferenceController(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+        super(context, parent, lifecycle);
+        mPreference = new SettingPref(
+            TYPE_GLOBAL, KEY_DOCK_AUDIO_MEDIA, Global.DOCK_AUDIO_MEDIA_ENABLED,
+            DEFAULT_DOCK_AUDIO_MEDIA, DOCK_AUDIO_MEDIA_DISABLED, DOCK_AUDIO_MEDIA_ENABLED) {
+            @Override
+            public boolean isApplicable(Context context) {
+                return context.getResources().getBoolean(
+                    com.android.settings.R.bool.has_dock_settings);
+            }
+
+            @Override
+            protected String getCaption(Resources res, int value) {
+                switch(value) {
+                    case DOCK_AUDIO_MEDIA_DISABLED:
+                        return res.getString(
+                            com.android.settings.R.string.dock_audio_media_disabled);
+                    case DOCK_AUDIO_MEDIA_ENABLED:
+                        return res.getString(
+                            com.android.settings.R.string.dock_audio_media_enabled);
+                    default:
+                        throw new IllegalArgumentException();
+                }
+            }
+        };
+    }
+}
diff --git a/src/com/android/settings/notification/DockingSoundPreferenceController.java b/src/com/android/settings/notification/DockingSoundPreferenceController.java
new file mode 100644
index 0000000..ee277f0
--- /dev/null
+++ b/src/com/android/settings/notification/DockingSoundPreferenceController.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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 static com.android.settings.notification.SettingPref.TYPE_GLOBAL;
+
+import android.content.Context;
+import android.provider.Settings.Global;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+public class DockingSoundPreferenceController extends SettingPrefController {
+
+    private static final String KEY_DOCKING_SOUNDS = "docking_sounds";
+
+    public DockingSoundPreferenceController(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+        super(context, parent, lifecycle);
+        mPreference = new SettingPref(
+            TYPE_GLOBAL, KEY_DOCKING_SOUNDS, Global.DOCK_SOUNDS_ENABLED, DEFAULT_ON) {
+            @Override
+            public boolean isApplicable(Context context) {
+                return context.getResources().getBoolean(R.bool.has_dock_settings);
+            }
+        };
+    }
+
+}
diff --git a/src/com/android/settings/notification/EmergencyTonePreferenceController.java b/src/com/android/settings/notification/EmergencyTonePreferenceController.java
new file mode 100644
index 0000000..bc21f44
--- /dev/null
+++ b/src/com/android/settings/notification/EmergencyTonePreferenceController.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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 static com.android.settings.notification.SettingPref.TYPE_GLOBAL;
+
+import android.content.Context;
+
+import android.content.res.Resources;
+import android.provider.Settings.Global;
+import android.telephony.TelephonyManager;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+public class EmergencyTonePreferenceController extends SettingPrefController {
+
+    private static final String KEY_EMERGENCY_TONE = "emergency_tone";
+    private static final int EMERGENCY_TONE_SILENT = 0;
+    private static final int EMERGENCY_TONE_ALERT = 1;
+    private static final int EMERGENCY_TONE_VIBRATE = 2;
+    private static final int DEFAULT_EMERGENCY_TONE = EMERGENCY_TONE_SILENT;
+
+    public EmergencyTonePreferenceController(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+        super(context, parent, lifecycle);
+        mPreference = new SettingPref(
+            TYPE_GLOBAL, KEY_EMERGENCY_TONE, Global.EMERGENCY_TONE, DEFAULT_EMERGENCY_TONE,
+            EMERGENCY_TONE_ALERT, EMERGENCY_TONE_VIBRATE, EMERGENCY_TONE_SILENT) {
+
+            @Override
+            public boolean isApplicable(Context context) {
+                final TelephonyManager telephony =
+                    (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+                return telephony != null
+                    && telephony.getCurrentPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
+            }
+
+            @Override
+            protected String getCaption(Resources res, int value) {
+                switch(value) {
+                    case EMERGENCY_TONE_SILENT:
+                        return res.getString(R.string.emergency_tone_silent);
+                    case EMERGENCY_TONE_ALERT:
+                        return res.getString(R.string.emergency_tone_alert);
+                    case EMERGENCY_TONE_VIBRATE:
+                        return res.getString(R.string.emergency_tone_vibrate);
+                    default:
+                        throw new IllegalArgumentException();
+                }
+            }
+        };
+    }
+
+}
diff --git a/src/com/android/settings/notification/OtherSoundSettings.java b/src/com/android/settings/notification/OtherSoundSettings.java
index cf47cb5..15e7f8c 100644
--- a/src/com/android/settings/notification/OtherSoundSettings.java
+++ b/src/com/android/settings/notification/OtherSoundSettings.java
@@ -16,177 +16,26 @@
 
 package com.android.settings.notification;
 
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.media.AudioManager;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.SystemProperties;
-import android.os.Vibrator;
 import android.provider.SearchIndexableResource;
-import android.provider.Settings.Global;
-import android.provider.Settings.System;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.Preference;
-import android.telephony.TelephonyManager;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.Utils;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.search.Indexable;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import static com.android.settings.notification.SettingPref.TYPE_GLOBAL;
-import static com.android.settings.notification.SettingPref.TYPE_SYSTEM;
-
-public class OtherSoundSettings extends SettingsPreferenceFragment implements Indexable {
+/* This class has been deprecated  Modifications to Other Sounds settings should be made in
+   {@link SoundSettings } instead. */
+@Deprecated
+public class OtherSoundSettings extends DashboardFragment {
     private static final String TAG = "OtherSoundSettings";
 
-    private static final int DEFAULT_ON = 1;
-
-    private static final int EMERGENCY_TONE_SILENT = 0;
-    private static final int EMERGENCY_TONE_ALERT = 1;
-    private static final int EMERGENCY_TONE_VIBRATE = 2;
-    private static final int DEFAULT_EMERGENCY_TONE = EMERGENCY_TONE_SILENT;
-
-    private static final int DOCK_AUDIO_MEDIA_DISABLED = 0;
-    private static final int DOCK_AUDIO_MEDIA_ENABLED = 1;
-    private static final int DEFAULT_DOCK_AUDIO_MEDIA = DOCK_AUDIO_MEDIA_DISABLED;
-
-    private static final String KEY_DIAL_PAD_TONES = "dial_pad_tones";
-    private static final String KEY_SCREEN_LOCKING_SOUNDS = "screen_locking_sounds";
-    private static final String KEY_CHARGING_SOUNDS = "charging_sounds";
-    private static final String KEY_DOCKING_SOUNDS = "docking_sounds";
-    private static final String KEY_TOUCH_SOUNDS = "touch_sounds";
-    private static final String KEY_VIBRATE_ON_TOUCH = "vibrate_on_touch";
-    private static final String KEY_DOCK_AUDIO_MEDIA = "dock_audio_media";
-    private static final String KEY_EMERGENCY_TONE = "emergency_tone";
-
-    // Boot Sounds needs to be a system property so it can be accessed during boot.
-    private static final String KEY_BOOT_SOUNDS = "boot_sounds";
-    private static final String PROPERTY_BOOT_SOUNDS = "persist.sys.bootanim.play_sound";
-
-    private static final SettingPref PREF_DIAL_PAD_TONES = new SettingPref(
-            TYPE_SYSTEM, KEY_DIAL_PAD_TONES, System.DTMF_TONE_WHEN_DIALING, DEFAULT_ON) {
-        @Override
-        public boolean isApplicable(Context context) {
-            return Utils.isVoiceCapable(context);
-        }
-    };
-
-    private static final SettingPref PREF_SCREEN_LOCKING_SOUNDS = new SettingPref(
-            TYPE_SYSTEM, KEY_SCREEN_LOCKING_SOUNDS, System.LOCKSCREEN_SOUNDS_ENABLED, DEFAULT_ON);
-
-    private static final SettingPref PREF_CHARGING_SOUNDS = new SettingPref(
-            TYPE_GLOBAL, KEY_CHARGING_SOUNDS, Global.CHARGING_SOUNDS_ENABLED, DEFAULT_ON);
-
-    private static final SettingPref PREF_DOCKING_SOUNDS = new SettingPref(
-            TYPE_GLOBAL, KEY_DOCKING_SOUNDS, Global.DOCK_SOUNDS_ENABLED, DEFAULT_ON) {
-        @Override
-        public boolean isApplicable(Context context) {
-            return hasDockSettings(context);
-        }
-    };
-
-    private static final SettingPref PREF_TOUCH_SOUNDS = new SettingPref(
-            TYPE_SYSTEM, KEY_TOUCH_SOUNDS, System.SOUND_EFFECTS_ENABLED, DEFAULT_ON) {
-        @Override
-        protected boolean setSetting(final Context context, final int value) {
-            AsyncTask.execute(new Runnable() {
-                @Override
-                public void run() {
-                    final AudioManager am =
-                            (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-                    if (value != 0) {
-                        am.loadSoundEffects();
-                    } else {
-                        am.unloadSoundEffects();
-                    }
-                }
-            });
-            return super.setSetting(context, value);
-        }
-    };
-
-    private static final SettingPref PREF_VIBRATE_ON_TOUCH = new SettingPref(
-            TYPE_SYSTEM, KEY_VIBRATE_ON_TOUCH, System.HAPTIC_FEEDBACK_ENABLED, DEFAULT_ON) {
-        @Override
-        public boolean isApplicable(Context context) {
-            return hasHaptic(context);
-        }
-    };
-
-    private static final SettingPref PREF_DOCK_AUDIO_MEDIA = new SettingPref(
-            TYPE_GLOBAL, KEY_DOCK_AUDIO_MEDIA, Global.DOCK_AUDIO_MEDIA_ENABLED,
-            DEFAULT_DOCK_AUDIO_MEDIA, DOCK_AUDIO_MEDIA_DISABLED, DOCK_AUDIO_MEDIA_ENABLED) {
-        @Override
-        public boolean isApplicable(Context context) {
-            return hasDockSettings(context);
-        }
-
-        @Override
-        protected String getCaption(Resources res, int value) {
-            switch(value) {
-                case DOCK_AUDIO_MEDIA_DISABLED:
-                    return res.getString(R.string.dock_audio_media_disabled);
-                case DOCK_AUDIO_MEDIA_ENABLED:
-                    return res.getString(R.string.dock_audio_media_enabled);
-                default:
-                    throw new IllegalArgumentException();
-            }
-        }
-    };
-
-    private static final SettingPref PREF_EMERGENCY_TONE = new SettingPref(
-            TYPE_GLOBAL, KEY_EMERGENCY_TONE, Global.EMERGENCY_TONE, DEFAULT_EMERGENCY_TONE,
-            EMERGENCY_TONE_ALERT, EMERGENCY_TONE_VIBRATE, EMERGENCY_TONE_SILENT) {
-        @Override
-        public boolean isApplicable(Context context) {
-            final int activePhoneType = TelephonyManager.getDefault().getCurrentPhoneType();
-            return activePhoneType == TelephonyManager.PHONE_TYPE_CDMA;
-        }
-
-        @Override
-        protected String getCaption(Resources res, int value) {
-            switch(value) {
-                case EMERGENCY_TONE_SILENT:
-                    return res.getString(R.string.emergency_tone_silent);
-                case EMERGENCY_TONE_ALERT:
-                    return res.getString(R.string.emergency_tone_alert);
-                case EMERGENCY_TONE_VIBRATE:
-                    return res.getString(R.string.emergency_tone_vibrate);
-                default:
-                    throw new IllegalArgumentException();
-            }
-        }
-    };
-
-    private static final SettingPref[] PREFS = {
-        PREF_DIAL_PAD_TONES,
-        PREF_SCREEN_LOCKING_SOUNDS,
-        PREF_CHARGING_SOUNDS,
-        PREF_DOCKING_SOUNDS,
-        PREF_TOUCH_SOUNDS,
-        PREF_VIBRATE_ON_TOUCH,
-        PREF_DOCK_AUDIO_MEDIA,
-        PREF_EMERGENCY_TONE,
-    };
-
-    private SwitchPreference mBootSounds;
-
-    private final SettingsObserver mSettingsObserver = new SettingsObserver();
-
-    private Context mContext;
-
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.NOTIFICATION_OTHER_SOUND;
@@ -198,84 +47,34 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        addPreferencesFromResource(R.xml.other_sound_settings);
-
-        mContext = getActivity();
-
-        for (SettingPref pref : PREFS) {
-            pref.init(this);
-        }
-
-        if (mContext.getResources().getBoolean(R.bool.has_boot_sounds)) {
-            mBootSounds = (SwitchPreference) findPreference(KEY_BOOT_SOUNDS);
-            mBootSounds.setChecked(SystemProperties.getBoolean(PROPERTY_BOOT_SOUNDS, true));
-        } else {
-            removePreference(KEY_BOOT_SOUNDS);
-        }
+    protected String getCategoryKey() {
+        return null;
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
-        mSettingsObserver.register(true);
+    protected String getLogTag() {
+        return TAG;
     }
 
     @Override
-    public void onPause() {
-        super.onPause();
-        mSettingsObserver.register(false);
+    protected int getPreferenceScreenResId() {
+        return R.xml.other_sound_settings;
     }
 
     @Override
-    public boolean onPreferenceTreeClick(Preference preference) {
-        if (mBootSounds != null && preference == mBootSounds) {
-            SystemProperties.set(PROPERTY_BOOT_SOUNDS, mBootSounds.isChecked() ? "1" : "0");
-            return false;
-        } else {
-            return super.onPreferenceTreeClick(preference);
-        }
-    }
-
-    private static boolean hasDockSettings(Context context) {
-        return context.getResources().getBoolean(R.bool.has_dock_settings);
-    }
-
-    private static boolean hasHaptic(Context context) {
-        final Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
-        return vibrator != null && vibrator.hasVibrator();
-    }
-
-    // === Callbacks ===
-
-    private final class SettingsObserver extends ContentObserver {
-        public SettingsObserver() {
-            super(new Handler());
-        }
-
-        public void register(boolean register) {
-            final ContentResolver cr = getContentResolver();
-            if (register) {
-                for (SettingPref pref : PREFS) {
-                    cr.registerContentObserver(pref.getUri(), false, this);
-                }
-            } else {
-                cr.unregisterContentObserver(this);
-            }
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            super.onChange(selfChange, uri);
-            for (SettingPref pref : PREFS) {
-                if (pref.getUri().equals(uri)) {
-                    pref.update(mContext);
-                    return;
-                }
-            }
-        }
+    protected List<PreferenceController> getPreferenceControllers(Context context) {
+        final List<PreferenceController> controllers = new ArrayList<>();
+        Lifecycle lifecycle = getLifecycle();
+        controllers.add(new DialPadTonePreferenceController(context, this, lifecycle));
+        controllers.add(new ScreenLockSoundPreferenceController(context, this, lifecycle));
+        controllers.add(new ChargingSoundPreferenceController(context, this, lifecycle));
+        controllers.add(new DockingSoundPreferenceController(context, this, lifecycle));
+        controllers.add(new TouchSoundPreferenceController(context, this, lifecycle));
+        controllers.add(new VibrateOnTouchPreferenceController(context, this, lifecycle));
+        controllers.add(new DockAudioMediaPreferenceController(context, this, lifecycle));
+        controllers.add(new BootSoundPreferenceController(context));
+        controllers.add(new EmergencyTonePreferenceController(context, this, lifecycle));
+        return controllers;
     }
 
     // === Indexing ===
@@ -292,11 +91,23 @@
 
         public List<String> getNonIndexableKeys(Context context) {
             final ArrayList<String> rt = new ArrayList<String>();
-            for (SettingPref pref : PREFS) {
-                if (!pref.isApplicable(context)) {
-                    rt.add(pref.getKey());
-                }
-            }
+            new DialPadTonePreferenceController(context, null /* SettingsPreferenceFragment */,
+                null /* Lifecycle */).updateNonIndexableKeys(rt);
+            new ScreenLockSoundPreferenceController(context, null /* SettingsPreferenceFragment */,
+                null /* Lifecycle */).updateNonIndexableKeys(rt);
+            new ChargingSoundPreferenceController(context, null /* SettingsPreferenceFragment */,
+                null /* Lifecycle */).updateNonIndexableKeys(rt);
+            new DockingSoundPreferenceController(context, null /* SettingsPreferenceFragment */,
+                null /* Lifecycle */).updateNonIndexableKeys(rt);
+            new TouchSoundPreferenceController(context, null /* SettingsPreferenceFragment */,
+                null /* Lifecycle */).updateNonIndexableKeys(rt);
+            new VibrateOnTouchPreferenceController(context, null /* SettingsPreferenceFragment */,
+                null /* Lifecycle */).updateNonIndexableKeys(rt);
+            new DockAudioMediaPreferenceController(context, null /* SettingsPreferenceFragment */,
+                null /* Lifecycle */).updateNonIndexableKeys(rt);
+            new BootSoundPreferenceController(context).updateNonIndexableKeys(rt);
+            new EmergencyTonePreferenceController(context, null /* SettingsPreferenceFragment */,
+                null /* Lifecycle */).updateNonIndexableKeys(rt);
             return rt;
         }
     };
diff --git a/src/com/android/settings/notification/ScreenLockSoundPreferenceController.java b/src/com/android/settings/notification/ScreenLockSoundPreferenceController.java
new file mode 100644
index 0000000..f9905c0
--- /dev/null
+++ b/src/com/android/settings/notification/ScreenLockSoundPreferenceController.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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 static com.android.settings.notification.SettingPref.TYPE_SYSTEM;
+
+import android.content.Context;
+
+import android.provider.Settings.System;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+public class ScreenLockSoundPreferenceController extends SettingPrefController {
+
+    private static final String KEY_SCREEN_LOCKING_SOUNDS = "screen_locking_sounds";
+
+    public ScreenLockSoundPreferenceController(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+        super(context, parent, lifecycle);
+        mPreference = new SettingPref(
+            TYPE_SYSTEM, KEY_SCREEN_LOCKING_SOUNDS, System.LOCKSCREEN_SOUNDS_ENABLED, DEFAULT_ON);
+    }
+
+}
diff --git a/src/com/android/settings/notification/SettingPrefController.java b/src/com/android/settings/notification/SettingPrefController.java
new file mode 100644
index 0000000..d126fc4
--- /dev/null
+++ b/src/com/android/settings/notification/SettingPrefController.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 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.content.ContentResolver;
+import android.content.Context;
+
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
+import java.util.List;
+
+public abstract class SettingPrefController extends PreferenceController implements
+    LifecycleObserver, OnResume, OnPause {
+
+    protected static final int DEFAULT_ON = 1;
+
+    private SettingsPreferenceFragment mParent;
+    protected SettingsObserver mSettingsObserver;
+    protected SettingPref mPreference;
+
+    public SettingPrefController(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+        super(context);
+        mParent = parent;
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mPreference.init(mParent);
+        if (isAvailable()) {
+            mSettingsObserver = new SettingsObserver();
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return mPreference.getKey();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mPreference.isApplicable(mContext);
+    }
+
+    @Override
+    public void updateNonIndexableKeys(List<String> keys) {
+        if (!mPreference.isApplicable(mContext)) {
+            keys.add(mPreference.getKey());
+        }
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        mPreference.update(mContext);
+    }
+
+    @Override
+    public void onResume() {
+        if (mSettingsObserver != null) {
+            mSettingsObserver.register(true /* register */);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mSettingsObserver != null) {
+            mSettingsObserver.register(false /* register */);
+        }
+    }
+
+    @VisibleForTesting
+    final class SettingsObserver extends ContentObserver {
+        public SettingsObserver() {
+            super(new Handler());
+        }
+
+        public void register(boolean register) {
+            final ContentResolver cr = mContext.getContentResolver();
+            if (register) {
+                cr.registerContentObserver(mPreference.getUri(), false, this);
+            } else {
+                cr.unregisterContentObserver(this);
+            }
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            super.onChange(selfChange, uri);
+            if (mPreference.getUri().equals(uri)) {
+                mPreference.update(mContext);
+            }
+        }
+    }
+
+}
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index f156a84..a55278d 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -41,6 +41,7 @@
 import com.android.settings.core.lifecycle.Lifecycle;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.SummaryLoader;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.drawer.CategoryKey;
 import java.text.NumberFormat;
@@ -113,14 +114,17 @@
 
     @Override
     protected int getPreferenceScreenResId() {
-        return R.xml.sound_settings;
+        return mDashboardFeatureProvider.isEnabled()
+            ? R.xml.ia_sound_settings : R.xml.sound_settings;
     }
 
     @Override
     protected List<PreferenceController> getPreferenceControllers(Context context) {
         final List<PreferenceController> controllers = new ArrayList<>();
         Lifecycle lifecycle = getLifecycle();
-        controllers.add(new CastPreferenceController(context));
+        if (!mDashboardFeatureProvider.isEnabled()) {
+            controllers.add(new CastPreferenceController(context));
+        }
         controllers.add(new ZenModePreferenceController(context));
         controllers.add(new EmergencyBroadcastPreferenceController(context));
         controllers.add(new VibrateWhenRingPreferenceController(context));
@@ -141,6 +145,19 @@
         mWorkSoundController = new WorkSoundPreferenceController(context, this, getLifecycle());
         controllers.add(mWorkSoundController);
 
+        // === Other Sound Settings ===
+        if (mDashboardFeatureProvider.isEnabled()) {
+            controllers.add(new DialPadTonePreferenceController(context, this, lifecycle));
+            controllers.add(new ScreenLockSoundPreferenceController(context, this, lifecycle));
+            controllers.add(new ChargingSoundPreferenceController(context, this, lifecycle));
+            controllers.add(new DockingSoundPreferenceController(context, this, lifecycle));
+            controllers.add(new TouchSoundPreferenceController(context, this, lifecycle));
+            controllers.add(new VibrateOnTouchPreferenceController(context, this, lifecycle));
+            controllers.add(new DockAudioMediaPreferenceController(context, this, lifecycle));
+            controllers.add(new BootSoundPreferenceController(context));
+            controllers.add(new EmergencyTonePreferenceController(context, this, lifecycle));
+        }
+
         return controllers;
     }
 
@@ -297,10 +314,38 @@
                 context, null /* Callback */, null /* Lifecycle */).updateNonIndexableKeys(rt);
             new RingVolumePreferenceController(
                 context, null /* Callback */, null /* Lifecycle */).updateNonIndexableKeys(rt);
-            new CastPreferenceController(context).updateNonIndexableKeys(rt);
             new PhoneRingtonePreferenceController(context).updateNonIndexableKeys(rt);
             new VibrateWhenRingPreferenceController(context).updateNonIndexableKeys(rt);
             new EmergencyBroadcastPreferenceController(context).updateNonIndexableKeys(rt);
+            if (FeatureFactory.getFactory(context).getDashboardFeatureProvider(context)
+                .isEnabled()) {
+                new DialPadTonePreferenceController(context,
+                    null /* SettingsPreferenceFragment */,
+                    null /* Lifecycle */).updateNonIndexableKeys(rt);
+                new ScreenLockSoundPreferenceController(context,
+                    null /* SettingsPreferenceFragment */,
+                    null /* Lifecycle */).updateNonIndexableKeys(rt);
+                new ChargingSoundPreferenceController(context,
+                    null /* SettingsPreferenceFragment */,
+                    null /* Lifecycle */).updateNonIndexableKeys(rt);
+                new DockingSoundPreferenceController(context,
+                    null /* SettingsPreferenceFragment */,
+                    null /* Lifecycle */).updateNonIndexableKeys(rt);
+                new TouchSoundPreferenceController(context, null /* SettingsPreferenceFragment */,
+                    null /* Lifecycle */).updateNonIndexableKeys(rt);
+                new VibrateOnTouchPreferenceController(context,
+                    null /* SettingsPreferenceFragment */,
+                    null /* Lifecycle */).updateNonIndexableKeys(rt);
+                new DockAudioMediaPreferenceController(context,
+                    null /* SettingsPreferenceFragment */,
+                    null /* Lifecycle */).updateNonIndexableKeys(rt);
+                new BootSoundPreferenceController(context).updateNonIndexableKeys(rt);
+                new EmergencyTonePreferenceController(context,
+                    null /* SettingsPreferenceFragment */,
+                    null /* Lifecycle */).updateNonIndexableKeys(rt);
+            } else {
+                new CastPreferenceController(context).updateNonIndexableKeys(rt);
+            }
 
             return rt;
         }
diff --git a/src/com/android/settings/notification/TouchSoundPreferenceController.java b/src/com/android/settings/notification/TouchSoundPreferenceController.java
new file mode 100644
index 0000000..4ca5ea0
--- /dev/null
+++ b/src/com/android/settings/notification/TouchSoundPreferenceController.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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 static com.android.settings.notification.SettingPref.TYPE_SYSTEM;
+
+import android.content.Context;
+
+import android.media.AudioManager;
+import android.os.AsyncTask;
+import android.provider.Settings.System;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+public class TouchSoundPreferenceController extends SettingPrefController {
+
+    private static final String KEY_TOUCH_SOUNDS = "touch_sounds";
+
+    public TouchSoundPreferenceController(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+        super(context, parent, lifecycle);
+        mPreference = new SettingPref(
+            TYPE_SYSTEM, KEY_TOUCH_SOUNDS, System.SOUND_EFFECTS_ENABLED, DEFAULT_ON) {
+            @Override
+            protected boolean setSetting(final Context context, final int value) {
+                AsyncTask.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        final AudioManager am =
+                            (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+                        if (value != 0) {
+                            am.loadSoundEffects();
+                        } else {
+                            am.unloadSoundEffects();
+                        }
+                    }
+                });
+                return super.setSetting(context, value);
+            }
+        };
+    }
+}
diff --git a/src/com/android/settings/notification/VibrateOnTouchPreferenceController.java b/src/com/android/settings/notification/VibrateOnTouchPreferenceController.java
new file mode 100644
index 0000000..8fd938e
--- /dev/null
+++ b/src/com/android/settings/notification/VibrateOnTouchPreferenceController.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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 static com.android.settings.notification.SettingPref.TYPE_SYSTEM;
+
+import android.content.Context;
+import android.os.Vibrator;
+import android.provider.Settings.System;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+public class VibrateOnTouchPreferenceController extends SettingPrefController {
+
+    private static final String KEY_VIBRATE_ON_TOUCH = "vibrate_on_touch";
+
+    public VibrateOnTouchPreferenceController(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+        super(context, parent, lifecycle);
+        mPreference = new SettingPref(
+            TYPE_SYSTEM, KEY_VIBRATE_ON_TOUCH, System.HAPTIC_FEEDBACK_ENABLED, DEFAULT_ON) {
+            @Override
+            public boolean isApplicable(Context context) {
+                return hasHaptic(context);
+            }
+        };
+
+    }
+
+    private static boolean hasHaptic(Context context) {
+        final Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+        return vibrator != null && vibrator.hasVibrator();
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/notification/BootSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BootSoundPreferenceControllerTest.java
new file mode 100644
index 0000000..c547c63
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/BootSoundPreferenceControllerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 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.content.Context;
+import android.os.SystemProperties;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BootSoundPreferenceControllerTest {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private SwitchPreference mPreference;
+
+    private BootSoundPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        SettingsShadowSystemProperties.clear();
+        when(mContext.getResources().getBoolean(com.android.settings.R.bool.has_boot_sounds))
+            .thenReturn(true);
+        mController = new BootSoundPreferenceController(mContext);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
+    }
+
+    @Test
+    public void isAvailable_hasBootSounds_shouldReturnTrue() {
+        when(mContext.getResources().getBoolean(
+            com.android.settings.R.bool.has_boot_sounds)).thenReturn(true);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_noBootSounds_shouldReturnFale() {
+        when(mContext.getResources().getBoolean(
+            com.android.settings.R.bool.has_boot_sounds)).thenReturn(false);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Config(shadows = {SettingsShadowSystemProperties.class})
+    @Test
+    public void displayPreference_bootSoundEnabled_shouldCheckedPreference() {
+        SettingsShadowSystemProperties.set(BootSoundPreferenceController.PROPERTY_BOOT_SOUNDS, "1");
+
+        mController.displayPreference(mScreen);
+
+        verify(mPreference).setChecked(true);
+    }
+
+    @Config(shadows = {SettingsShadowSystemProperties.class})
+    @Test
+    public void displayPreference_bootSoundDisabled_shouldUncheckedPreference() {
+        SettingsShadowSystemProperties.set(BootSoundPreferenceController.PROPERTY_BOOT_SOUNDS, "0");
+
+        mController.displayPreference(mScreen);
+
+        verify(mPreference).setChecked(false);
+    }
+
+    @Config(shadows = {SettingsShadowSystemProperties.class})
+    @Test
+    public void handlePreferenceTreeClick_preferenceChecked_shouldEnableBootSound() {
+        when(mPreference.isChecked()).thenReturn(true);
+
+        mController.handlePreferenceTreeClick(mPreference);
+
+        assertThat(SystemProperties.getBoolean(
+            BootSoundPreferenceController.PROPERTY_BOOT_SOUNDS, true)).isTrue();
+    }
+
+    @Config(shadows = {SettingsShadowSystemProperties.class})
+    @Test
+    public void handlePreferenceTreeClick_preferenceUnchecked_shouldDisableBootSound() {
+        when(mPreference.isChecked()).thenReturn(false);
+
+        mController.handlePreferenceTreeClick(mPreference);
+
+        assertThat(SystemProperties.getBoolean(
+            BootSoundPreferenceController.PROPERTY_BOOT_SOUNDS, true)).isFalse();
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ChargingSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ChargingSoundPreferenceControllerTest.java
new file mode 100644
index 0000000..4cdf7c7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ChargingSoundPreferenceControllerTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings.Global;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ChargingSoundPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private SoundSettings mSetting;
+    @Mock
+    private Context mContext;
+
+    private ChargingSoundPreferenceController mController;
+    private SwitchPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mSetting.getActivity()).thenReturn(mActivity);
+        when(mActivity.getContentResolver()).thenReturn(mContentResolver);
+        mPreference = new SwitchPreference(ShadowApplication.getInstance().getApplicationContext());
+        mController = new ChargingSoundPreferenceController(mContext, mSetting, null);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        doReturn(mScreen).when(mSetting).getPreferenceScreen();
+    }
+
+    @Test
+    public void isAvailable_isAlwaysTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void displayPreference_chargingSoundEnabled_shouldCheckedPreference() {
+        Global.putInt(mContentResolver, Global.CHARGING_SOUNDS_ENABLED, 1);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void displayPreference_chargingSoundDisabled_shouldUncheckedPreference() {
+        Global.putInt(mContentResolver, Global.CHARGING_SOUNDS_ENABLED, 0);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceChecked_shouldEnabledChargingSound() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, true);
+
+        assertThat(Global.getInt(mContentResolver, Global.CHARGING_SOUNDS_ENABLED, 1))
+            .isEqualTo(1);
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceUnchecked_shouldDisabledChargingSound() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, false);
+
+        assertThat(Global.getInt(mContentResolver, Global.CHARGING_SOUNDS_ENABLED, 1))
+            .isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/DialPadTonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/DialPadTonePreferenceControllerTest.java
new file mode 100644
index 0000000..e9410e6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/DialPadTonePreferenceControllerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings.System;
+import android.telephony.TelephonyManager;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DialPadTonePreferenceControllerTest {
+
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private SoundSettings mSetting;
+    @Mock
+    private Context mContext;
+
+    private DialPadTonePreferenceController mController;
+    private SwitchPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+        when(mSetting.getActivity()).thenReturn(mActivity);
+        when(mActivity.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+        when(mActivity.getContentResolver()).thenReturn(mContentResolver);
+        mPreference = new SwitchPreference(ShadowApplication.getInstance().getApplicationContext());
+        mController = new DialPadTonePreferenceController(mContext, mSetting, null);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        doReturn(mScreen).when(mSetting).getPreferenceScreen();
+    }
+
+    @Test
+    public void isAvailable_voiceCapable_shouldReturnTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_notVoiceCapable_shouldReturnFalse() {
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void displayPreference_dialToneEnabled_shouldCheckedPreference() {
+        System.putInt(mContentResolver, System.DTMF_TONE_WHEN_DIALING, 1);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void displayPreference_dialToneDisabled_shouldUncheckedPreference() {
+        System.putInt(mContentResolver, System.DTMF_TONE_WHEN_DIALING, 0);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceChecked_shouldEnabledDialTone() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, true);
+
+        assertThat(System.getInt(mContentResolver, System.DTMF_TONE_WHEN_DIALING, 1)).isEqualTo(1);
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceUnchecked_shouldDisabledDialTone() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, false);
+
+        assertThat(System.getInt(mContentResolver, System.DTMF_TONE_WHEN_DIALING, 1)).isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/DockAudioMediaPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/DockAudioMediaPreferenceControllerTest.java
new file mode 100644
index 0000000..bc6eb3a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/DockAudioMediaPreferenceControllerTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.support.v7.preference.DropDownPreference;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings.Global;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DockAudioMediaPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private Activity mActivity;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private SoundSettings mSetting;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private Context mContext;
+
+    private DockAudioMediaPreferenceController mController;
+    private DropDownPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        final Context appContext = ShadowApplication.getInstance().getApplicationContext();
+        when(mSetting.getActivity()).thenReturn(mActivity);
+        when(mActivity.getContentResolver()).thenReturn(mContentResolver);
+        when(mActivity.getResources().getBoolean(com.android.settings.R.bool.has_dock_settings))
+            .thenReturn(true);
+        when(mActivity.getResources().getString(anyInt())).thenReturn("test string");
+        mPreference = new DropDownPreference(appContext);
+        mController = new DockAudioMediaPreferenceController(mContext, mSetting, null);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        doReturn(mScreen).when(mSetting).getPreferenceScreen();
+    }
+
+    @Test
+    public void isAvailable_hasDockSettings_shouldReturnTrue() {
+        when(mContext.getResources().getBoolean(com.android.settings.R.bool.has_dock_settings))
+            .thenReturn(true);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_noDockSettings_shouldReturnFalse() {
+        when(mContext.getResources().getBoolean(com.android.settings.R.bool.has_dock_settings))
+            .thenReturn(false);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void displayPreference_dockAudioDisabled_shouldSelectFirstItem() {
+        Global.putInt(mContentResolver, Global.DOCK_AUDIO_MEDIA_ENABLED, 0);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.getValue()).isEqualTo("0");
+    }
+
+    @Test
+    public void displayPreference_dockAudioEnabled_shouldSelectSecondItem() {
+        Global.putInt(mContentResolver, Global.DOCK_AUDIO_MEDIA_ENABLED, 1);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.getValue()).isEqualTo("1");
+    }
+
+    @Test
+    public void onPreferenceChanged_firstItemSelected_shouldDisableDockAudio() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, "0");
+
+        assertThat(Global.getInt(mContentResolver, Global.DOCK_AUDIO_MEDIA_ENABLED, 0))
+            .isEqualTo(0);
+    }
+
+    @Test
+    public void onPreferenceChanged_secondItemSelected_shouldEnableDockAudio() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, "1");
+
+        assertThat(Global.getInt(mContentResolver, Global.DOCK_AUDIO_MEDIA_ENABLED, 0))
+            .isEqualTo(1);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/DockingSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/DockingSoundPreferenceControllerTest.java
new file mode 100644
index 0000000..3350fb9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/DockingSoundPreferenceControllerTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings.Global;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DockingSoundPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private Activity mActivity;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private SoundSettings mSetting;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private Context mContext;
+
+    private DockingSoundPreferenceController mController;
+    private SwitchPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mSetting.getActivity()).thenReturn(mActivity);
+        when(mActivity.getContentResolver()).thenReturn(mContentResolver);
+        mPreference = new SwitchPreference(ShadowApplication.getInstance().getApplicationContext());
+        when(mActivity.getResources().getBoolean(com.android.settings.R.bool.has_dock_settings))
+            .thenReturn(true);
+        mController = new DockingSoundPreferenceController(mContext, mSetting, null);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        doReturn(mScreen).when(mSetting).getPreferenceScreen();
+    }
+
+    @Test
+    public void isAvailable_hasDockSettings_shouldReturnTrue() {
+        when(mContext.getResources().getBoolean(com.android.settings.R.bool.has_dock_settings))
+            .thenReturn(true);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_noDockSettings_shouldReturnFalse() {
+        when(mContext.getResources().getBoolean(com.android.settings.R.bool.has_dock_settings))
+            .thenReturn(false);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void displayPreference_dockingSoundEnabled_shouldCheckedPreference() {
+        Global.putInt(mContentResolver, Global.DOCK_SOUNDS_ENABLED, 1);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void displayPreference_dockingSoundDisabled_shouldUncheckedPreference() {
+        Global.putInt(mContentResolver, Global.DOCK_SOUNDS_ENABLED, 0);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceChecked_shouldEnabledDockingSound() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, true);
+
+        assertThat(Global.getInt(mContentResolver, Global.DOCK_SOUNDS_ENABLED, 1)).isEqualTo(1);
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceUnchecked_shouldDisabledDockingSound() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, false);
+
+        assertThat(Global.getInt(mContentResolver, Global.DOCK_SOUNDS_ENABLED, 1)).isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/EmergencyTonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/EmergencyTonePreferenceControllerTest.java
new file mode 100644
index 0000000..0124566
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/EmergencyTonePreferenceControllerTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.support.v7.preference.DropDownPreference;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings.Global;
+import android.telephony.TelephonyManager;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class EmergencyTonePreferenceControllerTest {
+
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private SoundSettings mSetting;
+    @Mock
+    private Context mContext;
+
+    private EmergencyTonePreferenceController mController;
+    private DropDownPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        final Context appContext = ShadowApplication.getInstance().getApplicationContext();
+        when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+        when(mTelephonyManager.getCurrentPhoneType()).thenReturn(TelephonyManager.PHONE_TYPE_CDMA);
+        when(mSetting.getActivity()).thenReturn(mActivity);
+        when(mActivity.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+        when(mActivity.getContentResolver()).thenReturn(mContentResolver);
+        when(mActivity.getResources()).thenReturn(appContext.getResources());
+        mPreference = new DropDownPreference(appContext);
+        mController = new EmergencyTonePreferenceController(mContext, mSetting, null);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        doReturn(mScreen).when(mSetting).getPreferenceScreen();
+    }
+
+    @Test
+    public void isAvailable_cdma_shouldReturnTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_notCdma_shouldReturnFalse() {
+        when(mTelephonyManager.getCurrentPhoneType()).thenReturn(TelephonyManager.PHONE_TYPE_GSM);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void displayPreference_emergencyToneOff_shouldSelectFirstItem() {
+        Global.putInt(mContentResolver, Global.EMERGENCY_TONE, 0);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.getValue()).isEqualTo("0");
+    }
+
+    @Test
+    public void displayPreference_emergencyToneAlert_shouldSelectSecondItem() {
+        Global.putInt(mContentResolver, Global.EMERGENCY_TONE, 1);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.getValue()).isEqualTo("1");
+    }
+
+    @Test
+    public void displayPreference_emergencyToneVibrate_shouldSelectThirdItem() {
+        Global.putInt(mContentResolver, Global.EMERGENCY_TONE, 2);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.getValue()).isEqualTo("2");
+    }
+
+    @Test
+    public void onPreferenceChanged_firstItemSelected_shouldSetEmergencyToneToOff() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, "0");
+
+        assertThat(Global.getInt(mContentResolver, Global.EMERGENCY_TONE, 0)).isEqualTo(0);
+    }
+
+    @Test
+    public void onPreferenceChanged_secondItemSelected_shouldSetEmergencyToneToAlert() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, "1");
+
+        assertThat(Global.getInt(mContentResolver, Global.EMERGENCY_TONE, 0)).isEqualTo(1);
+    }
+
+    @Test
+    public void onPreferenceChanged_thirdItemSelected_shouldSetEmergencyToneToVibrate() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, "2");
+
+        assertThat(Global.getInt(mContentResolver, Global.EMERGENCY_TONE, 0)).isEqualTo(2);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ScreenLockSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ScreenLockSoundPreferenceControllerTest.java
new file mode 100644
index 0000000..8963a5d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ScreenLockSoundPreferenceControllerTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings.System;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ScreenLockSoundPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private SoundSettings mSetting;
+    @Mock
+    private Context mContext;
+
+    private ScreenLockSoundPreferenceController mController;
+    private SwitchPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mSetting.getActivity()).thenReturn(mActivity);
+        when(mActivity.getContentResolver()).thenReturn(mContentResolver);
+        mPreference = new SwitchPreference(ShadowApplication.getInstance().getApplicationContext());
+        mController = new ScreenLockSoundPreferenceController(mContext, mSetting, null);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        doReturn(mScreen).when(mSetting).getPreferenceScreen();
+    }
+
+    @Test
+    public void isAvailable_isAlwaysTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void displayPreference_lockScreenSoundEnabled_shouldCheckedPreference() {
+        System.putInt(mContentResolver, System.LOCKSCREEN_SOUNDS_ENABLED, 1);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void displayPreference_lockScreenSoundDisabled_shouldUncheckedPreference() {
+        System.putInt(mContentResolver, System.LOCKSCREEN_SOUNDS_ENABLED, 0);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceChecked_shouldEnabledLockScreenSound() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, true);
+
+        assertThat(System.getInt(mContentResolver, System.LOCKSCREEN_SOUNDS_ENABLED, 1))
+            .isEqualTo(1);
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceUnchecked_shouldDisabledLockScreenSound() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, false);
+
+        assertThat(System.getInt(mContentResolver, System.LOCKSCREEN_SOUNDS_ENABLED, 1))
+            .isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/SettingPrefControllerTest.java b/tests/robotests/src/com/android/settings/notification/SettingPrefControllerTest.java
new file mode 100644
index 0000000..b36b19b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/SettingPrefControllerTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings.Global;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.android.settings.notification.SettingPref.TYPE_GLOBAL;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SettingPrefControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private OtherSoundSettings mSetting;
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private ContentResolver mContentResolver;
+
+    private Context mContext;
+    private PreferenceControllerTestable mController;
+    private SettingPref mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(ShadowApplication.getInstance().getApplicationContext());
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mSetting.getActivity()).thenReturn(mActivity);
+        doReturn(mScreen).when(mSetting).getPreferenceScreen();
+        mController = new PreferenceControllerTestable(mContext, mSetting, null);
+        mPreference = mController.getPref();
+    }
+
+    @Test
+    public void displayPreference_shouldInitPreference() {
+        mController.displayPreference(mScreen);
+
+        verify(mPreference).init(mSetting);
+    }
+
+    @Test
+    public void isAvailable_shouldCallisApplicable() {
+        mController.isAvailable();
+
+        verify(mPreference).isApplicable(mContext);
+    }
+
+    @Test
+    public void getPreferenceKey_shouldReturnPrefKey() {
+        assertThat(mController.getPreferenceKey()).isEqualTo(mController.KEY_TEST);
+    }
+
+    @Test
+    public void updateState_shouldUpdatePreference() {
+        mController.updateState(null);
+
+        verify(mPreference).update(mContext);
+    }
+
+    @Test
+    public void onResume_shouldRegisterContentObserver() {
+        mController.displayPreference(mScreen);
+        mController.onResume();
+
+        verify(mContentResolver).registerContentObserver(
+            Global.getUriFor("Setting1"), false, mController.getObserver());
+    }
+
+    @Test
+    public void onPause_shouldUnregisterContentObserver() {
+        mController.displayPreference(mScreen);
+        mController.onPause();
+
+        verify(mContentResolver).unregisterContentObserver(mController.getObserver());
+    }
+
+    @Test
+    public void onContentChange_shouldUpdatePreference() {
+        mController.displayPreference(mScreen);
+        mController.onResume();
+        mController.getObserver().onChange(false, Global.getUriFor("Setting1"));
+
+        verify(mPreference).update(mContext);
+    }
+
+    @Test
+    public void updateNonIndexableKeys_applicable_shouldNotUpdate() {
+        final List<String> keys = new ArrayList<>();
+
+        mController.updateNonIndexableKeys(keys);
+
+        assertThat(keys).isEmpty();
+    }
+
+    @Test
+    public void updateNonIndexableKeys_notApplicable_shouldUpdate() {
+        mController.setApplicable(false);
+        final List<String> keys = new ArrayList<>();
+
+        mController.updateNonIndexableKeys(keys);
+
+        assertThat(keys).isNotEmpty();
+    }
+
+    private class PreferenceControllerTestable extends SettingPrefController {
+
+        private static final String KEY_TEST = "key1";
+        private boolean mApplicable = true;
+
+        public PreferenceControllerTestable(Context context, SettingsPreferenceFragment parent,
+            Lifecycle lifecycle) {
+            super(context, parent, lifecycle);
+            mPreference = spy(new SettingPref(
+                TYPE_GLOBAL, KEY_TEST, "Setting1", 1) {
+                @Override
+                public boolean isApplicable(Context context) {
+                    return mApplicable;
+                }
+            });
+        }
+
+        SettingPref getPref() {
+            return mPreference;
+        }
+
+        PreferenceControllerTestable.SettingsObserver getObserver() {
+            return mSettingsObserver;
+        }
+
+        void setApplicable(boolean applicable) {
+            mApplicable = applicable;
+        }
+
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/notification/TouchSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/TouchSoundPreferenceControllerTest.java
new file mode 100644
index 0000000..f530f66
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/TouchSoundPreferenceControllerTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.AudioManager;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings.System;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class TouchSoundPreferenceControllerTest {
+
+    @Mock
+    private AudioManager mAudioManager;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private SoundSettings mSetting;
+    @Mock
+    private Context mContext;
+
+    private TouchSoundPreferenceController mController;
+    private SwitchPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mActivity.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+        when(mSetting.getActivity()).thenReturn(mActivity);
+        when(mActivity.getContentResolver()).thenReturn(mContentResolver);
+        mPreference = new SwitchPreference(ShadowApplication.getInstance().getApplicationContext());
+        mController = new TouchSoundPreferenceController(mContext, mSetting, null);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        doReturn(mScreen).when(mSetting).getPreferenceScreen();
+    }
+
+    @Test
+    public void isAvailable_isAlwaysTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void displayPreference_soundEffectEnabled_shouldCheckedPreference() {
+        System.putInt(mContentResolver, System.SOUND_EFFECTS_ENABLED, 1);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void displayPreference_soundEffectDisabled_shouldUncheckedPreference() {
+        System.putInt(mContentResolver, System.SOUND_EFFECTS_ENABLED, 0);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceChecked_shouldEnabledSoundEffect() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, true);
+
+        assertThat(System.getInt(mContentResolver, System.SOUND_EFFECTS_ENABLED, 1)).isEqualTo(1);
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceUnchecked_shouldDisabledSoundEffect() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, false);
+
+        assertThat(System.getInt(mContentResolver, System.SOUND_EFFECTS_ENABLED, 1)).isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/VibrateOnTouchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VibrateOnTouchPreferenceControllerTest.java
new file mode 100644
index 0000000..440b69e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/VibrateOnTouchPreferenceControllerTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Vibrator;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings.System;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class VibrateOnTouchPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private SoundSettings mSetting;
+    @Mock
+    private Context mContext;
+    @Mock
+    private Vibrator mVibrator;
+
+    private VibrateOnTouchPreferenceController mController;
+    private SwitchPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mActivity.getSystemService(Context.VIBRATOR_SERVICE)).thenReturn(mVibrator);
+        when(mContext.getSystemService(Context.VIBRATOR_SERVICE)).thenReturn(mVibrator);
+        when(mVibrator.hasVibrator()).thenReturn(true);
+        when(mSetting.getActivity()).thenReturn(mActivity);
+        when(mActivity.getContentResolver()).thenReturn(mContentResolver);
+        mPreference = new SwitchPreference(ShadowApplication.getInstance().getApplicationContext());
+        mController = new VibrateOnTouchPreferenceController(mContext, mSetting, null);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        doReturn(mScreen).when(mSetting).getPreferenceScreen();
+    }
+
+    @Test
+    public void isAvailable_hasHaptic_shouldReturnTrue() {
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_noHaptic_shouldReturnFalse() {
+        when(mVibrator.hasVibrator()).thenReturn(false);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void displayPreference_hapticEnabled_shouldCheckedPreference() {
+        System.putInt(mContentResolver, System.HAPTIC_FEEDBACK_ENABLED, 1);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void displayPreference_hapticDisabled_shouldUncheckedPreference() {
+        System.putInt(mContentResolver, System.HAPTIC_FEEDBACK_ENABLED, 0);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceChecked_shouldEnabledHaptic() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, true);
+
+        assertThat(System.getInt(mContentResolver, System.HAPTIC_FEEDBACK_ENABLED, 1)).isEqualTo(1);
+    }
+
+    @Test
+    public void onPreferenceChanged_preferenceUnchecked_shouldDisabledHaptic() {
+        mController.displayPreference(mScreen);
+
+        mPreference.getOnPreferenceChangeListener().onPreferenceChange(mPreference, false);
+
+        assertThat(System.getInt(mContentResolver, System.HAPTIC_FEEDBACK_ENABLED, 1)).isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowSystemProperties.java b/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowSystemProperties.java
new file mode 100644
index 0000000..e2a863a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowSystemProperties.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.testutils.shadow;
+
+import android.os.SystemProperties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowSystemProperties;
+
+/**
+ * This class provides write capability to ShadowSystemProperties.
+ */
+@Implements(SystemProperties.class)
+public class SettingsShadowSystemProperties extends ShadowSystemProperties {
+
+    private static final Map<String, String> sValues = new HashMap<>();
+
+    @Implementation
+    public static synchronized boolean getBoolean(String key, boolean def) {
+        if (sValues.containsKey(key)) {
+            String val = sValues.get(key);
+            return "y".equals(val) || "yes".equals(val) || "1".equals(val) || "true".equals(val)
+                || "on".equals(val);
+        }
+        return ShadowSystemProperties.getBoolean(key, def);
+    }
+
+    public static synchronized void set(String key, String val) {
+        sValues.put(key, val);
+    }
+
+    public static synchronized void clear() {
+        sValues.clear();
+    }
+
+}