diff --git a/.idea/misc.xml b/.idea/misc.xml index 3d21ecd..54207cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,7 +37,7 @@ - + diff --git a/Application/src/main/AndroidManifest.xml b/Application/src/main/AndroidManifest.xml index f99e75c..e56cc40 100644 --- a/Application/src/main/AndroidManifest.xml +++ b/Application/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - - - - - - + PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) + --> + - - - + + + - - - - + + + - - + + + + + - + \ No newline at end of file diff --git a/Application/src/main/java/fr/centralesupelec/students/clientble/BluetoothLeService.java b/Application/src/main/java/fr/centralesupelec/students/clientble/BluetoothLeService.java index 9ac9c31..1cddf6a 100644 --- a/Application/src/main/java/fr/centralesupelec/students/clientble/BluetoothLeService.java +++ b/Application/src/main/java/fr/centralesupelec/students/clientble/BluetoothLeService.java @@ -34,6 +34,7 @@ import android.os.IBinder; import android.util.Log; import java.util.List; +import java.util.StringTokenizer; import java.util.UUID; /** @@ -64,10 +65,6 @@ public class BluetoothLeService extends Service { public final static String EXTRA_DATA = "fr.centralesupelec.students.clientble.EXTRA_DATA"; - /* TODO - public final static UUID UUID_HEART_RATE_MEASUREMENT = - UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); - */ // Implements callback methods for GATT events that the app cares about. For example, // connection change and services discovered. @@ -113,11 +110,13 @@ public class BluetoothLeService extends Service { @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + Log.d(TAG, "onCharacteristicChanged() appelé."); broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } }; private void broadcastUpdate(final String action) { + Log.d(TAG, "broadcastUpdate(String) appelé."); final Intent intent = new Intent(action); sendBroadcast(intent); } @@ -125,7 +124,7 @@ public class BluetoothLeService extends Service { private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { final Intent intent = new Intent(action); - + Log.d(TAG, "broadcastUpdate(String, BluetoothGattChar.) appelé."); /* TODO // This is special handling for the Heart Rate Measurement profile. Data parsing is // carried out as per profile specifications: @@ -148,10 +147,18 @@ public class BluetoothLeService extends Service { // For all other profiles, writes the data formatted in HEX. final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { - final StringBuilder stringBuilder = new StringBuilder(data.length); - for(byte byteChar : data) - stringBuilder.append(String.format("%02X ", byteChar)); - intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); + if (SampleGattAttributes.SENSOR_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) { + int value = (data[0]<<8)&0x0000ff00 | (data[1]<<0)&0x000000ff; + intent.putExtra(EXTRA_DATA, String.format("%d", value)); + } else { + final StringBuilder stringBuilder = new StringBuilder(data.length); + //stringBuilder.append(String.format("%d", data));/ + //stringBuilder.append(" --- "); + for (byte byteChar : data) + stringBuilder.append(String.format("%02X ", byteChar)); + Log.d(TAG, String.format(stringBuilder.toString())); + intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); + } } /* TODO } @@ -301,6 +308,7 @@ public class BluetoothLeService extends Service { Log.w(TAG, "BluetoothAdapter not initialized"); return; } + Log.d(TAG, "setChar.Notification() appelé"); mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); /* TODO @@ -322,7 +330,11 @@ public class BluetoothLeService extends Service { */ public List getSupportedGattServices() { if (mBluetoothGatt == null) return null; - return mBluetoothGatt.getServices(); } + + public BluetoothGattService getPrivateService() { + if (mBluetoothGatt == null) return null; + return mBluetoothGatt.getService(SampleGattAttributes.PRIVATE_SERVICE_UUID); + } } diff --git a/Application/src/main/java/fr/centralesupelec/students/clientble/DeviceScanActivity.java b/Application/src/main/java/fr/centralesupelec/students/clientble/DeviceScanActivity.java index b983c5e..e72fcb3 100644 --- a/Application/src/main/java/fr/centralesupelec/students/clientble/DeviceScanActivity.java +++ b/Application/src/main/java/fr/centralesupelec/students/clientble/DeviceScanActivity.java @@ -180,14 +180,25 @@ public class DeviceScanActivity extends ListActivity { protected void onListItemClick(ListView l, View v, int position, long id) { final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position); if (device == null) return; - final Intent intent = new Intent(this, DeviceControlActivity.class); - intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName()); - intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress()); - if (mScanning) { - mBluetoothAdapter.stopLeScan(mLeScanCallback); - mScanning = false; + if (true) { + final Intent intent = new Intent(this, DeviceControlActivity.class); + intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName()); + intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress()); + if (mScanning) { + mBluetoothAdapter.stopLeScan(mLeScanCallback); + mScanning = false; + } + startActivity(intent); + } else { + final Intent intent = new Intent(this, SimpleDetailActivity.class); + intent.putExtra(SimpleDetailActivity.EXTRAS_DEVICE_NAME, device.getName()); + intent.putExtra(SimpleDetailActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress()); + if (mScanning) { + mBluetoothAdapter.stopLeScan(mLeScanCallback); + mScanning = false; + } + startActivity(intent); } - startActivity(intent); } private void scanLeDevice(final boolean enable) { diff --git a/Application/src/main/java/fr/centralesupelec/students/clientble/SampleGattAttributes.java b/Application/src/main/java/fr/centralesupelec/students/clientble/SampleGattAttributes.java index 7cea0c3..c7c0e1d 100644 --- a/Application/src/main/java/fr/centralesupelec/students/clientble/SampleGattAttributes.java +++ b/Application/src/main/java/fr/centralesupelec/students/clientble/SampleGattAttributes.java @@ -18,12 +18,21 @@ package fr.centralesupelec.students.clientble; import java.util.HashMap; +import java.util.UUID; /** * This class includes a small subset of standard GATT attributes for demonstration purposes. */ public class SampleGattAttributes { private static HashMap attributes = new HashMap(); + public static final String PRIVATE_SERVICE_UUID_STRING = + "11223344-5566-7788-9900-aabbccddeeff"; + public static final UUID PRIVATE_SERVICE_UUID = + UUID.fromString(PRIVATE_SERVICE_UUID_STRING); + public static final String SENSOR_CHARACTERISTIC_UUID_STRING = + "01020304-0506-0708-0900-0a0b0c0d0e0f"; + public static final UUID SENSOR_CHARACTERISTIC_UUID = + UUID.fromString(SENSOR_CHARACTERISTIC_UUID_STRING); static { // Sample Services. @@ -32,8 +41,7 @@ public class SampleGattAttributes { attributes.put("00001800-0000-1000-8000-00805f9b34fb", "Generic Access"); attributes.put("0000180f-0000-1000-8000-00805f9b34fb", "Battery Service"); - attributes.put("11223344-5566-7788-9900-aabbccddeeff", "Private Service"); - + attributes.put(PRIVATE_SERVICE_UUID_STRING, "Private Service"); // Sample Characteristics. attributes.put("00002a19-0000-1000-8000-00805f9b34fb", "Battery Level"); @@ -42,7 +50,7 @@ public class SampleGattAttributes { attributes.put("00002a04-0000-1000-8000-00805f9b34fb", "Peripheral Preferred Connection Parameters"); attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name"); - attributes.put("01020304-0506-0708-0900-0a0b0c0d0e0f", "5-byte r char."); + attributes.put(SENSOR_CHARACTERISTIC_UUID_STRING, "5-byte r notif. sensor value"); attributes.put("ff020304-0506-0708-0900-0a0b0c0d0e0f", "3-byte rw notif. char."); } diff --git a/Application/src/main/java/fr/centralesupelec/students/clientble/SimpleDetailActivity.java b/Application/src/main/java/fr/centralesupelec/students/clientble/SimpleDetailActivity.java new file mode 100644 index 0000000..28303ae --- /dev/null +++ b/Application/src/main/java/fr/centralesupelec/students/clientble/SimpleDetailActivity.java @@ -0,0 +1,209 @@ +package fr.centralesupelec.students.clientble; + +import android.app.Activity; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.TextView; + +public class SimpleDetailActivity extends Activity { + private final static String TAG = SimpleDetailActivity.class.getSimpleName(); + + public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME"; + public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS"; + + private TextView mDeviceAddressView; + private TextView mConnectionStateView; + private TextView mSensorValueView; + + private String mDeviceName; + private String mDeviceAddress; + + + private BluetoothLeService mBluetoothLeService; + private boolean mConnected = false; + private BluetoothGattCharacteristic mSensorValueCharac; + + + // Code to manage Service lifecycle. + private final ServiceConnection mServiceConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName componentName, IBinder service) { + mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService(); + if (!mBluetoothLeService.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth"); + finish(); + } + // Automatically connects to the device upon successful start-up initialization. + mBluetoothLeService.connect(mDeviceAddress); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + mBluetoothLeService = null; + } + }; + + // Handles various events fired by the Service. + // ACTION_GATT_CONNECTED: connected to a GATT server. + // ACTION_GATT_DISCONNECTED: disconnected from a GATT server. + // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services. + // ACTION_DATA_AVAILABLE: received data from the device. This can be a result of read + // or notification operations. + private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { + mConnected = true; + updateConnectionState(R.string.connected); + invalidateOptionsMenu(); + } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { + mConnected = false; + updateConnectionState(R.string.disconnected); + invalidateOptionsMenu(); + clearUI(); + } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { + // Show all the supported services and characteristics on the user interface. + Log.d(TAG, "ACTION_GATT_SERVICES_DISCOVERED reçu."); + displayValues(); + } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { + Log.d(TAG, "ACTION_DATA_AVAILABLE reçu."); + final String data = intent.getStringExtra(BluetoothLeService.EXTRA_DATA); + Log.d(TAG, data); + displayData(data); + } + } + }; + + private void clearUI() { + mSensorValueView.setText(R.string.no_data); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.simple_detail_layout); + + final Intent intent = getIntent(); + + mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME); + + mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS); + mDeviceAddressView = (TextView) findViewById(R.id.device_address); + mDeviceAddressView.setText(mDeviceAddress); + + mConnectionStateView = (TextView) findViewById(R.id.connection_state); + + mSensorValueView = (TextView) findViewById(R.id.sensor_value); + + getActionBar().setTitle(mDeviceName); + getActionBar().setDisplayHomeAsUpEnabled(true); + Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); + bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE); + } + + + @Override + protected void onResume() { + super.onResume(); + registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter()); + if (mBluetoothLeService != null) { + final boolean result = mBluetoothLeService.connect(mDeviceAddress); + Log.d(TAG, "Connect request result=" + result); + } + } + + @Override + protected void onPause() { + super.onPause(); + unregisterReceiver(mGattUpdateReceiver); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unbindService(mServiceConnection); + mBluetoothLeService = null; + } + + @Override + public boolean onCreateOptionsMenu(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); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_connect: + mBluetoothLeService.connect(mDeviceAddress); + return true; + case R.id.menu_disconnect: + mBluetoothLeService.disconnect(); + return true; + case android.R.id.home: + onBackPressed(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void updateConnectionState(final int resourceId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + mConnectionStateView.setText(resourceId); + } + }); + } + + private void displayData(String data) { + if (data != null) { + mSensorValueView.setText(data); + } + } + + private void displayValues() { + BluetoothGattService privateService = mBluetoothLeService.getPrivateService(); + if (privateService == null) { + Log.w(TAG, "Service Gatt privé non détecté."); + return; + } + mSensorValueCharac = + privateService.getCharacteristic(SampleGattAttributes.SENSOR_CHARACTERISTIC_UUID); + final int charaProp = mSensorValueCharac.getProperties(); + mBluetoothLeService.readCharacteristic(mSensorValueCharac); + if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { + Log.d(TAG, "Demande de notification."); + mBluetoothLeService.setCharacteristicNotification(mSensorValueCharac, true); + } + } + + private static IntentFilter makeGattUpdateIntentFilter() { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); + intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); + return intentFilter; + } +} diff --git a/Application/src/main/res/layout/simple_detail_layout.xml b/Application/src/main/res/layout/simple_detail_layout.xml new file mode 100644 index 0000000..5511b28 --- /dev/null +++ b/Application/src/main/res/layout/simple_detail_layout.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Application/src/main/res/values/strings.xml b/Application/src/main/res/values/strings.xml index 19f3dce..9148452 100644 --- a/Application/src/main/res/values/strings.xml +++ b/Application/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ - -