Création d’une activité de visualisation

This commit is contained in:
Louis-Guillaume DUBOIS 2017-06-02 16:01:40 +02:00
parent 27a00dd9b5
commit 0496165464
No known key found for this signature in database
GPG key ID: 96472D986598B31E
8 changed files with 345 additions and 40 deletions

View file

@ -37,7 +37,7 @@
<ConfirmationsSetting value="0" id="Add" /> <ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" /> <ConfirmationsSetting value="0" id="Remove" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="JDK" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8 (1)" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View file

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
Copyright 2013 The Android Open Source Project Copyright 2013 The Android Open Source Project
Copyright 2017 Louis-Guillaume Dubois Copyright 2017 Louis-Guillaume Dubois
@ -15,36 +14,48 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.centralesupelec.students.clientble" package="fr.centralesupelec.students.clientble"
android:versionCode="1" android:versionCode="1"
android:versionName="1.0"> android:versionName="1.0">
<!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle --> <!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
<!-- Declare this required feature if you want to make the app available to BLE-capable
<!--
Declare this required feature if you want to make the app available to BLE-capable
devices only. If you want to make your app available to devices that don't support BLE, devices only. If you want to make your app available to devices that don't support BLE,
you should omit this in the manifest. Instead, determine BLE capability by using you should omit this in the manifest. Instead, determine BLE capability by using
PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) --> PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE)
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> -->
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application android:label="@string/app_name" <application
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light"> android:theme="@android:style/Theme.Holo.Light">
<activity android:name=".DeviceScanActivity" <activity
android:name=".DeviceScanActivity"
android:label="@string/app_name"> android:label="@string/app_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".DeviceControlActivity"/> <activity android:name=".DeviceControlActivity" />
<service android:name=".BluetoothLeService" android:enabled="true"/>
<service
android:name=".BluetoothLeService"
android:enabled="true" />
<activity android:name=".SimpleDetailActivity"></activity>
</application> </application>
</manifest> </manifest>

View file

@ -34,6 +34,7 @@ import android.os.IBinder;
import android.util.Log; import android.util.Log;
import java.util.List; import java.util.List;
import java.util.StringTokenizer;
import java.util.UUID; import java.util.UUID;
/** /**
@ -64,10 +65,6 @@ public class BluetoothLeService extends Service {
public final static String EXTRA_DATA = public final static String EXTRA_DATA =
"fr.centralesupelec.students.clientble.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, // Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered. // connection change and services discovered.
@ -113,11 +110,13 @@ public class BluetoothLeService extends Service {
@Override @Override
public void onCharacteristicChanged(BluetoothGatt gatt, public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) { BluetoothGattCharacteristic characteristic) {
Log.d(TAG, "onCharacteristicChanged() appelé.");
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
} }
}; };
private void broadcastUpdate(final String action) { private void broadcastUpdate(final String action) {
Log.d(TAG, "broadcastUpdate(String) appelé.");
final Intent intent = new Intent(action); final Intent intent = new Intent(action);
sendBroadcast(intent); sendBroadcast(intent);
} }
@ -125,7 +124,7 @@ public class BluetoothLeService extends Service {
private void broadcastUpdate(final String action, private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) { final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action); final Intent intent = new Intent(action);
Log.d(TAG, "broadcastUpdate(String, BluetoothGattChar.) appelé.");
/* TODO /* TODO
// This is special handling for the Heart Rate Measurement profile. Data parsing is // This is special handling for the Heart Rate Measurement profile. Data parsing is
// carried out as per profile specifications: // 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. // For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue(); final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) { if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length); if (SampleGattAttributes.SENSOR_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
for(byte byteChar : data) int value = (data[0]<<8)&0x0000ff00 | (data[1]<<0)&0x000000ff;
stringBuilder.append(String.format("%02X ", byteChar)); intent.putExtra(EXTRA_DATA, String.format("%d", value));
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); } 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 /* TODO
} }
@ -301,6 +308,7 @@ public class BluetoothLeService extends Service {
Log.w(TAG, "BluetoothAdapter not initialized"); Log.w(TAG, "BluetoothAdapter not initialized");
return; return;
} }
Log.d(TAG, "setChar.Notification() appelé");
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
/* TODO /* TODO
@ -322,7 +330,11 @@ public class BluetoothLeService extends Service {
*/ */
public List<BluetoothGattService> getSupportedGattServices() { public List<BluetoothGattService> getSupportedGattServices() {
if (mBluetoothGatt == null) return null; if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getServices(); return mBluetoothGatt.getServices();
} }
public BluetoothGattService getPrivateService() {
if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getService(SampleGattAttributes.PRIVATE_SERVICE_UUID);
}
} }

View file

@ -180,14 +180,25 @@ public class DeviceScanActivity extends ListActivity {
protected void onListItemClick(ListView l, View v, int position, long id) { protected void onListItemClick(ListView l, View v, int position, long id) {
final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position); final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
if (device == null) return; if (device == null) return;
final Intent intent = new Intent(this, DeviceControlActivity.class); if (true) {
intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName()); final Intent intent = new Intent(this, DeviceControlActivity.class);
intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress()); intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
if (mScanning) { intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
mBluetoothAdapter.stopLeScan(mLeScanCallback); if (mScanning) {
mScanning = false; 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) { private void scanLeDevice(final boolean enable) {

View file

@ -18,12 +18,21 @@
package fr.centralesupelec.students.clientble; package fr.centralesupelec.students.clientble;
import java.util.HashMap; import java.util.HashMap;
import java.util.UUID;
/** /**
* This class includes a small subset of standard GATT attributes for demonstration purposes. * This class includes a small subset of standard GATT attributes for demonstration purposes.
*/ */
public class SampleGattAttributes { public class SampleGattAttributes {
private static HashMap<String, String> attributes = new HashMap(); private static HashMap<String, String> 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 { static {
// Sample Services. // Sample Services.
@ -32,8 +41,7 @@ public class SampleGattAttributes {
attributes.put("00001800-0000-1000-8000-00805f9b34fb", "Generic Access"); attributes.put("00001800-0000-1000-8000-00805f9b34fb", "Generic Access");
attributes.put("0000180f-0000-1000-8000-00805f9b34fb", "Battery Service"); 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. // Sample Characteristics.
attributes.put("00002a19-0000-1000-8000-00805f9b34fb", "Battery Level"); 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("00002a04-0000-1000-8000-00805f9b34fb", "Peripheral Preferred Connection Parameters");
attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name"); 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."); attributes.put("ff020304-0506-0708-0900-0a0b0c0d0e0f", "3-byte rw notif. char.");
} }

View file

@ -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;
}
}

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_device_address"
android:textSize="18sp"/>
<Space android:layout_width="5dp"
android:layout_height="wrap_content"/>
<TextView android:id="@+id/device_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"/>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_state"
android:textSize="18sp"/>
<Space android:layout_width="5dp"
android:layout_height="wrap_content"/>
<TextView android:id="@+id/connection_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/disconnected"
android:textSize="18sp"/>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_data"
android:textSize="18sp" />
<Space android:layout_width="5dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/sensor_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>

View file

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.