From 7f15ea3d1e9f5b55931c1263e3744ac68e4f5831 Mon Sep 17 00:00:00 2001 From: Alexandros Schillings Date: Wed, 31 Aug 2016 19:09:38 +0100 Subject: [PATCH] We now have a "connecting" state in the ControlActivity. --- .../btlescan/services/BluetoothLeService.java | 114 +++++++++++++----- .../ui/control/DeviceControlActivity.java | 69 ++++++++--- sample_app/src/main/res/values/strings.xml | 1 + 3 files changed, 135 insertions(+), 49 deletions(-) diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/services/BluetoothLeService.java b/sample_app/src/main/java/uk/co/alt236/btlescan/services/BluetoothLeService.java index 53be95a..2320a13 100644 --- a/sample_app/src/main/java/uk/co/alt236/btlescan/services/BluetoothLeService.java +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/services/BluetoothLeService.java @@ -39,22 +39,20 @@ import java.util.List; */ public class BluetoothLeService extends Service { public final static String ACTION_GATT_CONNECTED = BluetoothLeService.class.getName() + ".ACTION_GATT_CONNECTED"; + public final static String ACTION_GATT_CONNECTING = BluetoothLeService.class.getName() + ".ACTION_GATT_CONNECTING"; public final static String ACTION_GATT_DISCONNECTED = BluetoothLeService.class.getName() + ".ACTION_GATT_DISCONNECTED"; public final static String ACTION_GATT_SERVICES_DISCOVERED = BluetoothLeService.class.getName() + ".ACTION_GATT_SERVICES_DISCOVERED"; public final static String ACTION_DATA_AVAILABLE = BluetoothLeService.class.getName() + ".ACTION_DATA_AVAILABLE"; public final static String EXTRA_DATA_RAW = BluetoothLeService.class.getName() + ".EXTRA_DATA_RAW"; public final static String EXTRA_UUID_CHAR = BluetoothLeService.class.getName() + ".EXTRA_UUID_CHAR"; private final static String TAG = BluetoothLeService.class.getSimpleName(); - - private static final int STATE_DISCONNECTED = 0; - private static final int STATE_CONNECTING = 1; - private static final int STATE_CONNECTED = 2; private final IBinder mBinder = new LocalBinder(); private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private String mBluetoothDeviceAddress; private BluetoothGatt mBluetoothGatt; - private int mConnectionState = STATE_DISCONNECTED; + private State mConnectionState = State.DISCONNECTED; + // Implements callback methods for GATT events that the app cares about. For example, // connection change and services discovered. private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @@ -64,28 +62,35 @@ public class BluetoothLeService extends Service { } @Override - public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) { + public void onCharacteristicRead(final BluetoothGatt gatt, + final BluetoothGattCharacteristic characteristic, + final int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } @Override - public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { - final String intentAction; + public void onConnectionStateChange(final BluetoothGatt gatt, + final int status, + final int newState) { + + Log.d(TAG, "onConnectionStateChange: status=" + status + ", newState=" + newState); + if (newState == BluetoothProfile.STATE_CONNECTED) { - intentAction = ACTION_GATT_CONNECTED; - mConnectionState = STATE_CONNECTED; - broadcastUpdate(intentAction); + setConnectionState(State.CONNECTED, true); Log.i(TAG, "Connected to GATT server."); // Attempts to discover services after successful connection. Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices()); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - intentAction = ACTION_GATT_DISCONNECTED; - mConnectionState = STATE_DISCONNECTED; + // Make sure we tidy up. On certain devices reusing a Gatt after a disconnection + // can cause problems. + disconnect(); + + setConnectionState(State.DISCONNECTED, true); Log.i(TAG, "Disconnected from GATT server."); - broadcastUpdate(intentAction); } } @@ -139,37 +144,73 @@ public class BluetoothLeService extends Service { * callback. */ public boolean connect(final String address) { + + final boolean retVal; if (mBluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); - return false; - } + retVal = false; - // Previously connected device. Try to reconnect. - if (mBluetoothDeviceAddress != null + // Previously connected device. Try to reconnect. + } else if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); if (mBluetoothGatt.connect()) { - mConnectionState = STATE_CONNECTING; - return true; + Log.d(TAG, "Connection attempt OK."); + setConnectionState(State.CONNECTING, true); + retVal = true; } else { - return false; + Log.w(TAG, "Connection attempt failed."); + setConnectionState(State.DISCONNECTED, true); + retVal = false; + } + } else { + + final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); + if (device == null) { + Log.w(TAG, "Device not found. Unable to connect."); + retVal = false; + } else { + // We want to directly connect to the device, so we are setting the autoConnect + // parameter to false. + + Log.d(TAG, "Trying to create a new connection."); + mBluetoothGatt = device.connectGatt(this, false, mGattCallback); + mBluetoothDeviceAddress = address; + setConnectionState(State.CONNECTING, true); + retVal = true; } } - final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); - if (device == null) { - Log.w(TAG, "Device not found. Unable to connect."); - return false; + return retVal; + } + + private synchronized void setConnectionState(final State newState, final boolean broadCast) { + Log.i(TAG, "Setting internal state to " + newState); + mConnectionState = newState; + + final String broadcastAction; + + switch (newState) { + case CONNECTED: + broadcastAction = ACTION_GATT_CONNECTED; + break; + case CONNECTING: + broadcastAction = ACTION_GATT_CONNECTING; + break; + case DISCONNECTED: + broadcastAction = ACTION_GATT_DISCONNECTED; + break; + default: + throw new IllegalArgumentException("Unknown state: " + newState); } - // We want to directly connect to the device, so we are setting the autoConnect - // parameter to false. - mBluetoothGatt = device.connectGatt(this, false, mGattCallback); - Log.d(TAG, "Trying to create a new connection."); - mBluetoothDeviceAddress = address; - mConnectionState = STATE_CONNECTING; - return true; + + if (broadCast) { + Log.i(TAG, "Broadcasting " + broadcastAction); + broadcastUpdate(broadcastAction); + } + } /** @@ -184,6 +225,9 @@ public class BluetoothLeService extends Service { return; } mBluetoothGatt.disconnect(); + + // Reusing a Gatt after disconnecting can cause problems + mBluetoothGatt = null; } /** @@ -271,4 +315,10 @@ public class BluetoothLeService extends Service { return BluetoothLeService.this; } } + + private enum State { + DISCONNECTED, + CONNECTING, + CONNECTED + } } \ No newline at end of file diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/ui/control/DeviceControlActivity.java b/sample_app/src/main/java/uk/co/alt236/btlescan/ui/control/DeviceControlActivity.java index 4f6d756..96ace50 100644 --- a/sample_app/src/main/java/uk/co/alt236/btlescan/ui/control/DeviceControlActivity.java +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/ui/control/DeviceControlActivity.java @@ -121,7 +121,8 @@ public class DeviceControlActivity extends AppCompatActivity { } }; private BluetoothLeDevice mDevice; - private boolean mConnected = false; + private State mCurrentState = State.DISCONNECTED; + private String mExportString; // Handles various events fired by the Service. // ACTION_GATT_CONNECTED: connected to a GATT server. @@ -134,14 +135,16 @@ public class DeviceControlActivity extends AppCompatActivity { public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { - mConnected = true; - updateConnectionState(R.string.connected); + updateConnectionState(State.CONNECTED); invalidateOptionsMenu(); } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { - mConnected = false; - updateConnectionState(R.string.disconnected); - invalidateOptionsMenu(); clearUI(); + updateConnectionState(State.DISCONNECTED); + invalidateOptionsMenu(); + } else if (BluetoothLeService.ACTION_GATT_CONNECTING.equals(action)) { + clearUI(); + updateConnectionState(State.CONNECTING); + invalidateOptionsMenu(); } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { // Show all the supported services and characteristics on the user interface. displayGattServices(mBluetoothLeService.getSupportedGattServices()); @@ -159,6 +162,7 @@ public class DeviceControlActivity extends AppCompatActivity { }; private void clearUI() { + mExportString = null; mGattServicesList.setAdapter((SimpleExpandableListAdapter) null); mGattUUID.setText(R.string.no_data); mGattUUIDDesc.setText(R.string.no_data); @@ -206,12 +210,26 @@ public class DeviceControlActivity extends AppCompatActivity { @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.gatt_services, menu); - if (mConnected) { - menu.findItem(R.id.menu_connect).setVisible(false); - menu.findItem(R.id.menu_disconnect).setVisible(true); - } else { - menu.findItem(R.id.menu_connect).setVisible(true); - menu.findItem(R.id.menu_disconnect).setVisible(false); + + switch (mCurrentState) { + + case DISCONNECTED: + menu.findItem(R.id.menu_connect).setVisible(true); + menu.findItem(R.id.menu_disconnect).setVisible(false); + menu.findItem(R.id.menu_refresh).setActionView(null); + break; + case CONNECTING: + menu.findItem(R.id.menu_connect).setVisible(false); + menu.findItem(R.id.menu_disconnect).setVisible(false); + menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_progress_indeterminate); + break; + case CONNECTED: + menu.findItem(R.id.menu_connect).setVisible(false); + menu.findItem(R.id.menu_disconnect).setVisible(true); + menu.findItem(R.id.menu_refresh).setActionView(null); + break; + default: + throw new IllegalStateException("Don't know how to handle: " + mCurrentState); } if (mExportString == null) { @@ -278,25 +296,35 @@ public class DeviceControlActivity extends AppCompatActivity { } } - private void updateConnectionState(final int resourceId) { + private void updateConnectionState(final State state) { + mCurrentState = state; + runOnUiThread(new Runnable() { @Override public void run() { final int colourId; + final int resId; - switch (resourceId) { - case R.string.connected: + switch (state) { + case CONNECTED: colourId = android.R.color.holo_green_dark; + resId = R.string.connected; break; - case R.string.disconnected: + case DISCONNECTED: colourId = android.R.color.holo_red_dark; + resId = R.string.disconnected; + break; + case CONNECTING: + colourId = android.R.color.black; + resId = R.string.connecting; break; default: colourId = android.R.color.black; + resId = 0; break; } - mConnectionState.setText(resourceId); + mConnectionState.setText(resId); mConnectionState.setTextColor(ContextCompat.getColor(DeviceControlActivity.this, colourId)); } }); @@ -308,6 +336,7 @@ public class DeviceControlActivity extends AppCompatActivity { intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTING); return intentFilter; } @@ -324,4 +353,10 @@ public class DeviceControlActivity extends AppCompatActivity { intent.putExtra(DeviceControlActivity.EXTRA_DEVICE, device); return intent; } + + private enum State { + DISCONNECTED, + CONNECTING, + CONNECTED + } } \ No newline at end of file diff --git a/sample_app/src/main/res/values/strings.xml b/sample_app/src/main/res/values/strings.xml index c0c87be..17d8515 100644 --- a/sample_app/src/main/res/values/strings.xml +++ b/sample_app/src/main/res/values/strings.xml @@ -5,6 +5,7 @@ Bluetooth LE Scanner Connected Disconnected + Connecting Invalid Device Data! No data Not supported