We now have a "connecting" state in the ControlActivity.
This commit is contained in:
@@ -39,22 +39,20 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class BluetoothLeService extends Service {
|
public class BluetoothLeService extends Service {
|
||||||
public final static String ACTION_GATT_CONNECTED = BluetoothLeService.class.getName() + ".ACTION_GATT_CONNECTED";
|
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_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_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 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_DATA_RAW = BluetoothLeService.class.getName() + ".EXTRA_DATA_RAW";
|
||||||
public final static String EXTRA_UUID_CHAR = BluetoothLeService.class.getName() + ".EXTRA_UUID_CHAR";
|
public final static String EXTRA_UUID_CHAR = BluetoothLeService.class.getName() + ".EXTRA_UUID_CHAR";
|
||||||
private final static String TAG = BluetoothLeService.class.getSimpleName();
|
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 final IBinder mBinder = new LocalBinder();
|
||||||
private BluetoothManager mBluetoothManager;
|
private BluetoothManager mBluetoothManager;
|
||||||
private BluetoothAdapter mBluetoothAdapter;
|
private BluetoothAdapter mBluetoothAdapter;
|
||||||
private String mBluetoothDeviceAddress;
|
private String mBluetoothDeviceAddress;
|
||||||
private BluetoothGatt mBluetoothGatt;
|
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,
|
// Implements callback methods for GATT events that the app cares about. For example,
|
||||||
// connection change and services discovered.
|
// connection change and services discovered.
|
||||||
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
|
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
|
||||||
@@ -64,28 +62,35 @@ public class BluetoothLeService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
|
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
|
public void onConnectionStateChange(final BluetoothGatt gatt,
|
||||||
final String intentAction;
|
final int status,
|
||||||
|
final int newState) {
|
||||||
|
|
||||||
|
Log.d(TAG, "onConnectionStateChange: status=" + status + ", newState=" + newState);
|
||||||
|
|
||||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
intentAction = ACTION_GATT_CONNECTED;
|
setConnectionState(State.CONNECTED, true);
|
||||||
mConnectionState = STATE_CONNECTED;
|
|
||||||
broadcastUpdate(intentAction);
|
|
||||||
Log.i(TAG, "Connected to GATT server.");
|
Log.i(TAG, "Connected to GATT server.");
|
||||||
// Attempts to discover services after successful connection.
|
// Attempts to discover services after successful connection.
|
||||||
Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices());
|
Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices());
|
||||||
|
|
||||||
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||||
intentAction = ACTION_GATT_DISCONNECTED;
|
// Make sure we tidy up. On certain devices reusing a Gatt after a disconnection
|
||||||
mConnectionState = STATE_DISCONNECTED;
|
// can cause problems.
|
||||||
|
disconnect();
|
||||||
|
|
||||||
|
setConnectionState(State.DISCONNECTED, true);
|
||||||
Log.i(TAG, "Disconnected from GATT server.");
|
Log.i(TAG, "Disconnected from GATT server.");
|
||||||
broadcastUpdate(intentAction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,37 +144,73 @@ public class BluetoothLeService extends Service {
|
|||||||
* callback.
|
* callback.
|
||||||
*/
|
*/
|
||||||
public boolean connect(final String address) {
|
public boolean connect(final String address) {
|
||||||
|
|
||||||
|
final boolean retVal;
|
||||||
if (mBluetoothAdapter == null || address == null) {
|
if (mBluetoothAdapter == null || address == null) {
|
||||||
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
|
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
|
||||||
return false;
|
retVal = false;
|
||||||
}
|
|
||||||
|
|
||||||
// Previously connected device. Try to reconnect.
|
// Previously connected device. Try to reconnect.
|
||||||
if (mBluetoothDeviceAddress != null
|
} else if (mBluetoothDeviceAddress != null
|
||||||
&& address.equals(mBluetoothDeviceAddress)
|
&& address.equals(mBluetoothDeviceAddress)
|
||||||
&& mBluetoothGatt != null) {
|
&& mBluetoothGatt != null) {
|
||||||
|
|
||||||
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
|
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
|
||||||
if (mBluetoothGatt.connect()) {
|
if (mBluetoothGatt.connect()) {
|
||||||
mConnectionState = STATE_CONNECTING;
|
Log.d(TAG, "Connection attempt OK.");
|
||||||
return true;
|
setConnectionState(State.CONNECTING, true);
|
||||||
|
retVal = true;
|
||||||
} else {
|
} 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);
|
return retVal;
|
||||||
if (device == null) {
|
}
|
||||||
Log.w(TAG, "Device not found. Unable to connect.");
|
|
||||||
return false;
|
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.
|
if (broadCast) {
|
||||||
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
|
Log.i(TAG, "Broadcasting " + broadcastAction);
|
||||||
Log.d(TAG, "Trying to create a new connection.");
|
broadcastUpdate(broadcastAction);
|
||||||
mBluetoothDeviceAddress = address;
|
}
|
||||||
mConnectionState = STATE_CONNECTING;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,6 +225,9 @@ public class BluetoothLeService extends Service {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mBluetoothGatt.disconnect();
|
mBluetoothGatt.disconnect();
|
||||||
|
|
||||||
|
// Reusing a Gatt after disconnecting can cause problems
|
||||||
|
mBluetoothGatt = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -271,4 +315,10 @@ public class BluetoothLeService extends Service {
|
|||||||
return BluetoothLeService.this;
|
return BluetoothLeService.this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
DISCONNECTED,
|
||||||
|
CONNECTING,
|
||||||
|
CONNECTED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+52
-17
@@ -121,7 +121,8 @@ public class DeviceControlActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
private BluetoothLeDevice mDevice;
|
private BluetoothLeDevice mDevice;
|
||||||
private boolean mConnected = false;
|
private State mCurrentState = State.DISCONNECTED;
|
||||||
|
|
||||||
private String mExportString;
|
private String mExportString;
|
||||||
// Handles various events fired by the Service.
|
// Handles various events fired by the Service.
|
||||||
// ACTION_GATT_CONNECTED: connected to a GATT server.
|
// 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) {
|
public void onReceive(final Context context, final Intent intent) {
|
||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
|
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
|
||||||
mConnected = true;
|
updateConnectionState(State.CONNECTED);
|
||||||
updateConnectionState(R.string.connected);
|
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
|
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
|
||||||
mConnected = false;
|
|
||||||
updateConnectionState(R.string.disconnected);
|
|
||||||
invalidateOptionsMenu();
|
|
||||||
clearUI();
|
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)) {
|
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
|
||||||
// Show all the supported services and characteristics on the user interface.
|
// Show all the supported services and characteristics on the user interface.
|
||||||
displayGattServices(mBluetoothLeService.getSupportedGattServices());
|
displayGattServices(mBluetoothLeService.getSupportedGattServices());
|
||||||
@@ -159,6 +162,7 @@ public class DeviceControlActivity extends AppCompatActivity {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private void clearUI() {
|
private void clearUI() {
|
||||||
|
mExportString = null;
|
||||||
mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
|
mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
|
||||||
mGattUUID.setText(R.string.no_data);
|
mGattUUID.setText(R.string.no_data);
|
||||||
mGattUUIDDesc.setText(R.string.no_data);
|
mGattUUIDDesc.setText(R.string.no_data);
|
||||||
@@ -206,12 +210,26 @@ public class DeviceControlActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(final Menu menu) {
|
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.gatt_services, menu);
|
getMenuInflater().inflate(R.menu.gatt_services, menu);
|
||||||
if (mConnected) {
|
|
||||||
menu.findItem(R.id.menu_connect).setVisible(false);
|
switch (mCurrentState) {
|
||||||
menu.findItem(R.id.menu_disconnect).setVisible(true);
|
|
||||||
} else {
|
case DISCONNECTED:
|
||||||
menu.findItem(R.id.menu_connect).setVisible(true);
|
menu.findItem(R.id.menu_connect).setVisible(true);
|
||||||
menu.findItem(R.id.menu_disconnect).setVisible(false);
|
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) {
|
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() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final int colourId;
|
final int colourId;
|
||||||
|
final int resId;
|
||||||
|
|
||||||
switch (resourceId) {
|
switch (state) {
|
||||||
case R.string.connected:
|
case CONNECTED:
|
||||||
colourId = android.R.color.holo_green_dark;
|
colourId = android.R.color.holo_green_dark;
|
||||||
|
resId = R.string.connected;
|
||||||
break;
|
break;
|
||||||
case R.string.disconnected:
|
case DISCONNECTED:
|
||||||
colourId = android.R.color.holo_red_dark;
|
colourId = android.R.color.holo_red_dark;
|
||||||
|
resId = R.string.disconnected;
|
||||||
|
break;
|
||||||
|
case CONNECTING:
|
||||||
|
colourId = android.R.color.black;
|
||||||
|
resId = R.string.connecting;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
colourId = android.R.color.black;
|
colourId = android.R.color.black;
|
||||||
|
resId = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
mConnectionState.setText(resourceId);
|
mConnectionState.setText(resId);
|
||||||
mConnectionState.setTextColor(ContextCompat.getColor(DeviceControlActivity.this, colourId));
|
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_DISCONNECTED);
|
||||||
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
|
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
|
||||||
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
|
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
|
||||||
|
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTING);
|
||||||
return intentFilter;
|
return intentFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,4 +353,10 @@ public class DeviceControlActivity extends AppCompatActivity {
|
|||||||
intent.putExtra(DeviceControlActivity.EXTRA_DEVICE, device);
|
intent.putExtra(DeviceControlActivity.EXTRA_DEVICE, device);
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
DISCONNECTED,
|
||||||
|
CONNECTING,
|
||||||
|
CONNECTED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<string name="app_name">Bluetooth LE Scanner</string>
|
<string name="app_name">Bluetooth LE Scanner</string>
|
||||||
<string name="connected">Connected</string>
|
<string name="connected">Connected</string>
|
||||||
<string name="disconnected">Disconnected</string>
|
<string name="disconnected">Disconnected</string>
|
||||||
|
<string name="connecting">Connecting</string>
|
||||||
<string name="invalid_device_data">Invalid Device Data!</string>
|
<string name="invalid_device_data">Invalid Device Data!</string>
|
||||||
<string name="no_data">No data</string>
|
<string name="no_data">No data</string>
|
||||||
<string name="not_supported">Not supported</string>
|
<string name="not_supported">Not supported</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user