Merge "Add new touch and notification vibration intensity settings."
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3e9d716..7abcc97 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4431,6 +4431,12 @@
     <string name="accessibility_autoclick_preference_title">Click after pointer stops moving</string>
     <!-- Title for accessibility preference for configuring amount of time that has to pass after pointer stops moving before click action can be performed (if automatic click after pointer stops moving feature is enabled). [CHAR LIMIT=NONE] -->
     <string name="accessibility_autoclick_delay_preference_title">Delay before click</string>
+    <!-- Title for accessibility preference screen for configuring vibrations. -->
+    <string name="accessibility_vibration_settings_title">Vibration</string>
+    <!-- Title for accessibility preference for configuring notification vibrations. -->
+    <string name="accessibility_notification_vibration_title">Ring &amp; notification vibration</string>
+    <!-- Title for accessibility preference for configuring touch feedback vibrations. -->
+    <string name="accessibility_touch_vibration_title">Touch vibration</string>
     <!-- Used in the acessibilty service settings to control turning on/off the service entirely -->
     <string name="accessibility_service_master_switch_title">Use service</string>
     <!-- Used in the Color correction settings screen to control turning on/off the feature entirely -->
@@ -4474,6 +4480,30 @@
         <item quantity="other">Very long delay (<xliff:g id="click_delay_label" example="200">%1$d</xliff:g> ms)</item>
     </plurals>
 
+    <!-- Summary for vibration settings preference when ring & notification are set to off-->
+    <string name="accessibility_vibration_summary_off">Ring &amp; notification set to off</string>
+
+    <!-- Summary for vibration settings preference when ring & notification are set to low-->
+    <string name="accessibility_vibration_summary_low">Ring &amp; notification set to low</string>
+
+    <!-- Summary for vibration settings preference when ring & notification are set to medium-->
+    <string name="accessibility_vibration_summary_medium">Ring &amp; notification set to medium</string>
+
+    <!-- Summary for vibration settings preference when ring & notification are set to high-->
+    <string name="accessibility_vibration_summary_high">Ring &amp; notification set to high</string>
+
+    <!-- Label describing an option turning vibrations off. [CHAR LIMIT=15] -->
+    <string name="accessibility_vibration_intensity_off">Off</string>
+
+    <!-- Label describing a low intensity vibration option. [CHAR LIMIT=15] -->
+    <string name="accessibility_vibration_intensity_low">Low</string>
+
+    <!-- Label describing a medium intensity vibration option. [CHAR LIMIT=15] -->
+    <string name="accessibility_vibration_intensity_medium">Medium</string>
+
+    <!-- Label describing a high intensity vibration option. [CHAR LIMIT=15] -->
+    <string name="accessibility_vibration_intensity_high">High</string>
+
     <!-- Title for accessibility menu item to lauch a settings activity. [CHAR LIMIT=15] -->
     <string name="accessibility_menu_item_settings">Settings</string>
 
diff --git a/res/xml/accessibility_notification_vibration_settings.xml b/res/xml/accessibility_notification_vibration_settings.xml
new file mode 100644
index 0000000..b37d363
--- /dev/null
+++ b/res/xml/accessibility_notification_vibration_settings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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"
+    android:title="@string/accessibility_notification_vibration_title" />
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index b5da848..060868e 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -93,6 +93,12 @@
                 android:entries="@array/long_press_timeout_selector_titles"
                 android:entryValues="@array/long_press_timeout_selector_values"
                 android:persistent="false"/>
+
+        <Preference
+            android:fragment="com.android.settings.accessibility.VibrationSettings"
+            android:key="vibration_preference_screen"
+            android:title="@string/accessibility_vibration_settings_title" />
+
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/accessibility_touch_vibration_settings.xml b/res/xml/accessibility_touch_vibration_settings.xml
new file mode 100644
index 0000000..ed8f550
--- /dev/null
+++ b/res/xml/accessibility_touch_vibration_settings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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"
+    android:title="@string/accessibility_touch_vibration_title" />
diff --git a/res/xml/accessibility_vibration_settings.xml b/res/xml/accessibility_vibration_settings.xml
new file mode 100644
index 0000000..d61454d
--- /dev/null
+++ b/res/xml/accessibility_vibration_settings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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"
+        android:key="accessibility_settings_screen"
+        android:title="@string/accessibility_vibration_settings_title"
+        android:persistent="true">
+
+    <Preference
+        android:fragment="com.android.settings.accessibility.NotificationVibrationPreferenceFragment"
+        android:key="notification_vibration_preference_screen"
+        android:title="@string/accessibility_notification_vibration_title" />
+
+    <Preference
+        android:fragment="com.android.settings.accessibility.TouchVibrationPreferenceFragment"
+        android:key="touch_vibration_preference_screen"
+        android:title="@string/accessibility_touch_vibration_title" />
+</PreferenceScreen>
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index df8d4c8..7335bae 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.accessibility;
 
+import static android.os.Vibrator.VibrationIntensity;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -28,6 +30,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.os.Vibrator;
 import android.provider.SearchIndexableResource;
 import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
@@ -111,6 +114,8 @@
             "tts_settings_preference";
     private static final String AUTOCLICK_PREFERENCE_SCREEN =
             "autoclick_preference_screen";
+    private static final String VIBRATION_PREFERENCE_SCREEN =
+            "vibration_preference_screen";
 
     @VisibleForTesting static final String TOGGLE_INVERSION_PREFERENCE =
             "toggle_inversion_preference";
@@ -215,6 +220,7 @@
     private Preference mAutoclickPreferenceScreen;
     private Preference mAccessibilityShortcutPreferenceScreen;
     private Preference mDisplayDaltonizerPreferenceScreen;
+    private Preference mVibrationPreferenceScreen;
     private SwitchPreference mToggleInversionPreference;
 
     private int mLongPressTimeoutDefault;
@@ -452,9 +458,11 @@
         // Display color adjustments.
         mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN);
 
-        // Accessibility shortcut
+        // Accessibility shortcut.
         mAccessibilityShortcutPreferenceScreen = findPreference(ACCESSIBILITY_SHORTCUT_PREFERENCE);
 
+        // Vibrations.
+        mVibrationPreferenceScreen = findPreference(VIBRATION_PREFERENCE_SCREEN);
     }
 
     private void updateAllPreferences() {
@@ -661,6 +669,8 @@
         mSelectLongPressTimeoutPreference.setValue(value);
         mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValueToTitleMap.get(value));
 
+        updateVibrationSummary(mVibrationPreferenceScreen);
+
         updateFeatureSummary(Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
                 mCaptioningPreferenceScreen);
         updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
@@ -726,6 +736,29 @@
         pref.setSummary(entries[index]);
     }
 
+    private void updateVibrationSummary(Preference pref) {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        final int intensity = Settings.System.getInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                vibrator.getDefaultNotificationVibrationIntensity());
+        mVibrationPreferenceScreen.setSummary(getVibrationSummary(getContext(), intensity));
+    }
+
+    private String getVibrationSummary(Context context, @VibrationIntensity int intensity) {
+        switch (intensity) {
+            case Vibrator.VIBRATION_INTENSITY_OFF:
+                return context.getString(R.string.accessibility_vibration_summary_off);
+            case Vibrator.VIBRATION_INTENSITY_LOW:
+                return context.getString(R.string.accessibility_vibration_summary_low);
+            case Vibrator.VIBRATION_INTENSITY_MEDIUM:
+                return context.getString(R.string.accessibility_vibration_summary_medium);
+            case Vibrator.VIBRATION_INTENSITY_HIGH:
+                return context.getString(R.string.accessibility_vibration_summary_high);
+            default:
+                return "";
+        }
+    }
+
     private void updateLockScreenRotationCheckbox() {
         Context context = getActivity();
         if (context != null) {
diff --git a/src/com/android/settings/accessibility/NotificationVibrationPreferenceFragment.java b/src/com/android/settings/accessibility/NotificationVibrationPreferenceFragment.java
new file mode 100644
index 0000000..6340bb1
--- /dev/null
+++ b/src/com/android/settings/accessibility/NotificationVibrationPreferenceFragment.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+
+/**
+ * Fragment for picking accessibility shortcut service
+ */
+public class NotificationVibrationPreferenceFragment extends VibrationPreferenceFragment {
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.ACCESSIBILITY_VIBRATION_NOTIFICATION;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.accessibility_notification_vibration_settings;
+    }
+
+    /**
+     * Get the setting string of the vibration intensity setting this preference is dealing with.
+     */
+    @Override
+    protected String getVibrationIntensitySetting() {
+        return Settings.System.NOTIFICATION_VIBRATION_INTENSITY;
+    }
+
+    @Override
+    protected int getDefaultVibrationIntensity() {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        return vibrator.getDefaultNotificationVibrationIntensity();
+    }
+}
diff --git a/src/com/android/settings/accessibility/TouchVibrationPreferenceFragment.java b/src/com/android/settings/accessibility/TouchVibrationPreferenceFragment.java
new file mode 100644
index 0000000..ea36833
--- /dev/null
+++ b/src/com/android/settings/accessibility/TouchVibrationPreferenceFragment.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import android.graphics.drawable.Drawable;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+
+/**
+ * Fragment for picking accessibility shortcut service
+ */
+public class TouchVibrationPreferenceFragment extends VibrationPreferenceFragment {
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.ACCESSIBILITY_VIBRATION_TOUCH;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.accessibility_touch_vibration_settings;
+    }
+
+    /**
+     * Get the setting string of the vibration intensity setting this preference is dealing with.
+     */
+    @Override
+    protected String getVibrationIntensitySetting() {
+        return Settings.System.HAPTIC_FEEDBACK_INTENSITY;
+    }
+
+    @Override
+    protected int getDefaultVibrationIntensity() {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        return vibrator.getDefaultHapticFeedbackIntensity();
+    }
+
+    @Override
+    public void onVibrationIntensitySelected(int intensity) {
+        // We want to keep HAPTIC_FEEDBACK_ENABLED consistent with this setting since some
+        // applications check it directly before triggering their own haptic feedback.
+        final boolean hapticFeedbackEnabled = !(intensity == Vibrator.VIBRATION_INTENSITY_OFF);
+        Settings.System.putInt(getContext().getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_ENABLED, hapticFeedbackEnabled ? 1 : 0);
+    }
+}
diff --git a/src/com/android/settings/accessibility/VibrationPreferenceFragment.java b/src/com/android/settings/accessibility/VibrationPreferenceFragment.java
new file mode 100644
index 0000000..f81208f
--- /dev/null
+++ b/src/com/android/settings/accessibility/VibrationPreferenceFragment.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import static android.os.Vibrator.VibrationIntensity;
+
+import android.support.annotation.VisibleForTesting;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.widget.RadioButtonPickerFragment;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Fragment for changing vibration settings.
+ */
+public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragment {
+    private static final String TAG = "VibrationPreferenceFragment";
+
+    @VisibleForTesting
+    final static String KEY_INTENSITY_OFF = "intensity_off";
+    @VisibleForTesting
+    final static String KEY_INTENSITY_LOW = "intensity_low";
+    @VisibleForTesting
+    final static String KEY_INTENSITY_MEDIUM = "intensity_medium";
+    @VisibleForTesting
+    final static String KEY_INTENSITY_HIGH = "intensity_high";
+
+    private final Map<String, VibrationIntensityCandidateInfo> mCandidates;
+    private final SettingsObserver mSettingsObserver;
+
+    public VibrationPreferenceFragment() {
+        mCandidates = new ArrayMap<>();
+        mCandidates.put(KEY_INTENSITY_OFF,
+                new VibrationIntensityCandidateInfo(KEY_INTENSITY_OFF,
+                    R.string.accessibility_vibration_intensity_off,
+                    Vibrator.VIBRATION_INTENSITY_OFF));
+        mCandidates.put(KEY_INTENSITY_LOW,
+                new VibrationIntensityCandidateInfo(KEY_INTENSITY_LOW,
+                    R.string.accessibility_vibration_intensity_low,
+                    Vibrator.VIBRATION_INTENSITY_LOW));
+        mCandidates.put(KEY_INTENSITY_MEDIUM,
+                new VibrationIntensityCandidateInfo(KEY_INTENSITY_MEDIUM,
+                    R.string.accessibility_vibration_intensity_medium,
+                    Vibrator.VIBRATION_INTENSITY_MEDIUM));
+        mCandidates.put(KEY_INTENSITY_HIGH,
+                new VibrationIntensityCandidateInfo(KEY_INTENSITY_HIGH,
+                    R.string.accessibility_vibration_intensity_high,
+                    Vibrator.VIBRATION_INTENSITY_HIGH));
+        mSettingsObserver = new SettingsObserver();
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mSettingsObserver.register();
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        mSettingsObserver.unregister();
+    }
+
+    /**
+     * Get the setting string of the vibration intensity setting this preference is dealing with.
+     */
+    protected abstract String getVibrationIntensitySetting();
+
+    /**
+     * Get the default intensity for the desired setting.
+     */
+    protected abstract int getDefaultVibrationIntensity();
+
+    /**
+     * When a new vibration intensity is selected by the user.
+     */
+    protected void onVibrationIntensitySelected(int intensity) { }
+
+    @Override
+    protected List<? extends CandidateInfo> getCandidates() {
+        List<VibrationIntensityCandidateInfo> candidates = new ArrayList<>(mCandidates.values());
+        candidates.sort(
+                Comparator.comparing(VibrationIntensityCandidateInfo::getIntensity).reversed());
+        return candidates;
+    }
+
+    @Override
+    protected String getDefaultKey() {
+        final int vibrationIntensity = Settings.System.getInt(getContext().getContentResolver(),
+                getVibrationIntensitySetting(), getDefaultVibrationIntensity());
+        for (VibrationIntensityCandidateInfo candidate : mCandidates.values()) {
+            if (candidate.getIntensity() == vibrationIntensity) {
+                return candidate.getKey();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected boolean setDefaultKey(String key) {
+        VibrationIntensityCandidateInfo candidate = mCandidates.get(key);
+        if (candidate == null) {
+            Log.e(TAG, "Tried to set unknown intensity (key=" + key + ")!");
+            return false;
+        }
+        Settings.System.putInt(getContext().getContentResolver(),
+                getVibrationIntensitySetting(), candidate.getIntensity());
+        onVibrationIntensitySelected(candidate.getIntensity());
+        return true;
+    }
+
+    @VisibleForTesting
+    class VibrationIntensityCandidateInfo extends CandidateInfo {
+        private String mKey;
+        private int mLabelId;
+        @VibrationIntensity
+        private int mIntensity;
+
+        public VibrationIntensityCandidateInfo(String key, int labelId, int intensity) {
+            super(true /* enabled */);
+            mKey = key;
+            mLabelId = labelId;
+            mIntensity = intensity;
+        }
+
+        @Override
+        public CharSequence loadLabel() {
+            return getContext().getString(mLabelId);
+        }
+
+        @Override
+        public Drawable loadIcon() {
+            return null;
+        }
+
+        @Override
+        public String getKey() {
+            return mKey;
+        }
+
+        public int getIntensity() {
+            return mIntensity;
+        }
+    }
+
+    private class SettingsObserver extends ContentObserver {
+        public SettingsObserver() {
+            super(new Handler());
+        }
+
+        public void register() {
+            getContext().getContentResolver().registerContentObserver(
+                    Settings.System.getUriFor(getVibrationIntensitySetting()), false, this);
+        }
+
+        public void unregister() {
+            getContext().getContentResolver().unregisterContentObserver(this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            updateCandidates();
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/VibrationSettings.java b/src/com/android/settings/accessibility/VibrationSettings.java
new file mode 100644
index 0000000..8aa4513
--- /dev/null
+++ b/src/com/android/settings/accessibility/VibrationSettings.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.provider.SearchIndexableResource;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Activity with the accessibility settings.
+ */
+public class VibrationSettings extends SettingsPreferenceFragment implements Indexable {
+
+    // Preferences
+    @VisibleForTesting
+    static final String NOTIFICATION_VIBRATION_PREFERENCE_SCREEN =
+            "notification_vibration_preference_screen";
+    @VisibleForTesting
+    static final String TOUCH_VIBRATION_PREFERENCE_SCREEN =
+            "touch_vibration_preference_screen";
+
+    private final Handler mHandler = new Handler();
+    private final SettingsContentObserver mSettingsContentObserver;
+
+    private Preference mNotificationVibrationPreferenceScreen;
+    private Preference mTouchVibrationPreferenceScreen;
+
+    public VibrationSettings() {
+        List<String> vibrationSettings = new ArrayList<>();
+        vibrationSettings.add(Settings.System.HAPTIC_FEEDBACK_INTENSITY);
+        vibrationSettings.add(Settings.System.NOTIFICATION_VIBRATION_INTENSITY);
+        mSettingsContentObserver = new SettingsContentObserver(mHandler, vibrationSettings) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                updatePreferences();
+            }
+        };
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.ACCESSIBILITY_VIBRATION;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        addPreferencesFromResource(R.xml.accessibility_vibration_settings);
+        initializePreferences();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updatePreferences();
+        mSettingsContentObserver.register(getContentResolver());
+    }
+
+    @Override
+    public void onPause() {
+        mSettingsContentObserver.unregister(getContentResolver());
+        super.onPause();
+    }
+
+    private void initializePreferences() {
+        // Notification and notification vibration strength adjustments.
+        mNotificationVibrationPreferenceScreen =
+                findPreference(NOTIFICATION_VIBRATION_PREFERENCE_SCREEN);
+
+        // Touch feedback strength adjustments.
+        mTouchVibrationPreferenceScreen = findPreference(TOUCH_VIBRATION_PREFERENCE_SCREEN);
+    }
+
+    private void updatePreferences() {
+        updateNotificationVibrationSummary(mNotificationVibrationPreferenceScreen);
+        updateTouchVibrationSummary(mTouchVibrationPreferenceScreen);
+    }
+
+    private void updateNotificationVibrationSummary(Preference pref) {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        final int intensity = Settings.System.getInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                vibrator.getDefaultNotificationVibrationIntensity());
+        CharSequence summary = getVibrationIntensitySummary(getContext(), intensity);
+        mNotificationVibrationPreferenceScreen.setSummary(summary);
+    }
+
+    private void updateTouchVibrationSummary(Preference pref) {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        final int intensity = Settings.System.getInt(getContext().getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                vibrator.getDefaultHapticFeedbackIntensity());
+        CharSequence summary = getVibrationIntensitySummary(getContext(), intensity);
+        mTouchVibrationPreferenceScreen.setSummary(summary);
+    }
+
+    public static String getVibrationIntensitySummary(Context context, int intensity) {
+        switch (intensity) {
+            case Vibrator.VIBRATION_INTENSITY_OFF:
+                return context.getString(R.string.accessibility_vibration_intensity_off);
+            case Vibrator.VIBRATION_INTENSITY_LOW:
+                return context.getString(R.string.accessibility_vibration_intensity_low);
+            case Vibrator.VIBRATION_INTENSITY_MEDIUM:
+                return context.getString(R.string.accessibility_vibration_intensity_medium);
+            case Vibrator.VIBRATION_INTENSITY_HIGH:
+                return context.getString(R.string.accessibility_vibration_intensity_high);
+            default:
+                return "";
+        }
+    }
+
+    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                        boolean enabled) {
+                    List<SearchIndexableResource> indexables = new ArrayList<>();
+                    SearchIndexableResource indexable = new SearchIndexableResource(context);
+                    indexable.xmlResId = R.xml.accessibility_vibration_settings;
+                    indexables.add(indexable);
+                    return indexables;
+                }
+            };
+}
diff --git a/src/com/android/settings/search/SearchIndexableResourcesImpl.java b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
index 1edc2de..bcf412c 100644
--- a/src/com/android/settings/search/SearchIndexableResourcesImpl.java
+++ b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
@@ -26,6 +26,7 @@
 import com.android.settings.accessibility.AccessibilitySettings;
 import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
 import com.android.settings.accessibility.MagnificationPreferenceFragment;
+import com.android.settings.accessibility.VibrationSettings;
 import com.android.settings.accounts.AccountDashboardFragment;
 import com.android.settings.applications.AppAndNotificationDashboardFragment;
 import com.android.settings.applications.DefaultAppSettings;
@@ -176,6 +177,7 @@
         addIndex(NightDisplaySettings.class);
         addIndex(SmartBatterySettings.class);
         addIndex(MyDeviceInfoFragment.class);
+        addIndex(VibrationSettings.class);
     }
 
     @Override
diff --git a/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java
new file mode 100644
index 0000000..f6a5a82
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_OFF;
+import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_LOW;
+import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_MEDIUM;
+import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_HIGH;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.UserManager;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import com.android.settings.TestConfig;
+import com.android.settings.accessibility.VibrationPreferenceFragment.VibrationIntensityCandidateInfo;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.widget.RadioButtonPickerFragment.CandidateInfo;
+
+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.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class VibrationPreferenceFragmentTest {
+    public static final Map<Integer, String> INTENSITY_TO_KEY = new HashMap<>();
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Activity mActivity;
+    @Mock
+    private UserManager mUserManager;
+
+    private Context mContext;
+    private TestVibrationPreferenceFragment mFragment;
+
+    static {
+        INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_OFF, KEY_INTENSITY_OFF);
+        INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_LOW, KEY_INTENSITY_LOW);
+        INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_MEDIUM, KEY_INTENSITY_MEDIUM);
+        INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_HIGH, KEY_INTENSITY_HIGH);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        FakeFeatureFactory.setupForTest();
+
+        mContext = RuntimeEnvironment.application;
+
+        mFragment = spy(new TestVibrationPreferenceFragment());
+        doReturn(mUserManager).when(mActivity).getSystemService(Context.USER_SERVICE);
+        doReturn(mContext).when(mFragment).getContext();
+        mFragment.onAttach(mActivity);
+    }
+
+    @Test
+    public void changeIntensitySetting_shouldResultInCorrespondingKey() {
+        for (Map.Entry<Integer, String> entry : INTENSITY_TO_KEY.entrySet()) {
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.HAPTIC_FEEDBACK_INTENSITY, entry.getKey());
+            assertThat(mFragment.getDefaultKey()).isEqualTo(entry.getValue());
+        }
+    }
+
+    @Test
+    public void initialDefaultKey_shouldBeMedium() {
+        assertThat(mFragment.getDefaultKey()).isEqualTo(KEY_INTENSITY_MEDIUM);
+    }
+
+    @Test
+    public void candidates_shouldBeSortedByIntensity() {
+        final List<? extends CandidateInfo> candidates = mFragment.getCandidates();
+        assertThat(candidates.size()).isEqualTo(INTENSITY_TO_KEY.size());
+        VibrationIntensityCandidateInfo prevCandidate =
+                (VibrationIntensityCandidateInfo) candidates.get(0);
+        for (int i = 1; i < candidates.size(); i++) {
+            VibrationIntensityCandidateInfo candidate =
+                    (VibrationIntensityCandidateInfo) candidates.get(i);
+            assertThat(candidate.getIntensity()).isLessThan(prevCandidate.getIntensity());
+        }
+    }
+
+    private class TestVibrationPreferenceFragment extends VibrationPreferenceFragment {
+        @Override
+        protected int getPreferenceScreenResId() {
+            return 0;
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return 0;
+        }
+
+        /**
+        * Get the setting string of the vibration intensity setting this preference is dealing with.
+        */
+        @Override
+        protected String getVibrationIntensitySetting() {
+            return Settings.System.HAPTIC_FEEDBACK_INTENSITY;
+        }
+
+        @Override
+        protected int getDefaultVibrationIntensity() {
+            return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+        }
+    }
+}
+