Bluetooth LE example connect to Bluetooth LE device and display GATT Services

Bluetooth LE example connect to Bluetooth LE device and display GATT Services



Last post of Bluetooth LE example show how to "Scan specified BLE devices with ScanFilter". This post show how to connect to the device and display the supported service by the device.


Modify from last "Scan specified BLE devices with ScanFilter". Basically this part copy from "example code of Bluetooth Le Gatt", refer to "Bluetooth Le Gatt example to link with Arduino/Genuino 101".

Create a new activity ControlActivity.java
package com.blogspot.android_er.androidblegatt;

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.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.SimpleExpandableListAdapter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ControlActivity extends AppCompatActivity {

private final static String TAG = ControlActivity.class.getSimpleName();

public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";

private String mDeviceName;
private String mDeviceAddress;

private boolean mConnected = false;
private BluetoothGattCharacteristic mNotifyCharacteristic;
private BluetoothLeService mBluetoothLeService;

TextView textViewState;
private ExpandableListView mGattServicesList;

private final String LIST_NAME = "NAME";
private final String LIST_UUID = "UUID";

private ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics =
new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

// 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("GATT_CONNECTED");
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
mConnected = false;
updateConnectionState("GATT_DISCONNECTED");
clearUI();
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
// Show all the supported services and characteristics on the user interface.
displayGattServices(mBluetoothLeService.getSupportedGattServices());
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));

}
}
};

private void clearUI() {
mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
}

private void updateConnectionState(final String st) {
runOnUiThread(new Runnable() {
@Override
public void run() {
textViewState.setText(st);
}
});
}

private void displayData(String data) {
if (data != null) {
textViewState.setText(data);
}
}

// Demonstrates how to iterate through the supported GATT Services/Characteristics.
// In this sample, we populate the data structure that is bound to the ExpandableListView
// on the UI.
private void displayGattServices(List<BluetoothGattService> gattServices) {

if (gattServices == null) return;
String uuid = null;
String unknownServiceString = "Unknown Service";
String unknownCharaString = "Unknown Characteristic";
ArrayList<HashMap<String, String>> gattServiceData =
new ArrayList<HashMap<String, String>>();
ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
= new ArrayList<ArrayList<HashMap<String, String>>>();
mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
HashMap<String, String> currentServiceData = new HashMap<String, String>();
uuid = gattService.getUuid().toString();
currentServiceData.put(
LIST_NAME, lookup(uuid, unknownServiceString));
currentServiceData.put(LIST_UUID, uuid);
gattServiceData.add(currentServiceData);

ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
new ArrayList<HashMap<String, String>>();
List<BluetoothGattCharacteristic> gattCharacteristics =
gattService.getCharacteristics();
ArrayList<BluetoothGattCharacteristic> charas =
new ArrayList<BluetoothGattCharacteristic>();

// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
charas.add(gattCharacteristic);
HashMap<String, String> currentCharaData = new HashMap<String, String>();
uuid = gattCharacteristic.getUuid().toString();
currentCharaData.put(
LIST_NAME, lookup(uuid, unknownCharaString));
currentCharaData.put(LIST_UUID, uuid);
gattCharacteristicGroupData.add(currentCharaData);

}
mGattCharacteristics.add(charas);
gattCharacteristicData.add(gattCharacteristicGroupData);
}

SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
this,
gattServiceData,
android.R.layout.simple_expandable_list_item_2,
new String[] {LIST_NAME, LIST_UUID},
new int[] { android.R.id.text1, android.R.id.text2 },
gattCharacteristicData,
android.R.layout.simple_expandable_list_item_2,
new String[] {LIST_NAME, LIST_UUID},
new int[] { android.R.id.text1, android.R.id.text2 }
);
mGattServicesList.setAdapter(gattServiceAdapter);
}

// If a given GATT characteristic is selected, check for supported features. This sample
// demonstrates Read and Notify features. See
// http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete
// list of supported characteristic features.
private final ExpandableListView.OnChildClickListener servicesListClickListner =
new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
int childPosition, long id) {
if (mGattCharacteristics != null) {
final BluetoothGattCharacteristic characteristic =
mGattCharacteristics.get(groupPosition).get(childPosition);
final int charaProp = characteristic.getProperties();
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
// If there is an active notification on a characteristic, clear
// it first so it doesnt update the data field on the user interface.
if (mNotifyCharacteristic != null) {
mBluetoothLeService.setCharacteristicNotification(
mNotifyCharacteristic, false);
mNotifyCharacteristic = null;
}
mBluetoothLeService.readCharacteristic(characteristic);
}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
mNotifyCharacteristic = characteristic;
mBluetoothLeService.setCharacteristicNotification(
characteristic, true);
}
return true;
}
return false;
}
};

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_control);

final Intent intent = getIntent();
mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);

TextView textViewDeviceName = (TextView)findViewById(R.id.textDeviceName);
TextView textViewDeviceAddr = (TextView)findViewById(R.id.textDeviceAddress);
textViewState = (TextView)findViewById(R.id.textState);

textViewDeviceName.setText(mDeviceName);
textViewDeviceAddr.setText(mDeviceAddress);

mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list);
mGattServicesList.setOnChildClickListener(servicesListClickListner);

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

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

private static HashMap<String, String> attributes = new HashMap();

public static String lookup(String uuid, String defaultName) {
String name = attributes.get(uuid);
return name == null ? defaultName : name;
}
}


And associated layout, activity_control.xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

android_layout_width="match_parent"
android_layout_height="match_parent"
android_padding="16dp"
android_orientation="vertical"
tools_context="com.blogspot.android_er.androidblegatt.ControlActivity">

<TextView
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="Hello World!" />

<TextView
android_id="@+id/textDeviceName"
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_textSize="20dp"
android_textStyle="bold"/>
<TextView
android_id="@+id/textDeviceAddress"
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_textSize="20dp"
android_textStyle="bold" />
<TextView
android_id="@+id/textState"
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_textSize="20dp"
android_textStyle="bold" />
<ExpandableListView android_id="@+id/gatt_services_list"
android_layout_width="match_parent"
android_layout_height="wrap_content"/>
</LinearLayout>


Modify MainActivity.java to add a button to start ControlActivity.
package com.blogspot.android_er.androidblegatt;

import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScanner mBluetoothLeScanner;

private boolean mScanning;

private static final int RQS_ENABLE_BLUETOOTH = 1;

Button btnScan;
ListView listViewLE;

List<BluetoothDevice> listBluetoothDevice;
ListAdapter adapterLeScanResult;

private Handler mHandler;
private static final long SCAN_PERIOD = 10000;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Check if BLE is supported on the device.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this,
"BLUETOOTH_LE not supported in this device!",
Toast.LENGTH_SHORT).show();
finish();
}

getBluetoothAdapterAndLeScanner();

// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(this,
"bluetoothManager.getAdapter()==null",
Toast.LENGTH_SHORT).show();
finish();
return;
}

btnScan = (Button)findViewById(R.id.scan);
btnScan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
scanLeDevice(true);
}
});
listViewLE = (ListView)findViewById(R.id.lelist);

listBluetoothDevice = new ArrayList<>();
adapterLeScanResult = new ArrayAdapter<BluetoothDevice>(
this, android.R.layout.simple_list_item_1, listBluetoothDevice);
listViewLE.setAdapter(adapterLeScanResult);
listViewLE.setOnItemClickListener(scanResultOnItemClickListener);

mHandler = new Handler();

}

AdapterView.OnItemClickListener scanResultOnItemClickListener =
new AdapterView.OnItemClickListener(){

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final BluetoothDevice device =
(BluetoothDevice) parent.getItemAtPosition(position);

String msg = device.getAddress() + " "
+ device.getBluetoothClass().toString() + " "
+ getBTDevieType(device);

new AlertDialog.Builder(MainActivity.this)
.setTitle(device.getName())
.setMessage(msg)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

}
})
.setNeutralButton("CONNECT", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final Intent intent = new Intent(MainActivity.this,
ControlActivity.class);
intent.putExtra(ControlActivity.EXTRAS_DEVICE_NAME,
device.getName());
intent.putExtra(ControlActivity.EXTRAS_DEVICE_ADDRESS,
device.getAddress());

if (mScanning) {
mBluetoothLeScanner.stopScan(scanCallback);
mScanning = false;
btnScan.setEnabled(true);
}
startActivity(intent);
}
})

.show();

}
};

private String getBTDevieType(BluetoothDevice d){
String type = "";

switch (d.getType()){
case BluetoothDevice.DEVICE_TYPE_CLASSIC:
type = "DEVICE_TYPE_CLASSIC";
break;
case BluetoothDevice.DEVICE_TYPE_DUAL:
type = "DEVICE_TYPE_DUAL";
break;
case BluetoothDevice.DEVICE_TYPE_LE:
type = "DEVICE_TYPE_LE";
break;
case BluetoothDevice.DEVICE_TYPE_UNKNOWN:
type = "DEVICE_TYPE_UNKNOWN";
break;
default:
type = "unknown...";
}

return type;
}

@Override
protected void onResume() {
super.onResume();

if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, RQS_ENABLE_BLUETOOTH);
}
}
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

if (requestCode == RQS_ENABLE_BLUETOOTH && resultCode == Activity.RESULT_CANCELED) {
finish();
return;
}

getBluetoothAdapterAndLeScanner();

// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(this,
"bluetoothManager.getAdapter()==null",
Toast.LENGTH_SHORT).show();
finish();
return;
}

super.onActivityResult(requestCode, resultCode, data);
}

private void getBluetoothAdapterAndLeScanner(){
// Get BluetoothAdapter and BluetoothLeScanner.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

mScanning = false;
}

/*
to call startScan (ScanCallback callback),
Requires BLUETOOTH_ADMIN permission.
Must hold ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get results.
*/
private void scanLeDevice(final boolean enable) {
if (enable) {
listBluetoothDevice.clear();
listViewLE.invalidateViews();

// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothLeScanner.stopScan(scanCallback);
listViewLE.invalidateViews();

Toast.makeText(MainActivity.this,
"Scan timeout",
Toast.LENGTH_LONG).show();

mScanning = false;
btnScan.setEnabled(true);
}
}, SCAN_PERIOD);

//mBluetoothLeScanner.startScan(scanCallback);

//scan specified devices only with ScanFilter
ScanFilter scanFilter =
new ScanFilter.Builder()
.setServiceUuid(BluetoothLeService.ParcelUuid_GENUINO101_ledService)
.build();
List<ScanFilter> scanFilters = new ArrayList<ScanFilter>();
scanFilters.add(scanFilter);

ScanSettings scanSettings =
new ScanSettings.Builder().build();

mBluetoothLeScanner.startScan(scanFilters, scanSettings, scanCallback);

mScanning = true;
btnScan.setEnabled(false);
} else {
mBluetoothLeScanner.stopScan(scanCallback);
mScanning = false;
btnScan.setEnabled(true);
}
}

private ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);

addBluetoothDevice(result.getDevice());
}

@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
for(ScanResult result : results){
addBluetoothDevice(result.getDevice());
}
}

@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Toast.makeText(MainActivity.this,
"onScanFailed: " + String.valueOf(errorCode),
Toast.LENGTH_LONG).show();
}

private void addBluetoothDevice(BluetoothDevice device){
if(!listBluetoothDevice.contains(device)){
listBluetoothDevice.add(device);
listViewLE.invalidateViews();
}
}
};
}


Modify AndroidManifest.xml to add activity of ".ControlActivity".
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="com.blogspot.android_er.androidblegatt">

<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_ADMIN"/>
<uses-permission android_name="android.permission.ACCESS_COARSE_LOCATION"/>

<application
android_allowBackup="true"
android_icon="@mipmap/ic_launcher"
android_label="@string/app_name"
android_supportsRtl="true"
android_theme="@style/AppTheme">
<activity android_name=".MainActivity">
<intent-filter>
<action android_name="android.intent.action.MAIN" />

<category android_name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android_name=".ControlActivity"/>
<service android_name=".BluetoothLeService" android_enabled="true"/>
</application>

</manifest>



~ Bluetooth LE Gatt Example, step-by-step

Comments

Popular posts from this blog

brengos13 linux

Board Game Giveaways!

Boardwalk Empire 2ª Temporada S02E01 HDTV