Merge "Update zen onboarding"
diff --git a/res/layout/choose_lock_pattern_common.xml b/res/layout/choose_lock_pattern_common.xml
index c4a9381..77dd12d 100644
--- a/res/layout/choose_lock_pattern_common.xml
+++ b/res/layout/choose_lock_pattern_common.xml
@@ -50,6 +50,7 @@
             <TextView
                 android:id="@+id/suw_layout_title"
                 style="@style/SuwGlifHeaderTitle"
+                android:accessibilityLiveRegion="polite"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content" />
 
diff --git a/res/xml/bluetooth_screen.xml b/res/xml/bluetooth_screen.xml
index 407feb6..9f09a8a 100644
--- a/res/xml/bluetooth_screen.xml
+++ b/res/xml/bluetooth_screen.xml
@@ -21,7 +21,7 @@
 
     <com.android.settingslib.RestrictedPreference
         android:key="bluetooth_screen_add_bt_devices"
-        android:title="@string/connected_device_add_device_title"
+        android:title="@string/bluetooth_pairing_pref_title"
         android:icon="@drawable/ic_menu_add"
         android:summary="@string/connected_device_add_device_summary"
         android:fragment="com.android.settings.bluetooth.BluetoothPairingDetail"
diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml
index 6d25c58..75f9988 100644
--- a/res/xml/connected_devices.xml
+++ b/res/xml/connected_devices.xml
@@ -49,21 +49,6 @@
         settings:allowDividerAbove="true"/>
 
     <Preference
-        android:fragment="com.android.settings.connecteddevice.BluetoothDashboardFragment"
-        android:key="bluetooth_settings"
-        android:title="@string/bluetooth_settings_title"
-        android:icon="@drawable/ic_settings_bluetooth"/>
-
-    <PreferenceCategory
-        android:key="dashboard_tile_placeholder" />
-
-    <SwitchPreference
-        android:key="toggle_nfc"
-        android:title="@string/nfc_quick_toggle_title"
-        android:icon="@drawable/ic_nfc"
-        android:summary="@string/nfc_quick_toggle_summary"/>
-
-    <Preference
         android:key="connection_preferences"
         android:title="@string/connected_device_connections_title"
         android:fragment="com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment"/>
diff --git a/res/xml/connected_devices_advanced.xml b/res/xml/connected_devices_advanced.xml
index 1917df1..f81fd79 100644
--- a/res/xml/connected_devices_advanced.xml
+++ b/res/xml/connected_devices_advanced.xml
@@ -20,12 +20,26 @@
     android:key="connected_devices_advanced_screen"
     android:title="@string/connected_device_connections_title">
 
+    <Preference
+        android:fragment="com.android.settings.connecteddevice.BluetoothDashboardFragment"
+        android:key="bluetooth_settings"
+        android:title="@string/bluetooth_settings_title"
+        android:icon="@drawable/ic_settings_bluetooth"
+        android:order="-9"/>
+
+    <SwitchPreference
+        android:key="toggle_nfc"
+        android:title="@string/nfc_quick_toggle_title"
+        android:icon="@drawable/ic_nfc"
+        android:summary="@string/nfc_quick_toggle_summary"
+        android:order="-7"/>
+
     <com.android.settingslib.RestrictedPreference
         android:fragment="com.android.settings.nfc.AndroidBeam"
         android:key="android_beam_settings"
         android:title="@string/android_beam_settings_title"
         android:icon="@drawable/ic_android"
-        android:order="-4"/>
+        android:order="-6"/>
 
     <SwitchPreference
         android:key="bluetooth_on_while_driving"
@@ -33,7 +47,7 @@
         android:icon="@drawable/ic_settings_bluetooth"
         android:summary="@string/bluetooth_on_while_driving_summary"
         settings:controller="com.android.settings.connecteddevice.BluetoothOnWhileDrivingPreferenceController"
-        android:order="-2"/>
+        android:order="-4"/>
 
     <com.android.settingslib.RestrictedPreference
         android:key="connected_device_printing"
@@ -41,7 +55,7 @@
         android:summary="@string/summary_placeholder"
         android:icon="@drawable/ic_settings_print"
         android:fragment="com.android.settings.print.PrintSettingsFragment"
-        android:order="-1"/>
+        android:order="-3"/>
 
     <Preference
         android:key="bt_received_files"
@@ -50,6 +64,6 @@
 
     <PreferenceCategory
         android:key="dashboard_tile_placeholder"
-        android:order="50"/>
+        android:order="-8"/>
 
 </PreferenceScreen>
diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml
index 5bee7b0..006862c 100644
--- a/res/xml/sound_settings.xml
+++ b/res/xml/sound_settings.xml
@@ -62,11 +62,6 @@
         android:order="-160"
         settings:controller="com.android.settings.notification.RingVolumePreferenceController"/>
 
-    <!-- Also vibrate for calls -->
-    <SwitchPreference
-        android:key="vibrate_when_ringing"
-        android:title="@string/vibrate_when_ringing_title"
-        android:order="-155"/>
 
     <!-- Alarm volume -->
     <com.android.settings.notification.VolumeSeekBarPreference
@@ -84,6 +79,13 @@
         android:order="-140"
         settings:controller="com.android.settings.notification.NotificationVolumePreferenceController"/>
 
+    <!-- Also vibrate for calls -->
+    <SwitchPreference
+        android:key="vibrate_when_ringing"
+        android:title="@string/vibrate_when_ringing_title"
+        settings:controller="com.android.settings.notification.VibrateWhenRingPreferenceController"
+        android:order="-130"/>
+
     <!-- Interruptions -->
     <com.android.settingslib.RestrictedPreference
         android:key="zen_mode"
diff --git a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
index e347c3f..e437b76 100644
--- a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
@@ -24,6 +24,7 @@
 import com.android.settings.bluetooth.BluetoothFilesPreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.nfc.AndroidBeamPreferenceController;
+import com.android.settings.nfc.NfcPreferenceController;
 import com.android.settings.print.PrintSettingPreferenceController;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -80,10 +81,16 @@
 
         final PrintSettingPreferenceController printerController =
                 new PrintSettingPreferenceController(context);
+        final NfcPreferenceController nfcPreferenceController =
+                new NfcPreferenceController(context);
+
         if (lifecycle != null) {
             lifecycle.addObserver(beamPreferenceController);
             lifecycle.addObserver(printerController);
+            lifecycle.addObserver(nfcPreferenceController);
         }
+
+        controllers.add(nfcPreferenceController);
         controllers.add(printerController);
 
         return controllers;
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
index 32a51f5..7582950 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
@@ -26,11 +26,8 @@
 import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.nfc.NfcPreferenceController;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.search.SearchIndexable;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -65,25 +62,6 @@
     }
 
     @Override
-    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
-        return buildPreferenceControllers(context, getLifecycle());
-    }
-
-    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
-            Lifecycle lifecycle) {
-        final List<AbstractPreferenceController> controllers = new ArrayList<>();
-        final NfcPreferenceController nfcPreferenceController =
-                new NfcPreferenceController(context);
-        controllers.add(nfcPreferenceController);
-
-        if (lifecycle != null) {
-            lifecycle.addObserver(nfcPreferenceController);
-        }
-
-        return controllers;
-    }
-
-    @Override
     public void onAttach(Context context) {
         super.onAttach(context);
         use(AvailableMediaDeviceGroupController.class).init(this);
@@ -141,12 +119,6 @@
                 }
 
                 @Override
-                public List<AbstractPreferenceController> createPreferenceControllers(Context
-                        context) {
-                    return buildPreferenceControllers(context, null /* lifecycle */);
-                }
-
-                @Override
                 public List<String> getNonIndexableKeys(Context context) {
                     List<String> keys = super.getNonIndexableKeys(context);
                     // Disable because they show dynamic data
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index 33818e0..7ad22a9 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -25,17 +25,24 @@
 import android.os.UserHandle;
 import android.preference.SeekBarVolumizer;
 import android.provider.SearchIndexableResource;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
 import android.text.TextUtils;
 
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.RingtonePreference;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.sound.HandsFreeProfileOutputPreferenceController;
+import com.android.settings.sound.MediaOutputPreferenceController;
 import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settings.widget.UpdatableListPreferenceDialogFragment;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.instrumentation.Instrumentable;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.search.SearchIndexable;
 
@@ -50,7 +57,6 @@
     private static final String SELECTED_PREFERENCE_KEY = "selected_preference";
     private static final int REQUEST_CODE = 200;
     private static final String KEY_ZEN_MODE = "zen_mode";
-
     private static final int SAMPLE_CUTOFF = 2000;  // manually cap sample playback at 2 seconds
 
     @VisibleForTesting
@@ -71,6 +77,9 @@
     };
 
     private RingtonePreference mRequestPreference;
+    private UpdatableListPreferenceDialogFragment mDialogFragment;
+    private String mMediaOutputControllerKey;
+    private String mHfpOutputControllerKey;
 
     @Override
     public int getMetricsCategory() {
@@ -85,6 +94,11 @@
             if (!TextUtils.isEmpty(selectedPreference)) {
                 mRequestPreference = (RingtonePreference) findPreference(selectedPreference);
             }
+
+            UpdatableListPreferenceDialogFragment dialogFragment =
+                    (UpdatableListPreferenceDialogFragment) getFragmentManager()
+                            .findFragmentByTag(TAG);
+            mDialogFragment = dialogFragment;
         }
     }
 
@@ -115,6 +129,23 @@
     }
 
     @Override
+    public void onDisplayPreferenceDialog(Preference preference) {
+        final int metricsCategory;
+        if (mHfpOutputControllerKey.equals(preference.getKey())) {
+            metricsCategory = MetricsProto.MetricsEvent.DIALOG_SWITCH_HFP_DEVICES;
+        } else if (mMediaOutputControllerKey.equals(preference.getKey())) {
+            metricsCategory = MetricsProto.MetricsEvent.DIALOG_SWITCH_A2DP_DEVICES;
+        } else {
+            metricsCategory = Instrumentable.METRICS_CATEGORY_UNKNOWN;
+        }
+
+        mDialogFragment = UpdatableListPreferenceDialogFragment.
+                newInstance(preference.getKey(), metricsCategory);
+        mDialogFragment.setTargetFragment(this, 0);
+        mDialogFragment.show(getFragmentManager(), TAG);
+    }
+
+    @Override
     protected String getLogTag() {
         return TAG;
     }
@@ -155,6 +186,14 @@
         volumeControllers.add(use(NotificationVolumePreferenceController.class));
         volumeControllers.add(use(CallVolumePreferenceController.class));
 
+        use(MediaOutputPreferenceController.class).setCallback(listPreference ->
+                onPreferenceDataChanged(listPreference));
+        mMediaOutputControllerKey = use(MediaOutputPreferenceController.class).getPreferenceKey();
+        use(HandsFreeProfileOutputPreferenceController.class).setCallback(listPreference ->
+            onPreferenceDataChanged(listPreference));
+        mHfpOutputControllerKey =
+                use(HandsFreeProfileOutputPreferenceController.class).getPreferenceKey();
+
         for (VolumeSeekBarPreferenceController controller : volumeControllers) {
             controller.setCallback(mVolumeCallback);
             getLifecycle().addObserver(controller);
@@ -197,7 +236,6 @@
             SoundSettings fragment, Lifecycle lifecycle) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new ZenModePreferenceController(context, lifecycle, KEY_ZEN_MODE));
-        controllers.add(new VibrateWhenRingPreferenceController(context));
 
         // Volumes are added via xml
 
@@ -291,4 +329,10 @@
             workSoundController.enableWorkSync();
         }
     }
-}
+
+    private void onPreferenceDataChanged(ListPreference preference) {
+        if (mDialogFragment != null) {
+            mDialogFragment.onListPreferenceUpdated(preference);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/VibrateWhenRingPreferenceController.java b/src/com/android/settings/notification/VibrateWhenRingPreferenceController.java
index d5b04f9..7f26731 100644
--- a/src/com/android/settings/notification/VibrateWhenRingPreferenceController.java
+++ b/src/com/android/settings/notification/VibrateWhenRingPreferenceController.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.notification;
 
+import static android.provider.Settings.System.VIBRATE_WHEN_RINGING;
+
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -24,26 +26,41 @@
 import android.provider.Settings;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
-import androidx.preference.TwoStatePreference;
 
 import com.android.settings.Utils;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settings.core.TogglePreferenceController;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
 import com.android.settingslib.core.lifecycle.events.OnResume;
 
-import static android.provider.Settings.System.VIBRATE_WHEN_RINGING;
-
-public class VibrateWhenRingPreferenceController extends AbstractPreferenceController
-        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener,
-        LifecycleObserver, OnResume, OnPause {
+public class VibrateWhenRingPreferenceController extends TogglePreferenceController
+        implements LifecycleObserver, OnResume, OnPause {
 
     private static final String KEY_VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
+    private final int DEFAULT_VALUE = 0;
+    private final int NOTIFICATION_VIBRATE_WHEN_RINGING = 1;
     private SettingObserver mSettingObserver;
 
-    public VibrateWhenRingPreferenceController(Context context) {
-        super(context);
+    public VibrateWhenRingPreferenceController(Context context, String key) {
+        super(context, key);
+    }
+
+    @Override
+    public boolean isChecked() {
+        return Settings.System.getInt(mContext.getContentResolver(),
+                VIBRATE_WHEN_RINGING, DEFAULT_VALUE) != DEFAULT_VALUE;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        return Settings.System.putInt(mContext.getContentResolver(), VIBRATE_WHEN_RINGING,
+                isChecked ? NOTIFICATION_VIBRATE_WHEN_RINGING : DEFAULT_VALUE);
+    }
+
+    @Override
+    @AvailabilityStatus
+    public int getAvailabilityStatus() {
+        return Utils.isVoiceCapable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
@@ -70,38 +87,10 @@
         }
     }
 
-    @Override
-    public boolean handlePreferenceTreeClick(Preference preference) {
-        return false;
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_VIBRATE_WHEN_RINGING;
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return Utils.isVoiceCapable(mContext);
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        ((TwoStatePreference) preference).setChecked(
-            Settings.System.getInt(mContext.getContentResolver(), VIBRATE_WHEN_RINGING, 0) != 0);
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final boolean val = (Boolean) newValue;
-        return Settings.System.putInt(mContext.getContentResolver(),
-            VIBRATE_WHEN_RINGING, val ? 1 : 0);
-    }
-
     private final class SettingObserver extends ContentObserver {
 
         private final Uri VIBRATE_WHEN_RINGING_URI =
-            Settings.System.getUriFor(VIBRATE_WHEN_RINGING);
+                Settings.System.getUriFor(VIBRATE_WHEN_RINGING);
 
         private final Preference mPreference;
 
@@ -127,5 +116,4 @@
             }
         }
     }
-
 }
diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
index 265564e..e0c8c88 100644
--- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java
+++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
@@ -78,6 +78,7 @@
     protected final LocalBluetoothProfileManager mProfileManager;
     protected int mSelectedIndex;
     protected Preference mPreference;
+    protected AudioSwitchCallback mAudioSwitchPreferenceCallback;
 
     private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
     private final LocalBluetoothManager mLocalBluetoothManager;
@@ -85,6 +86,10 @@
     private final WiredHeadsetBroadcastReceiver mReceiver;
     private final Handler mHandler;
 
+    public interface AudioSwitchCallback {
+        void onPreferenceDataChanged(ListPreference preference);
+    }
+
     public AudioSwitchPreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -207,6 +212,10 @@
     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
     }
 
+    public void setCallback(AudioSwitchCallback callback) {
+        mAudioSwitchPreferenceCallback = callback;
+    }
+
     protected boolean isStreamFromOutputDevice(int streamType, int device) {
         return (device & mAudioManager.getDevicesForStream(streamType)) != 0;
     }
@@ -335,6 +344,7 @@
         listPreference.setEntryValues(mediaValues);
         listPreference.setValueIndex(mSelectedIndex);
         listPreference.setSummary(mediaOutputs[mSelectedIndex]);
+        mAudioSwitchPreferenceCallback.onPreferenceDataChanged(listPreference);
     }
 
     private int getConnectedDeviceIndex(String hardwareAddress) {
diff --git a/src/com/android/settings/widget/UpdatableListPreferenceDialogFragment.java b/src/com/android/settings/widget/UpdatableListPreferenceDialogFragment.java
new file mode 100644
index 0000000..1434694
--- /dev/null
+++ b/src/com/android/settings/widget/UpdatableListPreferenceDialogFragment.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 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.widget;
+
+import android.app.AlertDialog;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.ListPreference;
+import androidx.preference.PreferenceDialogFragment;
+import com.android.settingslib.core.instrumentation.Instrumentable;
+
+import java.util.ArrayList;
+
+/**
+ * {@link PreferenceDialogFragment} that updates the available options
+ * when {@code onListPreferenceUpdated} is called."
+ */
+public class UpdatableListPreferenceDialogFragment extends PreferenceDialogFragment implements
+        Instrumentable {
+
+    private static final String SAVE_STATE_INDEX = "UpdatableListPreferenceDialogFragment.index";
+    private static final String SAVE_STATE_ENTRIES =
+            "UpdatableListPreferenceDialogFragment.entries";
+    private static final String SAVE_STATE_ENTRY_VALUES =
+            "UpdatableListPreferenceDialogFragment.entryValues";
+    private static final String METRICS_CATEGORY_KEY = "metrics_category_key";
+    private ArrayAdapter mAdapter;
+    private int mClickedDialogEntryIndex;
+    private ArrayList<CharSequence> mEntries;
+    private CharSequence[] mEntryValues;
+    private int mMetricsCategory = Instrumentable.METRICS_CATEGORY_UNKNOWN;
+
+    public static UpdatableListPreferenceDialogFragment newInstance(
+            String key, int metricsCategory) {
+        UpdatableListPreferenceDialogFragment fragment =
+                new UpdatableListPreferenceDialogFragment();
+        Bundle args = new Bundle(1);
+        args.putString(ARG_KEY, key);
+        args.putInt(METRICS_CATEGORY_KEY, metricsCategory);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final Bundle bundle = getArguments();
+        mMetricsCategory =
+                bundle.getInt(METRICS_CATEGORY_KEY, Instrumentable.METRICS_CATEGORY_UNKNOWN);
+        if (savedInstanceState == null) {
+            mEntries = new ArrayList<>();
+            setPreferenceData(getListPreference());
+        } else {
+            mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0);
+            mEntries = savedInstanceState.getCharSequenceArrayList(SAVE_STATE_ENTRIES);
+            mEntryValues =
+                    savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex);
+        outState.putCharSequenceArrayList(SAVE_STATE_ENTRIES, mEntries);
+        outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
+    }
+
+    @Override
+    public void onDialogClosed(boolean positiveResult) {
+        final ListPreference preference = getListPreference();
+        if (positiveResult && mClickedDialogEntryIndex >= 0) {
+            final String value = mEntryValues[mClickedDialogEntryIndex].toString();
+            if (preference.callChangeListener(value)) {
+                preference.setValue(value);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    void setAdapter(ArrayAdapter adapter) {
+        mAdapter = adapter;
+    }
+
+    @VisibleForTesting
+    void setEntries(ArrayList<CharSequence> entries) {
+        mEntries = entries;
+    }
+
+    @VisibleForTesting
+    ArrayAdapter getAdapter() {
+        return mAdapter;
+    }
+
+    @VisibleForTesting
+    void setMetricsCategory(Bundle bundle) {
+        mMetricsCategory =
+                bundle.getInt(METRICS_CATEGORY_KEY, Instrumentable.METRICS_CATEGORY_UNKNOWN);
+    }
+
+    @Override
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+        super.onPrepareDialogBuilder(builder);
+        final TypedArray a = getContext().obtainStyledAttributes(
+                null,
+                com.android.internal.R.styleable.AlertDialog,
+                com.android.internal.R.attr.alertDialogStyle, 0);
+
+        mAdapter = new ArrayAdapter<>(
+                getContext(),
+                a.getResourceId(
+                        com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout,
+                        com.android.internal.R.layout.select_dialog_singlechoice),
+                mEntries);
+
+        builder.setSingleChoiceItems(mAdapter, mClickedDialogEntryIndex,
+                (dialog, which) -> {
+                    mClickedDialogEntryIndex = which;
+                    onClick(dialog, -1);
+                    dialog.dismiss();
+                });
+        builder.setPositiveButton(null, null);
+        a.recycle();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return mMetricsCategory;
+    }
+
+    private ListPreference getListPreference() {
+        return (ListPreference) getPreference();
+    }
+
+    private void setPreferenceData(ListPreference preference) {
+        mEntries.clear();
+        mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue());
+        for (CharSequence entry : preference.getEntries()) {
+            mEntries.add(entry);
+        }
+        mEntryValues = preference.getEntryValues();
+    }
+
+    public void onListPreferenceUpdated(ListPreference preference) {
+        if (mAdapter != null) {
+            setPreferenceData(preference);
+            mAdapter.notifyDataSetChanged();
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/VibrateWhenRingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VibrateWhenRingPreferenceControllerTest.java
index 2708b65..19dd042 100644
--- a/tests/robotests/src/com/android/settings/notification/VibrateWhenRingPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/VibrateWhenRingPreferenceControllerTest.java
@@ -17,11 +17,17 @@
 package com.android.settings.notification;
 
 import static android.provider.Settings.System.VIBRATE_WHEN_RINGING;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.provider.Settings;
 import androidx.preference.Preference;
@@ -37,26 +43,32 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowContentResolver;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 public class VibrateWhenRingPreferenceControllerTest {
 
-    @Mock
+    private static final String KEY_VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
+    private final int DEFAULT_VALUE = 0;
+    private final int NOTIFICATION_VIBRATE_WHEN_RINGING = 1;
     private Context mContext;
+    private ContentResolver mContentResolver;
     @Mock
     private PreferenceScreen mScreen;
     @Mock
     private TelephonyManager mTelephonyManager;
-
     private VibrateWhenRingPreferenceController mController;
     private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mContentResolver = mContext.getContentResolver();
         when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
-        mController = new VibrateWhenRingPreferenceController(mContext);
-        mPreference = new Preference(RuntimeEnvironment.application);
+        mController = new VibrateWhenRingPreferenceController(mContext, KEY_VIBRATE_WHEN_RINGING);
+        mPreference = new Preference(mContext);
         mPreference.setKey(mController.getPreferenceKey());
         when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
@@ -80,26 +92,95 @@
     }
 
     @Test
+    public void testOnPreferenceChange_turnOn_returnOn() {
+        mController.onPreferenceChange(null, true);
+        final int mode = Settings.System.getInt(mContext.getContentResolver(),
+                VIBRATE_WHEN_RINGING, DEFAULT_VALUE);
+
+        assertThat(mode).isEqualTo(NOTIFICATION_VIBRATE_WHEN_RINGING);
+    }
+
+    @Test
+    public void testOnPreferenceChange_turnOff_returnOff() {
+        mController.onPreferenceChange(null, false);
+        final int mode = Settings.System.getInt(mContext.getContentResolver(),
+                VIBRATE_WHEN_RINGING, DEFAULT_VALUE);
+
+        assertThat(mode).isEqualTo(DEFAULT_VALUE);
+    }
+
+    @Test
+    public void voiceCapable_availabled() {
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void voiceCapable_notAvailabled() {
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
     public void updateState_settingIsOn_preferenceShouldBeChecked() {
         final TwoStatePreference preference = mock(TwoStatePreference.class);
-        final Context context = RuntimeEnvironment.application;
-        Settings.System.putInt(context.getContentResolver(), VIBRATE_WHEN_RINGING, 1);
+        Settings.System.putInt(mContext.getContentResolver(), VIBRATE_WHEN_RINGING, 1);
 
-        mController = new VibrateWhenRingPreferenceController(context);
         mController.updateState(preference);
 
-        verify(preference).setChecked(true);
+        assertThat(mController.isChecked()).isTrue();
     }
 
     @Test
     public void updateState_settingIsOff_preferenceShouldNotBeChecked() {
         final TwoStatePreference preference = mock(TwoStatePreference.class);
-        final Context context = RuntimeEnvironment.application;
-        Settings.System.putInt(context.getContentResolver(), VIBRATE_WHEN_RINGING, 0);
+        Settings.System.putInt(mContext.getContentResolver(), VIBRATE_WHEN_RINGING, 0);
 
-        mController = new VibrateWhenRingPreferenceController(context);
         mController.updateState(preference);
 
-        verify(preference).setChecked(false);
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    @Test
+    public void setChecked_settingsIsOn() {
+        mController.setChecked(true);
+        final int mode = Settings.System.getInt(mContext.getContentResolver(), VIBRATE_WHEN_RINGING,
+                -1);
+
+        assertThat(mode).isEqualTo(NOTIFICATION_VIBRATE_WHEN_RINGING);
+    }
+
+    @Test
+    public void setChecked_settingsIsOff() {
+        mController.setChecked(false);
+        final int mode = Settings.System.getInt(mContext.getContentResolver(), VIBRATE_WHEN_RINGING,
+                -1);
+
+        assertThat(mode).isEqualTo(DEFAULT_VALUE);
+    }
+
+    @Test
+    public void testObserver_onResume_shouldRegisterObserver() {
+        final ShadowContentResolver shadowContentResolver = Shadow.extract(mContentResolver);
+        mController.displayPreference(mScreen);
+
+        mController.onResume();
+
+        assertThat(shadowContentResolver.getContentObservers(
+                Settings.System.getUriFor(VIBRATE_WHEN_RINGING))).isNotEmpty();
+    }
+
+    @Test
+    public void testObserver_onPause_shouldUnregisterObserver() {
+        final ShadowContentResolver shadowContentResolver = Shadow.extract(mContentResolver);
+        mController.displayPreference(mScreen);
+
+        mController.onResume();
+        mController.onPause();
+
+        assertThat(shadowContentResolver.getContentObservers(
+                Settings.System.getUriFor(VIBRATE_WHEN_RINGING))).isEmpty();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
index c548815..381587a 100644
--- a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.settings.sound;
 
-
 import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
 import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
 import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
@@ -93,6 +92,8 @@
     private HeadsetProfile mHeadsetProfile;
     @Mock
     private HearingAidProfile mHearingAidProfile;
+    @Mock
+    private AudioSwitchPreferenceController.AudioSwitchCallback mAudioSwitchPreferenceCallback;
 
     private Context mContext;
     private PreferenceScreen mScreen;
@@ -156,6 +157,7 @@
         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
         mScreen.addPreference(mPreference);
         mController.displayPreference(mScreen);
+        mController.setCallback(mAudioSwitchPreferenceCallback);
     }
 
     @After
diff --git a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
index f827bba..0b894da 100644
--- a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
@@ -94,6 +94,8 @@
     private A2dpProfile mA2dpProfile;
     @Mock
     private HearingAidProfile mHearingAidProfile;
+    @Mock
+    private AudioSwitchPreferenceController.AudioSwitchCallback mAudioSwitchPreferenceCallback;
 
     private Context mContext;
     private PreferenceScreen mScreen;
@@ -157,6 +159,7 @@
         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
         mScreen.addPreference(mPreference);
         mController.displayPreference(mScreen);
+        mController.setCallback(mAudioSwitchPreferenceCallback);
     }
 
     @After
diff --git a/tests/robotests/src/com/android/settings/widget/UpdatableListPreferenceDialogFragmentTest.java b/tests/robotests/src/com/android/settings/widget/UpdatableListPreferenceDialogFragmentTest.java
new file mode 100644
index 0000000..57c9f6b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/UpdatableListPreferenceDialogFragmentTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 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.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.widget.ArrayAdapter;
+import androidx.preference.ListPreference;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+
+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;
+
+import java.util.ArrayList;
+
+import static org.mockito.Mockito.spy;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = ShadowBluetoothUtils.class)
+public class UpdatableListPreferenceDialogFragmentTest {
+
+    private Context mContext;
+    private UpdatableListPreferenceDialogFragment mUpdatableListPrefDlgFragment;
+    private static final String KEY = "Test_Key";
+    private ArrayAdapter mAdapter;
+    private ArrayList<CharSequence> mEntries;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+
+        mUpdatableListPrefDlgFragment = UpdatableListPreferenceDialogFragment
+                .newInstance(KEY, MetricsProto.MetricsEvent.DIALOG_SWITCH_A2DP_DEVICES);
+        mEntries = spy(new ArrayList<>());
+        mUpdatableListPrefDlgFragment.setEntries(mEntries);
+        mUpdatableListPrefDlgFragment.
+                setMetricsCategory(mUpdatableListPrefDlgFragment.getArguments());
+        initAdapter();
+    }
+
+    private void initAdapter() {
+        mAdapter = spy(new ArrayAdapter<>(
+                mContext,
+                com.android.internal.R.layout.select_dialog_singlechoice,
+                mEntries));
+        mUpdatableListPrefDlgFragment.setAdapter(mAdapter);
+    }
+
+    @Test
+    public void getMetricsCategory() {
+        assertThat(mUpdatableListPrefDlgFragment.getMetricsCategory())
+                .isEqualTo(MetricsProto.MetricsEvent.DIALOG_SWITCH_A2DP_DEVICES);
+    }
+
+    @Test
+    public void onListPreferenceUpdated_verifyAdapterCanBeUpdate() {
+        assertThat(mUpdatableListPrefDlgFragment.getAdapter().getCount()).
+                isEqualTo(0);
+
+        ListPreference listPreference = new ListPreference(mContext);
+        final CharSequence[] charSequences = {"Test_DEVICE_1", "Test_DEVICE_2"};
+        listPreference.setEntries(charSequences);
+        mUpdatableListPrefDlgFragment.onListPreferenceUpdated(listPreference);
+
+        assertThat(mUpdatableListPrefDlgFragment.getAdapter().getCount()).
+                isEqualTo(2);
+    }
+}
\ No newline at end of file