Merge "Add progressive disclsoure"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a0814db..966e075 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2031,10 +2031,8 @@
         <!-- Bluetooth stuff -->
 
         <activity android:name=".bluetooth.BluetoothPairingDialog"
-                  android:label="@string/bluetooth_pairing_request"
                   android:excludeFromRecents="true"
-                  android:windowSoftInputMode="stateVisible|adjustResize"
-                  android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert">
+                  android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar">
             <intent-filter android:priority="1">
                 <action android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -3095,10 +3093,23 @@
                        android:value="true" />
         </activity-alias>
 
+        <activity-alias android:name="SoundDashboardAlias"
+                        android:targetActivity="Settings$SoundSettingsActivity">
+            <intent-filter android:priority="5">
+                <action android:name="com.android.settings.action.SETTINGS" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.category"
+                       android:value="com.android.settings.category.ia.homepage" />
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.notification.SoundSettings" />
+            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+                       android:value="true" />
+        </activity-alias>
+
         <activity android:name=".Settings$StorageDashboardActivity"
                   android:label="@string/storage_settings"
                   android:icon="@drawable/ic_settings_storage">
-            <intent-filter android:priority="5">
+            <intent-filter android:priority="4">
                 <action android:name="com.android.settings.action.SETTINGS" />
             </intent-filter>
             <meta-data android:name="com.android.settings.category"
diff --git a/res/layout/bluetooth_pin_confirm.xml b/res/layout/bluetooth_pin_confirm.xml
index 1e5eb0b..33140d9 100644
--- a/res/layout/bluetooth_pin_confirm.xml
+++ b/res/layout/bluetooth_pin_confirm.xml
@@ -41,7 +41,7 @@
             android:text="@string/bluetooth_pairing_key_msg"
             android:visibility="gone"
             android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="@*android:color/secondary_text_material_light"  />
+            android:textColor="@color/bluetooth_dialog_text_color"  />
 
         <TextView
             android:id="@+id/pairing_subhead"
@@ -64,7 +64,7 @@
             android:gravity="center_vertical"
             android:text="@string/bluetooth_enter_passkey_msg"
             android:textAppearance="@android:style/TextAppearance.Material.Subhead"
-            android:textColor="@*android:color/secondary_text_material_light"
+            android:textColor="@color/bluetooth_dialog_text_color"
             android:visibility="gone" />
 
         <CheckBox
@@ -74,7 +74,7 @@
             android:layout_marginStart="@dimen/bluetooth_dialog_padding"
             android:layout_marginEnd="@dimen/bluetooth_dialog_padding"
             android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="@*android:color/secondary_text_material_light"  />
+            android:textColor="@color/bluetooth_dialog_text_color"  />
 
     </LinearLayout>
 
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 2b6a730..9f3dd5b 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -21,7 +21,7 @@
 
     <color name="material_empty_color_light">#FFCED7DB</color>
 
-
+    <color name="bluetooth_dialog_text_color">#8a000000</color>
 
     <color name="crypt_keeper_clock_background">#ff9a9a9a</color>
     <color name="crypt_keeper_clock_foreground">#ff666666</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 6d56e18..75d8697 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -52,17 +52,10 @@
     <string name="gesture_double_twist_sensor_name" translatable="false"></string>
     <string name="gesture_double_twist_sensor_vendor" translatable="false"></string>
 
-    <!-- Pickup sensor name and vendor used by gesture setting -->
-    <string name="gesture_pickup_sensor_name" translatable="false"></string>
-    <string name="gesture_pickup_sensor_vendor" translatable="false"></string>
-
     <!-- When true enable gesture setting. -->
     <bool name="config_gesture_settings_enabled">false</bool>
 
     <!-- If the Storage Manager settings are enabled. -->
     <bool name="config_storage_manager_settings_enabled">false</bool>
 
-    <!-- When true show double-tap gesture setting. -->
-    <bool name="config_gesture_double_tap_settings_enabled">false</bool>
-
 </resources>
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 07f6ebf..b434777 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -274,6 +274,7 @@
             Settings.NetworkDashboardActivity.class.getName(),
             "com.android.settings.Settings.BatteryDashboardAlias",
             "com.android.settings.Settings.DisplayDashboardAlias",
+            "com.android.settings.Settings.SoundDashboardAlias",
             "com.android.settings.Settings.SecurityDashboardAlias",
             Settings.SystemDashboardActivity.class.getName(),
             Settings.SupportDashboardActivity.class.getName(),
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingController.java b/src/com/android/settings/bluetooth/BluetoothPairingController.java
new file mode 100644
index 0000000..38b30a8
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothPairingController.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2016 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.bluetooth;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.Intent;
+import android.text.Editable;
+import android.util.Log;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import com.android.settings.R;
+import com.android.settings.bluetooth.BluetoothPairingDialogFragment.BluetoothPairingDialogListener;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import java.util.Locale;
+
+/**
+ * A controller used by {@link BluetoothPairingDialog} to manage connection state while we try to
+ * pair with a bluetooth device. It includes methods that allow the
+ * {@link BluetoothPairingDialogFragment} to interrogate the current state as well.
+ */
+public class BluetoothPairingController implements OnCheckedChangeListener,
+        BluetoothPairingDialogListener {
+
+    private static final String TAG = "BTPairingController";
+
+    // Different types of dialogs we can map to
+    public static final int INVALID_DIALOG_TYPE = -1;
+    public static final int USER_ENTRY_DIALOG = 0;
+    public static final int CONFIRMATION_DIALOG = 1;
+    public static final int DISPLAY_PASSKEY_DIALOG = 2;
+
+    private static final int BLUETOOTH_PIN_MAX_LENGTH = 16;
+    private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
+
+    // Bluetooth dependencies for the connection we are trying to establish
+    private LocalBluetoothManager mBluetoothManager;
+    private BluetoothDevice mDevice;
+    private int mType;
+    private String mUserInput;
+    private String mPasskeyFormatted;
+    private int mPasskey;
+    private String mDeviceName;
+    private LocalBluetoothProfile mPbapClientProfile;
+
+    /**
+     * Creates an instance of a BluetoothPairingController.
+     *
+     * @param intent - must contain {@link BluetoothDevice#EXTRA_PAIRING_VARIANT}, {@link
+     * BluetoothDevice#EXTRA_PAIRING_KEY}, and {@link BluetoothDevice#EXTRA_DEVICE}. Missing extra
+     * will lead to undefined behavior.
+     */
+    public BluetoothPairingController(Intent intent, Context context) {
+        mBluetoothManager = Utils.getLocalBtManager(context);
+        mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+        String message = "";
+        if (mBluetoothManager == null) {
+            throw new IllegalStateException("Could not obtain LocalBluetoothManager");
+        } else if (mDevice == null) {
+            throw new IllegalStateException("Could not find BluetoothDevice");
+        }
+
+        mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
+        mPasskey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
+        mDeviceName = mBluetoothManager.getCachedDeviceManager().getName(mDevice);
+        mPbapClientProfile = mBluetoothManager.getProfileManager().getPbapClientProfile();
+        mPasskeyFormatted = formatKey(mPasskey);
+
+    }
+
+    @Override
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        if (isChecked) {
+            mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
+        } else {
+            mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
+        }
+    }
+
+    @Override
+    public void onDialogPositiveClick(BluetoothPairingDialogFragment dialog) {
+        if (getDialogType() == USER_ENTRY_DIALOG) {
+            onPair(mUserInput);
+        } else {
+            onPair(null);
+        }
+    }
+
+    @Override
+    public void onDialogNegativeClick(BluetoothPairingDialogFragment dialog) {
+        onCancel();
+    }
+
+    /**
+     * A method for querying which bluetooth pairing dialog fragment variant this device requires.
+     *
+     * @return - The dialog view variant needed for this device.
+     */
+    public int getDialogType() {
+        switch (mType) {
+            case BluetoothDevice.PAIRING_VARIANT_PIN:
+            case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+                return USER_ENTRY_DIALOG;
+
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+            case BluetoothDevice.PAIRING_VARIANT_CONSENT:
+            case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+                return CONFIRMATION_DIALOG;
+
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
+                return DISPLAY_PASSKEY_DIALOG;
+
+            default:
+                return INVALID_DIALOG_TYPE;
+        }
+    }
+
+    /**
+     * @return - A string containing the name provided by the device.
+     */
+    public String getDeviceName() {
+        return mDeviceName;
+    }
+
+    /**
+     * A method for querying if the bluetooth device has a profile already set up on this device.
+     *
+     * @return - A boolean indicating if the device has previous knowledge of a profile for this
+     * device.
+     */
+    public boolean isProfileReady() {
+        return mPbapClientProfile != null && mPbapClientProfile.isProfileReady();
+    }
+
+    /**
+     * A method for querying if the bluetooth device has access to contacts on the device.
+     *
+     * @return - A boolean indicating if the bluetooth device has permission to access the device
+     * contacts
+     */
+    public boolean getContactSharingState() {
+        switch (mDevice.getPhonebookAccessPermission()) {
+            case BluetoothDevice.ACCESS_ALLOWED:
+                return true;
+            case BluetoothDevice.ACCESS_REJECTED:
+                return false;
+            default:
+                if (mDevice.getBluetoothClass().getDeviceClass()
+                        == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
+                    return true;
+                }
+                return false;
+        }
+    }
+
+    /**
+     * A method for querying if the provided editable is a valid passkey/pin format for this device.
+     *
+     * @param s - The passkey/pin
+     * @return - A boolean indicating if the passkey/pin is of the correct format.
+     */
+    public boolean isPasskeyValid(Editable s) {
+        boolean requires16Digits = mType == BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS;
+        return s.length() >= 16 && requires16Digits || s.length() > 0 && !requires16Digits;
+    }
+
+    /**
+     * A method for querying what message should be shown to the user as additional text in the
+     * dialog for this device. Returns -1 to indicate a device type that does not use this message.
+     *
+     * @return - The message ID to show the user.
+     */
+    public int getDeviceVariantMessageID() {
+        switch (mType) {
+            case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
+            case BluetoothDevice.PAIRING_VARIANT_PIN:
+                return R.string.bluetooth_enter_pin_other_device;
+
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+                return R.string.bluetooth_enter_passkey_other_device;
+
+            default:
+                return -1;
+        }
+    }
+
+    /**
+     * A method for querying what message hint should be shown to the user as additional text in the
+     * dialog for this device. Returns -1 to indicate a device type that does not use this message.
+     *
+     * @return - The message ID to show the user.
+     */
+    public int getDeviceVariantMessageHint() {
+        switch (mType) {
+            case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
+                return R.string.bluetooth_pin_values_hint_16_digits;
+
+            case BluetoothDevice.PAIRING_VARIANT_PIN:
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+                return R.string.bluetooth_pin_values_hint;
+
+            default:
+                return -1;
+        }
+    }
+
+    /**
+     * A method for querying the maximum passkey/pin length for this device.
+     *
+     * @return - An int indicating the maximum length
+     */
+    public int getDeviceMaxPasskeyLength() {
+        switch (mType) {
+            case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
+            case BluetoothDevice.PAIRING_VARIANT_PIN:
+                return BLUETOOTH_PIN_MAX_LENGTH;
+
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+                return BLUETOOTH_PASSKEY_MAX_LENGTH;
+
+            default:
+                return 0;
+        }
+
+    }
+
+    /**
+     * A method for querying if the device uses an alphanumeric passkey.
+     *
+     * @return - a boolean indicating if the passkey can be alphanumeric.
+     */
+    public boolean pairingCodeIsAlphanumeric() {
+        switch (mType) {
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+                return false;
+
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * A method used by the dialogfragment to notify the controller that the dialog has been
+     * displayed for bluetooth device types that just care about it being displayed.
+     */
+    protected void notifyDialogDisplayed() {
+        // send an OK to the framework, indicating that the dialog has been displayed.
+        if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
+            mDevice.setPairingConfirmation(true);
+        } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
+            byte[] pinBytes = BluetoothDevice.convertPinToBytes(mPasskeyFormatted);
+            mDevice.setPin(pinBytes);
+        }
+    }
+
+    /**
+     * A method for querying if this bluetooth device type has a key it would like displayed
+     * to the user.
+     *
+     * @return - A boolean indicating if a key exists which should be displayed to the user.
+     */
+    public boolean isDisplayPairingKeyVariant() {
+        switch (mType) {
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
+            case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * A method for querying if this bluetooth device type has other content it would like displayed
+     * to the user.
+     *
+     * @return - A boolean indicating if content exists which should be displayed to the user.
+     */
+    public boolean hasPairingContent() {
+        switch (mType) {
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * A method for obtaining any additional content this bluetooth device has for displaying to the
+     * user.
+     *
+     * @return - A string containing the additional content, null if none exists.
+     * @see {@link BluetoothPairingController#hasPairingContent()}
+     */
+    public String getPairingContent() {
+        if (hasPairingContent()) {
+            return mPasskeyFormatted;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * A method that exists to allow the fragment to update the controller with input the user has
+     * provided in the fragment.
+     *
+     * @param input - A string containing the user input.
+     */
+    protected void updateUserInput(String input) {
+        mUserInput = input;
+    }
+
+    /**
+     * Returns the provided passkey in a format that this device expects. Only works for numeric
+     * passkeys/pins.
+     *
+     * @param passkey - An integer containing the passkey to format.
+     * @return - A string containing the formatted passkey/pin
+     */
+    private String formatKey(int passkey) {
+        switch (mType) {
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
+                return String.format(Locale.US, "%06d", passkey);
+
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
+                return String.format("%04d", passkey);
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * handles the necessary communication with the bluetooth device to establish a successful
+     * pairing
+     *
+     * @param passkey - The passkey we will attempt to pair to the device with.
+     */
+    private void onPair(String passkey) {
+        Log.d(TAG, "Pairing dialog accepted");
+        switch (mType) {
+            case BluetoothDevice.PAIRING_VARIANT_PIN:
+            case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
+                byte[] pinBytes = BluetoothDevice.convertPinToBytes(passkey);
+                if (pinBytes == null) {
+                    return;
+                }
+                mDevice.setPin(pinBytes);
+                break;
+
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+                int pass = Integer.parseInt(passkey);
+                mDevice.setPasskey(pass);
+                break;
+
+            case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+            case BluetoothDevice.PAIRING_VARIANT_CONSENT:
+                mDevice.setPairingConfirmation(true);
+                break;
+
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
+            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
+                // Do nothing.
+                break;
+
+            case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+                mDevice.setRemoteOutOfBandData();
+                break;
+
+            default:
+                Log.e(TAG, "Incorrect pairing type received");
+        }
+    }
+
+    /**
+     * A method for properly ending communication with the bluetooth device. Will be called by the
+     * {@link BluetoothPairingDialogFragment} when it is dismissed.
+     */
+    public void onCancel() {
+        Log.d(TAG, "Pairing dialog canceled");
+        mDevice.cancelPairingUserInput();
+    }
+
+    /**
+     * A method for checking if this device is equal to another device.
+     *
+     * @param device - The other device being compared to this device.
+     * @return - A boolean indicating if the devices were equal.
+     */
+    public boolean deviceEquals(BluetoothDevice device) {
+        return mDevice == device;
+    }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
old mode 100755
new mode 100644
index 27cd532..ed63fcb
--- a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -16,65 +16,28 @@
 
 package com.android.settings.bluetooth;
 
-import android.bluetooth.BluetoothClass;
+import android.annotation.Nullable;
+import android.app.Activity;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothUuid;
 import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
-import android.text.Editable;
-import android.text.InputFilter;
-import android.text.InputFilter.LengthFilter;
-import android.text.InputType;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.TextWatcher;
-import android.text.style.TtsSpan;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
-import com.android.settings.R;
-import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfile;
-
-import java.util.Locale;
 
 /**
  * BluetoothPairingDialog asks the user to enter a PIN / Passkey / simple confirmation
  * for pairing with a remote Bluetooth device. It is an activity that appears as a dialog.
  */
-public final class BluetoothPairingDialog extends AlertActivity implements
-        CompoundButton.OnCheckedChangeListener, DialogInterface.OnClickListener, TextWatcher {
-    private static final String TAG = "BluetoothPairingDialog";
+public final class BluetoothPairingDialog extends Activity {
+    public static final String FRAGMENT_TAG = "bluetooth.pairing.fragment";
 
-    private static final int BLUETOOTH_PIN_MAX_LENGTH = 16;
-    private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
-
-    private LocalBluetoothManager mBluetoothManager;
-    private CachedBluetoothDeviceManager mCachedDeviceManager;
-    private BluetoothDevice mDevice;
-    private int mType;
-    private String mPairingKey;
-    private EditText mPairingView;
-    private Button mOkButton;
-    private LocalBluetoothProfile mPbapClientProfile;
+    private BluetoothPairingController mBluetoothPairingController;
     private boolean mReceiverRegistered;
 
     /**
      * Dismiss the dialog if the bond state changes to bonded or none,
-     * or if pairing was canceled for {@link #mDevice}.
+     * or if pairing was canceled for {@link BluetoothPairingController#mDevice}.
      */
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -82,14 +45,14 @@
             String action = intent.getAction();
             if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
                 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
-                                                   BluetoothDevice.ERROR);
+                        BluetoothDevice.ERROR);
                 if (bondState == BluetoothDevice.BOND_BONDED ||
                         bondState == BluetoothDevice.BOND_NONE) {
                     dismiss();
                 }
             } else if (BluetoothDevice.ACTION_PAIRING_CANCEL.equals(action)) {
                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                if (device == null || device.equals(mDevice)) {
+                if (device == null || mBluetoothPairingController.deviceEquals(device)) {
                     dismiss();
                 }
             }
@@ -97,72 +60,29 @@
     };
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        boolean fragmentFound = true;
 
+        BluetoothPairingDialogFragment bluetoothFragment =
+                (BluetoothPairingDialogFragment) getFragmentManager()
+                        .findFragmentByTag(FRAGMENT_TAG);
         Intent intent = getIntent();
-        if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
-            Log.e(TAG, "Error: this activity may be started only with intent " +
-                  BluetoothDevice.ACTION_PAIRING_REQUEST);
-            finish();
-            return;
+        mBluetoothPairingController = new BluetoothPairingController(intent, this);
+
+        // check if the fragment exists already
+        if (bluetoothFragment == null) {
+            fragmentFound = false;
+            bluetoothFragment = new BluetoothPairingDialogFragment();
         }
 
-        mBluetoothManager = Utils.getLocalBtManager(this);
-        if (mBluetoothManager == null) {
-            Log.e(TAG, "Error: BluetoothAdapter not supported by system");
-            finish();
-            return;
+        // set the controller
+        bluetoothFragment.setPairingController(mBluetoothPairingController);
+
+        // pass the fragment to the manager when it is created from scratch
+        if (!fragmentFound) {
+            bluetoothFragment.show(getFragmentManager(), FRAGMENT_TAG);
         }
-        mCachedDeviceManager = mBluetoothManager.getCachedDeviceManager();
-        mPbapClientProfile = mBluetoothManager.getProfileManager().getPbapClientProfile();
-
-        mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-        mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
-
-        switch (mType) {
-            case BluetoothDevice.PAIRING_VARIANT_PIN:
-            case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
-            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
-                createUserEntryDialog();
-                break;
-
-            case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
-                int passkey =
-                    intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
-                if (passkey == BluetoothDevice.ERROR) {
-                    Log.e(TAG, "Invalid Confirmation Passkey received, not showing any dialog");
-                    return;
-                }
-                mPairingKey = String.format(Locale.US, "%06d", passkey);
-                createConfirmationDialog();
-                break;
-
-            case BluetoothDevice.PAIRING_VARIANT_CONSENT:
-            case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
-                createConsentDialog();
-                break;
-
-            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
-            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
-                int pairingKey =
-                    intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
-                if (pairingKey == BluetoothDevice.ERROR) {
-                    Log.e(TAG, "Invalid Confirmation Passkey or PIN received, not showing any dialog");
-                    return;
-                }
-                if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
-                    mPairingKey = String.format("%06d", pairingKey);
-                } else {
-                    mPairingKey = String.format("%04d", pairingKey);
-                }
-                createDisplayPasskeyOrPinDialog();
-                break;
-
-            default:
-                Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
-        }
-
         /*
          * Leave this registered through pause/resume since we still want to
          * finish the activity in the background if pairing is canceled.
@@ -172,210 +92,6 @@
         mReceiverRegistered = true;
     }
 
-    private void createUserEntryDialog() {
-        final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.bluetooth_pairing_request,
-                mCachedDeviceManager.getName(mDevice));
-        p.mView = createPinEntryView();
-        p.mPositiveButtonText = getString(android.R.string.ok);
-        p.mPositiveButtonListener = this;
-        p.mNegativeButtonText = getString(android.R.string.cancel);
-        p.mNegativeButtonListener = this;
-        setupAlert();
-
-        mOkButton = mAlert.getButton(BUTTON_POSITIVE);
-        mOkButton.setEnabled(false);
-    }
-
-    private View createPinEntryView() {
-        View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
-        TextView messageViewCaptionHint = (TextView) view.findViewById(R.id.pin_values_hint);
-        TextView messageView2 = (TextView) view.findViewById(R.id.message_below_pin);
-        CheckBox alphanumericPin = (CheckBox) view.findViewById(R.id.alphanumeric_pin);
-        CheckBox contactSharing = (CheckBox) view.findViewById(
-                R.id.phonebook_sharing_message_entry_pin);
-        contactSharing.setText(getString(R.string.bluetooth_pairing_shares_phonebook,
-                mCachedDeviceManager.getName(mDevice)));
-        if (mPbapClientProfile != null && mPbapClientProfile.isProfileReady()) {
-            contactSharing.setVisibility(View.GONE);
-        }
-        if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_ALLOWED) {
-            contactSharing.setChecked(true);
-        } else if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_REJECTED){
-            contactSharing.setChecked(false);
-        } else {
-            if (mDevice.getBluetoothClass().getDeviceClass()
-                    == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
-                contactSharing.setChecked(true);
-                mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
-            } else {
-                contactSharing.setChecked(false);
-                mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
-            }
-        }
-
-        contactSharing.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
-                if (isChecked) {
-                    mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
-                } else {
-                    mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
-                }
-            }
-        });
-
-        mPairingView = (EditText) view.findViewById(R.id.text);
-        mPairingView.addTextChangedListener(this);
-        alphanumericPin.setOnCheckedChangeListener(this);
-
-        int messageId;
-        int messageIdHint = R.string.bluetooth_pin_values_hint;
-        int maxLength;
-        switch (mType) {
-            case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
-                messageIdHint = R.string.bluetooth_pin_values_hint_16_digits;
-                // FALLTHROUGH
-            case BluetoothDevice.PAIRING_VARIANT_PIN:
-                messageId = R.string.bluetooth_enter_pin_other_device;
-                // Maximum of 16 characters in a PIN
-                maxLength = BLUETOOTH_PIN_MAX_LENGTH;
-                break;
-
-            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
-                messageId = R.string.bluetooth_enter_passkey_other_device;
-                // Maximum of 6 digits for passkey
-                maxLength = BLUETOOTH_PASSKEY_MAX_LENGTH;
-                alphanumericPin.setVisibility(View.GONE);
-                break;
-
-            default:
-                Log.e(TAG, "Incorrect pairing type for createPinEntryView: " + mType);
-                return null;
-        }
-
-        messageViewCaptionHint.setText(messageIdHint);
-        messageView2.setText(messageId);
-        mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
-        mPairingView.setFilters(new InputFilter[] {
-                new LengthFilter(maxLength) });
-
-        return view;
-    }
-
-    private View createView() {
-        View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_confirm, null);
-        TextView pairingViewCaption = (TextView) view.findViewById(R.id.pairing_caption);
-        TextView pairingViewContent = (TextView) view.findViewById(R.id.pairing_subhead);
-        TextView messagePairing = (TextView) view.findViewById(R.id.pairing_code_message);
-        CheckBox contactSharing = (CheckBox) view.findViewById(
-                R.id.phonebook_sharing_message_confirm_pin);
-        contactSharing.setText(getString(R.string.bluetooth_pairing_shares_phonebook,
-                mCachedDeviceManager.getName(mDevice)));
-        if (mPbapClientProfile != null && mPbapClientProfile.isProfileReady()) {
-            contactSharing.setVisibility(View.GONE);
-        }
-        if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_ALLOWED) {
-            contactSharing.setChecked(true);
-        } else if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_REJECTED){
-            contactSharing.setChecked(false);
-        } else {
-            if (mDevice.getBluetoothClass().getDeviceClass()
-                    == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
-                contactSharing.setChecked(true);
-                mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
-            } else {
-                contactSharing.setChecked(false);
-                mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
-            }
-        }
-
-        contactSharing.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
-                if (isChecked) {
-                    mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
-                } else {
-                    mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
-                }
-            }
-        });
-
-        SpannableString pairingContent = null;
-        switch (mType) {
-            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
-            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
-                messagePairing.setVisibility(View.VISIBLE);
-            case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION: {
-                pairingContent = new SpannableString(mPairingKey);
-                pairingContent.setSpan(new TtsSpan.DigitsBuilder(mPairingKey).build(),
-                        0, mPairingKey.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-                break;
-            }
-            case BluetoothDevice.PAIRING_VARIANT_CONSENT:
-                messagePairing.setVisibility(view.GONE);
-                break;
-            case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
-                messagePairing.setVisibility(View.VISIBLE);
-                break;
-
-            default:
-                Log.e(TAG, "Incorrect pairing type received, not creating view");
-                return null;
-        }
-
-        if (pairingContent != null) {
-            pairingViewCaption.setVisibility(View.VISIBLE);
-            pairingViewContent.setVisibility(View.VISIBLE);
-            pairingViewContent.setText(pairingContent);
-        }
-
-        return view;
-    }
-
-    private void createConfirmationDialog() {
-        final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.bluetooth_pairing_request,
-                mCachedDeviceManager.getName(mDevice));
-        p.mView = createView();
-        p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
-        p.mPositiveButtonListener = this;
-        p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
-        p.mNegativeButtonListener = this;
-        setupAlert();
-    }
-
-    private void createConsentDialog() {
-        final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.bluetooth_pairing_request,
-                mCachedDeviceManager.getName(mDevice));
-        p.mView = createView();
-        p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
-        p.mPositiveButtonListener = this;
-        p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
-        p.mNegativeButtonListener = this;
-        setupAlert();
-    }
-
-    private void createDisplayPasskeyOrPinDialog() {
-        final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.bluetooth_pairing_request,
-                mCachedDeviceManager.getName(mDevice));
-        p.mView = createView();
-        p.mNegativeButtonText = getString(android.R.string.cancel);
-        p.mNegativeButtonListener = this;
-        setupAlert();
-
-        // Since its only a notification, send an OK to the framework,
-        // indicating that the dialog has been displayed.
-        if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
-            mDevice.setPairingConfirmation(true);
-        } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
-            byte[] pinBytes = BluetoothDevice.convertPinToBytes(mPairingKey);
-            mDevice.setPin(pinBytes);
-        }
-    }
-
     @Override
     protected void onDestroy() {
         super.onDestroy();
@@ -385,95 +101,9 @@
         }
     }
 
-    public void afterTextChanged(Editable s) {
-        if (mOkButton != null) {
-            if (mType == BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS) {
-                mOkButton.setEnabled(s.length() >= 16);
-            } else {
-                mOkButton.setEnabled(s.length() > 0);
-            }
-        }
-    }
-
-    private void onPair(String value) {
-        Log.i(TAG, "Pairing dialog accepted");
-        switch (mType) {
-            case BluetoothDevice.PAIRING_VARIANT_PIN:
-            case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
-                byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
-                if (pinBytes == null) {
-                    return;
-                }
-                mDevice.setPin(pinBytes);
-                break;
-
-            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
-                int passkey = Integer.parseInt(value);
-                mDevice.setPasskey(passkey);
-                break;
-
-            case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
-            case BluetoothDevice.PAIRING_VARIANT_CONSENT:
-                mDevice.setPairingConfirmation(true);
-                break;
-
-            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
-            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
-                // Do nothing.
-                break;
-
-            case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
-                mDevice.setRemoteOutOfBandData();
-                break;
-
-            default:
-                Log.e(TAG, "Incorrect pairing type received");
-        }
-    }
-
-    private void onCancel() {
-        Log.i(TAG, "Pairing dialog canceled");
-        mDevice.cancelPairingUserInput();
-    }
-
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
-            onCancel();
-        }
-        return super.onKeyDown(keyCode,event);
-    }
-
-    public void onClick(DialogInterface dialog, int which) {
-        switch (which) {
-            case BUTTON_POSITIVE:
-                if (mPairingView != null) {
-                    onPair(mPairingView.getText().toString());
-                } else {
-                    onPair(null);
-                }
-                break;
-
-            case BUTTON_NEGATIVE:
-            default:
-                onCancel();
-                break;
-        }
-    }
-
-    /* Not used */
-    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-    }
-
-    /* Not used */
-    public void onTextChanged(CharSequence s, int start, int before, int count) {
-    }
-
-    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-        // change input type for soft keyboard to numeric or alphanumeric
-        if (isChecked) {
-            mPairingView.setInputType(InputType.TYPE_CLASS_TEXT);
-        } else {
-            mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
+    private void dismiss() {
+        if (!isFinishing()) {
+            finish();
         }
     }
 }
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java
new file mode 100644
index 0000000..d4247c0
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2016 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.bluetooth;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.InputFilter.LengthFilter;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.TextView;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * A dialogFragment used by {@link BluetoothPairingDialog} to create an appropriately styled dialog
+ * for the bluetooth device.
+ */
+public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment implements
+        TextWatcher, OnClickListener {
+
+    private static final String TAG = "BTPairingDialogFragment";
+
+    private AlertDialog.Builder mBuilder;
+    private BluetoothPairingController mPairingController;
+    private AlertDialog mDialog;
+    private EditText mPairingView;
+
+    /**
+     * The interface we expect a listener to implement. Typically this should be done by
+     * the controller.
+     */
+    public interface BluetoothPairingDialogListener {
+
+        void onDialogNegativeClick(BluetoothPairingDialogFragment dialog);
+
+        void onDialogPositiveClick(BluetoothPairingDialogFragment dialog);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        if (mPairingController == null) {
+            throw new IllegalStateException(
+                    "Must call setPairingController() before showing dialog");
+        }
+        mBuilder = new AlertDialog.Builder(getActivity());
+        mDialog = setupDialog();
+        mDialog.setCanceledOnTouchOutside(false);
+        return mDialog;
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        // enable the positive button when we detect potentially valid input
+        Button positiveButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
+        if (positiveButton != null) {
+            positiveButton.setEnabled(mPairingController.isPasskeyValid(s));
+        }
+        // notify the controller about user input
+        mPairingController.updateUserInput(s.toString());
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            mPairingController.onDialogPositiveClick(this);
+        } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+            mPairingController.onDialogNegativeClick(this);
+        }
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.BLUETOOTH_DIALOG_FRAGMENT;
+    }
+
+    /**
+     * Sets the controller that the fragment should use. this method MUST be called
+     * before you try to show the dialog or an error will be thrown. An implementation
+     * of a pairing controller can be found at {@link BluetoothPairingController}.
+     */
+    public void setPairingController(BluetoothPairingController pairingController) {
+        mPairingController = pairingController;
+    }
+
+    /**
+     * Creates the appropriate type of dialog and returns it.
+     */
+    private AlertDialog setupDialog() {
+        AlertDialog dialog;
+        switch (mPairingController.getDialogType()) {
+            case BluetoothPairingController.USER_ENTRY_DIALOG:
+                dialog = createUserEntryDialog();
+                break;
+            case BluetoothPairingController.CONFIRMATION_DIALOG:
+                dialog = createConsentDialog();
+                break;
+            case BluetoothPairingController.DISPLAY_PASSKEY_DIALOG:
+                dialog = createDisplayPasskeyOrPinDialog();
+                break;
+            default:
+                dialog = null;
+                Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
+        }
+        return dialog;
+    }
+
+    /**
+     * Returns a dialog with UI elements that allow a user to provide input.
+     */
+    private AlertDialog createUserEntryDialog() {
+        mBuilder.setTitle(getString(R.string.bluetooth_pairing_request,
+                mPairingController.getDeviceName()));
+        mBuilder.setView(createPinEntryView());
+        mBuilder.setPositiveButton(getString(android.R.string.ok), this);
+        mBuilder.setNegativeButton(getString(android.R.string.cancel), this);
+        AlertDialog dialog = mBuilder.create();
+        dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(false);
+        return dialog;
+    }
+
+    /**
+     * Creates the custom view with UI elements for user input.
+     */
+    private View createPinEntryView() {
+        View view = getActivity().getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
+        TextView messageViewCaptionHint = (TextView) view.findViewById(R.id.pin_values_hint);
+        TextView messageView2 = (TextView) view.findViewById(R.id.message_below_pin);
+        CheckBox alphanumericPin = (CheckBox) view.findViewById(R.id.alphanumeric_pin);
+        CheckBox contactSharing = (CheckBox) view.findViewById(
+                R.id.phonebook_sharing_message_entry_pin);
+        contactSharing.setText(getString(R.string.bluetooth_pairing_shares_phonebook,
+                mPairingController.getDeviceName()));
+        EditText pairingView = (EditText) view.findViewById(R.id.text);
+
+        contactSharing.setVisibility(mPairingController.isProfileReady()
+                ? View.GONE : View.VISIBLE);
+        contactSharing.setOnCheckedChangeListener(mPairingController);
+        contactSharing.setChecked(mPairingController.getContactSharingState());
+
+        mPairingView = pairingView;
+
+        pairingView.addTextChangedListener(this);
+        alphanumericPin.setOnCheckedChangeListener((buttonView, isChecked) -> {
+            // change input type for soft keyboard to numeric or alphanumeric
+            if (isChecked) {
+                mPairingView.setInputType(InputType.TYPE_CLASS_TEXT);
+            } else {
+                mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
+            }
+        });
+
+        int messageId = mPairingController.getDeviceVariantMessageID();
+        int messageIdHint = mPairingController.getDeviceVariantMessageHint();
+        int maxLength = mPairingController.getDeviceMaxPasskeyLength();
+        alphanumericPin.setVisibility(mPairingController.pairingCodeIsAlphanumeric()
+                ? View.VISIBLE : View.GONE);
+
+        messageViewCaptionHint.setText(messageIdHint);
+        messageView2.setText(messageId);
+        pairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
+        pairingView.setFilters(new InputFilter[]{
+                new LengthFilter(maxLength)});
+
+        return view;
+    }
+
+    /**
+     * Creates a dialog with UI elements that allow the user to confirm a pairing request.
+     */
+    private AlertDialog createConfirmationDialog() {
+        mBuilder.setTitle(getString(R.string.bluetooth_pairing_request,
+                mPairingController.getDeviceName()));
+        mBuilder.setView(createView());
+        mBuilder.setPositiveButton(getString(R.string.bluetooth_pairing_accept),
+                this);
+        mBuilder.setNegativeButton(getString(R.string.bluetooth_pairing_decline),
+                this);
+        AlertDialog dialog = mBuilder.create();
+        return dialog;
+    }
+
+    /**
+     * Creates a dialog with UI elements that allow the user to consent to a pairing request.
+     */
+    private AlertDialog createConsentDialog() {
+        return createConfirmationDialog();
+    }
+
+    /**
+     * Creates a dialog that informs users of a pairing request and shows them the passkey/pin
+     * of the device.
+     */
+    private AlertDialog createDisplayPasskeyOrPinDialog() {
+        mBuilder.setTitle(getString(R.string.bluetooth_pairing_request,
+                mPairingController.getDeviceName()));
+        mBuilder.setView(createView());
+        mBuilder.setNegativeButton(getString(android.R.string.cancel), this);
+        AlertDialog dialog = mBuilder.create();
+
+        // Tell the controller the dialog has been created.
+        mPairingController.notifyDialogDisplayed();
+
+        return dialog;
+    }
+
+    /**
+     * Creates a custom view for dialogs which need to show users additional information but do
+     * not require user input.
+     */
+    private View createView() {
+        View view = getActivity().getLayoutInflater().inflate(R.layout.bluetooth_pin_confirm, null);
+        TextView pairingViewCaption = (TextView) view.findViewById(R.id.pairing_caption);
+        TextView pairingViewContent = (TextView) view.findViewById(R.id.pairing_subhead);
+        TextView messagePairing = (TextView) view.findViewById(R.id.pairing_code_message);
+        CheckBox contactSharing = (CheckBox) view.findViewById(
+                R.id.phonebook_sharing_message_confirm_pin);
+        contactSharing.setText(getString(R.string.bluetooth_pairing_shares_phonebook,
+                mPairingController.getDeviceName()));
+
+        contactSharing.setVisibility(
+                mPairingController.isProfileReady() ? View.GONE : View.VISIBLE);
+        contactSharing.setChecked(mPairingController.getContactSharingState());
+        contactSharing.setOnCheckedChangeListener(mPairingController);
+
+        messagePairing.setVisibility(mPairingController.isDisplayPairingKeyVariant()
+                ? View.VISIBLE : View.GONE);
+        if (mPairingController.hasPairingContent()) {
+            pairingViewCaption.setVisibility(View.VISIBLE);
+            pairingViewContent.setVisibility(View.VISIBLE);
+            pairingViewContent.setText(mPairingController.getPairingContent());
+        }
+        return view;
+    }
+
+}
diff --git a/src/com/android/settings/gestures/GestureSettings.java b/src/com/android/settings/gestures/GestureSettings.java
index ad9955c..a8f70d9 100644
--- a/src/com/android/settings/gestures/GestureSettings.java
+++ b/src/com/android/settings/gestures/GestureSettings.java
@@ -20,9 +20,8 @@
 import android.content.res.Resources;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.os.Build;
 import android.os.Bundle;
-import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.SearchIndexableResource;
 import android.provider.Settings.Secure;
 import android.support.v7.preference.Preference;
@@ -32,6 +31,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.search.BaseSearchIndexProvider;
@@ -59,6 +59,8 @@
 
     private List<GesturePreference> mPreferences;
 
+    private AmbientDisplayConfiguration mAmbientConfig;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -76,16 +78,16 @@
         }
 
         // Ambient Display
-        boolean dozeEnabled = isDozeAvailable(context);
-        if (dozeEnabled && isPickupAvailable(context)) {
-            int pickup = Secure.getInt(getContentResolver(), Secure.DOZE_PULSE_ON_PICK_UP, 1);
-            addPreference(PREF_KEY_PICK_UP, pickup != 0);
+        mAmbientConfig = new AmbientDisplayConfiguration(context);
+        if (mAmbientConfig.pulseOnPickupAvailable()) {
+            boolean pickup = mAmbientConfig.pulseOnPickupEnabled(UserHandle.myUserId());
+            addPreference(PREF_KEY_PICK_UP, pickup);
         } else {
             removePreference(PREF_KEY_PICK_UP);
         }
-        if (dozeEnabled && isDoubleTapAvailable(context)) {
-            int doubleTap = Secure.getInt(getContentResolver(), Secure.DOZE_PULSE_ON_DOUBLE_TAP, 1);
-            addPreference(PREF_KEY_DOUBLE_TAP_SCREEN, doubleTap != 0);
+        if (mAmbientConfig.pulseOnDoubleTapAvailable()) {
+            boolean doubleTap = mAmbientConfig.pulseOnDoubleTapEnabled(UserHandle.myUserId());
+            addPreference(PREF_KEY_DOUBLE_TAP_SCREEN, doubleTap);
         } else {
             removePreference(PREF_KEY_DOUBLE_TAP_SCREEN);
         }
@@ -178,15 +180,6 @@
                 com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled);
     }
 
-    private static boolean isDozeAvailable(Context context) {
-        String name = Build.IS_DEBUGGABLE ? SystemProperties.get(DEBUG_DOZE_COMPONENT) : null;
-        if (TextUtils.isEmpty(name)) {
-            name = context.getResources().getString(
-                    com.android.internal.R.string.config_dozeComponent);
-        }
-        return !TextUtils.isEmpty(name);
-    }
-
     private static boolean isSystemUINavigationAvailable(Context context) {
         return context.getResources().getBoolean(
                 com.android.internal.R.bool.config_supportSystemNavigationKeys);
@@ -202,11 +195,6 @@
                 R.string.gesture_double_twist_sensor_vendor);
     }
 
-    private static boolean isPickupAvailable(Context context) {
-        return hasSensor(context, R.string.gesture_pickup_sensor_name,
-                R.string.gesture_pickup_sensor_vendor);
-    }
-
     private static boolean hasSensor(Context context, int nameResId, int vendorResId) {
         Resources resources = context.getResources();
         String name = resources.getString(nameResId);
@@ -223,11 +211,6 @@
         return false;
     }
 
-    private static boolean isDoubleTapAvailable(Context context) {
-        return context.getResources().getBoolean(
-                R.bool.config_gesture_double_tap_settings_enabled);
-    }
-
     private void addPreference(String key, boolean enabled) {
         GesturePreference preference = (GesturePreference) findPreference(key);
         preference.setChecked(enabled);
@@ -253,13 +236,15 @@
             @Override
             public List<String> getNonIndexableKeys(Context context) {
                 ArrayList<String> result = new ArrayList<String>();
+                AmbientDisplayConfiguration ambientConfig
+                        = new AmbientDisplayConfiguration(context);
                 if (!isCameraDoubleTapPowerGestureAvailable(context.getResources())) {
                     result.add(PREF_KEY_DOUBLE_TAP_POWER);
                 }
-                if (!isDozeAvailable(context) || !isPickupAvailable(context)) {
+                if (!ambientConfig.pulseOnPickupAvailable()) {
                     result.add(PREF_KEY_PICK_UP);
                 }
-                if (!isDozeAvailable(context) || !isDoubleTapAvailable(context)) {
+                if (!ambientConfig.pulseOnDoubleTapAvailable()) {
                     result.add(PREF_KEY_DOUBLE_TAP_SCREEN);
                 }
                 if (!isSystemUINavigationAvailable(context)) {