Merge "Bluetooth: Dismiss pairing dialog on user click" into oc-dev
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
index ed63fcb..97382c3 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
@@ -24,16 +24,17 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
/**
* 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 Activity {
+public class BluetoothPairingDialog extends Activity {
public static final String FRAGMENT_TAG = "bluetooth.pairing.fragment";
private BluetoothPairingController mBluetoothPairingController;
- private boolean mReceiverRegistered;
+ private boolean mReceiverRegistered = false;
/**
* Dismiss the dialog if the bond state changes to bonded or none,
@@ -62,23 +63,26 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- boolean fragmentFound = true;
-
- BluetoothPairingDialogFragment bluetoothFragment =
- (BluetoothPairingDialogFragment) getFragmentManager()
- .findFragmentByTag(FRAGMENT_TAG);
Intent intent = getIntent();
mBluetoothPairingController = new BluetoothPairingController(intent, this);
-
- // check if the fragment exists already
+ // build the dialog fragment
+ boolean fragmentFound = true;
+ // check if the fragment has been preloaded
+ BluetoothPairingDialogFragment bluetoothFragment =
+ (BluetoothPairingDialogFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
+ // dismiss the fragment if it is already used
+ if (bluetoothFragment != null && (bluetoothFragment.isPairingControllerSet()
+ || bluetoothFragment.isPairingDialogActivitySet())) {
+ bluetoothFragment.dismiss();
+ bluetoothFragment = null;
+ }
+ // build a new fragment if it is null
if (bluetoothFragment == null) {
fragmentFound = false;
bluetoothFragment = new BluetoothPairingDialogFragment();
}
-
- // set the controller
bluetoothFragment.setPairingController(mBluetoothPairingController);
-
+ bluetoothFragment.setPairingDialogActivity(this);
// pass the fragment to the manager when it is created from scratch
if (!fragmentFound) {
bluetoothFragment.show(getFragmentManager(), FRAGMENT_TAG);
@@ -101,8 +105,15 @@
}
}
- private void dismiss() {
+ @VisibleForTesting
+ void dismiss() {
if (!isFinishing()) {
+ BluetoothPairingDialogFragment bluetoothFragment =
+ (BluetoothPairingDialogFragment) getFragmentManager()
+ .findFragmentByTag(FRAGMENT_TAG);
+ if (bluetoothFragment != null) {
+ bluetoothFragment.dismiss();
+ }
finish();
}
}
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java
index 3d786fb..7b8fc6c 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java
@@ -47,6 +47,7 @@
private AlertDialog.Builder mBuilder;
private AlertDialog mDialog;
private BluetoothPairingController mPairingController;
+ private BluetoothPairingDialog mPairingDialogActivity;
private EditText mPairingView;
/**
* The interface we expect a listener to implement. Typically this should be done by
@@ -61,9 +62,13 @@
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- if (mPairingController == null) {
+ if (!isPairingControllerSet()) {
throw new IllegalStateException(
- "Must call setPairingController() before showing dialog");
+ "Must call setPairingController() before showing dialog");
+ }
+ if (!isPairingDialogActivitySet()) {
+ throw new IllegalStateException(
+ "Must call setPairingDialogActivity() before showing dialog");
}
mBuilder = new AlertDialog.Builder(getActivity());
mDialog = setupDialog();
@@ -97,6 +102,7 @@
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
mPairingController.onDialogNegativeClick(this);
}
+ mPairingDialogActivity.dismiss();
}
@Override
@@ -119,8 +125,8 @@
* controller may not be substituted once it is assigned. Forcibly switching a
* controller for a new one will lead to undefined behavior.
*/
- public void setPairingController(BluetoothPairingController pairingController) {
- if (mPairingController != null) {
+ void setPairingController(BluetoothPairingController pairingController) {
+ if (isPairingControllerSet()) {
throw new IllegalStateException("The controller can only be set once. "
+ "Forcibly replacing it will lead to undefined behavior");
}
@@ -128,6 +134,33 @@
}
/**
+ * Checks whether mPairingController is set
+ * @return True when mPairingController is set, False otherwise
+ */
+ boolean isPairingControllerSet() {
+ return mPairingController != null;
+ }
+
+ /**
+ * Sets the BluetoothPairingDialog activity that started this fragment
+ * @param pairingDialogActivity The pairing dialog activty that started this fragment
+ */
+ void setPairingDialogActivity(BluetoothPairingDialog pairingDialogActivity) {
+ if (isPairingDialogActivitySet()) {
+ throw new IllegalStateException("The pairing dialog activity can only be set once");
+ }
+ mPairingDialogActivity = pairingDialogActivity;
+ }
+
+ /**
+ * Checks whether mPairingDialogActivity is set
+ * @return True when mPairingDialogActivity is set, False otherwise
+ */
+ boolean isPairingDialogActivitySet() {
+ return mPairingDialogActivity != null;
+ }
+
+ /**
* Creates the appropriate type of dialog and returns it.
*/
private AlertDialog setupDialog() {
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java
index 96aace9..4d02fd5 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java
@@ -52,7 +52,7 @@
if (powerManager.isInteractive() && shouldShowDialog) {
// Since the screen is on and the BT-related activity is in the foreground,
// just open the dialog
- context.startActivity(pairingIntent);
+ context.startActivityAsUser(pairingIntent, UserHandle.CURRENT);
} else {
// Put up a notification that leads to the dialog
intent.setClass(context, BluetoothPairingService.class);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java
index 90f2106..73f6b84 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDialogTest.java
@@ -54,9 +54,13 @@
@Mock
private BluetoothPairingController controller;
+ @Mock
+ private BluetoothPairingDialog dialogActivity;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ doNothing().when(dialogActivity).dismiss();
}
@Test
@@ -214,6 +218,17 @@
fail("Setting the controller multiple times should throw an exception.");
}
+ @Test(expected = IllegalStateException.class)
+ public void dialogDoesNotAllowSwappingActivity() {
+ // instantiate a fragment
+ BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
+ frag.setPairingDialogActivity(dialogActivity);
+
+ // this should throw an error
+ frag.setPairingDialogActivity(dialogActivity);
+ fail("Setting the dialog activity multiple times should throw an exception.");
+ }
+
@Test
public void dialogPositiveButtonDisabledWhenUserInputInvalid() {
// set the correct dialog type
@@ -342,11 +357,52 @@
.contains(device);
}
+ @Test
+ public void pairingDialogDismissedOnPositiveClick() {
+ // set the dialog variant to confirmation/consent
+ when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
+
+ // we don't care what this does, just that it is called
+ doNothing().when(controller).onDialogPositiveClick(any());
+
+ // build the fragment
+ BluetoothPairingDialogFragment frag = makeFragment();
+
+ // click the button and verify that the controller hook was called
+ frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_POSITIVE);
+
+ verify(controller, times(1)).onDialogPositiveClick(any());
+ verify(dialogActivity, times(1)).dismiss();
+ }
+
+ @Test
+ public void pairingDialogDismissedOnNegativeClick() {
+ // set the dialog variant to confirmation/consent
+ when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
+
+ // we don't care what this does, just that it is called
+ doNothing().when(controller).onDialogNegativeClick(any());
+
+ // build the fragment
+ BluetoothPairingDialogFragment frag = makeFragment();
+
+ // click the button and verify that the controller hook was called
+ frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_NEGATIVE);
+
+ verify(controller, times(1)).onDialogNegativeClick(any());
+ verify(dialogActivity, times(1)).dismiss();
+ }
+
private BluetoothPairingDialogFragment makeFragment() {
BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
+ assertThat(frag.isPairingControllerSet()).isFalse();
frag.setPairingController(controller);
+ assertThat(frag.isPairingDialogActivitySet()).isFalse();
+ frag.setPairingDialogActivity(dialogActivity);
FragmentTestUtil.startFragment(frag);
assertThat(frag.getmDialog()).isNotNull();
+ assertThat(frag.isPairingControllerSet()).isTrue();
+ assertThat(frag.isPairingDialogActivitySet()).isTrue();
return frag;
}
}