From 875d45aabb11b92b12959d2d952d5324208cad59 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 26 Nov 2015 14:10:32 +0000 Subject: [PATCH] Show error message when pairing fails in SUW. Also, prevent multiple dialogs from coming up, dismiss the dialog when the device goes back into tablet mode and prevent a SysUI crash when we attempt to stop a scan after the bluetooth adapter turns off. Bug: 25662777 Change-Id: Ib99eaecb0b60388e1f1efb89199ac4ffdb281536 --- .../com/android/systemui/keyboard/KeyboardUI.java | 81 ++++++++++++++++++---- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java index ea7270ddb487..4b775a5a61e4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java +++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java @@ -38,7 +38,9 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings.Secure; import android.text.TextUtils; +import android.util.Pair; import android.util.Slog; +import android.widget.Toast; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -46,6 +48,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.bluetooth.Utils; import com.android.systemui.R; import com.android.systemui.SystemUI; @@ -76,8 +79,9 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha private static final int STATE_WAITING_FOR_BLUETOOTH = 4; private static final int STATE_PAIRING = 5; private static final int STATE_PAIRED = 6; - private static final int STATE_USER_CANCELLED = 7; - private static final int STATE_DEVICE_NOT_FOUND = 8; + private static final int STATE_PAIRING_FAILED = 7; + private static final int STATE_USER_CANCELLED = 8; + private static final int STATE_DEVICE_NOT_FOUND = 9; private static final int MSG_INIT = 0; private static final int MSG_ON_BOOT_COMPLETED = 1; @@ -90,6 +94,7 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha private static final int MSG_SHOW_BLUETOOTH_DIALOG = 8; private static final int MSG_DISMISS_BLUETOOTH_DIALOG = 9; private static final int MSG_BLE_ABORT_SCAN = 10; + private static final int MSG_SHOW_ERROR = 11; private volatile KeyboardHandler mHandler; private volatile KeyboardUIHandler mUIHandler; @@ -178,6 +183,7 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha mLocalBluetoothAdapter = bluetoothManager.getBluetoothAdapter(); mProfileManager = bluetoothManager.getProfileManager(); bluetoothManager.getEventManager().registerCallback(new BluetoothCallbackHandler()); + Utils.setErrorListener(new BluetoothErrorListener()); InputManager im = context.getSystemService(InputManager.class); im.registerOnTabletModeChangedListener(this, mHandler); @@ -204,13 +210,15 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha if (mInTabletMode != InputManager.SWITCH_STATE_OFF) { if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) { stopScanning(); + } else if (mState == STATE_WAITING_FOR_BLUETOOTH) { + mUIHandler.sendEmptyMessage(MSG_DISMISS_BLUETOOTH_DIALOG); } mState = STATE_WAITING_FOR_TABLET_MODE_EXIT; return; } final int btState = mLocalBluetoothAdapter.getState(); - if (btState == BluetoothAdapter.STATE_TURNING_ON || btState == BluetoothAdapter.STATE_ON + if ((btState == BluetoothAdapter.STATE_TURNING_ON || btState == BluetoothAdapter.STATE_ON) && mState == STATE_WAITING_FOR_BLUETOOTH) { // If we're waiting for bluetooth but it has come on in the meantime, or is coming // on, just dismiss the dialog. This frequently happens during device startup. @@ -334,7 +342,10 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha private void stopScanning() { if (mScanCallback != null) { - mLocalBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback); + BluetoothLeScanner scanner = mLocalBluetoothAdapter.getBluetoothLeScanner(); + if (scanner != null) { + scanner.stopScan(mScanCallback); + } mScanCallback = null; } } @@ -370,10 +381,14 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha // Should only be called on the handler thread private void onDeviceBondStateChangedInternal(CachedBluetoothDevice d, int bondState) { - if (d.getName().equals(mKeyboardName) && bondState == BluetoothDevice.BOND_BONDED) { - // We don't need to manually connect to the device here because it will automatically - // try to connect after it has been paired. - mState = STATE_PAIRED; + if (mState == STATE_PAIRING && d.getName().equals(mKeyboardName)) { + if (bondState == BluetoothDevice.BOND_BONDED) { + // We don't need to manually connect to the device here because it will + // automatically try to connect after it has been paired. + mState = STATE_PAIRED; + } else if (bondState == BluetoothDevice.BOND_NONE) { + mState = STATE_PAIRING_FAILED; + } } } @@ -385,6 +400,17 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha } } + // Should only be called on the handler thread. We want to be careful not to show errors for + // pairings not initiated by this UI, so we only pop up the toast when we're at an appropriate + // point in our pairing flow and it's the expected device. + private void onShowErrorInternal(Context context, String name, int messageResId) { + if ((mState == STATE_PAIRING || mState == STATE_PAIRING_FAILED) + && mKeyboardName.equals(name)) { + String message = context.getString(messageResId, name); + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + } + } + private final class KeyboardUIHandler extends Handler { public KeyboardUIHandler() { super(Looper.getMainLooper(), null, true /*async*/); @@ -393,19 +419,27 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha public void handleMessage(Message msg) { switch(msg.what) { case MSG_SHOW_BLUETOOTH_DIALOG: { - DialogInterface.OnClickListener listener = new BluetoothDialogClickListener(); + if (mDialog != null) { + // Don't show another dialog if one is already present + break; + } + DialogInterface.OnClickListener clickListener = + new BluetoothDialogClickListener(); + DialogInterface.OnDismissListener dismissListener = + new BluetoothDialogDismissListener(); mDialog = new BluetoothDialog(mContext); mDialog.setTitle(R.string.enable_bluetooth_title); mDialog.setMessage(R.string.enable_bluetooth_message); - mDialog.setPositiveButton(R.string.enable_bluetooth_confirmation_ok, listener); - mDialog.setNegativeButton(android.R.string.cancel, listener); + mDialog.setPositiveButton( + R.string.enable_bluetooth_confirmation_ok, clickListener); + mDialog.setNegativeButton(android.R.string.cancel, clickListener); + mDialog.setOnDismissListener(dismissListener); mDialog.show(); break; } case MSG_DISMISS_BLUETOOTH_DIALOG: { if (mDialog != null) { mDialog.dismiss(); - mDialog = null; } break; } @@ -469,6 +503,10 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha onBleScanFailedInternal(); break; } + case MSG_SHOW_ERROR: { + Pair p = (Pair) msg.obj; + onShowErrorInternal(p.first, p.second, msg.arg1); + } } } } @@ -482,6 +520,14 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha } } + private final class BluetoothDialogDismissListener + implements DialogInterface.OnDismissListener { + @Override + public void onDismiss(DialogInterface dialog) { + mDialog = null; + } + } + private final class KeyboardScanCallback extends ScanCallback { private boolean isDeviceDiscoverable(ScanResult result) { @@ -564,6 +610,13 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { } } + private final class BluetoothErrorListener implements Utils.ErrorListener { + public void onShowError(Context context, String name, int messageResId) { + mHandler.obtainMessage(MSG_SHOW_ERROR, messageResId, 0 /*unused*/, + new Pair<>(context, name)).sendToTarget(); + } + } + private static String stateToString(int state) { switch (state) { case STATE_NOT_ENABLED: @@ -580,13 +633,15 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha return "STATE_PAIRING"; case STATE_PAIRED: return "STATE_PAIRED"; + case STATE_PAIRING_FAILED: + return "STATE_PAIRING_FAILED"; case STATE_USER_CANCELLED: return "STATE_USER_CANCELLED"; case STATE_DEVICE_NOT_FOUND: return "STATE_DEVICE_NOT_FOUND"; case STATE_UNKNOWN: default: - return "STATE_UNKNOWN"; + return "STATE_UNKNOWN (" + state + ")"; } } } -- 2.11.0