CGMS refactored to use new BLE Common Library

This commit is contained in:
Aleksander Nowakowski
2018-04-24 23:19:32 +02:00
parent b7aa00fb4f
commit b8ec4f3499
4 changed files with 409 additions and 464 deletions

View File

@@ -28,20 +28,22 @@ import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.SparseArray;
import java.util.Deque;
import java.util.LinkedList;
import java.util.UUID;
import no.nordicsemi.android.ble.BleManager;
import no.nordicsemi.android.ble.Request;
import no.nordicsemi.android.ble.common.callback.RecordAccessControlPointDataCallback;
import no.nordicsemi.android.ble.common.callback.cgm.CGMSpecificOpsControlPointDataCallback;
import no.nordicsemi.android.ble.common.callback.cgm.ContinuousGlucoseMeasurementDataCallback;
import no.nordicsemi.android.ble.common.data.RecordAccessControlPointData;
import no.nordicsemi.android.ble.common.data.cgm.CGMSpecificOpsControlPointData;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.log.LogContract;
import no.nordicsemi.android.nrftoolbox.battery.BatteryManager;
import no.nordicsemi.android.nrftoolbox.parser.CGMMeasurementParser;
import no.nordicsemi.android.nrftoolbox.parser.CGMSpecificOpsControlPointParser;
import no.nordicsemi.android.nrftoolbox.parser.RecordAccessControlPointParser;
import no.nordicsemi.android.nrftoolbox.utility.DebugLogger;
public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
private static final String TAG = "CGMSManager";
@@ -50,6 +52,7 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
* Cycling Speed and Cadence service UUID
*/
public static final UUID CGMS_UUID = UUID.fromString("0000181F-0000-1000-8000-00805f9b34fb");
private static final UUID CGM_FEAURE_UUID = UUID.fromString("00002AA8-0000-1000-8000-00805f9b34fb");
private static final UUID CGM_MEASUREMENT_UUID = UUID.fromString("00002AA7-0000-1000-8000-00805f9b34fb");
private static final UUID CGM_OPS_CONTROL_POINT_UUID = UUID.fromString("00002AAC-0000-1000-8000-00805f9b34fb");
/**
@@ -57,49 +60,15 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
*/
private static final UUID RACP_UUID = UUID.fromString("00002A52-0000-1000-8000-00805f9b34fb");
private static final int OP_CODE_REPORT_STORED_RECORDS = 1;
private static final int OP_CODE_DELETE_STORED_RECORDS = 2;
private static final int OP_CODE_ABORT_OPERATION = 3;
private static final int OP_CODE_REPORT_NUMBER_OF_RECORDS = 4;
private static final int OP_CODE_NUMBER_OF_STORED_RECORDS_RESPONSE = 5;
private static final int OP_CODE_RESPONSE_CODE = 6;
private static final int OPERATOR_NULL = 0;
private static final int OPERATOR_ALL_RECORDS = 1;
private static final int OPERATOR_LESS_THEN_OR_EQUAL = 2;
private static final int OPERATOR_GREATER_THEN_OR_EQUAL = 3;
private static final int OPERATOR_WITHING_RANGE = 4;
private static final int OPERATOR_FIRST_RECORD = 5;
private static final int OPERATOR_LAST_RECORD = 6;
/**
* The filter type is used for range operators ({@link #OPERATOR_LESS_THEN_OR_EQUAL}, {@link #OPERATOR_GREATER_THEN_OR_EQUAL}, {@link #OPERATOR_WITHING_RANGE}.<br/>
* The syntax of the operand is: [Filter Type][Minimum][Maximum].<br/>
* This filter selects the records by the sequence number.
*/
private static final int FILTER_TYPE_SEQUENCE_NUMBER = 1;
/**
* The filter type is used for range operators ({@link #OPERATOR_LESS_THEN_OR_EQUAL}, {@link #OPERATOR_GREATER_THEN_OR_EQUAL}, {@link #OPERATOR_WITHING_RANGE}.<br/>
* The syntax of the operand is: [Filter Type][Minimum][Maximum].<br/>
* This filter selects the records by the user facing time (base time + offset time).
*/
private static final int FILTER_TYPE_USER_FACING_TIME = 2;
private static final int RESPONSE_SUCCESS = 1;
private static final int RESPONSE_OP_CODE_NOT_SUPPORTED = 2;
private static final int RESPONSE_INVALID_OPERATOR = 3;
private static final int RESPONSE_OPERATOR_NOT_SUPPORTED = 4;
private static final int RESPONSE_INVALID_OPERAND = 5;
private static final int RESPONSE_NO_RECORDS_FOUND = 6;
private static final int RESPONSE_ABORT_UNSUCCESSFUL = 7;
private static final int RESPONSE_PROCEDURE_NOT_COMPLETED = 8;
private static final int RESPONSE_OPERAND_NOT_SUPPORTED = 9;
private BluetoothGattCharacteristic mCGMFeatureCharacteristic;
private BluetoothGattCharacteristic mCGMMeasurementCharacteristic;
private BluetoothGattCharacteristic mCGMOpsControlPointCharacteristic;
private BluetoothGattCharacteristic mCGMSpecificOpsControlPointCharacteristic;
private BluetoothGattCharacteristic mRecordAccessControlPointCharacteristic;
private SparseArray<CGMSRecord> mRecords = new SparseArray<>();
private boolean mAbort;
/** A flag set to true if the remote device supports E2E CRC. */
private boolean mSecured;
private long mSessionStartTime;
CGMSManager(final Context context) {
@@ -118,164 +87,151 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
@Override
protected void initialize(@NonNull final BluetoothDevice device) {
// Enable Battery service
super.initialize(device);
enableNotifications(mCGMMeasurementCharacteristic);
enableIndications(mCGMOpsControlPointCharacteristic)
.done(() -> mSessionStartTime = System.currentTimeMillis());
writeCharacteristic(mCGMOpsControlPointCharacteristic, new byte[] { 26 });
enableIndications(mRecordAccessControlPointCharacteristic);
// Enable Continuous Glucose Measurement notifications
enableNotifications(mCGMMeasurementCharacteristic)
.with(new ContinuousGlucoseMeasurementDataCallback() {
@Override
public void onDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data) {
log(LogContract.Log.Level.APPLICATION, "\"" + CGMMeasurementParser.parse(mCGMMeasurementCharacteristic) + "\" received");
super.onDataReceived(device, data);
}
@Override
public void onContinuousGlucoseMeasurementReceived(@NonNull final BluetoothDevice device, final float glucoseConcentration, @Nullable final Float cgmTrend, @Nullable final Float cgmQuality, final CGMStatus status, final int timeOffset, final boolean secured) {
final long timestamp = mSessionStartTime + (timeOffset * 60000L); // Sequence number is in minutes since Start Session
final CGMSRecord record = new CGMSRecord(timeOffset, glucoseConcentration, timestamp);
mCallbacks.onCGMValueReceived(device, record);
}
@Override
public void onContinuousGlucoseMeasurementReceivedWithCrcError(@NonNull final BluetoothDevice device, @NonNull final Data data) {
log(LogContract.Log.Level.WARNING, "Continuous Glucose Measurement record received with CRC error");
}
})
.fail((error) -> log(LogContract.Log.Level.WARNING, "Failed to enable Continuous Glucose Measurement notifications (" + error + ")"));
// Enable CGM Specific Ops indications
enableIndications(mCGMSpecificOpsControlPointCharacteristic)
.with(new CGMSpecificOpsControlPointDataCallback() {
@Override
public void onDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data) {
log(LogContract.Log.Level.APPLICATION, "\"" + CGMSpecificOpsControlPointParser.parse(mCGMSpecificOpsControlPointCharacteristic) + "\" received");
super.onDataReceived(device, data);
}
@Override
public void onCGMSpecificOpsOperationCompleted(@NonNull final BluetoothDevice device, final int requestCode, final boolean secured) {
switch (requestCode) {
case CGM_OP_CODE_START_SESSION:
mSessionStartTime = System.currentTimeMillis();
log(LogContract.Log.Level.APPLICATION, "Session started");
break;
case CGM_OP_CODE_STOP_SESSION:
mSessionStartTime = 0;
log(LogContract.Log.Level.APPLICATION, "Session stopped");
break;
}
}
@Override
public void onCGMSpecificOpsOperationError(@NonNull final BluetoothDevice device, final int requestCode, final int error, final boolean secured) {
switch (requestCode) {
case CGM_OP_CODE_START_SESSION:
mSessionStartTime = 0;
log(LogContract.Log.Level.WARNING, "Session not started, error " + error);
break;
case CGM_OP_CODE_STOP_SESSION:
mSessionStartTime = 0;
log(LogContract.Log.Level.APPLICATION, "Session stopped, error " + error);
break;
}
}
@Override
public void onCGMSpecificOpsResponseReceivedWithCrcError(@NonNull final BluetoothDevice device, @NonNull final Data data) {
log(LogContract.Log.Level.ERROR, "Request failed: CRC error");
}
});
enableIndications(mRecordAccessControlPointCharacteristic)
.with(new RecordAccessControlPointDataCallback() {
@Override
public void onDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data) {
log(LogContract.Log.Level.APPLICATION, "\"" + RecordAccessControlPointParser.parse(mRecordAccessControlPointCharacteristic) + "\" received");
super.onDataReceived(device, data);
}
@Override
public void onRecordAccessOperationCompleted(@NonNull final BluetoothDevice device, final int requestCode) {
switch (requestCode) {
case RACP_OP_CODE_ABORT_OPERATION:
mCallbacks.onOperationAborted(device);
break;
default:
mCallbacks.onOperationCompleted(device);
break;
}
}
@Override
public void onRecordAccessOperationCompletedWithNoRecordsFound(@NonNull final BluetoothDevice device, final int requestCode) {
mCallbacks.onOperationCompleted(device);
}
@Override
public void onNumberOfRecordsReceived(@NonNull final BluetoothDevice device, final int numberOfRecords) {
mCallbacks.onNumberOfRecordsRequested(device, numberOfRecords);
if (numberOfRecords > 0) {
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportNumberOfAllStoredRecords());
} else {
mCallbacks.onOperationCompleted(device);
}
}
@Override
public void onRecordAccessOperationError(@NonNull final BluetoothDevice device, final int requestCode, final int errorCode) {
log(LogContract.Log.Level.WARNING, "Record Access operation failed (error " + errorCode + ")");
if (errorCode == RACP_ERROR_OP_CODE_NOT_SUPPORTED) {
mCallbacks.onOperationNotSupported(device);
} else {
mCallbacks.onOperationFailed(device);
}
}
})
.fail((error) -> log(LogContract.Log.Level.WARNING, "Failed to enabled Record Access Control Point indications (error " + error + ")"));
// Start Continuous Glucose session
writeCharacteristic(mCGMSpecificOpsControlPointCharacteristic, CGMSpecificOpsControlPointData.startSession(mSecured))
.done(() -> log(LogContract.Log.Level.APPLICATION, "\"" + CGMSpecificOpsControlPointParser.parse(mCGMSpecificOpsControlPointCharacteristic) + "\" sent"))
.fail((error) -> log(LogContract.Log.Level.ERROR, "Failed to start session (error " + error + ")"));
}
@Override
protected boolean isRequiredServiceSupported(@NonNull final BluetoothGatt gatt) {
final BluetoothGattService service = gatt.getService(CGMS_UUID);
if (service != null) {
mCGMFeatureCharacteristic = service.getCharacteristic(CGM_FEAURE_UUID);
mCGMMeasurementCharacteristic = service.getCharacteristic(CGM_MEASUREMENT_UUID);
mCGMOpsControlPointCharacteristic = service.getCharacteristic(CGM_OPS_CONTROL_POINT_UUID);
mCGMSpecificOpsControlPointCharacteristic = service.getCharacteristic(CGM_OPS_CONTROL_POINT_UUID);
mRecordAccessControlPointCharacteristic = service.getCharacteristic(RACP_UUID);
}
return mCGMMeasurementCharacteristic != null && mCGMOpsControlPointCharacteristic != null && mRecordAccessControlPointCharacteristic != null;
}
@Override
protected boolean isOptionalServiceSupported(@NonNull final BluetoothGatt gatt) {
super.isOptionalServiceSupported(gatt); // ignore the result
final BluetoothGattService service = gatt.getService(CGMS_UUID);
if (service != null) {
mCGMOpsControlPointCharacteristic = service.getCharacteristic(CGM_OPS_CONTROL_POINT_UUID);
}
return mCGMOpsControlPointCharacteristic != null;
return mCGMFeatureCharacteristic != null && mCGMMeasurementCharacteristic != null &&
mCGMSpecificOpsControlPointCharacteristic != null && mRecordAccessControlPointCharacteristic != null;
}
@Override
protected void onDeviceDisconnected() {
super.onDeviceDisconnected();
mCGMOpsControlPointCharacteristic = null;
mCGMFeatureCharacteristic = null;
mCGMMeasurementCharacteristic = null;
mCGMSpecificOpsControlPointCharacteristic = null;
mRecordAccessControlPointCharacteristic = null;
}
@Override
protected void onCharacteristicWrite(@NonNull final BluetoothGatt gatt, @NonNull final BluetoothGattCharacteristic characteristic) {
if (characteristic.getUuid().equals(RACP_UUID)) {
log(LogContract.Log.Level.APPLICATION, "\"" + RecordAccessControlPointParser.parse(characteristic) + "\" sent");
} else { // uuid == CGM_OPS_CONTROL_POINT_UUID
log(LogContract.Log.Level.APPLICATION, "\"" + CGMSpecificOpsControlPointParser.parse(characteristic) + "\" sent");
}
}
@Override
public void onCharacteristicNotified(@NonNull final BluetoothGatt gatt, @NonNull final BluetoothGattCharacteristic characteristic) {
log(LogContract.Log.Level.APPLICATION, "\"" + CGMMeasurementParser.parse(characteristic) + "\" received");
// CGM Measurement characteristic may have one or more CGM records
int totalSize = characteristic.getValue().length;
int offset = 0;
while (offset < totalSize) {
final int cgmSize = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset);
final float cgmValue = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset + 2);
final int sequenceNumber = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset + 4);
final long timestamp = mSessionStartTime + (sequenceNumber * 60000L); // Sequence number is in minutes since Start Session
//This will send callback to CGMSActivity when new concentration value is received from CGMS device
final CGMSRecord cgmsRecord = new CGMSRecord(sequenceNumber, cgmValue, timestamp);
mRecords.put(cgmsRecord.sequenceNumber, cgmsRecord);
mCallbacks.onCGMValueReceived(gatt.getDevice(), cgmsRecord);
offset += cgmSize;
}
}
@Override
protected void onCharacteristicIndicated(@NonNull final BluetoothGatt gatt, @NonNull final BluetoothGattCharacteristic characteristic) {
if (characteristic.getUuid().equals(RACP_UUID)) {
log(LogContract.Log.Level.APPLICATION, "\"" + RecordAccessControlPointParser.parse(characteristic) + "\" received");
// Record Access Control Point characteristic
int offset = 0;
final int opCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset);
offset += 2; // skip the operator
if (opCode == OP_CODE_NUMBER_OF_STORED_RECORDS_RESPONSE) {
// We've obtained the number of all records
final int number = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset);
mCallbacks.onNumberOfRecordsRequested(gatt.getDevice(), number);
// Request the records
if (number > 0) {
final BluetoothGattCharacteristic racpCharacteristic = mRecordAccessControlPointCharacteristic;
setOpCode(racpCharacteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_ALL_RECORDS);
writeCharacteristic(racpCharacteristic);
} else {
mCallbacks.onOperationCompleted(gatt.getDevice());
}
} else if (opCode == OP_CODE_RESPONSE_CODE) {
final int requestedOpCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset);
final int responseCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset + 1);
DebugLogger.d(TAG, "Response result for: " + requestedOpCode + " is: " + responseCode);
switch (responseCode) {
case RESPONSE_SUCCESS:
if (!mAbort)
mCallbacks.onOperationCompleted(gatt.getDevice());
else
mCallbacks.onOperationAborted(gatt.getDevice());
break;
case RESPONSE_NO_RECORDS_FOUND:
mCallbacks.onOperationCompleted(gatt.getDevice());
break;
case RESPONSE_OP_CODE_NOT_SUPPORTED:
mCallbacks.onOperationNotSupported(gatt.getDevice());
break;
case RESPONSE_PROCEDURE_NOT_COMPLETED:
case RESPONSE_ABORT_UNSUCCESSFUL:
default:
mCallbacks.onOperationFailed(gatt.getDevice());
break;
}
mAbort = false;
}
} else { // uuid == CGM_OPS_CONTROL_POINT_UUID
log(LogContract.Log.Level.APPLICATION, "\"" + CGMSpecificOpsControlPointParser.parse(characteristic) + "\" received");
}
}
};
/**
* Writes given operation parameters to the characteristic
*
* @param characteristic the characteristic to write. This must be the Record Access Control Point characteristic
* @param opCode the operation code
* @param operator the operator (see {@link #OPERATOR_NULL} and others
* @param params optional parameters (one for >=, <=, two for the range, none for other operators)
*/
private void setOpCode(final BluetoothGattCharacteristic characteristic, final int opCode, final int operator, final Integer... params) {
final int size = 2 + ((params.length > 0) ? 1 : 0) + params.length * 2; // 1 byte for opCode, 1 for operator, 1 for filter type (if parameters exists) and 2 for each parameter
characteristic.setValue(new byte[size]);
// write the operation code
int offset = 0;
characteristic.setValue(opCode, BluetoothGattCharacteristic.FORMAT_UINT8, offset);
offset += 1;
// write the operator. This is always present but may be equal to OPERATOR_NULL
characteristic.setValue(operator, BluetoothGattCharacteristic.FORMAT_UINT8, offset);
offset += 1;
// if parameters exists, append them. Parameters should be sorted from minimum to maximum. Currently only one or two params are allowed
if (params.length > 0) {
// our implementation use only sequence number as a filer type
characteristic.setValue(FILTER_TYPE_SEQUENCE_NUMBER, BluetoothGattCharacteristic.FORMAT_UINT8, offset);
offset += 1;
for (final Integer i : params) {
characteristic.setValue(i, BluetoothGattCharacteristic.FORMAT_UINT16, offset);
offset += 2;
}
}
}
/**
* Returns a list of CGM records obtained from this device. The key in the array is the
*/
@@ -288,12 +244,12 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
*/
public void clear() {
mRecords.clear();
mCallbacks.onDatasetClear(getBluetoothDevice());
mCallbacks.onDatasetCleared(getBluetoothDevice());
}
/**
* Sends the request to obtain the last (most recent) record from glucose device. The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access
* Control Point indication with status code ({@link #RESPONSE_SUCCESS} or other in case of error.
* Control Point indication with status code Success or other in case of error.
*/
public void getLastRecord() {
if (mRecordAccessControlPointCharacteristic == null)
@@ -301,15 +257,13 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
clear();
mCallbacks.onOperationStarted(getBluetoothDevice());
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_LAST_RECORD);
writeCharacteristic(characteristic);
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportLastStoredRecord());
}
/**
* Sends the request to obtain the first (oldest) record from glucose device. The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access Control
* Point indication with status code ({@link #RESPONSE_SUCCESS} or other in case of error.
* Sends the request to obtain the first (oldest) record from glucose device.
* The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access Control
* Point indication with status code Success or other in case of error.
*/
public void getFirstRecord() {
if (mRecordAccessControlPointCharacteristic == null)
@@ -317,29 +271,25 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
clear();
mCallbacks.onOperationStarted(getBluetoothDevice());
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_FIRST_RECORD);
writeCharacteristic(characteristic);
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportFirstStoredRecord());
}
/**
* Sends abort operation signal to the device
* Sends abort operation signal to the device.
*/
public void abort() {
if (mRecordAccessControlPointCharacteristic == null)
return;
mAbort = true;
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_ABORT_OPERATION, OPERATOR_NULL);
writeCharacteristic(characteristic);
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.abortOperation());
}
/**
* Sends the request to obtain all records from glucose device. Initially we want to notify him/her about the number of the records so the {@link #OP_CODE_REPORT_NUMBER_OF_RECORDS} is send. The
* data will be returned to Glucose Measurement characteristic as a notification followed by Record Access Control Point indication with status code ({@link #RESPONSE_SUCCESS} or other in case of
* error.
* Sends the request to obtain all records from glucose device. Initially we want to notify the user
* about the number of the records so the Report Number of Stored Records request is send. The
* data will be returned to Glucose Measurement characteristic as a notification followed by
* Record Access Control Point indication with status code Success or other in case of error.
*/
public void getAllRecords() {
if (mRecordAccessControlPointCharacteristic == null)
@@ -347,16 +297,14 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
clear();
mCallbacks.onOperationStarted(getBluetoothDevice());
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_REPORT_NUMBER_OF_RECORDS, OPERATOR_ALL_RECORDS);
writeCharacteristic(characteristic);
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportNumberOfAllStoredRecords());
}
/**
* Sends the request to obtain all records from glucose device. Initially we want to notify him/her about the number of the records so the {@link #OP_CODE_REPORT_NUMBER_OF_RECORDS} is send. The
* data will be returned to Glucose Measurement characteristic as a notification followed by Record Access Control Point indication with status code ({@link #RESPONSE_SUCCESS} or other in case of
* error.
* Sends the request to obtain all records from glucose device. Initially we want to notify the user
* about the number of the records so the Report Number of Stored Records request is send. The
* data will be returned to Glucose Measurement characteristic as a notification followed by
* Record Access Control Point indication with status code Success or other in case of error.
*/
public void refreshRecords() {
if (mRecordAccessControlPointCharacteristic == null)
@@ -367,28 +315,27 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
} else {
mCallbacks.onOperationStarted(getBluetoothDevice());
// obtain the last sequence number
// Obtain the last sequence number
final int sequenceNumber = mRecords.keyAt(mRecords.size() - 1) + 1;
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_GREATER_THEN_OR_EQUAL, sequenceNumber);
writeCharacteristic(characteristic);
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber));
// Info:
// Operators OPERATOR_GREATER_THEN_OR_EQUAL, OPERATOR_LESS_THEN_OR_EQUAL and OPERATOR_RANGE are not supported by the CGMS sample from SDK
// The "Operation not supported" response will be received
}
}
/**
* Sends the request to remove all stored records from the Continuous Glucose Monitor device.
* This feature is not supported by the CGMS sample from the SDK, so monitor will answer with
* the Op Code Not Supported error.
*/
public void deleteAllRecords() {
if (mRecordAccessControlPointCharacteristic == null)
return;
clear();
mCallbacks.onOperationStarted(getBluetoothDevice());
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_DELETE_STORED_RECORDS, OPERATOR_ALL_RECORDS);
writeCharacteristic(characteristic);
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.deleteAllStoredRecords());
}
}

View File

@@ -25,27 +25,24 @@ package no.nordicsemi.android.nrftoolbox.cgms;
import android.bluetooth.BluetoothDevice;
import android.support.annotation.NonNull;
import no.nordicsemi.android.ble.BleManagerCallbacks;
import no.nordicsemi.android.nrftoolbox.battery.BatteryManagerCallbacks;
public interface CGMSManagerCallbacks extends BatteryManagerCallbacks {
/**
* Called when new CGM value has been obtained from the sensor.
*/
void onCGMValueReceived(final @NonNull BluetoothDevice device, final CGMSRecord record);
void onOperationStarted(final @NonNull BluetoothDevice device);
void onCGMValueReceived(@NonNull final BluetoothDevice device, final CGMSRecord record);
void onOperationCompleted(final @NonNull BluetoothDevice device);
void onOperationStarted(final @NonNull BluetoothDevice device);
void onOperationFailed(final @NonNull BluetoothDevice device);
void onOperationCompleted(final @NonNull BluetoothDevice device);
void onOperationAborted(final @NonNull BluetoothDevice device);
void onOperationFailed(final @NonNull BluetoothDevice device);
void onOperationNotSupported(final @NonNull BluetoothDevice device);
void onOperationAborted(final @NonNull BluetoothDevice device);
void onDatasetClear(final @NonNull BluetoothDevice device);
void onOperationNotSupported(final @NonNull BluetoothDevice device);
void onNumberOfRecordsRequested(final @NonNull BluetoothDevice device, final int value);
void onDatasetCleared(final @NonNull BluetoothDevice device);
void onNumberOfRecordsRequested(final @NonNull BluetoothDevice device, final int value);
}

View File

@@ -3,7 +3,7 @@ package no.nordicsemi.android.nrftoolbox.cgms;
import android.os.Parcel;
import android.os.Parcelable;
public class CGMSRecord implements Parcelable{
class CGMSRecord implements Parcelable{
/** Record sequence number. */
protected int sequenceNumber;
/** The base time of the measurement (start time + sequenceNumber of minutes). */
@@ -11,13 +11,13 @@ public class CGMSRecord implements Parcelable{
/** The glucose concentration in mg/dL. */
protected float glucoseConcentration;
protected CGMSRecord(int sequenceNumber, float glucoseConcentration, long timestamp) {
CGMSRecord(final int sequenceNumber, final float glucoseConcentration, final long timestamp) {
this.sequenceNumber = sequenceNumber;
this.glucoseConcentration = glucoseConcentration;
this.timestamp = timestamp;
}
protected CGMSRecord(Parcel in) {
private CGMSRecord(final Parcel in) {
this.sequenceNumber = in.readInt();
this.glucoseConcentration = in.readFloat();
this.timestamp = in.readLong();
@@ -25,12 +25,12 @@ public class CGMSRecord implements Parcelable{
public static final Creator<CGMSRecord> CREATOR = new Creator<CGMSRecord>() {
@Override
public CGMSRecord createFromParcel(Parcel in) {
public CGMSRecord createFromParcel(final Parcel in) {
return new CGMSRecord(in);
}
@Override
public CGMSRecord[] newArray(int size) {
public CGMSRecord[] newArray(final int size) {
return new CGMSRecord[size];
}
};
@@ -41,7 +41,7 @@ public class CGMSRecord implements Parcelable{
}
@Override
public void writeToParcel(Parcel parcel, int i) {
public void writeToParcel(final Parcel parcel, final int flags) {
parcel.writeInt(sequenceNumber);
parcel.writeFloat(glucoseConcentration);
parcel.writeLong(timestamp);

View File

@@ -9,11 +9,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.util.SparseArray;
import no.nordicsemi.android.ble.BleManager;
import no.nordicsemi.android.ble.common.profile.cgm.CGMTypes;
import no.nordicsemi.android.log.Logger;
import no.nordicsemi.android.nrftoolbox.FeaturesActivity;
import no.nordicsemi.android.nrftoolbox.R;
@@ -21,267 +23,266 @@ import no.nordicsemi.android.nrftoolbox.ToolboxApplication;
import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
public class CGMService extends BleProfileService implements CGMSManagerCallbacks {
private static final String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.cgms.ACTION_DISCONNECT";
private static final String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.cgms.ACTION_DISCONNECT";
public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
public static final String BROADCAST_NEW_CGMS_VALUE = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_NEW_CGMS_VALUE";
public static final String BROADCAST_DATA_SET_CLEAR = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_DATA_SET_CLEAR";
public static final String OPERATION_STARTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_STARTED";
public static final String OPERATION_COMPLETED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_COMPLETED";
public static final String OPERATION_SUPPORTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_SUPPORTED";
public static final String OPERATION_NOT_SUPPORTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_NOT_SUPPORTED";
public static final String OPERATION_FAILED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_FAILED";
public static final String OPERATION_ABORTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_ABORTED";
public static final String EXTRA_CGMS_RECORD = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_CGMS_RECORD";
public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_DATA";
public static final String BROADCAST_NEW_CGMS_VALUE = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_NEW_CGMS_VALUE";
public static final String BROADCAST_DATA_SET_CLEAR = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_DATA_SET_CLEAR";
public static final String OPERATION_STARTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_STARTED";
public static final String OPERATION_COMPLETED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_COMPLETED";
public static final String OPERATION_SUPPORTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_SUPPORTED";
public static final String OPERATION_NOT_SUPPORTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_NOT_SUPPORTED";
public static final String OPERATION_FAILED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_FAILED";
public static final String OPERATION_ABORTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_ABORTED";
public static final String EXTRA_CGMS_RECORD = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_CGMS_RECORD";
public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_DATA";
private final static int NOTIFICATION_ID = 229;
private final static int OPEN_ACTIVITY_REQ = 0;
private final static int DISCONNECT_REQ = 1;
private final static int NOTIFICATION_ID = 229;
private final static int OPEN_ACTIVITY_REQ = 0;
private final static int DISCONNECT_REQ = 1;
private CGMSManager mManager;
private final LocalBinder mBinder = new CGMSBinder();
private CGMSManager mManager;
private final LocalBinder mBinder = new CGMSBinder();
/**
* This local binder is an interface for the bonded activity to operate with the RSC sensor
*/
/**
* This local binder is an interface for the bonded activity to operate with the RSC sensor
*/
public class CGMSBinder extends LocalBinder {
/**
* Returns all records as a sparse array where sequence number is the key.
*
* @return the records list
*/
public SparseArray<CGMSRecord> getRecords() {
return mManager.getRecords();
}
/**
* Clears the records list locally
*/
public void clear() {
if (mManager != null)
mManager.clear();
}
/**
* Sends the request to obtain the first (oldest) record from glucose device.
* The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access Control
* Point indication with status code ({@link CGMSManager# RESPONSE_SUCCESS} or other in case of error.
*/
public void getFirstRecord() {
if(mManager != null)
mManager.getFirstRecord();
}
/**
* Sends the request to obtain the last (most recent) record from glucose device.
* The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access
* Control Point indication with status code ({@link CGMSManager#RESPONSE_SUCCESS} or other in case of error.
*/
public void getLastRecord() {
if(mManager != null)
mManager.getLastRecord();
}
/**
* Sends the request to obtain all records from glucose device.
* Initially we want to notify user about the number of the records so the {@link CGMSManager#OP_CODE_REPORT_NUMBER_OF_RECORDS} is send.
* The data will be returned to Glucose Measurement characteristic as a series of notifications followed by Record Access Control Point
* indication with status code ({@link CGMSManager#RESPONSE_SUCCESS} or other in case of error.
*/
public void getAllRecords() {
if(mManager != null)
mManager.getAllRecords();
}
public class CGMSBinder extends LocalBinder {
/**
* Returns all records as a sparse array where sequence number is the key.
*
* @return the records list
*/
public SparseArray<CGMSRecord> getRecords() {
return mManager.getRecords();
}
/**
* Sends the request to obtain all records from glucose device with sequence number greater than the last one already obtained.
* The data will be returned to Glucose Measurement characteristic as a series of notifications followed by Record Access Control Point
* indication with status code ({@link CGMSManager#RESPONSE_SUCCESS} or other in case of error.
*/
public void refreshRecords() {
if (mManager != null)
mManager.refreshRecords();
}
/**
* Sends abort operation signal to the device
*/
public void abort() {
if(mManager != null)
mManager.abort();
}
* Clears the records list locally
*/
public void clear() {
if (mManager != null)
mManager.clear();
}
/**
* Sends Delete op code with All stored records parameter. This method may not be supported by the SDK sample.
*/
public void deleteAllRecords() {
if(mManager != null)
mManager.deleteAllRecords();
}
}
* Sends the request to obtain the first (oldest) record from glucose device.
* The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access Control
* Point indication with status code ({@link CGMSManager# RESPONSE_SUCCESS} or other in case of error.
*/
public void getFirstRecord() {
if (mManager != null)
mManager.getFirstRecord();
}
@Override
protected LocalBinder getBinder() {
return mBinder;
}
/**
* Sends the request to obtain the last (most recent) record from glucose device.
* The data will be returned to Glucose Measurement characteristic as a notification followed by Record Access
* Control Point indication with status code Success or other in case of error.
*/
public void getLastRecord() {
if (mManager != null)
mManager.getLastRecord();
}
@Override
protected BleManager initializeManager() {
return mManager = new CGMSManager(this);
}
/**
* Sends the request to obtain all records from glucose device.
* Initially we want to notify user about the number of the records so the Report Number of Stored Records is send.
* The data will be returned to Glucose Measurement characteristic as a series of notifications followed
* by Record Access Control Point indication with status code Success or other in case of error.
*/
public void getAllRecords() {
if (mManager != null)
mManager.getAllRecords();
}
/**
* Sends the request to obtain all records from glucose device with sequence number greater
* than the last one already obtained. The data will be returned to Glucose Measurement
* characteristic as a series of notifications followed by Record Access Control Point
* indication with status code Success or other in case of error.
*/
public void refreshRecords() {
if (mManager != null)
mManager.refreshRecords();
}
/**
* Sends abort operation signal to the device
*/
public void abort() {
if (mManager != null)
mManager.abort();
}
/**
* Sends Delete op code with All stored records parameter. This method may not be supported by the SDK sample.
*/
public void deleteAllRecords() {
if (mManager != null)
mManager.deleteAllRecords();
}
}
@Override
protected LocalBinder getBinder() {
return mBinder;
}
@Override
protected BleManager initializeManager() {
return mManager = new CGMSManager(this);
}
@Override
public void onCreate() {
super.onCreate();
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DISCONNECT);
registerReceiver(mDisconnectActionBroadcastReceiver, filter);
}
@Override
public void onCreate() {
super.onCreate();
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DISCONNECT);
registerReceiver(mDisconnectActionBroadcastReceiver, filter);
}
@Override
public void onDestroy() {
// when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
cancelNotification();
unregisterReceiver(mDisconnectActionBroadcastReceiver);
@Override
public void onDestroy() {
// when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
cancelNotification();
unregisterReceiver(mDisconnectActionBroadcastReceiver);
super.onDestroy();
}
super.onDestroy();
}
@Override
protected void onRebind() {
// when the activity rebinds to the service, remove the notification
cancelNotification();
}
@Override
protected void onRebind() {
// when the activity rebinds to the service, remove the notification
cancelNotification();
}
@Override
protected void onUnbind() {
// when the activity closes we need to show the notification that user is connected to the sensor
createNotification(R.string.csc_notification_connected_message, 0);
}
@Override
protected void onUnbind() {
// when the activity closes we need to show the notification that user is connected to the sensor
createNotification(R.string.csc_notification_connected_message, 0);
}
/**
* Creates the notification
*
* @param messageResId
* the message resource id. The message must have one String parameter,<br />
* f.e. <code>&lt;string name="name"&gt;%s is connected&lt;/string&gt;</code>
* @param defaults
* signals that will be used to notify the user
*/
private void createNotification(final int messageResId, final int defaults) {
final Intent parentIntent = new Intent(this, FeaturesActivity.class);
parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final Intent targetIntent = new Intent(this, CGMSActivity.class);
/**
* Creates the notification
*
* @param messageResId the message resource id. The message must have one String parameter,<br />
* f.e. <code>&lt;string name="name"&gt;%s is connected&lt;/string&gt;</code>
* @param defaults signals that will be used to notify the user
*/
private void createNotification(final int messageResId, final int defaults) {
final Intent parentIntent = new Intent(this, FeaturesActivity.class);
parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final Intent targetIntent = new Intent(this, CGMSActivity.class);
final Intent disconnect = new Intent(ACTION_DISCONNECT);
final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
final Intent disconnect = new Intent(ACTION_DISCONNECT);
final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
// both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[] { parentIntent, targetIntent }, PendingIntent.FLAG_UPDATE_CURRENT);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
builder.setContentIntent(pendingIntent);
builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
builder.setSmallIcon(R.drawable.ic_stat_notify_cgms);
builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.csc_notification_action_disconnect), disconnectAction));
// both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[]{parentIntent, targetIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, ToolboxApplication.CONNECTED_DEVICE_CHANNEL);
builder.setContentIntent(pendingIntent);
builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
builder.setSmallIcon(R.drawable.ic_stat_notify_cgms);
builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.csc_notification_action_disconnect), disconnectAction));
final Notification notification = builder.build();
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(NOTIFICATION_ID, notification);
}
final Notification notification = builder.build();
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(NOTIFICATION_ID, notification);
}
/**
* Cancels the existing notification. If there is no active notification this method does nothing
*/
private void cancelNotification() {
final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.cancel(NOTIFICATION_ID);
}
/**
* Cancels the existing notification. If there is no active notification this method does nothing
*/
private void cancelNotification() {
final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.cancel(NOTIFICATION_ID);
}
/**
* This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
*/
private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
if (isConnected())
getBinder().disconnect();
else
stopSelf();
}
};
/**
* This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
*/
private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
if (isConnected())
getBinder().disconnect();
else
stopSelf();
}
};
@Override
public void onCGMValueReceived(@NonNull final BluetoothDevice device, final CGMSRecord record) {
final Intent broadcast = new Intent(BROADCAST_NEW_CGMS_VALUE);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_CGMS_RECORD, record);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onCGMValueReceived(@NonNull final BluetoothDevice device, final CGMSRecord record) {
final Intent broadcast = new Intent(BROADCAST_NEW_CGMS_VALUE);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_CGMS_RECORD, record);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationStarted(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_STARTED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationStarted(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_STARTED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationCompleted(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_COMPLETED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationCompleted(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_COMPLETED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationFailed(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_FAILED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationFailed(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_FAILED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationAborted(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_ABORTED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationAborted(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_ABORTED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationNotSupported(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_NOT_SUPPORTED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, false);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationNotSupported(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(OPERATION_NOT_SUPPORTED);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_DATA, false);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onDatasetClear(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(BROADCAST_DATA_SET_CLEAR);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onDatasetCleared(@NonNull final BluetoothDevice device) {
final Intent broadcast = new Intent(BROADCAST_DATA_SET_CLEAR);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onNumberOfRecordsRequested(@NonNull final BluetoothDevice device, int value) {
showToast(getResources().getQuantityString(R.plurals.gls_progress, value, value));
}
@Override
public void onNumberOfRecordsRequested(@NonNull final BluetoothDevice device, final int value) {
showToast(getResources().getQuantityString(R.plurals.gls_progress, value, value));
}
@Override
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
broadcast.putExtra(EXTRA_DEVICE, device);
broadcast.putExtra(EXTRA_BATTERY_LEVEL, batteryLevel);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
broadcast.putExtra(EXTRA_DEVICE, device);
broadcast.putExtra(EXTRA_BATTERY_LEVEL, batteryLevel);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
}