Bluetooth: Dismiss pairing dialog on user click
* Existing pairing dialog should be dismissed when user clicks on Yes/No * In a pairing session with multiple pairing dialogs, this is necessary as otherwise the second pairing dialog will not be shown * Modified unit test to test this behavior * Launch pairing dialog as UserHandle.CURRENT to avoid Context warnings Bug: 35833536 Test: make, unit test, pair with Bluetooth devices Change-Id: I1823b78d287134505f59eab7caca2329ecc3a36f
This commit is contained in:
@@ -24,16 +24,17 @@ import android.content.Context;
|
||||
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 @@ public final class BluetoothPairingDialog extends Activity {
|
||||
@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 @@ public final class BluetoothPairingDialog extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
private void dismiss() {
|
||||
@VisibleForTesting
|
||||
void dismiss() {
|
||||
if (!isFinishing()) {
|
||||
BluetoothPairingDialogFragment bluetoothFragment =
|
||||
(BluetoothPairingDialogFragment) getFragmentManager()
|
||||
.findFragmentByTag(FRAGMENT_TAG);
|
||||
if (bluetoothFragment != null) {
|
||||
bluetoothFragment.dismiss();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@@ -47,6 +47,7 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i
|
||||
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,10 +62,14 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
if (mPairingController == null) {
|
||||
if (!isPairingControllerSet()) {
|
||||
throw new IllegalStateException(
|
||||
"Must call setPairingController() before showing dialog");
|
||||
}
|
||||
if (!isPairingDialogActivitySet()) {
|
||||
throw new IllegalStateException(
|
||||
"Must call setPairingDialogActivity() before showing dialog");
|
||||
}
|
||||
mBuilder = new AlertDialog.Builder(getActivity());
|
||||
mDialog = setupDialog();
|
||||
mDialog.setCanceledOnTouchOutside(false);
|
||||
@@ -97,6 +102,7 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i
|
||||
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
|
||||
mPairingController.onDialogNegativeClick(this);
|
||||
}
|
||||
mPairingDialogActivity.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,14 +125,41 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i
|
||||
* 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");
|
||||
}
|
||||
mPairingController = pairingController;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@@ -52,7 +52,7 @@ public final class BluetoothPairingRequest extends BroadcastReceiver {
|
||||
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);
|
||||
|
@@ -54,9 +54,13 @@ public class BluetoothPairingDialogTest {
|
||||
@Mock
|
||||
private BluetoothPairingController controller;
|
||||
|
||||
@Mock
|
||||
private BluetoothPairingDialog dialogActivity;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
doNothing().when(dialogActivity).dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -214,6 +218,17 @@ public class BluetoothPairingDialogTest {
|
||||
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 @@ public class BluetoothPairingDialogTest {
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user