/*
Copyright 2014 Diogo Gomes <diogogomes@gmail.com>
This file is part of OpenVidonn.
OpenVidonn is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
OpenVidonn is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Foobar. If not, see http://www.gnu.org/licenses/.
*/
package com.diogogomes.openvidonn.app;
import android.app.Fragment;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.util.Pair;
import android.widget.Toast;
import com.diogogomes.openvidonn.app.model.Alarm;
import com.diogogomes.openvidonn.app.model.DayHistory;
import com.diogogomes.openvidonn.app.model.History;
import com.diogogomes.openvidonn.app.model.Movement;
import com.diogogomes.openvidonn.app.model.PersonalInfo;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Service for managing connection and data communication with a GATT server hosted on a
* given Bluetooth LE device.
*/
public class BluetoothLeService extends Service {
private final static String TAG = BluetoothLeService.class.getSimpleName();
private History hist = new History();
private HashMap<String, HashMap<String, BluetoothGattCharacteristic>> mGattCharacteristics = new HashMap<String, HashMap<String, BluetoothGattCharacteristic>>();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private String mBluetoothDeviceAddress;
private BluetoothGatt mBluetoothGatt;
private int mConnectionState = STATE_DISCONNECTED;
private boolean mScanning = false;
private Handler mHandler = new Handler();
private static final long RECONNECT_PERIOD = 10000;
enum BluetoothCommunicationType {READ, WRITE};
private BlockingQueue<Pair<BluetoothCommunicationType, BluetoothGattCharacteristic>> bluetoothCommunicationQueue = new LinkedBlockingQueue<Pair<BluetoothCommunicationType, BluetoothGattCharacteristic>>();
public final static int REQUEST_ENABLE_BT = 1;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
public final static String CANT_CONNECT_VIDONN = "com.diogogomes.openvidonn.cant_connect_vidonn";
public final static String NEW_BLUETOOTH_DEVICE_FOUND = "com.diogogomes.openvidonn.new_bluetooth_device_found";
public final static String BLUETOOTH_DEVICE = "com.diogogomes.openvidonn.args.bluetooth_device";
public final static String ACTION_GATT_CONNECTED = "com.diogogomes.bluetooth.le.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED = "com.diogogomes.bluetooth.le.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.diogogomes.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE = "com.diogogomes.bluetooth.le.ACTION_DATA_AVAILABLE";
public final static String VIDONN_MOVEMENT = "com.diogogomes.openvidonn.MOVEMENT";
public final static String VIDONN_HISTORY = "com.diogogomes.openvidonn.HISTORY";
public final static String VIDONN_PERSONAL_INFO = "com.diogogomes.openvidonn.PERSONAL_INFO";
public final static String VIDONN_ALARMS = "com.diogogomes.openvidonn.ALARMS";
public static final String BATTERY_LEVEL = "com.diogogomes.openvidonn.BATTERY_LEVEL";
public boolean isScanning() {
return mScanning;
}
public boolean isConnected() {
Log.d(TAG, "mConnectionState = " + mConnectionState);
return mConnectionState == STATE_CONNECTED;
}
// Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.d(TAG, "onConnectionStateChange(" + gatt.getDevice().getName() + ", " + status + ", "+ newState+")");
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
bluetoothCommunicationQueue.clear();
broadcastUpdate(intentAction);
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;
Log.i(TAG, "Disconnected from GATT server.");
bluetoothCommunicationQueue.clear();
broadcastUpdate(intentAction);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> gattServices = getSupportedGattServices();
if (gattServices == null) return;
mGattCharacteristics = new HashMap<String, HashMap<String, BluetoothGattCharacteristic>>();
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
HashMap<String,BluetoothGattCharacteristic> charas = new HashMap<String, BluetoothGattCharacteristic>();
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
charas.put(gattCharacteristic.getUuid().toString(), gattCharacteristic);
//Log.e(TAG, charas.toString() + " - Properties: " + gattCharacteristic.getProperties() + "Permissions: "+ gattCharacteristic.getPermissions());
}
mGattCharacteristics.put(gattService.getUuid().toString(), charas);
}
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
Log.d(TAG, "onCharacteristicChanged()");
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
bluetoothCommunicationQueue.remove();
processNextInQueue();
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
Log.d(TAG, "onCharacteristicWrite()");
bluetoothCommunicationQueue.remove();
processNextInQueue();
}
private void processNextInQueue() {
if(bluetoothCommunicationQueue.isEmpty()) return;
Pair<BluetoothCommunicationType, BluetoothGattCharacteristic> e = bluetoothCommunicationQueue.element();
switch (e.first) {
case READ:
if(!mBluetoothGatt.readCharacteristic(e.second)) {// Skiping characteristics that fail (usually due to permissions...)
Log.e(TAG, "error reading " + e.second.getUuid());
bluetoothCommunicationQueue.remove();
processNextInQueue();
}
break;
case WRITE:
if(!mBluetoothGatt.writeCharacteristic(e.second)) {
Log.e(TAG, "error writing " + e.second.getUuid());
bluetoothCommunicationQueue.remove();
processNextInQueue();
}
break;
}
}
};
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
if(characteristic.getService().getUuid().toString().equals(VidonnGattAttributes.VIDONN_SERVICE)) {
broadcastUpdateVidonn(action, characteristic);
} else if(characteristic.getService().getUuid().toString().equals(VidonnGattAttributes.BATTERY_SERVICE)) {
final Intent intent = new Intent(action);
if(VidonnGattAttributes.BATTERY_LEVEL.equals(characteristic.getUuid().toString())) {
int batt_level = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
Log.i(TAG, "Battery Level = " + batt_level);
intent.putExtra(BATTERY_LEVEL, batt_level);
}
sendBroadcast(intent);
}
}
private Calendar decodeDate(final int date ) {
int year = ((date & 0x7e00) >> 9) + 2000;
int month = ((date & 0x01e0) >> 5);
int day = (date & 0x001f) + 1;
return new GregorianCalendar(year, month, day);
}
private byte calculateChecksum(byte [] packet) {
byte chksum = 0;
for(int i=1; i<packet.length -1; i++) {
chksum += (i ^ packet[i]);
}
return chksum;
}
private boolean verifyChecksum(byte [] packet) {
byte chksum = calculateChecksum(packet);
if(packet[packet.length-1] == chksum)
return true;
return false;
}
private void broadcastUpdateVidonn(String action, BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
if(!verifyChecksum(characteristic.getValue())) return;
if (VidonnGattAttributes.VIDONN_MOVEMENT.equals(characteristic.getUuid().toString())) {
final int date = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 1);
final Calendar cal = decodeDate(date);
final int hour = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 3);
final int minute = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 4);
final int second = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 5);
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, second);
final int steps = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 6);
final int distance = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 10) / 10;
final int calories = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 14) / 100;
Movement move = new Movement(cal, steps, distance, calories);
Log.i(TAG, "Movement = "+ move);
intent.putExtra(VIDONN_MOVEMENT,move );
} else if(VidonnGattAttributes.VIDONN_HISTORY.equals(characteristic.getUuid().toString())) {
final int index = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1);
final int page = (index & 0xF); // 0 to 7 i
final int day = (index >> 4 & 0xF); // 0 to 6 j
final int date = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 2);
final Calendar cal = decodeDate(date);
int data[] = new int[6];
for(int i=0; i<data.length; i++)
data[i] = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 4+i*2);
if( page < 4 ) {
DayHistory dh = hist.addEntry(cal, History.ENTRY_TYPE.STEPS, page, data);
//intent.putExtra(VIDONN_HISTORY_DAY, dh);
} else {
hist.addEntry(cal, History.ENTRY_TYPE.DISTANCE, page-4, data); // -4 to reset page to 0
if(!hist.isComplete()) {
return; //lets avoid redrawing the interface if there is nothing new to show
}
}
intent.putExtra(VIDONN_HISTORY, hist);
} else if(VidonnGattAttributes.VIDONN_PERSONAL_INFO.equals(characteristic.getUuid().toString())) {
final int height = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1);
final int weight = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 2);
final int gender = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 3);
final int age = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 4);
PersonalInfo pinfo = new PersonalInfo(height, weight, gender, age);
Log.i(TAG, "PersonalInfo = " + pinfo.toString());
intent.putExtra(VIDONN_PERSONAL_INFO, pinfo);
} else if(VidonnGattAttributes.VIDONN_ALARM.equals(characteristic.getUuid().toString())) {
int record = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1);
Log.d(TAG, "Got alarms for record = " + record);
ArrayList<Alarm> alarms = new ArrayList<Alarm>();
for(int i=0; i<4; i++) {
byte level = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 2+i*4).byteValue();
byte week = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 3+i*4).byteValue();
boolean enabled = (week & 0x80) == 0 ? false : true;
int hour = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 4+i*4);
int minutes = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 5+i*4);
alarms.add(new Alarm(hour, minutes, week, enabled, level, record * 100 + i));
}
intent.putParcelableArrayListExtra(VIDONN_ALARMS, alarms);
}
sendBroadcast(intent);
}
public class LocalBinder extends Binder {
BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind()");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind()");
// After using a given device, you should make sure that BluetoothGatt.close() is called
// such that resources are cleaned up properly. In this particular example, close() is
// invoked when the UI is disconnected from the Service.
close();
return super.onUnbind(intent);
}
private final IBinder mBinder = new LocalBinder();
/**
* Initializes a reference to the local Bluetooth adapter.
*
* @return Return true if the initialization is successful.
*/
public boolean initialize() {
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
if(!mBluetoothAdapter.isEnabled()) {
Log.e(TAG, "BluetoothAdapter is not enabled");
return false;
}
return true;
}
public void scanLeDevice(final boolean start) {
if (start) {
if(mScanning) return;
if(isConnected())
disconnect(); //first release previous device if any
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
if(!mScanning) return;
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
Log.d(TAG, (start ? "start" : "stop") + " scan for LE device => " + (mScanning ? "scanning" : "not scanning"));
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
final Intent intent = new Intent(NEW_BLUETOOTH_DEVICE_FOUND);
intent.putExtra(BLUETOOTH_DEVICE, device);
sendBroadcast(intent);
}
};
/**
* Connects to the GATT server hosted on the Bluetooth LE device.
*
* @param address The device address of the destination device.
*
* @return Return true if the connection is initiated successfully. The connection result
* is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
// Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) {
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
if((mBluetoothManager.getConnectionState(device, BluetoothProfile.GATT) != BluetoothProfile.STATE_CONNECTED)) {
Log.d(TAG, "ConnectionState = " + mBluetoothManager.getConnectionState(device, BluetoothProfile.GATT));
if (mBluetoothGatt.connect()) {
Log.d(TAG, "Reconnecting...");
mConnectionState = STATE_CONNECTING;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if(mConnectionState == STATE_CONNECTING) {
disconnect();
broadcastUpdate(CANT_CONNECT_VIDONN);
}
}
}, RECONNECT_PERIOD);
return false;
} else {
return false;
}
} else
return true;
}
if (device == null) {
Log.w(TAG, "Device not found. Unable to connect.");
return false;
}
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
Log.d(TAG, "Trying to create a new connection.");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
/**
* Disconnects an existing connection or cancel a pending connection. The disconnection result
* is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public void disconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.disconnect();
}
/**
* After using a given BLE device, the app must call this method to ensure resources are
* released properly.
*/
public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
public void readVidonnCharacteristic(String characteristic) {
if(mGattCharacteristics == null)
return;
BluetoothGattCharacteristic c = mGattCharacteristics.get(VidonnGattAttributes.VIDONN_SERVICE).get(characteristic);
readCharacteristic(c);
}
public void readHistory(boolean force) {
if(!hist.isComplete() || force)
for(int i=0; i<56; i++) {
readVidonnCharacteristic(VidonnGattAttributes.VIDONN_HISTORY);
}
else
readVidonnCharacteristic(VidonnGattAttributes.VIDONN_HISTORY);
}
public void readAlarms() {
readVidonnCharacteristic(VidonnGattAttributes.VIDONN_ALARM);
readVidonnCharacteristic(VidonnGattAttributes.VIDONN_ALARM);
}
public void readBatteryLevel() {
if(mGattCharacteristics == null)
return;
BluetoothGattCharacteristic b = mGattCharacteristics.get(VidonnGattAttributes.BATTERY_SERVICE).get(VidonnGattAttributes.BATTERY_LEVEL);
readCharacteristic(b);
}
public void writeAlarms(ArrayList<Alarm> alarms) {
if(mGattCharacteristics == null)
return;
BluetoothGattCharacteristic c = mGattCharacteristics.get(VidonnGattAttributes.VIDONN_SERVICE).get(VidonnGattAttributes.VIDONN_ALARM);
Log.e(TAG, "writeAlarms:");
byte packet[] = new byte[19];
packet[0] = (byte) 0xF5;
int i = 0;
for(Object a : alarms) {
Alarm alarm = (Alarm) a;
Log.e(TAG, alarm.toStringDebug());
packet[1] = (i<4) ? (byte) 0x00 : (byte) 0x01;
packet[2+(i%4)*4] = alarm.getLevel();
packet[3+(i%4)*4] = alarm.getWeekdays();
packet[3+(i%4)*4] = (byte) (packet[3+(i%4)*4] | (alarm.isEnabled() ? (byte) 0x80 : (byte) 0x00));
packet[4+(i%4)*4] = (byte) alarm.getHour();
packet[5+(i%4)*4] = (byte) alarm.getMinutes();
i++;
if(i%4==0) {
packet[packet.length - 1] = calculateChecksum(packet);
c.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
c.setValue(packet);
writeCharacteristic(c);
}
}
}
public void writeDateTime() {
if(mGattCharacteristics == null)
return;
BluetoothGattCharacteristic c = mGattCharacteristics.get(VidonnGattAttributes.VIDONN_SERVICE).get(VidonnGattAttributes.VIDONN_DATETIME);
Calendar today = Calendar.getInstance();
byte packet[] = new byte[8];
packet[0] = (byte) 0xF5;
packet[1] = (byte) (today.get(Calendar.YEAR) - 2000);
packet[2] = (byte) (today.get(Calendar.MONTH));
packet[3] = (byte) (today.get(Calendar.DAY_OF_MONTH)-1);
packet[4] = (byte) (today.get(Calendar.HOUR_OF_DAY));
packet[5] = (byte) (today.get(Calendar.MINUTE));
packet[6] = (byte) (today.get(Calendar.SECOND));
packet[packet.length-1] = calculateChecksum(packet);
c.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
c.setValue(packet);
writeCharacteristic(c);
}
public void writePersonalInformation(PersonalInfo pinfo) {
if(mGattCharacteristics == null)
return;
BluetoothGattCharacteristic c = mGattCharacteristics.get(VidonnGattAttributes.VIDONN_SERVICE).get(VidonnGattAttributes.VIDONN_PERSONAL_INFO);
if(c == null) {
Log.e(TAG, "Could not access VIDONN_PERSONAL_INFO");
Toast.makeText(getApplication(), R.string.error_writing_to_bracelet, Toast.LENGTH_SHORT).show();
return;
}
Log.d(TAG, "write PersonalInfo = " + pinfo);
byte packet[] = c.getValue();
if(packet == null || packet.length < 5) {
Log.e(TAG, "Invalid characteristic in VIDONN_PERSONAL_INFO ");
Toast.makeText(getApplication(), R.string.error_writing_to_bracelet, Toast.LENGTH_SHORT).show();
return;
}
packet[1] = (byte) pinfo.height;
packet[2] = (byte) pinfo.weight;
packet[3] = (byte) pinfo.getGender();
packet[4] = (byte) pinfo.age;
packet[packet.length-1] = calculateChecksum(packet);
c.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
c.setValue(packet);
writeCharacteristic(c);
}
private boolean communicate(BluetoothCommunicationType type, BluetoothGattCharacteristic characteristic) {
try {
bluetoothCommunicationQueue.put(new Pair<BluetoothCommunicationType, BluetoothGattCharacteristic>(type, characteristic));
} catch (InterruptedException e) {
e.printStackTrace();
}
if(bluetoothCommunicationQueue.size() > 1) return true;
Log.d(TAG, "Last BT communication on the queue");
boolean status =false;
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return status;
}
if(characteristic == null) {
Log.e(TAG, "Null characteristic !?");
return status;
}
switch (type) {
case WRITE:
status= mBluetoothGatt.writeCharacteristic(characteristic);
break;
case READ:
status = mBluetoothGatt.readCharacteristic(characteristic);
break;
}
return status;
}
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
String log = "0x";
for (byte b: characteristic.getValue()){
log+=String.format("%3x", b);
}
Log.i(TAG, "write: " + log);
boolean status = communicate(BluetoothCommunicationType.WRITE, characteristic);
Log.d(TAG, "writeCharacteristic status = " + status);
}
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
boolean status = communicate(BluetoothCommunicationType.READ, characteristic);
if(!status)
Log.e(TAG, "readCharacteristic status = " + status);
}
/**
* Enables or disables notification on a give characteristic.
*
* @param characteristic Characteristic to act on.
* @param enabled If true, enable notification. False otherwise.
*/
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to Heart Rate Measurement.
/* if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
} */
}
/**
* Retrieves a list of supported GATT services on the connected device. This should be
* invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
*
* @return A {@code List} of supported services.
*/
public List<BluetoothGattService> getSupportedGattServices() {
if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getServices();
}
}