Convert VibrationSettings to DashboardFragment.

- DashboardFragment integrates Slices and settings search better than
  SettingsPreferenceFragment, and is more testable.

Test: robotests
Change-Id: I5f73836f94712c03521eac6b3f67964524381078
diff --git a/res/xml/accessibility_vibration_settings.xml b/res/xml/accessibility_vibration_settings.xml
index 17468ea..d24834a 100644
--- a/res/xml/accessibility_vibration_settings.xml
+++ b/res/xml/accessibility_vibration_settings.xml
@@ -16,8 +16,7 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
         android:key="accessibility_settings_vibration_screen"
-        android:title="@string/accessibility_vibration_settings_title"
-        android:persistent="true">
+        android:title="@string/accessibility_vibration_settings_title">
 
     <Preference
         android:fragment="com.android.settings.accessibility.NotificationVibrationPreferenceFragment"
diff --git a/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceController.java b/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceController.java
new file mode 100644
index 0000000..020c2a6
--- /dev/null
+++ b/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceController.java
@@ -0,0 +1,43 @@
+/*
+ * 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.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+
+public class HapticFeedbackIntensityPreferenceController
+        extends VibrationIntensityPreferenceController {
+
+    @VisibleForTesting
+    static final String PREF_KEY = "touch_vibration_preference_screen";
+
+    public HapticFeedbackIntensityPreferenceController(Context context) {
+        super(context, PREF_KEY, Settings.System.HAPTIC_FEEDBACK_INTENSITY);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    protected int getDefaultIntensity() {
+        return mVibrator.getDefaultHapticFeedbackIntensity();
+    }
+
+}
diff --git a/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceController.java b/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceController.java
new file mode 100644
index 0000000..a016478
--- /dev/null
+++ b/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceController.java
@@ -0,0 +1,42 @@
+/*
+ * 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.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+
+public class NotificationVibrationIntensityPreferenceController
+        extends VibrationIntensityPreferenceController {
+
+    @VisibleForTesting
+    static final String PREF_KEY = "notification_vibration_preference_screen";
+
+    public NotificationVibrationIntensityPreferenceController(Context context) {
+        super(context, PREF_KEY, Settings.System.NOTIFICATION_VIBRATION_INTENSITY);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    protected int getDefaultIntensity() {
+        return mVibrator.getDefaultNotificationVibrationIntensity();
+    }
+}
diff --git a/src/com/android/settings/accessibility/VibrationIntensityPreferenceController.java b/src/com/android/settings/accessibility/VibrationIntensityPreferenceController.java
new file mode 100644
index 0000000..e12c60a
--- /dev/null
+++ b/src/com/android/settings/accessibility/VibrationIntensityPreferenceController.java
@@ -0,0 +1,114 @@
+/*
+ * 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.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+public abstract class VibrationIntensityPreferenceController extends BasePreferenceController
+        implements LifecycleObserver, OnStart, OnStop {
+
+    protected final Vibrator mVibrator;
+    private final SettingObserver mSettingsContentObserver;
+    private final String mSettingKey;
+
+    private Preference mPreference;
+
+    public VibrationIntensityPreferenceController(Context context, String prefkey,
+            String settingKey) {
+        super(context, prefkey);
+        mVibrator = mContext.getSystemService(Vibrator.class);
+        mSettingKey = settingKey;
+        mSettingsContentObserver = new SettingObserver(settingKey) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                updateState(null);
+            }
+        };
+    }
+
+    @Override
+    public void onStart() {
+        mContext.getContentResolver().registerContentObserver(
+                mSettingsContentObserver.uri,
+                false /* notifyForDescendants */,
+                mSettingsContentObserver);
+    }
+
+    @Override
+    public void onStop() {
+        mContext.getContentResolver().unregisterContentObserver(mSettingsContentObserver);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        if (mPreference == null) {
+            return;
+        }
+        mPreference.setSummary(getSummary());
+    }
+
+    @Override
+    public String getSummary() {
+        final int intensity = Settings.System.getInt(mContext.getContentResolver(),
+                mSettingKey, getDefaultIntensity());
+
+        switch (intensity) {
+            case Vibrator.VIBRATION_INTENSITY_OFF:
+                return mContext.getString(R.string.accessibility_vibration_intensity_off);
+            case Vibrator.VIBRATION_INTENSITY_LOW:
+                return mContext.getString(R.string.accessibility_vibration_intensity_low);
+            case Vibrator.VIBRATION_INTENSITY_MEDIUM:
+                return mContext.getString(R.string.accessibility_vibration_intensity_medium);
+            case Vibrator.VIBRATION_INTENSITY_HIGH:
+                return mContext.getString(R.string.accessibility_vibration_intensity_high);
+            default:
+                return "";
+        }
+    }
+
+    protected abstract int getDefaultIntensity();
+
+    private static class SettingObserver extends ContentObserver {
+
+        public final Uri uri;
+
+        public SettingObserver(String settingKey) {
+            super(new Handler(Looper.getMainLooper()));
+            uri = Settings.System.getUriFor(settingKey);
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/VibrationSettings.java b/src/com/android/settings/accessibility/VibrationSettings.java
index 8aa4513..738f218 100644
--- a/src/com/android/settings/accessibility/VibrationSettings.java
+++ b/src/com/android/settings/accessibility/VibrationSettings.java
@@ -17,54 +17,24 @@
 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.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.search.Indexable;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Activity with the accessibility settings.
+ * Accessibility settings for the vibration.
  */
-public class VibrationSettings extends SettingsPreferenceFragment implements Indexable {
+public class VibrationSettings extends DashboardFragment {
 
-    // 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();
-            }
-        };
-    }
+    private static final String TAG = "VibrationSettings";
 
     @Override
     public int getMetricsCategory() {
@@ -72,70 +42,35 @@
     }
 
     @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        addPreferencesFromResource(R.xml.accessibility_vibration_settings);
-        initializePreferences();
+    protected int getPreferenceScreenResId() {
+        return R.xml.accessibility_vibration_settings;
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
-        updatePreferences();
-        mSettingsContentObserver.register(getContentResolver());
+    protected String getLogTag() {
+        return TAG;
     }
 
     @Override
-    public void onPause() {
-        mSettingsContentObserver.unregister(getContentResolver());
-        super.onPause();
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return buildControllers(context, getLifecycle());
     }
 
-    private void initializePreferences() {
-        // Notification and notification vibration strength adjustments.
-        mNotificationVibrationPreferenceScreen =
-                findPreference(NOTIFICATION_VIBRATION_PREFERENCE_SCREEN);
+    public static List<AbstractPreferenceController> buildControllers(Context context,
+            Lifecycle lifecycle) {
 
-        // 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 "";
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        final NotificationVibrationIntensityPreferenceController notifVibPrefController =
+                new NotificationVibrationIntensityPreferenceController(context);
+        final HapticFeedbackIntensityPreferenceController hapticPreferenceController =
+                new HapticFeedbackIntensityPreferenceController(context);
+        controllers.add(hapticPreferenceController);
+        controllers.add(notifVibPrefController);
+        if (lifecycle != null) {
+            lifecycle.addObserver(hapticPreferenceController);
+            lifecycle.addObserver(notifVibPrefController);
         }
+        return controllers;
     }
 
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
@@ -149,5 +84,11 @@
                     indexables.add(indexable);
                     return indexables;
                 }
+
+                @Override
+                public List<AbstractPreferenceController> getPreferenceControllers(
+                        Context context) {
+                    return buildControllers(context, null /* lifecycle */);
+                }
             };
 }
diff --git a/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java
new file mode 100644
index 0000000..b0246b9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.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 static com.google.common.truth.Truth.assertThat;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.content.Context;
+
+import com.android.settings.TestConfig;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class HapticFeedbackIntensityPreferenceControllerTest {
+
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+    private Context mContext;
+    private HapticFeedbackIntensityPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        mContext = RuntimeEnvironment.application;
+        mController = new HapticFeedbackIntensityPreferenceController(mContext);
+    }
+
+    @Test
+    public void verifyConstants() {
+        assertThat(mController.getPreferenceKey())
+                .isEqualTo(HapticFeedbackIntensityPreferenceController.PREF_KEY);
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.AVAILABLE);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java
new file mode 100644
index 0000000..2f55e00
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.provider.Settings.System.NOTIFICATION_VIBRATION_INTENSITY;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.content.Context;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class NotificationVibrationIntensityPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+    private Context mContext;
+    private NotificationVibrationIntensityPreferenceController mController;
+    private Preference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        mContext = RuntimeEnvironment.application;
+        mController = new NotificationVibrationIntensityPreferenceController(mContext) {
+            @Override
+            protected int getDefaultIntensity() {
+                return 10;
+            }
+        };
+        mLifecycle.addObserver(mController);
+        mPreference = new Preference(mContext);
+        mPreference.setSummary("Test");
+        when(mScreen.findPreference(mController.getPreferenceKey()))
+                .thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void verifyConstants() {
+        assertThat(mController.getPreferenceKey())
+                .isEqualTo(NotificationVibrationIntensityPreferenceController.PREF_KEY);
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.AVAILABLE);
+    }
+
+    @Test
+    public void updateState_shouldRefreshSummary() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
+        mController.updateState(null);
+        assertThat(mPreference.getSummary())
+                .isEqualTo(mContext.getString(R.string.accessibility_vibration_intensity_low));
+
+        Settings.System.putInt(mContext.getContentResolver(),
+                NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
+        mController.updateState(null);
+        assertThat(mPreference.getSummary())
+                .isEqualTo(mContext.getString(R.string.accessibility_vibration_intensity_high));
+
+        Settings.System.putInt(mContext.getContentResolver(),
+                NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        mController.updateState(null);
+        assertThat(mPreference.getSummary())
+                .isEqualTo(mContext.getString(R.string.accessibility_vibration_intensity_medium));
+
+        Settings.System.putInt(mContext.getContentResolver(),
+                NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+        mController.updateState(null);
+        assertThat(mPreference.getSummary())
+                .isEqualTo(mContext.getString(R.string.accessibility_vibration_intensity_off));
+    }
+}