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