From f7b7d70954707a6cb4bd12bee7af348180c4a46a Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 5 Oct 2016 12:42:40 +0200 Subject: [PATCH] CGMS profile fixed, BleManager callback report BluetootDevice This commit fixes some issues in CGMS profile. It also modiefied the BleManager and BleManagerCallback to report the related BluetoothDevice object. This is a preparation for adding multiconnect support. --- .../android/nrftoolbox/bpm/BPMActivity.java | 23 +- .../android/nrftoolbox/bpm/BPMManager.java | 28 +- .../nrftoolbox/bpm/BPMManagerCallbacks.java | 26 +- .../android/nrftoolbox/cgms/CGMSActivity.java | 429 ++++++------ .../android/nrftoolbox/cgms/CGMSManager.java | 608 +++++++++--------- .../nrftoolbox/cgms/CGMSManagerCallbacks.java | 24 +- .../android/nrftoolbox/cgms/CGMSRecord.java | 32 +- .../nrftoolbox/cgms/CGMSRecordsAdapter.java | 115 ++-- .../android/nrftoolbox/cgms/CGMService.java | 109 ++-- .../android/nrftoolbox/csc/CSCActivity.java | 22 +- .../android/nrftoolbox/csc/CSCManager.java | 9 +- .../nrftoolbox/csc/CSCManagerCallbacks.java | 8 +- .../android/nrftoolbox/csc/CSCService.java | 7 +- .../nrftoolbox/gls/GlucoseActivity.java | 25 +- .../nrftoolbox/gls/GlucoseManager.java | 52 +- .../gls/GlucoseManagerCallbacks.java | 19 +- .../android/nrftoolbox/hrs/HRSActivity.java | 12 +- .../android/nrftoolbox/hrs/HRSManager.java | 14 +- .../nrftoolbox/hrs/HRSManagerCallbacks.java | 12 +- .../android/nrftoolbox/hts/HTSActivity.java | 3 +- .../android/nrftoolbox/hts/HTSManager.java | 7 +- .../nrftoolbox/hts/HTSManagerCallbacks.java | 7 +- .../android/nrftoolbox/hts/HTSService.java | 5 +- .../parser/CGMMeasurementParser.java | 183 ++++++ .../CGMSpecificOpsControlPointParser.java | 294 +++++++++ .../nrftoolbox/profile/BleManager.java | 90 ++- .../profile/BleManagerCallbacks.java | 44 +- .../profile/BleProfileActivity.java | 18 +- .../BleProfileExpandableListActivity.java | 34 +- .../nrftoolbox/profile/BleProfileService.java | 74 ++- .../BleProfileServiceReadyActivity.java | 135 ++-- .../proximity/ProximityActivity.java | 17 +- .../proximity/ProximityManager.java | 13 +- .../proximity/ProximityManagerCallbacks.java | 6 +- .../proximity/ProximityService.java | 15 +- .../android/nrftoolbox/rsc/RSCActivity.java | 20 +- .../android/nrftoolbox/rsc/RSCManager.java | 9 +- .../nrftoolbox/rsc/RSCManagerCallbacks.java | 10 +- .../android/nrftoolbox/rsc/RSCService.java | 4 +- .../nrftoolbox/template/TemplateActivity.java | 3 +- .../nrftoolbox/template/TemplateManager.java | 7 +- .../template/TemplateManagerCallbacks.java | 7 +- .../nrftoolbox/template/TemplateService.java | 4 +- .../android/nrftoolbox/uart/UARTActivity.java | 2 +- .../android/nrftoolbox/uart/UARTManager.java | 10 +- .../nrftoolbox/uart/UARTManagerCallbacks.java | 6 +- .../UARTNewConfigurationDialogFragment.java | 15 +- .../android/nrftoolbox/uart/UARTService.java | 24 +- .../res/layout-land/activity_feature_cgms.xml | 1 + .../main/res/layout/activity_feature_cgms.xml | 1 + .../res/layout/activity_feature_cgms_item.xml | 59 +- app/src/main/res/values-land/strings_cgms.xml | 2 - app/src/main/res/values/strings_cgms.xml | 17 +- .../android/nrftoolbox/uart/UARTProfile.java | 5 +- 54 files changed, 1649 insertions(+), 1076 deletions(-) create mode 100644 app/src/main/java/no/nordicsemi/android/nrftoolbox/parser/CGMMeasurementParser.java create mode 100644 app/src/main/java/no/nordicsemi/android/nrftoolbox/parser/CGMSpecificOpsControlPointParser.java diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMActivity.java index 253d96f5..aa8b1f92 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMActivity.java @@ -21,6 +21,7 @@ */ package no.nordicsemi.android.nrftoolbox.bpm; +import android.bluetooth.BluetoothDevice; import android.os.Bundle; import android.widget.TextView; @@ -102,23 +103,23 @@ public class BPMActivity extends BleProfileActivity implements BPMManagerCallbac } @Override - public void onServicesDiscovered(final boolean optionalServicesFound) { + public void onServicesDiscovered(final BluetoothDevice device, final boolean optionalServicesFound) { // this may notify user or show some views } @Override - public void onDeviceReady() { + public void onDeviceReady(final BluetoothDevice device) { // this may notify user } @Override - public void onBloodPressureMeasurementRead(final float systolic, final float diastolic, final float meanArterialPressure, final int unit) { + public void onBloodPressureMeasurementRead(final BluetoothDevice device, final float systolic, final float diastolic, final float meanArterialPressure, final int unit) { runOnUiThread(new Runnable() { @Override public void run() { - mSystolicView.setText(Float.toString(systolic)); - mDiastolicView.setText(Float.toString(diastolic)); - mMeanAPView.setText(Float.toString(meanArterialPressure)); + mSystolicView.setText(String.valueOf(systolic)); + mDiastolicView.setText(String.valueOf(diastolic)); + mMeanAPView.setText(String.valueOf(meanArterialPressure)); mSystolicUnitView.setText(unit == UNIT_mmHG ? R.string.bpm_unit_mmhg : R.string.bpm_unit_kpa); mDiastolicUnitView.setText(unit == UNIT_mmHG ? R.string.bpm_unit_mmhg : R.string.bpm_unit_kpa); @@ -128,11 +129,11 @@ public class BPMActivity extends BleProfileActivity implements BPMManagerCallbac } @Override - public void onIntermediateCuffPressureRead(final float cuffPressure, final int unit) { + public void onIntermediateCuffPressureRead(final BluetoothDevice device, final float cuffPressure, final int unit) { runOnUiThread(new Runnable() { @Override public void run() { - mSystolicView.setText(Float.toString(cuffPressure)); + mSystolicView.setText(String.valueOf(cuffPressure)); mDiastolicView.setText(R.string.not_available_value); mMeanAPView.setText(R.string.not_available_value); @@ -144,12 +145,12 @@ public class BPMActivity extends BleProfileActivity implements BPMManagerCallbac } @Override - public void onPulseRateRead(final float pulseRate) { + public void onPulseRateRead(final BluetoothDevice device, final float pulseRate) { runOnUiThread(new Runnable() { @Override public void run() { if (pulseRate >= 0) - mPulseView.setText(Float.toString(pulseRate)); + mPulseView.setText(String.valueOf(pulseRate)); else mPulseView.setText(R.string.not_available_value); } @@ -157,7 +158,7 @@ public class BPMActivity extends BleProfileActivity implements BPMManagerCallbac } @Override - public void onTimestampRead(final Calendar calendar) { + public void onTimestampRead(final BluetoothDevice device, final Calendar calendar) { runOnUiThread(new Runnable() { @Override public void run() { diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMManager.java index ff6bd3da..8ed6dad2 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMManager.java @@ -76,8 +76,8 @@ public class BPMManager extends BleManager { protected Queue initGatt(final BluetoothGatt gatt) { final LinkedList requests = new LinkedList<>(); if (mICPCharacteristic != null) - requests.push(Request.newEnableNotificationsRequest(mICPCharacteristic)); - requests.push(Request.newEnableIndicationsRequest(mBPMCharacteristic)); + requests.add(Request.newEnableNotificationsRequest(mICPCharacteristic)); + requests.add(Request.newEnableIndicationsRequest(mBPMCharacteristic)); return requests; } @@ -105,22 +105,20 @@ public class BPMManager extends BleManager { @Override protected void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { // Intermediate Cuff Pressure characteristic read - if (mLogSession != null) - Logger.a(mLogSession, IntermediateCuffPressureParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + IntermediateCuffPressureParser.parse(characteristic) + "\" received"); - parseBPMValue(characteristic); + parseBPMValue(gatt, characteristic); } @Override protected void onCharacteristicIndicated(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { // Blood Pressure Measurement characteristic read - if (mLogSession != null) - Logger.a(mLogSession, BloodPressureMeasurementParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + BloodPressureMeasurementParser.parse(characteristic) + "\" received"); - parseBPMValue(characteristic); + parseBPMValue(gatt, characteristic); } - private void parseBPMValue(final BluetoothGattCharacteristic characteristic) { + private void parseBPMValue(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { // Both BPM and ICP have the same structure. // first byte - flags @@ -137,12 +135,12 @@ public class BPMManager extends BleManager { final float diastolic = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset + 2); final float meanArterialPressure = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset + 4); offset += 6; - mCallbacks.onBloodPressureMeasurementRead(systolic, diastolic, meanArterialPressure, unit); + mCallbacks.onBloodPressureMeasurementRead(gatt.getDevice(), systolic, diastolic, meanArterialPressure, unit); } else if (ICP_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) { // following bytes - cuff pressure. Diastolic and MAP are unused final float cuffPressure = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); offset += 6; - mCallbacks.onIntermediateCuffPressureRead(cuffPressure, unit); + mCallbacks.onIntermediateCuffPressureRead(gatt.getDevice(), cuffPressure, unit); } // parse timestamp if present @@ -155,17 +153,17 @@ public class BPMManager extends BleManager { calendar.set(Calendar.MINUTE, characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset + 5)); calendar.set(Calendar.SECOND, characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset + 6)); offset += 7; - mCallbacks.onTimestampRead(calendar); + mCallbacks.onTimestampRead(gatt.getDevice(), calendar); } else - mCallbacks.onTimestampRead(null); + mCallbacks.onTimestampRead(gatt.getDevice(), null); // parse pulse rate if present if (pulseRatePresent) { final float pulseRate = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); // offset += 2; - mCallbacks.onPulseRateRead(pulseRate); + mCallbacks.onPulseRateRead(gatt.getDevice(), pulseRate); } else - mCallbacks.onPulseRateRead(-1.0f); + mCallbacks.onPulseRateRead(gatt.getDevice(), -1.0f); } }; } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMManagerCallbacks.java index 702c2337..76550bd6 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/bpm/BPMManagerCallbacks.java @@ -21,47 +21,53 @@ */ package no.nordicsemi.android.nrftoolbox.bpm; +import android.bluetooth.BluetoothDevice; + import java.util.Calendar; import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; public interface BPMManagerCallbacks extends BleManagerCallbacks { - public static final int UNIT_mmHG = 0; - public static final int UNIT_kPa = 1; + int UNIT_mmHG = 0; + int UNIT_kPa = 1; /** * Called when new BPM value has been obtained from the sensor - * + * + * @param device the target device * @param systolic * @param diastolic * @param meanArterialPressure * @param unit * one of the following {@link #UNIT_kPa} or {@link #UNIT_mmHG} */ - public void onBloodPressureMeasurementRead(final float systolic, final float diastolic, final float meanArterialPressure, final int unit); + void onBloodPressureMeasurementRead(final BluetoothDevice device, final float systolic, final float diastolic, final float meanArterialPressure, final int unit); /** * Called when new ICP value has been obtained from the device - * + * + * @param device the target device * @param cuffPressure * @param unit * one of the following {@link #UNIT_kPa} or {@link #UNIT_mmHG} */ - public void onIntermediateCuffPressureRead(final float cuffPressure, final int unit); + void onIntermediateCuffPressureRead(final BluetoothDevice device, final float cuffPressure, final int unit); /** * Called when new pulse rate value has been obtained from the device. If there was no pulse rate in the packet the parameter will be equal -1.0f - * + * + * @param device the target device * @param pulseRate * pulse rate or -1.0f */ - public void onPulseRateRead(final float pulseRate); + void onPulseRateRead(final BluetoothDevice device, final float pulseRate); /** * Called when the timestamp value has been read from the device. If there was no timestamp information the parameter will be null - * + * + * @param device the target device * @param calendar * the timestamp or null */ - public void onTimestampRead(final Calendar calendar); + void onTimestampRead(final BluetoothDevice device, final Calendar calendar); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSActivity.java index 3788f6ca..792fcbd8 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSActivity.java @@ -22,6 +22,7 @@ package no.nordicsemi.android.nrftoolbox.cgms; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -34,7 +35,6 @@ import android.view.MenuItem; import android.view.View; import android.widget.ListView; import android.widget.PopupMenu; -import android.widget.TextView; import java.util.UUID; @@ -42,251 +42,232 @@ import no.nordicsemi.android.nrftoolbox.R; import no.nordicsemi.android.nrftoolbox.profile.BleProfileService; import no.nordicsemi.android.nrftoolbox.profile.BleProfileServiceReadyActivity; -public class CGMSActivity extends BleProfileServiceReadyActivity implements PopupMenu.OnMenuItemClickListener { +public class CGMSActivity extends BleProfileServiceReadyActivity implements PopupMenu.OnMenuItemClickListener { + private View mControlPanelStd; + private View mControlPanelAbort; + private ListView mRecordsListView; + private CGMSRecordsAdapter mCgmsRecordsAdapter; - private View mControlPanelStd; - private View mControlPanelAbort; - private TextView mUnitView; - private ListView mRecordsListView; - private CGMSRecordsAdapter mCgmsRecordsAdapter; + private CGMService.CGMSBinder mBinder; - private CGMService.CGMSBinder mBinder; + @Override + protected void onCreateView(Bundle savedInstanceState) { + setContentView(R.layout.activity_feature_cgms); + setGUI(); + } - @Override - protected void onCreateView(Bundle savedInstanceState) { - setContentView(R.layout.activity_feature_cgms); - setGUI(); - } + @Override + protected void onInitialize(Bundle savedInstanceState) { + LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, makeIntentFilter()); + } - @Override - protected void onInitialize(Bundle savedInstanceState) { - LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, makeIntentFilter()); - } + private void setGUI() { + mRecordsListView = (ListView) findViewById(R.id.list); + mControlPanelStd = findViewById(R.id.cgms_control_std); + mControlPanelAbort = findViewById(R.id.cgms_control_abort); - private void setGUI() { - mRecordsListView = (ListView) findViewById(R.id.list); - mUnitView = (TextView) findViewById(R.id.unit); - mControlPanelStd = findViewById(R.id.cgms_control_std); - mControlPanelAbort = findViewById(R.id.cgms_control_abort); + findViewById(R.id.action_last).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clearRecords(); + if (mBinder != null) { + mBinder.clear(); + mBinder.getLastRecord(); + } + } + }); + findViewById(R.id.action_all).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clearRecords(); + if (mBinder != null) { + clearRecords(); + mBinder.getAllRecords(); + } + } + }); + findViewById(R.id.action_abort).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mBinder != null) { + mBinder.abort(); + } + } + }); - findViewById(R.id.action_last).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - clearRecords(); - if(mBinder != null) { - mBinder.clear(); - mBinder.getLastRecord(); - } - } - }); - findViewById(R.id.action_all).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - clearRecords(); - if(mBinder != null){ - clearRecords(); - mBinder.getAllRecords(); - } - } - }); - findViewById(R.id.action_abort).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if(mBinder != null){ - mBinder.abort(); - } - } - }); + // create popup menu attached to the button More + findViewById(R.id.action_more).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PopupMenu menu = new PopupMenu(CGMSActivity.this, v); + menu.setOnMenuItemClickListener(CGMSActivity.this); + MenuInflater inflater = menu.getMenuInflater(); + inflater.inflate(R.menu.gls_more, menu.getMenu()); + menu.show(); + } + }); + } - // create popup menu attached to the button More - findViewById(R.id.action_more).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - PopupMenu menu = new PopupMenu(CGMSActivity.this, v); - menu.setOnMenuItemClickListener(CGMSActivity.this); - MenuInflater inflater = menu.getMenuInflater(); - inflater.inflate(R.menu.gls_more, menu.getMenu()); - menu.show(); - } - }); - } + private void loadAdapter(SparseArray records) { + mCgmsRecordsAdapter.clear(); + for (int i = 0; i < records.size(); i++) { + mCgmsRecordsAdapter.addItem(records.valueAt(i)); + } + mCgmsRecordsAdapter.notifyDataSetChanged(); + } - private void loadAdapter(SparseArray records){ - for(int i = 0; i < records.size(); i++){ - mCgmsRecordsAdapter.addItem(records.get(i)); - } - mCgmsRecordsAdapter.notifyDataSetChanged(); - } + @Override + protected void onDestroy() { + super.onDestroy(); + LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver); + } - @Override - protected void onDestroy() { - super.onDestroy(); - LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver); - } + @Override + protected void onServiceBinded(final CGMService.CGMSBinder binder) { + mBinder = binder; + final SparseArray cgmsRecords = binder.getRecords(); + if (cgmsRecords != null && cgmsRecords.size() > 0) { + if (mCgmsRecordsAdapter == null) { + mCgmsRecordsAdapter = new CGMSRecordsAdapter(CGMSActivity.this); + mRecordsListView.setAdapter(mCgmsRecordsAdapter); + } + loadAdapter(cgmsRecords); + } + } - @Override - protected void onServiceBinded(CGMService.CGMSBinder binder) { - mBinder = binder; - SparseArray cgmsRecords = binder.getCgmsRecords(); - if(cgmsRecords != null && cgmsRecords.size() > 0){ - if(mCgmsRecordsAdapter == null) { - mCgmsRecordsAdapter = new CGMSRecordsAdapter(CGMSActivity.this); - mRecordsListView.setAdapter(mCgmsRecordsAdapter); - } - loadAdapter(cgmsRecords); - } - } + @Override + protected void onServiceUnbinded() { + mBinder = null; + } - @Override - protected void onServiceUnbinded() { - mBinder = null; - } + @Override + protected Class getServiceClass() { + return CGMService.class; + } - @Override - protected Class getServiceClass() { - return CGMService.class; - } + @Override + protected int getLoggerProfileTitle() { + return R.string.cgms_feature_title; + } - @Override - protected int getLoggerProfileTitle() { - return R.string.cgms_feature_title; - } + @Override + protected int getAboutTextId() { + return R.string.cgms_about_text; + } - @Override - protected int getAboutTextId() { - return R.string.cgms_about_text; - } + @Override + protected int getDefaultDeviceName() { + return R.string.cgms_default_name; + } - @Override - protected int getDefaultDeviceName() { - return R.string.cgms_default_name; - } + @Override + protected UUID getFilterUUID() { + return CGMSManager.CGMS_UUID; + } - @Override - protected UUID getFilterUUID() { - return CGMSManager.CGMS_UUID; - } + @Override + public void onServicesDiscovered(final BluetoothDevice device, final boolean optionalServicesFound) { + // this may notify user or show some views + } - @Override - public void onServicesDiscovered(final boolean optionalServicesFound) { - // this may notify user or show some views - } + private void setOperationInProgress(final boolean progress) { + runOnUiThread(new Runnable() { + @Override + public void run() { + // setSupportProgressBarIndeterminateVisibility(progress); + mControlPanelStd.setVisibility(!progress ? View.VISIBLE : View.GONE); + mControlPanelAbort.setVisibility(progress ? View.VISIBLE : View.GONE); + } + }); + } - @Override - public void onDeviceReady() { + @Override + public void onDeviceDisconnected(final BluetoothDevice device) { + super.onDeviceDisconnected(device); + setOperationInProgress(false); + clearRecords(); + } - } + @Override + protected void setDefaultUI() { + // do nothing + } - private void setOperationInProgress(final boolean progress) { - runOnUiThread(new Runnable() { - @Override - public void run() { - // setSupportProgressBarIndeterminateVisibility(progress); - mControlPanelStd.setVisibility(!progress ? View.VISIBLE : View.GONE); - mControlPanelAbort.setVisibility(progress ? View.VISIBLE : View.GONE); - } - }); - } + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + switch (menuItem.getItemId()) { + case R.id.action_refresh: + if(mBinder != null) + mBinder.refreshRecords(); + break; + case R.id.action_first: + if (mBinder != null) + mBinder.getFirstRecord(); + break; + case R.id.action_clear: + if (mBinder != null) + mBinder.clear(); + break; + case R.id.action_delete_all: + if (mBinder != null) + mBinder.deleteAllRecords(); + break; + } + return true; + } - @Override - public void onDeviceDisconnected() { - super.onDeviceDisconnected(); - setOperationInProgress(false); - clearRecords(); - } + private void clearRecords() { + if (mCgmsRecordsAdapter != null) { + mCgmsRecordsAdapter.clear(); + mCgmsRecordsAdapter.notifyDataSetChanged(); + } + } - @Override - protected void setDefaultUI() { + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + final String action = intent.getAction(); - } + switch (action) { + case CGMService.BROADCAST_NEW_CGMS_VALUE: { + CGMSRecord cgmsRecord = intent.getExtras().getParcelable(CGMService.EXTRA_CGMS_RECORD); + if (mCgmsRecordsAdapter == null) { + mCgmsRecordsAdapter = new CGMSRecordsAdapter(CGMSActivity.this); + mRecordsListView.setAdapter(mCgmsRecordsAdapter); + } + mCgmsRecordsAdapter.addItem(cgmsRecord); + mCgmsRecordsAdapter.notifyDataSetChanged(); + break; + } + case CGMService.BROADCAST_DATA_SET_CLEAR: + // Update GUI + clearRecords(); + break; + case CGMService.OPERATION_STARTED: + // Update GUI + setOperationInProgress(true); + break; + case CGMService.OPERATION_FAILED: + // Update GUI + showToast(R.string.gls_operation_failed); + // breakthrough intended + default: + // Update GUI + setOperationInProgress(false); + } + } + }; - @Override - public boolean onMenuItemClick(MenuItem menuItem) { - switch (menuItem.getItemId()) { - case R.id.action_refresh: - /*if(mBinder != null) - mBinder.refreshRecords();*/ - break; - case R.id.action_first: - clearRecords(); - if(mBinder != null) - mBinder.getFirstRecord(); - break; - case R.id.action_clear: - clearRecords(); - if(mBinder != null) - mBinder.clear(); - break; - case R.id.action_delete_all: - clearRecords(); - if(mBinder != null) - mBinder.deleteAllRecords(); - break; - } - return true; - } - - private void clearRecords(){ - runOnUiThread(new Runnable() { - @Override - public void run() { - if(mCgmsRecordsAdapter != null){ - mCgmsRecordsAdapter.clear(); - mCgmsRecordsAdapter.notifyDataSetChanged(); - } - } - }); - } - - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(final Context context, final Intent intent) { - final String action = intent.getAction(); - if (CGMService.BROADCAST_CGMS_VALUES.equals(action)) { - CGMSRecord cgmsRecord = intent.getExtras().getParcelable(CGMService.EXTRA_CGMS_RECORD); - if(mCgmsRecordsAdapter == null){ - mCgmsRecordsAdapter = new CGMSRecordsAdapter(CGMSActivity.this); - mRecordsListView.setAdapter(mCgmsRecordsAdapter); - } - mCgmsRecordsAdapter.addItem(cgmsRecord); - mCgmsRecordsAdapter.notifyDataSetChanged(); - - } else if (CGMService.BROADCAST_DATA_SET_CHANGED.equals(action)) { - // Update GUI - clearRecords(); - } else if (CGMService.OPERATION_STARTED.equals(action)) { - // Update GUI - setOperationInProgress(true); - } else if (CGMService.OPERATION_COMPLETED.equals(action)) { - // Update GUI - setOperationInProgress(false); - } else if (CGMService.OPERATION_SUPPORTED.equals(action)) { - // Update GUI - setOperationInProgress(false); - } else if (CGMService.OPERATION_NOT_SUPPORTED.equals(action)) { - // Update GUI - setOperationInProgress(false); - } else if (CGMService.OPERATION_ABORTED.equals(action)) { - // Update GUI - setOperationInProgress(false); - } else if (CGMService.OPERATION_FAILED.equals(action)) { - // Update GUI - setOperationInProgress(false); - showToast(R.string.gls_operation_failed); - } - } - }; - - private static IntentFilter makeIntentFilter() { - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(CGMService.BROADCAST_CGMS_VALUES); - intentFilter.addAction(CGMService.BROADCAST_DATA_SET_CHANGED); - intentFilter.addAction(CGMService.OPERATION_STARTED); - intentFilter.addAction(CGMService.OPERATION_COMPLETED); - intentFilter.addAction(CGMService.OPERATION_SUPPORTED); - intentFilter.addAction(CGMService.OPERATION_NOT_SUPPORTED); - intentFilter.addAction(CGMService.OPERATION_ABORTED); - intentFilter.addAction(CGMService.OPERATION_FAILED); - return intentFilter; - } + private static IntentFilter makeIntentFilter() { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(CGMService.BROADCAST_NEW_CGMS_VALUE); + intentFilter.addAction(CGMService.BROADCAST_DATA_SET_CLEAR); + intentFilter.addAction(CGMService.OPERATION_STARTED); + intentFilter.addAction(CGMService.OPERATION_COMPLETED); + intentFilter.addAction(CGMService.OPERATION_SUPPORTED); + intentFilter.addAction(CGMService.OPERATION_NOT_SUPPORTED); + intentFilter.addAction(CGMService.OPERATION_ABORTED); + intentFilter.addAction(CGMService.OPERATION_FAILED); + return intentFilter; + } } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSManager.java index e1f1c863..24fb70cb 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSManager.java @@ -26,19 +26,15 @@ import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; import android.content.Context; -import android.nfc.Tag; -import android.util.Log; import android.util.SparseArray; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; import java.util.LinkedList; import java.util.Queue; import java.util.UUID; import no.nordicsemi.android.log.Logger; -import no.nordicsemi.android.nrftoolbox.parser.HeartRateMeasurementParser; +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.profile.BleManager; import no.nordicsemi.android.nrftoolbox.utility.DebugLogger; @@ -47,337 +43,363 @@ import no.nordicsemi.android.nrftoolbox.utility.DebugLogger; * Created by rora on 10.05.2016. */ public class CGMSManager extends BleManager { - private static final String TAG = "CGMSManager"; + private static final String TAG = "CGMSManager"; - /** Cycling Speed and Cadence service UUID */ - public final static UUID CGMS_UUID = UUID.fromString("0000181F-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"); - /** Record Access Control Point characteristic UUID */ - private final static UUID RACP_UUID = UUID.fromString("00002A52-0000-1000-8000-00805f9b34fb"); + /** + * Cycling Speed and Cadence service UUID + */ + public final static UUID CGMS_UUID = UUID.fromString("0000181F-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"); + private final static int OP_CODE_START_SESSION = 26; + /** + * Record Access Control Point characteristic UUID + */ + private final static UUID RACP_UUID = UUID.fromString("00002A52-0000-1000-8000-00805f9b34fb"); - private final static int OP_CODE_REPORT_STORED_RECORDS = 1; - private final static int OP_CODE_DELETE_STORED_RECORDS = 2; - private final static int OP_CODE_ABORT_OPERATION = 3; - private final static int OP_CODE_REPORT_NUMBER_OF_RECORDS = 4; - private final static int OP_CODE_NUMBER_OF_STORED_RECORDS_RESPONSE = 5; - private final static int OP_CODE_RESPONSE_CODE = 6; + private final static int OP_CODE_REPORT_STORED_RECORDS = 1; + private final static int OP_CODE_DELETE_STORED_RECORDS = 2; + private final static int OP_CODE_ABORT_OPERATION = 3; + private final static int OP_CODE_REPORT_NUMBER_OF_RECORDS = 4; + private final static int OP_CODE_NUMBER_OF_STORED_RECORDS_RESPONSE = 5; + private final static int OP_CODE_RESPONSE_CODE = 6; - private final static int OPERATOR_NULL = 0; - private final static int OPERATOR_ALL_RECORDS = 1; - private final static int OPERATOR_LESS_THEN_OR_EQUAL = 2; - private final static int OPERATOR_GREATER_THEN_OR_EQUAL = 3; - private final static int OPERATOR_WITHING_RANGE = 4; - private final static int OPERATOR_FIRST_RECORD = 5; - private final static int OPERATOR_LAST_RECORD = 6; + private final static int OPERATOR_NULL = 0; + private final static int OPERATOR_ALL_RECORDS = 1; + private final static int OPERATOR_LESS_THEN_OR_EQUAL = 2; + private final static int OPERATOR_GREATER_THEN_OR_EQUAL = 3; + private final static int OPERATOR_WITHING_RANGE = 4; + private final static int OPERATOR_FIRST_RECORD = 5; + private final static 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}.
- * The syntax of the operand is: [Filter Type][Minimum][Maximum].
- * This filter selects the records by the sequence number. - */ - private final static 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}.
- * The syntax of the operand is: [Filter Type][Minimum][Maximum].
- * This filter selects the records by the user facing time (base time + offset time). - */ - private final static int FILTER_TYPE_USER_FACING_TIME = 2; - private final static int RESPONSE_SUCCESS = 1; - private final static int RESPONSE_OP_CODE_NOT_SUPPORTED = 2; - private final static int RESPONSE_INVALID_OPERATOR = 3; - private final static int RESPONSE_OPERATOR_NOT_SUPPORTED = 4; - private final static int RESPONSE_INVALID_OPERAND = 5; - private final static int RESPONSE_NO_RECORDS_FOUND = 6; - private final static int RESPONSE_ABORT_UNSUCCESSFUL = 7; - private final static int RESPONSE_PROCEDURE_NOT_COMPLETED = 8; - private final static int RESPONSE_OPERAND_NOT_SUPPORTED = 9; + /** + * The filter type is used for range operators ({@link #OPERATOR_LESS_THEN_OR_EQUAL}, {@link #OPERATOR_GREATER_THEN_OR_EQUAL}, {@link #OPERATOR_WITHING_RANGE}.
+ * The syntax of the operand is: [Filter Type][Minimum][Maximum].
+ * This filter selects the records by the sequence number. + */ + private final static 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}.
+ * The syntax of the operand is: [Filter Type][Minimum][Maximum].
+ * This filter selects the records by the user facing time (base time + offset time). + */ + private final static int FILTER_TYPE_USER_FACING_TIME = 2; + private final static int RESPONSE_SUCCESS = 1; + private final static int RESPONSE_OP_CODE_NOT_SUPPORTED = 2; + private final static int RESPONSE_INVALID_OPERATOR = 3; + private final static int RESPONSE_OPERATOR_NOT_SUPPORTED = 4; + private final static int RESPONSE_INVALID_OPERAND = 5; + private final static int RESPONSE_NO_RECORDS_FOUND = 6; + private final static int RESPONSE_ABORT_UNSUCCESSFUL = 7; + private final static int RESPONSE_PROCEDURE_NOT_COMPLETED = 8; + private final static int RESPONSE_OPERAND_NOT_SUPPORTED = 9; - private final static SimpleDateFormat mTimeFormat= new SimpleDateFormat("dd MM YYYY HH:mm:ss"); + private BluetoothGattCharacteristic mCGMMeasurementCharacteristic; + private BluetoothGattCharacteristic mCGMOpsControlPointCharacteristic; + private BluetoothGattCharacteristic mRecordAccessControlPointCharacteristic; - private BluetoothGattCharacteristic mCGMMEasurementCharacteristic; - private BluetoothGattCharacteristic mCGMOpsControlPointCharacteristic; - private BluetoothGattCharacteristic mRecordAccessControlPointCharacteristic; + private static CGMSManager managerInstance = null; + private SparseArray mRecords = new SparseArray<>(); + private boolean mAbort; + private long mSessionStartTime; - private static CGMSManager managerInstance = null; - private SparseArray mRecords = new SparseArray<>(); - private boolean mAbort; - private long mSessionStartTime; + /** + * singleton implementation of HRSManager class + */ + public static synchronized CGMSManager getInstance(final Context context) { + if (managerInstance == null) { + managerInstance = new CGMSManager(context); + } + return managerInstance; + } - /** - * singleton implementation of HRSManager class - */ - public static synchronized CGMSManager getInstance(final Context context) { - if (managerInstance == null) { - managerInstance = new CGMSManager(context); - } - return managerInstance; - } - public CGMSManager(Context context) { - super(context); - } + public CGMSManager(Context context) { + super(context); + } - @Override - protected BleManagerGattCallback getGattCallback() { - return mGattCallback; - } + @Override + protected BleManagerGattCallback getGattCallback() { + return mGattCallback; + } - /** - * BluetoothGatt callbacks for connection/disconnection, service discovery, receiving notification, etc - */ - private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() { + /** + * BluetoothGatt callbacks for connection/disconnection, service discovery, receiving notification, etc + */ + private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() { - @Override - protected Queue initGatt(final BluetoothGatt gatt) { - final LinkedList requests = new LinkedList<>(); - requests.push(Request.newEnableNotificationsRequest(mCGMMEasurementCharacteristic)); - if (mCGMOpsControlPointCharacteristic != null) { - mSessionStartTime = System.currentTimeMillis(); - requests.push(Request.newWriteRequest(mCGMOpsControlPointCharacteristic, new byte[]{26} /*start session value*/)); - } - requests.push(Request.newEnableIndicationsRequest(mRecordAccessControlPointCharacteristic)); - return requests; - } + @Override + protected Queue initGatt(final BluetoothGatt gatt) { + final LinkedList requests = new LinkedList<>(); + requests.add(Request.newEnableNotificationsRequest(mCGMMeasurementCharacteristic)); + if (mCGMOpsControlPointCharacteristic != null) { + mSessionStartTime = System.currentTimeMillis(); + requests.add(Request.newEnableIndicationsRequest(mCGMOpsControlPointCharacteristic)); + requests.add(Request.newWriteRequest(mCGMOpsControlPointCharacteristic, new byte[]{OP_CODE_START_SESSION})); + } + requests.add(Request.newEnableIndicationsRequest(mRecordAccessControlPointCharacteristic)); + return requests; + } - @Override - protected boolean isRequiredServiceSupported(final BluetoothGatt gatt) { - final BluetoothGattService service = gatt.getService(CGMS_UUID); - if (service != null) { - mCGMMEasurementCharacteristic = service.getCharacteristic(CGM_MEASUREMENT_UUID); - mCGMOpsControlPointCharacteristic = service.getCharacteristic(CGM_OPS_CONTROL_POINT_UUID); - mRecordAccessControlPointCharacteristic = service.getCharacteristic(RACP_UUID); - } - return mCGMMEasurementCharacteristic != null && mCGMOpsControlPointCharacteristic != null && mRecordAccessControlPointCharacteristic != null; - } + @Override + protected boolean isRequiredServiceSupported(final BluetoothGatt gatt) { + final BluetoothGattService service = gatt.getService(CGMS_UUID); + if (service != null) { + mCGMMeasurementCharacteristic = service.getCharacteristic(CGM_MEASUREMENT_UUID); + mCGMOpsControlPointCharacteristic = service.getCharacteristic(CGM_OPS_CONTROL_POINT_UUID); + mRecordAccessControlPointCharacteristic = service.getCharacteristic(RACP_UUID); + } + return mCGMMeasurementCharacteristic != null && mCGMOpsControlPointCharacteristic != null && mRecordAccessControlPointCharacteristic != null; + } - @Override - protected boolean isOptionalServiceSupported(final BluetoothGatt gatt) { - final BluetoothGattService service = gatt.getService(CGMS_UUID); - if (service != null) { - mCGMOpsControlPointCharacteristic = service.getCharacteristic(CGM_OPS_CONTROL_POINT_UUID); - } - return mCGMOpsControlPointCharacteristic != null; - } + @Override + protected boolean isOptionalServiceSupported(final BluetoothGatt gatt) { + final BluetoothGattService service = gatt.getService(CGMS_UUID); + if (service != null) { + mCGMOpsControlPointCharacteristic = service.getCharacteristic(CGM_OPS_CONTROL_POINT_UUID); + } + return mCGMOpsControlPointCharacteristic != null; + } - @Override - public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - } + @Override + public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + } - @Override - protected void onDeviceDisconnected() { - mCGMOpsControlPointCharacteristic = null; - mCGMMEasurementCharacteristic = null; - //mRecordAccessControlPointCharacteristic = null; - } + @Override + protected void onDeviceDisconnected() { + mCGMOpsControlPointCharacteristic = null; + mCGMMeasurementCharacteristic = null; + mRecordAccessControlPointCharacteristic = null; + } - @Override - public void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - final UUID uuid = characteristic.getUuid(); - if(CGM_MEASUREMENT_UUID.equals(uuid)) { - if (mLogSession != null) - Logger.a(mLogSession, HeartRateMeasurementParser.parse(characteristic)); - byte [] data = characteristic.getValue(); - int cgmSize = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); - float cgmValue; - int timeOffset; - if (cgmSize > 0) { - cgmValue = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, 2); - timeOffset = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 4); - Date date = new Date(mSessionStartTime + (timeOffset * 3600)); - final String timeStamp = mTimeFormat.format(date); - //This will send callback to CGMSActivity when new concentration value is received from CGMS device - mCallbacks.onCGMValueReceived(cgmValue, timeStamp); - } - } else if (CGM_OPS_CONTROL_POINT_UUID.equals(uuid)){ - Log.v(TAG, "CGM Ops control"); - } else if (RACP_UUID.equals(uuid)){ - Log.v(TAG, "RACP Ops control"); - } - } + @Override + protected void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + if (characteristic.getUuid().equals(RACP_UUID)) { + Logger.a(mLogSession, "\"" + RecordAccessControlPointParser.parse(characteristic) + "\" sent"); + } else { // uuid == CGM_OPS_CONTROL_POINT_UUID + Logger.a(mLogSession, "\"" + CGMSpecificOpsControlPointParser.parse(characteristic) + "\" sent"); + } + } - @Override - protected void onCharacteristicIndicated(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - if (mLogSession != null) - Logger.a(mLogSession, RecordAccessControlPointParser.parse(characteristic)); + @Override + public void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + Logger.a(mLogSession, "\"" + CGMMeasurementParser.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 + // CGM Measurement characteristic + final int cgmSize = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); + if (cgmSize > 0) { + final float cgmValue = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, 2); + final int sequenceNumber = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 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); + } + } - 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); + @Override + protected void onCharacteristicIndicated(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + if (characteristic.getUuid().equals(RACP_UUID)) { + Logger.a(mLogSession, "\"" + RecordAccessControlPointParser.parse(characteristic) + "\" received"); - mCallbacks.onNumberOfRecordsRequested(number); + // Record Access Control Point characteristic + int offset = 0; + final int opCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset); + offset += 2; // skip the operator - // 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(); - } - } 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); + 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); - switch (responseCode) { - case RESPONSE_SUCCESS: - if (!mAbort) - mCallbacks.onOperationCompleted(); - else - mCallbacks.onOperationAborted(); - break; - case RESPONSE_NO_RECORDS_FOUND: - mCallbacks.onOperationCompleted(); - break; - case RESPONSE_OP_CODE_NOT_SUPPORTED: - mCallbacks.onOperationNotSupported(); - break; - case RESPONSE_PROCEDURE_NOT_COMPLETED: - case RESPONSE_ABORT_UNSUCCESSFUL: - default: - mCallbacks.onOperationFailed(); - break; - } - mAbort = false; - } - } + 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); - /** - * 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]); + 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 + Logger.a(mLogSession, "\"" + CGMSpecificOpsControlPointParser.parse(characteristic) + "\" received"); + } + } + }; - // write the operation code - int offset = 0; - characteristic.setValue(opCode, BluetoothGattCharacteristic.FORMAT_UINT8, offset); - offset += 1; + /** + * 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 operator. This is always present but may be equal to OPERATOR_NULL - characteristic.setValue(operator, BluetoothGattCharacteristic.FORMAT_UINT8, offset); - offset += 1; + // write the operation code + int offset = 0; + characteristic.setValue(opCode, 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; + // write the operator. This is always present but may be equal to OPERATOR_NULL + characteristic.setValue(operator, BluetoothGattCharacteristic.FORMAT_UINT8, offset); + offset += 1; - for (final Integer i : params) { - characteristic.setValue(i, BluetoothGattCharacteristic.FORMAT_UINT16, offset); - offset += 2; - } - } - } + // 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; - /** - * 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. - */ - public void getLastRecord() { - if (mRecordAccessControlPointCharacteristic == null) - return; + for (final Integer i : params) { + characteristic.setValue(i, BluetoothGattCharacteristic.FORMAT_UINT16, offset); + offset += 2; + } + } + } - clear(); - mCallbacks.onOperationStarted(); + /** + * Returns a list of CGM records obtained from this device. The key in the array is the + */ + public SparseArray getRecords() { + return mRecords; + } - final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; - setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_LAST_RECORD); - writeCharacteristic(characteristic); - } + /** + * Clears the records list locally + */ + public void clear() { + mRecords.clear(); + mCallbacks.onDatasetClear(mBluetoothDevice); + } - /** - * Returns all records as a sparse array where sequence number is the key. - * - * @return the records list - */ - public SparseArray getRecords() { - return mRecords; - } + /** + * 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. + */ + public void getLastRecord() { + if (mRecordAccessControlPointCharacteristic == null) + return; - /** - * Clears the records list locally - */ - public void clear() { - mRecords.clear(); - mCallbacks.onDatasetChanged(); - } + clear(); + mCallbacks.onOperationStarted(mBluetoothDevice); - /** - * Sends abort operation signal to the device - */ - public void abort() { - if (mRecordAccessControlPointCharacteristic == null) - return; + final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; + setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_LAST_RECORD); + writeCharacteristic(characteristic); + } - mAbort = true; - final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; - setOpCode(characteristic, OP_CODE_ABORT_OPERATION, OPERATOR_NULL); - writeCharacteristic(characteristic); - } + /** + * 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. + */ + public void getFirstRecord() { + if (mRecordAccessControlPointCharacteristic == null) + return; - /** - * 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. - */ - public void getFirstRecord() { - if (mRecordAccessControlPointCharacteristic == null) - return; + clear(); + mCallbacks.onOperationStarted(mBluetoothDevice); - clear(); - mCallbacks.onOperationStarted(); + final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; + setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_FIRST_RECORD); + writeCharacteristic(characteristic); + } - final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; - setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_FIRST_RECORD); - final LinkedList requests = new LinkedList<>(); - writeCharacteristic(characteristic); - } + /** + * Sends abort operation signal to the device + */ + public void abort() { + if (mRecordAccessControlPointCharacteristic == null) + return; - /** - * 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. - */ - public void getAllRecords() { - if (mRecordAccessControlPointCharacteristic == null) - return; + mAbort = true; + final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; + setOpCode(characteristic, OP_CODE_ABORT_OPERATION, OPERATOR_NULL); + writeCharacteristic(characteristic); + } - clear(); - mCallbacks.onOperationStarted(); + /** + * 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. + */ + public void getAllRecords() { + if (mRecordAccessControlPointCharacteristic == null) + return; - final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; - setOpCode(characteristic, OP_CODE_REPORT_NUMBER_OF_RECORDS, OPERATOR_ALL_RECORDS); - writeCharacteristic(characteristic); - } + clear(); + mCallbacks.onOperationStarted(mBluetoothDevice); - public void deleteAllRecords() { - if (mRecordAccessControlPointCharacteristic == null) - return; + final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; + setOpCode(characteristic, OP_CODE_REPORT_NUMBER_OF_RECORDS, OPERATOR_ALL_RECORDS); + writeCharacteristic(characteristic); + } - clear(); - mCallbacks.onOperationStarted(); + /** + * 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. + */ + public void refreshRecords() { + if (mRecordAccessControlPointCharacteristic == null) + return; - final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; - setOpCode(characteristic, OP_CODE_DELETE_STORED_RECORDS, OPERATOR_ALL_RECORDS); - writeCharacteristic(characteristic); - } + if (mRecords.size() == 0) { + getAllRecords(); + } else { + mCallbacks.onOperationStarted(mBluetoothDevice); + // 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); + // 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 + } + } + + public void deleteAllRecords() { + if (mRecordAccessControlPointCharacteristic == null) + return; + + clear(); + mCallbacks.onOperationStarted(mBluetoothDevice); + + final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; + setOpCode(characteristic, OP_CODE_DELETE_STORED_RECORDS, OPERATOR_ALL_RECORDS); + writeCharacteristic(characteristic); + } } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSManagerCallbacks.java index 040f7415..80849dc3 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSManagerCallbacks.java @@ -22,6 +22,8 @@ package no.nordicsemi.android.nrftoolbox.cgms; +import android.bluetooth.BluetoothDevice; + import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; /** @@ -29,26 +31,22 @@ import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; */ public interface CGMSManagerCallbacks extends BleManagerCallbacks { /** - * Called when new CGM value has been obtained from the sensor - * - * @param value - * the new value + * Called when new CGM value has been obtained from the sensor. */ - public void onCGMValueReceived(float value, String timeStamp); + void onCGMValueReceived(final BluetoothDevice device, final CGMSRecord record); + void onOperationStarted(final BluetoothDevice device); - public void onOperationStarted(); + void onOperationCompleted(final BluetoothDevice device); - public void onOperationCompleted(); + void onOperationFailed(final BluetoothDevice device); - public void onOperationFailed(); + void onOperationAborted(final BluetoothDevice device); - public void onOperationAborted(); + void onOperationNotSupported(final BluetoothDevice device); - public void onOperationNotSupported(); + void onDatasetClear(final BluetoothDevice device); - public void onDatasetChanged(); - - public void onNumberOfRecordsRequested(final int value); + void onNumberOfRecordsRequested(final BluetoothDevice device, final int value); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSRecord.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSRecord.java index 3568a9f1..ff076889 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSRecord.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSRecord.java @@ -3,31 +3,27 @@ package no.nordicsemi.android.nrftoolbox.cgms; import android.os.Parcel; import android.os.Parcelable; -import java.util.Calendar; - /** * Created by rora on 02.09.2016. */ public class CGMSRecord implements Parcelable{ - - /** Record sequence number */ + /** Record sequence number. */ protected int sequenceNumber; - /** The base time of the measurement */ - protected Calendar time; - /** Time offset of the record */ - protected String timeStamp; - protected float reading; + /** The base time of the measurement (start time + sequenceNumber of minutes). */ + protected long timestamp; + /** The glucose concentration in mg/dL. */ + protected float glucoseConcentration; - protected CGMSRecord(float cgmsValue, String timeStamp) { - sequenceNumber = 0; - this.timeStamp = timeStamp; - this.reading = cgmsValue; + protected CGMSRecord(int sequenceNumber, float glucoseConcentration, long timestamp) { + this.sequenceNumber = sequenceNumber; + this.glucoseConcentration = glucoseConcentration; + this.timestamp = timestamp; } protected CGMSRecord(Parcel in) { - sequenceNumber = in.readInt(); - timeStamp = in.readString(); - reading = in.readFloat(); + this.sequenceNumber = in.readInt(); + this.glucoseConcentration = in.readFloat(); + this.timestamp = in.readLong(); } public static final Creator CREATOR = new Creator() { @@ -50,7 +46,7 @@ public class CGMSRecord implements Parcelable{ @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(sequenceNumber); - parcel.writeString(timeStamp); - parcel.writeFloat(reading); + parcel.writeFloat(glucoseConcentration); + parcel.writeLong(timestamp); } } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSRecordsAdapter.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSRecordsAdapter.java index e773bb82..6b7feecf 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSRecordsAdapter.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMSRecordsAdapter.java @@ -1,87 +1,82 @@ package no.nordicsemi.android.nrftoolbox.cgms; import android.content.Context; -import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + import no.nordicsemi.android.nrftoolbox.R; /** * Created by rora on 02.09.2016. */ public class CGMSRecordsAdapter extends BaseAdapter { + private final static SimpleDateFormat mTimeFormat = new SimpleDateFormat("dd.MM.YYYY HH:mm:ss", Locale.US); - private SparseArray mRecords; - private LayoutInflater mInflator; - private Context context; + private List mRecords; + private LayoutInflater mInflater; - public CGMSRecordsAdapter(Context context) { - super(); - mRecords = new SparseArray<>(); - this.context = context; - mInflator = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } + public CGMSRecordsAdapter(final Context context) { + mRecords = new ArrayList<>(); + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } - @Override - public int getCount() { - return mRecords.size(); - } + @Override + public int getCount() { + return mRecords.size(); + } - @Override - public Object getItem(int i) { - return null; - } + @Override + public Object getItem(int i) { + return null; + } - @Override - public long getItemId(int i) { - return i; - } + @Override + public long getItemId(int i) { + return i; + } - @Override - public View getView(final int position, View convertView, ViewGroup parent) { + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + ViewHolder viewHolder; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.activity_feature_cgms_item, parent, false); + viewHolder = new ViewHolder(); + viewHolder.concentration = (TextView) convertView.findViewById(R.id.cgms_concentration); + viewHolder.time = (TextView) convertView.findViewById(R.id.time); + viewHolder.details = (TextView) convertView.findViewById(R.id.details); + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } - ViewHolder viewHolder; - if (convertView == null) { - convertView = mInflator.inflate(R.layout.activity_feature_cgms_item, null); - viewHolder = new ViewHolder(); - viewHolder.concentration = (TextView) convertView.findViewById(R.id.cgms_concentration); - viewHolder.time = (TextView) convertView.findViewById(R.id.time); - viewHolder.details = (TextView) convertView.findViewById(R.id.details); - convertView.setTag(viewHolder); - } else { - viewHolder = (ViewHolder) convertView.getTag(); - } + final CGMSRecord cgmsRecord = mRecords.get(position); + viewHolder.concentration.setText(String.valueOf(cgmsRecord.glucoseConcentration)); + viewHolder.details.setText(viewHolder.details.getResources().getString(R.string.cgms_details, cgmsRecord.sequenceNumber)); + viewHolder.time.setText(mTimeFormat.format(new Date(cgmsRecord.timestamp))); - final CGMSRecord cgmsRecord = mRecords.get(position); - final String concentration = String.valueOf(cgmsRecord.reading); + return convertView; + } - if (concentration != null && concentration.length() > 0) { - viewHolder.concentration.setText(concentration /*+ " " + context.getString(R.string.cgms_value_unit*/); - viewHolder.time.setText(cgmsRecord.timeStamp); - } + public void addItem(final CGMSRecord record) { + mRecords.add(record); + } - return convertView; - } + public void clear() { + mRecords.clear(); + } - public void addItem(CGMSRecord record) { - mRecords.put(mRecords.size(), record); - } - - public SparseArray getValues() { - return mRecords; - } - - public void clear() { - mRecords.clear(); - } - - static class ViewHolder { - TextView time; - TextView details; - TextView concentration; - } + private static class ViewHolder { + TextView time; + TextView details; + TextView concentration; + } } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMService.java index 3e91712d..9afbadb7 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/cgms/CGMService.java @@ -3,6 +3,7 @@ package no.nordicsemi.android.nrftoolbox.cgms; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -22,7 +23,8 @@ 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"; - public static final String BROADCAST_CGMS_VALUES = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_CGMS_VALUES"; + 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"; @@ -30,7 +32,6 @@ public class CGMService extends BleProfileService implements CGMSManagerCallback 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 BROADCAST_DATA_SET_CHANGED = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_DATA_SET_CHANGED"; public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_DATA"; private final static int NOTIFICATION_ID = 229; @@ -39,54 +40,32 @@ public class CGMService extends BleProfileService implements CGMSManagerCallback private CGMSManager mManager; private final LocalBinder mBinder = new CGMSBinder(); - private SparseArray mRecords = new SparseArray<>(); /** * This local binder is an interface for the bonded activity to operate with the RSC sensor */ public class CGMSBinder extends LocalBinder { - public SparseArray getCgmsRecords() { - return mRecords; - } - - /** - * 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() { - clear(); - if(mManager != null) - mManager.getLastRecord(); - } - /** * Returns all records as a sparse array where sequence number is the key. * * @return the records list */ public SparseArray getRecords() { - return mRecords; + return mManager.getRecords(); } /** * Clears the records list locally */ public void clear() { - mRecords.clear(); + if (mManager != null) + mManager.clear(); } /** - * Sends abort operation signal to the device - */ - public void abort() { - - if(mManager != null) - mManager.abort(); - } - - /** - * 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 + * 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() { @@ -95,18 +74,48 @@ public class CGMService extends BleProfileService implements CGMSManagerCallback } /** - * 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 CGMSManager#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 CGMSManager#RESPONSE_SUCCESS} or other in case of - * error. + * 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() { - clear(); if(mManager != null) mManager.getAllRecords(); } - public void deleteAllRecords() { + /** + * 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(); + } + + /** + * 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(); } @@ -212,56 +221,62 @@ public class CGMService extends BleProfileService implements CGMSManagerCallback }; @Override - public void onCGMValueReceived(float value, String timeStamp) { - final Intent broadcast = new Intent(BROADCAST_CGMS_VALUES); - CGMSRecord cgmsRecord = new CGMSRecord(value, timeStamp); - broadcast.putExtra(EXTRA_CGMS_RECORD, cgmsRecord); + public void onCGMValueReceived(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); - mRecords.put(mRecords.size(), cgmsRecord); } @Override - public void onOperationStarted() { + public void onOperationStarted(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() { + public void onOperationCompleted(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() { + public void onOperationFailed(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() { + public void onOperationAborted(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() { + public void onOperationNotSupported(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 onDatasetChanged() { - + public void onDatasetClear(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(int value) { + public void onNumberOfRecordsRequested(final BluetoothDevice device, int value) { showToast(getString(R.string.gls_progress, value)); } } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCActivity.java index d1f4fcaf..3602ce69 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCActivity.java @@ -22,6 +22,7 @@ package no.nordicsemi.android.nrftoolbox.csc; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -33,6 +34,7 @@ import android.support.v4.content.LocalBroadcastManager; import android.view.Menu; import android.widget.TextView; +import java.util.Locale; import java.util.UUID; import no.nordicsemi.android.nrftoolbox.R; @@ -172,7 +174,7 @@ public class CSCActivity extends BleProfileServiceReadyActivity { @Override protected Queue initGatt(final BluetoothGatt gatt) { final LinkedList requests = new LinkedList<>(); - requests.push(Request.newEnableNotificationsRequest(mCSCMeasurementCharacteristic)); + requests.add(Request.newEnableNotificationsRequest(mCSCMeasurementCharacteristic)); return requests; } @@ -83,8 +83,7 @@ public class CSCManager extends BleManager { @Override public void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - if (mLogSession != null) - Logger.a(mLogSession, CSCMeasurementParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + CSCMeasurementParser.parse(characteristic) + "\" received"); // Decode the new data int offset = 0; @@ -102,7 +101,7 @@ public class CSCManager extends BleManager { offset += 2; // Notify listener about the new measurement - mCallbacks.onWheelMeasurementReceived(wheelRevolutions, lastWheelEventTime); + mCallbacks.onWheelMeasurementReceived(gatt.getDevice(), wheelRevolutions, lastWheelEventTime); } if (crankRevPreset) { @@ -113,7 +112,7 @@ public class CSCManager extends BleManager { // offset += 2; // Notify listener about the new measurement - mCallbacks.onCrankMeasurementReceived(crankRevolutions, lastCrankEventTime); + mCallbacks.onCrankMeasurementReceived(gatt.getDevice(), crankRevolutions, lastCrankEventTime); } } }; diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCManagerCallbacks.java index 08a39000..23826095 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCManagerCallbacks.java @@ -21,12 +21,14 @@ */ package no.nordicsemi.android.nrftoolbox.csc; +import android.bluetooth.BluetoothDevice; + import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; public interface CSCManagerCallbacks extends BleManagerCallbacks { - public static final int NOT_AVAILABLE = -1; + int NOT_AVAILABLE = -1; - public void onWheelMeasurementReceived(final int wheelRevolutions, final int wheelCrankEventTime); + void onWheelMeasurementReceived(final BluetoothDevice device, final int wheelRevolutions, final int wheelCrankEventTime); - public void onCrankMeasurementReceived(final int crankRevolutions, final int lastCrankEventTime); + void onCrankMeasurementReceived(final BluetoothDevice device, final int crankRevolutions, final int lastCrankEventTime); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java index 66b57e73..b2d704a0 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java @@ -25,6 +25,7 @@ package no.nordicsemi.android.nrftoolbox.csc; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -126,7 +127,7 @@ public class CSCService extends BleProfileService implements CSCManagerCallbacks } @Override - public void onWheelMeasurementReceived(final int wheelRevolutions, final int lastWheelEventTime) { + public void onWheelMeasurementReceived(final BluetoothDevice device, final int wheelRevolutions, final int lastWheelEventTime) { Logger.a(getLogSession(), "Wheel rev: " + wheelRevolutions + "\nLast wheel event time: " + lastWheelEventTime + " ms"); final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); @@ -151,6 +152,7 @@ public class CSCService extends BleProfileService implements CSCManagerCallbacks mWheelCadence = (wheelRevolutions - mLastWheelRevolutions) * 60.0f / timeDifference; final Intent broadcast = new Intent(BROADCAST_WHEEL_DATA); + broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice()); broadcast.putExtra(EXTRA_SPEED, speed); broadcast.putExtra(EXTRA_DISTANCE, distance); broadcast.putExtra(EXTRA_TOTAL_DISTANCE, totalDistance); @@ -161,7 +163,7 @@ public class CSCService extends BleProfileService implements CSCManagerCallbacks } @Override - public void onCrankMeasurementReceived(int crankRevolutions, int lastCrankEventTime) { + public void onCrankMeasurementReceived(final BluetoothDevice device, int crankRevolutions, int lastCrankEventTime) { Logger.a(getLogSession(), "Crank rev: " + crankRevolutions + "\nLast crank event time: " + lastCrankEventTime + " ms"); if (mLastCrankEventTime == lastCrankEventTime) @@ -179,6 +181,7 @@ public class CSCService extends BleProfileService implements CSCManagerCallbacks final float gearRatio = mWheelCadence / crankCadence; final Intent broadcast = new Intent(BROADCAST_CRANK_DATA); + broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice()); broadcast.putExtra(EXTRA_GEAR_RATIO, gearRatio); broadcast.putExtra(EXTRA_CADENCE, (int) crankCadence); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseActivity.java index 06e1132a..84c21bca 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseActivity.java @@ -21,6 +21,7 @@ */ package no.nordicsemi.android.nrftoolbox.gls; +import android.bluetooth.BluetoothDevice; import android.os.Bundle; import android.util.SparseArray; import android.view.MenuInflater; @@ -158,46 +159,46 @@ public class GlucoseActivity extends BleProfileExpandableListActivity implements } @Override - public void onDeviceDisconnected() { - super.onDeviceDisconnected(); + public void onDeviceDisconnected(final BluetoothDevice device) { + super.onDeviceDisconnected(device); setOperationInProgress(false); } @Override - public void onOperationStarted() { + public void onOperationStarted(final BluetoothDevice device) { setOperationInProgress(true); } @Override - public void onOperationCompleted() { + public void onOperationCompleted(final BluetoothDevice device) { setOperationInProgress(false); } @Override - public void onOperationAborted() { + public void onOperationAborted(final BluetoothDevice device) { setOperationInProgress(false); } @Override - public void onOperationNotSupported() { + public void onOperationNotSupported(final BluetoothDevice device) { setOperationInProgress(false); showToast(R.string.gls_operation_not_supported); } @Override - public void onOperationFailed() { + public void onOperationFailed(final BluetoothDevice device) { setOperationInProgress(false); showToast(R.string.gls_operation_failed); } @Override - public void onError(final String message, final int errorCode) { - super.onError(message, errorCode); - onOperationFailed(); + public void onError(final BluetoothDevice device, final String message, final int errorCode) { + super.onError(device, message, errorCode); + onOperationFailed(device); } @Override - public void onDatasetChanged() { + public void onDatasetChanged(final BluetoothDevice device) { runOnUiThread(new Runnable() { @Override public void run() { @@ -215,7 +216,7 @@ public class GlucoseActivity extends BleProfileExpandableListActivity implements } @Override - public void onNumberOfRecordsRequested(final int value) { + public void onNumberOfRecordsRequested(final BluetoothDevice device, final int value) { showToast(getString(R.string.gls_progress, value)); } } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManager.java index f9a9f933..dfb709c5 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManager.java @@ -129,10 +129,10 @@ public class GlucoseManager extends BleManager { @Override protected Queue initGatt(final BluetoothGatt gatt) { final LinkedList requests = new LinkedList<>(); - requests.push(Request.newEnableNotificationsRequest(mGlucoseMeasurementCharacteristic)); + requests.add(Request.newEnableNotificationsRequest(mGlucoseMeasurementCharacteristic)); if (mGlucoseMeasurementContextCharacteristic != null) - requests.push(Request.newEnableNotificationsRequest(mGlucoseMeasurementContextCharacteristic)); - requests.push(Request.newEnableIndicationsRequest(mRecordAccessControlPointCharacteristic)); + requests.add(Request.newEnableNotificationsRequest(mGlucoseMeasurementContextCharacteristic)); + requests.add(Request.newEnableIndicationsRequest(mRecordAccessControlPointCharacteristic)); return requests; } @@ -160,12 +160,16 @@ public class GlucoseManager extends BleManager { } @Override - public void onCharacteristicNotified(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + protected void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + Logger.a(mLogSession, "\"" + RecordAccessControlPointParser.parse(characteristic) + "\" sent"); + } + + @Override + public void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { final UUID uuid = characteristic.getUuid(); if (GM_CHARACTERISTIC.equals(uuid)) { - if (mLogSession != null) - Logger.a(mLogSession, GlucoseMeasurementParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + GlucoseMeasurementParser.parse(characteristic) + "\" received"); int offset = 0; final int flags = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset); @@ -237,12 +241,11 @@ public class GlucoseManager extends BleManager { // if there is no context information following the measurement data, notify callback about the new record if (!contextInfoFollows) - mCallbacks.onDatasetChanged(); + mCallbacks.onDatasetChanged(gatt.getDevice()); } }); } else if (GM_CONTEXT_CHARACTERISTIC.equals(uuid)) { - if (mLogSession != null) - Logger.a(mLogSession, GlucoseMeasurementContextParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + GlucoseMeasurementContextParser.parse(characteristic) + "\" received"); int offset = 0; final int flags = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset); @@ -308,14 +311,13 @@ public class GlucoseManager extends BleManager { } // notify callback about the new record - mCallbacks.onDatasetChanged(); + mCallbacks.onDatasetChanged(gatt.getDevice()); } } @Override protected void onCharacteristicIndicated(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - if (mLogSession != null) - Logger.a(mLogSession, RecordAccessControlPointParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + RecordAccessControlPointParser.parse(characteristic) + "\" received"); // Record Access Control Point characteristic int offset = 0; @@ -326,7 +328,7 @@ public class GlucoseManager extends BleManager { // We've obtained the number of all records final int number = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); - mCallbacks.onNumberOfRecordsRequested(number); + mCallbacks.onNumberOfRecordsRequested(gatt.getDevice(), number); // Request the records if (number > 0) { @@ -334,7 +336,7 @@ public class GlucoseManager extends BleManager { setOpCode(racpCharacteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_ALL_RECORDS); writeCharacteristic(racpCharacteristic); } else { - mCallbacks.onOperationCompleted(); + mCallbacks.onOperationCompleted(gatt.getDevice()); } } else if (opCode == OP_CODE_RESPONSE_CODE) { final int requestedOpCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset); @@ -344,20 +346,20 @@ public class GlucoseManager extends BleManager { switch (responseCode) { case RESPONSE_SUCCESS: if (!mAbort) - mCallbacks.onOperationCompleted(); + mCallbacks.onOperationCompleted(gatt.getDevice()); else - mCallbacks.onOperationAborted(); + mCallbacks.onOperationAborted(gatt.getDevice()); break; case RESPONSE_NO_RECORDS_FOUND: - mCallbacks.onOperationCompleted(); + mCallbacks.onOperationCompleted(gatt.getDevice()); break; case RESPONSE_OP_CODE_NOT_SUPPORTED: - mCallbacks.onOperationNotSupported(); + mCallbacks.onOperationNotSupported(gatt.getDevice()); break; case RESPONSE_PROCEDURE_NOT_COMPLETED: case RESPONSE_ABORT_UNSUCCESSFUL: default: - mCallbacks.onOperationFailed(); + mCallbacks.onOperationFailed(gatt.getDevice()); break; } mAbort = false; @@ -416,7 +418,7 @@ public class GlucoseManager extends BleManager { */ public void clear() { mRecords.clear(); - mCallbacks.onDatasetChanged(); + mCallbacks.onDatasetChanged(mBluetoothDevice); } /** @@ -428,7 +430,7 @@ public class GlucoseManager extends BleManager { return; clear(); - mCallbacks.onOperationStarted(); + mCallbacks.onOperationStarted(mBluetoothDevice); final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_LAST_RECORD); @@ -444,7 +446,7 @@ public class GlucoseManager extends BleManager { return; clear(); - mCallbacks.onOperationStarted(); + mCallbacks.onOperationStarted(mBluetoothDevice); final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_FIRST_RECORD); @@ -461,7 +463,7 @@ public class GlucoseManager extends BleManager { return; clear(); - mCallbacks.onOperationStarted(); + mCallbacks.onOperationStarted(mBluetoothDevice); final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; setOpCode(characteristic, OP_CODE_REPORT_NUMBER_OF_RECORDS, OPERATOR_ALL_RECORDS); @@ -483,7 +485,7 @@ public class GlucoseManager extends BleManager { if (mRecords.size() == 0) { getAllRecords(); } else { - mCallbacks.onOperationStarted(); + mCallbacks.onOperationStarted(mBluetoothDevice); // obtain the last sequence number final int sequenceNumber = mRecords.keyAt(mRecords.size() - 1) + 1; @@ -519,7 +521,7 @@ public class GlucoseManager extends BleManager { return; clear(); - mCallbacks.onOperationStarted(); + mCallbacks.onOperationStarted(mBluetoothDevice); final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic; setOpCode(characteristic, OP_CODE_DELETE_STORED_RECORDS, OPERATOR_ALL_RECORDS); diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManagerCallbacks.java index 4e2affd3..87c69d76 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManagerCallbacks.java @@ -21,23 +21,22 @@ */ package no.nordicsemi.android.nrftoolbox.gls; +import android.bluetooth.BluetoothDevice; + import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; public interface GlucoseManagerCallbacks extends BleManagerCallbacks { - public static final int UNIT_mmHG = 0; - public static final int UNIT_kPa = 1; + void onOperationStarted(final BluetoothDevice device); - public void onOperationStarted(); + void onOperationCompleted(final BluetoothDevice device); - public void onOperationCompleted(); + void onOperationFailed(final BluetoothDevice device); - public void onOperationFailed(); + void onOperationAborted(final BluetoothDevice device); - public void onOperationAborted(); + void onOperationNotSupported(final BluetoothDevice device); - public void onOperationNotSupported(); + void onDatasetChanged(final BluetoothDevice device); - public void onDatasetChanged(); - - public void onNumberOfRecordsRequested(final int value); + void onNumberOfRecordsRequested(final BluetoothDevice device, final int value); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSActivity.java index 029627c3..f2d11891 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSActivity.java @@ -210,29 +210,29 @@ public class HRSActivity extends BleProfileActivity implements HRSManagerCallbac } @Override - public void onServicesDiscovered(final boolean optionalServicesFound) { + public void onServicesDiscovered(final BluetoothDevice device, final boolean optionalServicesFound) { // this may notify user or show some views } @Override - public void onDeviceReady() { + public void onDeviceReady(final BluetoothDevice device) { startShowGraph(); } @Override - public void onHRSensorPositionFound(final String position) { + public void onHRSensorPositionFound(final BluetoothDevice device, final String position) { setHRSPositionOnView(position); } @Override - public void onHRValueReceived(int value) { + public void onHRValueReceived(final BluetoothDevice device, int value) { mHrmValue = value; setHRSValueOnView(mHrmValue); } @Override - public void onDeviceDisconnected() { - super.onDeviceDisconnected(); + public void onDeviceDisconnected(final BluetoothDevice device) { + super.onDeviceDisconnected(device); runOnUiThread(new Runnable() { @Override public void run() { diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManager.java index 6c3d826d..2ab3b849 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManager.java @@ -77,8 +77,8 @@ public class HRSManager extends BleManager { protected Queue initGatt(final BluetoothGatt gatt) { final LinkedList requests = new LinkedList<>(); if (mHRLocationCharacteristic != null) - requests.push(Request.newReadRequest(mHRLocationCharacteristic)); - requests.push(Request.newEnableNotificationsRequest(mHRCharacteristic)); + requests.add(Request.newReadRequest(mHRLocationCharacteristic)); + requests.add(Request.newEnableNotificationsRequest(mHRCharacteristic)); return requests; } @@ -102,12 +102,11 @@ public class HRSManager extends BleManager { @Override public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - if (mLogSession != null) - Logger.a(mLogSession, BodySensorLocationParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + BodySensorLocationParser.parse(characteristic) + "\" received"); final String sensorPosition = getBodySensorPosition(characteristic.getValue()[0]); //This will send callback to HRSActivity when HR sensor position on body is found in HR device - mCallbacks.onHRSensorPositionFound(sensorPosition); + mCallbacks.onHRSensorPositionFound(gatt.getDevice(), sensorPosition); } @Override @@ -118,8 +117,7 @@ public class HRSManager extends BleManager { @Override public void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - if (mLogSession != null) - Logger.a(mLogSession, HeartRateMeasurementParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + HeartRateMeasurementParser.parse(characteristic) + "\" received"); int hrValue; if (isHeartRateInUINT16(characteristic.getValue()[0])) { @@ -128,7 +126,7 @@ public class HRSManager extends BleManager { hrValue = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1); } //This will send callback to HRSActivity when new HR value is received from HR device - mCallbacks.onHRValueReceived(hrValue); + mCallbacks.onHRValueReceived(gatt.getDevice(), hrValue); } }; diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManagerCallbacks.java index 3fe7597a..13127cc4 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManagerCallbacks.java @@ -21,23 +21,27 @@ */ package no.nordicsemi.android.nrftoolbox.hrs; +import android.bluetooth.BluetoothDevice; + import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; public interface HRSManagerCallbacks extends BleManagerCallbacks { /** * Called when the sensor position information has been obtained from the sensor - * + * + * @param device the bluetooth device from which the value was obtained * @param position * the sensor position */ - public void onHRSensorPositionFound(String position); + void onHRSensorPositionFound(final BluetoothDevice device, String position); /** * Called when new Heart Rate value has been obtained from the sensor - * + * + * @param device the bluetooth device from which the value was obtained * @param value * the new value */ - public void onHRValueReceived(int value); + void onHRValueReceived(final BluetoothDevice device, int value); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSActivity.java index f1b3507e..116444c2 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSActivity.java @@ -21,6 +21,7 @@ */ package no.nordicsemi.android.nrftoolbox.hts; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -179,7 +180,7 @@ public class HTSActivity extends BleProfileServiceReadyActivity { @Override protected Queue initGatt(final BluetoothGatt gatt) { final LinkedList requests = new LinkedList<>(); - requests.push(Request.newEnableIndicationsRequest(mHTCharacteristic)); + requests.add(Request.newEnableIndicationsRequest(mHTCharacteristic)); return requests; } @@ -93,12 +93,11 @@ public class HTSManager extends BleManager { @Override public void onCharacteristicIndicated(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - if (mLogSession != null) - Logger.a(mLogSession, TemperatureMeasurementParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + TemperatureMeasurementParser.parse(characteristic) + "\" received"); try { final double tempValue = decodeTemperature(characteristic.getValue()); - mCallbacks.onHTValueReceived(tempValue); + mCallbacks.onHTValueReceived(gatt.getDevice(), tempValue); } catch (Exception e) { DebugLogger.e(TAG, "Invalid temperature value", e); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSManagerCallbacks.java index 9063c9df..e7eb5baa 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSManagerCallbacks.java @@ -21,6 +21,8 @@ */ package no.nordicsemi.android.nrftoolbox.hts; +import android.bluetooth.BluetoothDevice; + import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; /** @@ -30,10 +32,11 @@ public interface HTSManagerCallbacks extends BleManagerCallbacks { /** * Called when Health Thermometer value has been received - * + * + * @param device the bluetooth device from which the value was obtained * @param value * the new value */ - public void onHTValueReceived(double value); + void onHTValueReceived(final BluetoothDevice device, double value); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSService.java index d30f3102..cb2ddbad 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSService.java @@ -25,6 +25,7 @@ package no.nordicsemi.android.nrftoolbox.hts; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -106,8 +107,9 @@ public class HTSService extends BleProfileService implements HTSManagerCallbacks } @Override - public void onHTValueReceived(final double value) { + public void onHTValueReceived(final BluetoothDevice device, final double value) { final Intent broadcast = new Intent(BROADCAST_HTS_MEASUREMENT); + broadcast.putExtra(EXTRA_DEVICE, device); broadcast.putExtra(EXTRA_TEMPERATURE, value); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); @@ -169,5 +171,4 @@ public class HTSService extends BleProfileService implements HTSManagerCallbacks stopSelf(); } }; - } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/parser/CGMMeasurementParser.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/parser/CGMMeasurementParser.java new file mode 100644 index 00000000..e091447f --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/parser/CGMMeasurementParser.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.nrftoolbox.parser; + +import android.bluetooth.BluetoothGattCharacteristic; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class CGMMeasurementParser { + private static final int FLAGS_CGM_TREND_INFO_PRESENT = 1; + private static final int FLAGS_CGM_QUALITY_PRESENT = 1 << 1; + private static final int FLAGS_SENSOR_STATUS_ANNUNCIATION_WARNING_OCTET_PRESENT = 1 << 2; + private static final int FLAGS_SENSOR_STATUS_ANNUNCIATION_CAL_TEMP_OCTET_PRESENT = 1 << 3; + private static final int FLAGS_SENSOR_STATUS_ANNUNCIATION_STATUS_OCTET_PRESENT = 1 << 4; + + private static final int SSA_SESSION_STOPPED = 1; + private static final int SSA_DEVICE_BATTERY_LOW = 1 << 1; + private static final int SSA_SENSOR_TYPE_INCORRECT = 1 << 2; + private static final int SSA_SENSOR_MALFUNCTION = 1 << 3; + private static final int SSA_DEVICE_SPEC_ALERT = 1 << 4; + private static final int SSA_GENERAL_DEVICE_FAULT = 1 << 5; + + private static final int SSA_TIME_SYNC_REQUIRED = 1 << 8; + private static final int SSA_CALIBRATION_NOT_ALLOWED = 1 << 9; + private static final int SSA_CALIBRATION_RECOMMENDED = 1 << 10; + private static final int SSA_CALIBRATION_REQUIRED = 1 << 11; + private static final int SSA_SENSOR_TEMP_TOO_HIGH = 1 << 12; + private static final int SSA_SENSOR_TEMP_TOO_LOW = 1 << 13; + + private static final int SSA_RESULT_LOWER_THAN_PATIENT_LOW_LEVEL = 1 << 16; + private static final int SSA_RESULT_HIGHER_THAN_PATIENT_HIGH_LEVEL = 1 << 17; + private static final int SSA_RESULT_LOWER_THAN_HYPO_LEVEL = 1 << 18; + private static final int SSA_RESULT_HIGHER_THAN_HYPER_LEVEL = 1 << 19; + private static final int SSA_SENSOR_RATE_OF_DECREASE_EXCEEDED = 1 << 20; + private static final int SSA_SENSOR_RATE_OF_INCREASE_EXCEEDED = 1 << 21; + private static final int SSA_RESULT_LOWER_THAN_DEVICE_CAN_PROCESS = 1 << 22; + private static final int SSA_RESULT_HIGHER_THAN_DEVICE_CAN_PROCESS = 1 << 23; + + public static String parse(final BluetoothGattCharacteristic characteristic) { + int offset = 0; + // Read size and flags bytes + final int size = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + final int flags = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + + /* + * false CGM Trend Information is not preset + * true CGM Trend Information is preset + */ + final boolean cgmTrendInformationPresent = (flags & FLAGS_CGM_TREND_INFO_PRESENT) > 0; + + /* + * false CGM Quality is not preset + * true CGM Quality is preset + */ + final boolean cgmQualityPresent = (flags & FLAGS_CGM_QUALITY_PRESENT) > 0; + + /* + * false Sensor Status Annunciation - Warning-Octet is not preset + * true Sensor Status Annunciation - Warning-Octet is preset + */ + final boolean ssaWarningOctetPresent = (flags & FLAGS_SENSOR_STATUS_ANNUNCIATION_WARNING_OCTET_PRESENT) > 0; + + /* + * false Sensor Status Annunciation - Calibration/Temp-Octet is not preset + * true Sensor Status Annunciation - Calibration/Temp-Octet is preset + */ + final boolean ssaCalTempOctetPresent = (flags & FLAGS_SENSOR_STATUS_ANNUNCIATION_CAL_TEMP_OCTET_PRESENT) > 0; + + /* + * false Sensor Status Annunciation - Status-Octet is not preset + * true Sensor Status Annunciation - Status-Octet is preset + */ + final boolean ssaStatusOctetPresent = (flags & FLAGS_SENSOR_STATUS_ANNUNCIATION_STATUS_OCTET_PRESENT) > 0; + + // Read CGM Glucose Concentration + final float glucoseConcentration = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); + offset += 2; + + // Read time offset + final int timeOffset = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); + offset += 2; + + final StringBuilder builder = new StringBuilder(); + builder.append("Glucose concentration: ").append(glucoseConcentration).append(" mg/dL\n"); + builder.append("Sequence number: ").append(timeOffset).append(" (minutes since start)\n"); + + if (ssaWarningOctetPresent) { + final int ssaWarningOctet = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + builder.append("Warnings:\n"); + if ((ssaWarningOctet & SSA_SESSION_STOPPED) > 0) + builder.append("- Session Stopped\n"); + if ((ssaWarningOctet & SSA_DEVICE_BATTERY_LOW) > 0) + builder.append("- Device Battery Low\n"); + if ((ssaWarningOctet & SSA_SENSOR_TYPE_INCORRECT) > 0) + builder.append("- Sensor Type Incorrect\n"); + if ((ssaWarningOctet & SSA_SENSOR_MALFUNCTION) > 0) + builder.append("- Sensor Malfunction\n"); + if ((ssaWarningOctet & SSA_DEVICE_SPEC_ALERT) > 0) + builder.append("- Device Specific Alert\n"); + if ((ssaWarningOctet & SSA_GENERAL_DEVICE_FAULT) > 0) + builder.append("- General Device Fault\n"); + } + + if (ssaCalTempOctetPresent) { + final int ssaCalTempOctet = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + builder.append("Cal/Temp Info:\n"); + if ((ssaCalTempOctet & SSA_TIME_SYNC_REQUIRED) > 0) + builder.append("- Time Synchronization Required\n"); + if ((ssaCalTempOctet & SSA_CALIBRATION_NOT_ALLOWED) > 0) + builder.append("- Calibration Not Allowed\n"); + if ((ssaCalTempOctet & SSA_CALIBRATION_RECOMMENDED) > 0) + builder.append("- Calibration Recommended\n"); + if ((ssaCalTempOctet & SSA_CALIBRATION_REQUIRED) > 0) + builder.append("- Calibration Required\n"); + if ((ssaCalTempOctet & SSA_SENSOR_TEMP_TOO_HIGH) > 0) + builder.append("- Sensor Temp Too High\n"); + if ((ssaCalTempOctet & SSA_SENSOR_TEMP_TOO_LOW) > 0) + builder.append("- Sensor Temp Too Low\n"); + } + + if (ssaStatusOctetPresent) { + final int ssaStatusOctet = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + builder.append("Status:\n"); + if ((ssaStatusOctet & SSA_RESULT_LOWER_THAN_PATIENT_LOW_LEVEL) > 0) + builder.append("- Result Lower then Patient Low Level\n"); + if ((ssaStatusOctet & SSA_RESULT_HIGHER_THAN_PATIENT_HIGH_LEVEL) > 0) + builder.append("- Result Higher then Patient High Level\n"); + if ((ssaStatusOctet & SSA_RESULT_LOWER_THAN_HYPO_LEVEL) > 0) + builder.append("- Result Lower then Hypo Level\n"); + if ((ssaStatusOctet & SSA_RESULT_HIGHER_THAN_HYPER_LEVEL) > 0) + builder.append("- Result Higher then Hyper Level\n"); + if ((ssaStatusOctet & SSA_SENSOR_RATE_OF_DECREASE_EXCEEDED) > 0) + builder.append("- Sensor Rate of Decrease Exceeded\n"); + if ((ssaStatusOctet & SSA_SENSOR_RATE_OF_INCREASE_EXCEEDED) > 0) + builder.append("- Sensor Rate of Increase Exceeded\n"); + if ((ssaStatusOctet & SSA_RESULT_LOWER_THAN_DEVICE_CAN_PROCESS) > 0) + builder.append("- Result Lower then Device Can Process\n"); + if ((ssaStatusOctet & SSA_RESULT_HIGHER_THAN_DEVICE_CAN_PROCESS) > 0) + builder.append("- Result Higher then Device Can Process\n"); + } + + if (cgmTrendInformationPresent) { + final float trend = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); + offset += 2; + builder.append("Trend: ").append(trend).append(" mg/dL/min\n"); + } + + if (cgmQualityPresent) { + final float quality = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); + offset += 2; + builder.append("Quality: ").append(quality).append("%\n"); + } + + if (size > offset + 1) { + final int crc = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); + // offset += 2; + builder.append(String.format(Locale.US, "E2E-CRC: 0x%04X\n", crc)); + } + builder.setLength(builder.length() - 1); // Remove last \n + return builder.toString(); + } +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/parser/CGMSpecificOpsControlPointParser.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/parser/CGMSpecificOpsControlPointParser.java new file mode 100644 index 00000000..410d9a9b --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/parser/CGMSpecificOpsControlPointParser.java @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.nrftoolbox.parser; + +import android.bluetooth.BluetoothGattCharacteristic; + +public class CGMSpecificOpsControlPointParser { + private final static int OP_SET_CGM_COMMUNICATION_INTERVAL = 1; + private final static int OP_GET_CGM_COMMUNICATION_INTERVAL = 2; + private final static int OP_CGM_COMMUNICATION_INTERVAL_RESPONSE = 3; + private final static int OP_SET_GLUCOSE_CALIBRATION_VALUE = 4; + private final static int OP_GET_GLUCOSE_CALIBRATION_VALUE = 5; + private final static int OP_GLUCOSE_CALIBRATION_VALUE_RESPONSE = 6; + private final static int OP_SET_PATIENT_HIGH_ALERT_LEVEL = 7; + private final static int OP_GET_PATIENT_HIGH_ALERT_LEVEL = 8; + private final static int OP_PATIENT_HIGH_ALERT_LEVEL_RESPONSE = 9; + private final static int OP_SET_PATIENT_LOW_ALERT_LEVEL = 10; + private final static int OP_GET_PATIENT_LOW_ALERT_LEVEL = 11; + private final static int OP_PATIENT_LOW_ALERT_LEVEL_RESPONSE = 12; + private final static int OP_SET_HYPO_ALERT_LEVEL = 13; + private final static int OP_GET_HYPO_ALERT_LEVEL = 14; + private final static int OP_HYPO_ALERT_LEVEL_RESPONSE = 15; + private final static int OP_SET_HYPER_ALERT_LEVEL = 16; + private final static int OP_GET_HYPER_ALERT_LEVEL = 17; + private final static int OP_HYPER_ALERT_LEVEL_RESPONSE = 18; + private final static int OP_SET_RATE_OF_DECREASE_ALERT_LEVEL = 19; + private final static int OP_GET_RATE_OF_DECREASE_ALERT_LEVEL = 20; + private final static int OP_RATE_OF_DECREASE_ALERT_LEVEL_RESPONSE = 21; + private final static int OP_SET_RATE_OF_INCREASE_ALERT_LEVEL = 22; + private final static int OP_GET_RATE_OF_INCREASE_ALERT_LEVEL = 23; + private final static int OP_RATE_OF_INCREASE_ALERT_LEVEL_RESPONSE = 24; + private final static int OP_RESET_DEVICE_SPECIFIC_ALERT = 25; + private final static int OP_CODE_START_SESSION = 26; + private final static int OP_CODE_STOP_SESSION = 27; + private final static int OP_CODE_RESPONSE_CODE = 28; + + // TODO this parser does not support E2E-CRC! + + public static String parse(final BluetoothGattCharacteristic characteristic) { + int offset = 0; + final int opCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + + final StringBuilder builder = new StringBuilder(); + builder.append(parseOpCode(opCode)); + switch (opCode) { + case OP_SET_CGM_COMMUNICATION_INTERVAL: + case OP_CGM_COMMUNICATION_INTERVAL_RESPONSE: { + final int interval = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset); + builder.append(" to ").append(interval).append(" min"); + break; + } + case OP_SET_GLUCOSE_CALIBRATION_VALUE: { + final float calConcentration = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); + offset += 2; + final int calTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); + offset += 2; + final int calTypeSampleLocation = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + final int calType = calTypeSampleLocation & 0x0F; + final int calSampleLocation = (calTypeSampleLocation & 0xF0) >> 4; + // final int calNextCalibrationTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); + // offset += 2; + // final int calCalibrationDataRecordNumber = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); + // offset += 2; + // final int calStatus = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + + builder.append(" to:\n"); + builder.append("Glucose Concentration of Calibration: ").append(calConcentration).append(" mg/dL\n"); + builder.append("Time: ").append(calTime).append(" min\n"); + builder.append("Type: ").append(parseType(calType)).append("\n"); + builder.append("Sample Location: ").append(parseSampleLocation(calSampleLocation)).append("\n"); + // builder.append("Next Calibration Time: ").append(parseNextCalibrationTime(calNextCalibrationTime)).append(" min\n"); // field ignored on Set + // builder.append("Data Record Number: ").append(calCalibrationDataRecordNumber).append("\n"); // field ignored on Set + // parseStatus(builder, calStatus); // field ignored on Set + break; + } + case OP_GET_GLUCOSE_CALIBRATION_VALUE: { + final int calibrationRecordNumber = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); + builder.append(": ").append(parseRecordNumber(calibrationRecordNumber)); + break; + } + case OP_GLUCOSE_CALIBRATION_VALUE_RESPONSE: { + final float calConcentration = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); + offset += 2; + final int calTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); + offset += 2; + final int calTypeSampleLocation = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + final int calType = calTypeSampleLocation & 0x0F; + final int calSampleLocation = (calTypeSampleLocation & 0xF0) >> 4; + final int calNextCalibrationTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); + offset += 2; + final int calCalibrationDataRecordNumber = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset); + offset += 2; + final int calStatus = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset); + + builder.append(":\n"); + if (calCalibrationDataRecordNumber > 0) { + builder.append("Glucose Concentration of Calibration: ").append(calConcentration).append(" mg/dL\n"); + builder.append("Time: ").append(calTime).append(" min\n"); + builder.append("Type: ").append(parseType(calType)).append("\n"); + builder.append("Sample Location: ").append(parseSampleLocation(calSampleLocation)).append("\n"); + builder.append("Next Calibration Time: ").append(parseNextCalibrationTime(calNextCalibrationTime)).append("\n"); + builder.append("Data Record Number: ").append(calCalibrationDataRecordNumber); + parseStatus(builder, calStatus); + } else { + builder.append("No Calibration Data Stored"); + } + break; + } + case OP_SET_PATIENT_HIGH_ALERT_LEVEL: + case OP_SET_PATIENT_LOW_ALERT_LEVEL: + case OP_SET_HYPO_ALERT_LEVEL: + case OP_SET_HYPER_ALERT_LEVEL: { + final float level = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); + builder.append(" to: ").append(level).append(" mg/dL"); + break; + } + case OP_PATIENT_HIGH_ALERT_LEVEL_RESPONSE: + case OP_PATIENT_LOW_ALERT_LEVEL_RESPONSE: + case OP_HYPO_ALERT_LEVEL_RESPONSE: + case OP_HYPER_ALERT_LEVEL_RESPONSE: { + final float level = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); + builder.append(": ").append(level).append(" mg/dL"); + break; + } + case OP_SET_RATE_OF_DECREASE_ALERT_LEVEL: + case OP_SET_RATE_OF_INCREASE_ALERT_LEVEL: { + final float level = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); + builder.append(" to: ").append(level).append(" mg/dL/min"); + break; + } + case OP_RATE_OF_DECREASE_ALERT_LEVEL_RESPONSE: + case OP_RATE_OF_INCREASE_ALERT_LEVEL_RESPONSE: { + final float level = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, offset); + builder.append(": ").append(level).append(" mg/dL/min"); + break; + } + case OP_CODE_RESPONSE_CODE: + final int requestOpCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + final int responseCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++); + builder.append(" to ").append(parseOpCode(requestOpCode)).append(": ").append(parseResponseCode(responseCode)); + break; + } + + return builder.toString(); + } + + private static String parseOpCode(final int code) { + switch (code) { + case OP_SET_CGM_COMMUNICATION_INTERVAL: + return "Set CGM Communication Interval"; + case OP_GET_CGM_COMMUNICATION_INTERVAL: + return "Get CGM Communication Interval"; + case OP_CGM_COMMUNICATION_INTERVAL_RESPONSE: + return "CGM Communication Interval"; + case OP_SET_GLUCOSE_CALIBRATION_VALUE: + return "Set CGM Calibration Value"; + case OP_GET_GLUCOSE_CALIBRATION_VALUE: + return "Get CGM Calibration Value"; + case OP_GLUCOSE_CALIBRATION_VALUE_RESPONSE: + return "CGM Calibration Value"; + case OP_SET_PATIENT_HIGH_ALERT_LEVEL: + return "Set Patient High Alert Level"; + case OP_GET_PATIENT_HIGH_ALERT_LEVEL: + return "Get Patient High Alert Level"; + case OP_PATIENT_HIGH_ALERT_LEVEL_RESPONSE: + return "Patient High Alert Level"; + case OP_SET_PATIENT_LOW_ALERT_LEVEL: + return "Set Patient Low Alert Level"; + case OP_GET_PATIENT_LOW_ALERT_LEVEL: + return "Get Patient Low Alert Level"; + case OP_PATIENT_LOW_ALERT_LEVEL_RESPONSE: + return "Patient Low Alert Level"; + case OP_SET_HYPO_ALERT_LEVEL: + return "Set Hypo Alert Level"; + case OP_GET_HYPO_ALERT_LEVEL: + return "Get Hypo Alert Level"; + case OP_HYPO_ALERT_LEVEL_RESPONSE: + return "Hypo Alert Level"; + case OP_SET_HYPER_ALERT_LEVEL: + return "Set Hyper Alert Level"; + case OP_GET_HYPER_ALERT_LEVEL: + return "Get Hyper Alert Level"; + case OP_HYPER_ALERT_LEVEL_RESPONSE: + return "Hyper Alert Level"; + case OP_SET_RATE_OF_DECREASE_ALERT_LEVEL: + return "Set Rate of Decrease Alert Level"; + case OP_GET_RATE_OF_DECREASE_ALERT_LEVEL: + return "Get Rate of Decrease Alert Level"; + case OP_RATE_OF_DECREASE_ALERT_LEVEL_RESPONSE: + return "Rate of Decrease Alert Level"; + case OP_SET_RATE_OF_INCREASE_ALERT_LEVEL: + return "Set Rate of Increase Alert Level"; + case OP_GET_RATE_OF_INCREASE_ALERT_LEVEL: + return "Get Rate of Increase Alert Level"; + case OP_RATE_OF_INCREASE_ALERT_LEVEL_RESPONSE: + return "Rate of Increase Alert Level"; + case OP_RESET_DEVICE_SPECIFIC_ALERT: + return "Reset Device Specific Alert"; + case OP_CODE_START_SESSION: + return "Start Session"; + case OP_CODE_STOP_SESSION: + return "Stop Session"; + case OP_CODE_RESPONSE_CODE: + return "Response"; + default: + return "Reserved for future use (" + code + ")"; + } + } + + private static String parseResponseCode(final int code) { + switch (code) { + case 1: return "Success"; + case 2: return "Op Code not supported"; + case 3: return "Invalid Operand"; + case 4: return "Procedure not completed"; + case 5: return "Parameter out of range"; + default: + return "Reserved for future use (" + code + ")"; + } + } + + private static String parseType(final int type) { + switch (type) { + case 1: return "Capillary Whole blood"; + case 2: return "Capillary Plasma"; + case 3: return "Capillary Whole blood"; + case 4: return "Venous Plasma"; + case 5: return "Arterial Whole blood"; + case 6: return "Arterial Plasma"; + case 7: return "Undetermined Whole blood"; + case 8: return "Undetermined Plasma"; + case 9: return "Interstitial Fluid (ISF)"; + case 10: return "Control Solution"; + default: return "Reserved for future use (" + type + ")"; + } + } + + private static String parseSampleLocation(final int location) { + switch (location) { + case 1: return "Finger"; + case 2: return "Alternate Site Test (AST)"; + case 3: return "Earlobe"; + case 4: return "Control solution"; + case 5: return "Subcutaneous tissue"; + case 15: return "Sample Location value not available"; + default: return "Reserved for future use (" + location + ")"; + } + } + + private static String parseNextCalibrationTime(final int time) { + if (time == 0) + return "Calibration Required Instantly"; + return time + " min"; + } + + private static String parseRecordNumber(final int time) { + if (time == 0xFFFF) + return "Last Calibration Data"; + return String.valueOf(time); + } + + private static void parseStatus(final StringBuilder builder, final int status) { + if (status == 0) + return; + builder.append("\nStatus:\n"); + if ((status & 1) > 0) + builder.append("- Calibration Data rejected"); + if ((status & 2) > 0) + builder.append("- Calibration Data out of range"); + if ((status & 4) > 0) + builder.append("- Calibration Process pending"); + if ((status & 0xF8) > 0) + builder.append("- Reserved for future use (").append(status).append(")"); + } +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManager.java index 47cf5dbc..b4c2ed5c 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManager.java @@ -33,11 +33,13 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; +import android.support.annotation.StringRes; import java.util.Queue; import java.util.UUID; import no.nordicsemi.android.log.ILogSession; +import no.nordicsemi.android.log.LogContract; import no.nordicsemi.android.log.Logger; import no.nordicsemi.android.nrftoolbox.error.GattError; import no.nordicsemi.android.nrftoolbox.utility.DebugLogger; @@ -53,8 +55,8 @@ import no.nordicsemi.android.nrftoolbox.utility.ParserUtils; *
  • The manager tries to read the Battery Level characteristic. No matter the result of this operation (for example the Battery Level characteristic may not have the READ property) * it tries to enable Battery Level notifications, to get battery updates from the device.
  • *
  • Afterwards, the manager initializes the device using given queue of commands. See {@link BleManagerGattCallback#initGatt(BluetoothGatt)} method for more details.
  • - *
  • When initialization complete, the {@link BleManagerCallbacks#onDeviceReady()} callback is called.
  • - * The manager also is responsible for parsing the Battery Level values and calling {@link BleManagerCallbacks#onBatteryValueReceived(int)} method.

    + *
  • When initialization complete, the {@link BleManagerCallbacks#onDeviceReady(BluetoothDevice)} callback is called.
  • + * The manager also is responsible for parsing the Battery Level values and calling {@link BleManagerCallbacks#onBatteryValueReceived(BluetoothDevice, int)} method.

    *

    Events from all profiles are being logged into the nRF Logger application, * which may be downloaded from Google Play: https://play.google.com/store/apps/details?id=no.nordicsemi.android.log

    *

    The nRF Logger application allows you to see application logs without need to connect it to the computer.

    @@ -82,11 +84,17 @@ public abstract class BleManager { * The log session or null if nRF Logger is not installed. */ protected ILogSession mLogSession; + protected BluetoothDevice mBluetoothDevice; protected E mCallbacks; private Handler mHandler; private BluetoothGatt mBluetoothGatt; private Context mContext; + /** + * This flag is set to false only when the {@link #shouldAutoConnect()} method returns true and the device got disconnected without calling {@link #disconnect()} method. + * If {@link #shouldAutoConnect()} returns false (dafault) this is always set to true. + */ private boolean mUserDisconnected; + /** Flag set to true when the device is connected. */ private boolean mConnected; private BroadcastReceiver mBondingBroadcastReceiver = new BroadcastReceiver() { @@ -105,11 +113,11 @@ public abstract class BleManager { switch (bondState) { case BluetoothDevice.BOND_BONDING: - mCallbacks.onBondingRequired(); + mCallbacks.onBondingRequired(device); break; case BluetoothDevice.BOND_BONDED: Logger.i(mLogSession, "Device bonded"); - mCallbacks.onBonded(); + mCallbacks.onBonded(device); // Start initializing again. // In fact, bonding forces additional, internal service discovery (at least on Nexus devices), so this method may safely be used to start this process again. @@ -199,6 +207,7 @@ public abstract class BleManager { mUserDisconnected = !autoConnect; // We will receive Linkloss events only when the device is connected with autoConnect=true Logger.v(mLogSession, "Connecting..."); Logger.d(mLogSession, "gatt = device.connectGatt(autoConnect = " + autoConnect + ")"); + mBluetoothDevice = device; mBluetoothGatt = device.connectGatt(mContext, autoConnect, getGattCallback()); } @@ -211,7 +220,7 @@ public abstract class BleManager { if (mConnected && mBluetoothGatt != null) { Logger.v(mLogSession, "Disconnecting..."); - mCallbacks.onDeviceDisconnecting(); + mCallbacks.onDeviceDisconnecting(mBluetoothGatt.getDevice()); Logger.d(mLogSession, "gatt.disconnect()"); mBluetoothGatt.disconnect(); return true; @@ -219,6 +228,13 @@ public abstract class BleManager { return false; } + /** + * This method returns true if the device is connected. Services could have not been discovered yet. + */ + public boolean isConnected() { + return mConnected; + } + /** * Closes and releases resources. May be also used to unregister broadcast listeners. */ @@ -230,9 +246,11 @@ public abstract class BleManager { // the receiver must have been not registered or unregistered before } if (mBluetoothGatt != null) { + Logger.d(mLogSession, "gatt.close()"); mBluetoothGatt.close(); mBluetoothGatt = null; } + mBluetoothDevice = null; mUserDisconnected = false; } @@ -248,6 +266,30 @@ public abstract class BleManager { mLogSession = session; } + /** + * Logs a message on the manager's log session. Does nothing when log session has not been set by {@link #setLogger(ILogSession)}. + * @param level + * the log level, one of {@link LogContract.Log.Level#DEBUG}, {@link LogContract.Log.Level#VERBOSE}, {@link LogContract.Log.Level#INFO}, {@link LogContract.Log.Level#WARNING}, + * {@link LogContract.Log.Level#ERROR} + * @param message + * the message to be logged + */ + public void log(final int level, final String message) { + Logger.log(mLogSession, level, message); + } + + /** + * Logs a message on the manager's log session. Does nothing when log session has not been set by {@link #setLogger(ILogSession)}. + * @param level + * the log level, one of {@link LogContract.Log.Level#DEBUG}, {@link LogContract.Log.Level#VERBOSE}, {@link LogContract.Log.Level#INFO}, {@link LogContract.Log.Level#WARNING}, + * {@link LogContract.Log.Level#ERROR} + * @param messageRes + * the message string resource to be logged + */ + public void log(final int level, @StringRes final int messageRes) { + Logger.log(mLogSession, level, messageRes); + } + /** * Sets the manager callback listener * @@ -573,7 +615,7 @@ public abstract class BleManager { * Called then the initialization queue is complete. */ protected void onDeviceReady() { - mCallbacks.onDeviceReady(); + mCallbacks.onDeviceReady(mBluetoothGatt.getDevice()); } /** @@ -617,9 +659,9 @@ public abstract class BleManager { // do nothing } - private void onError(final String message, final int errorCode) { + private void onError(final BluetoothDevice device, final String message, final int errorCode) { Logger.e(mLogSession, "Error (0x" + Integer.toHexString(errorCode) + "): " + GattError.parse(errorCode)); - mCallbacks.onError(message, errorCode); + mCallbacks.onError(device, message, errorCode); } @Override @@ -630,7 +672,7 @@ public abstract class BleManager { // Notify the parent activity/service Logger.i(mLogSession, "Connected to " + gatt.getDevice().getAddress()); mConnected = true; - mCallbacks.onDeviceConnected(); + mCallbacks.onDeviceConnected(gatt.getDevice()); /* * The onConnectionStateChange event is triggered just after the Android connects to a device. @@ -668,11 +710,11 @@ public abstract class BleManager { mConnected = false; if (mUserDisconnected) { Logger.i(mLogSession, "Disconnected"); - mCallbacks.onDeviceDisconnected(); + mCallbacks.onDeviceDisconnected(gatt.getDevice()); close(); } else { Logger.w(mLogSession, "Connection lost"); - mCallbacks.onLinklossOccur(); + mCallbacks.onLinklossOccur(gatt.getDevice()); // We are not closing the connection here as the device should try to reconnect automatically. // This may be only called when the shouldAutoConnect() method returned true. } @@ -681,7 +723,7 @@ public abstract class BleManager { // TODO Should the disconnect method be called or the connection is still valid? Does this ever happen? Logger.e(mLogSession, "Error (0x" + Integer.toHexString(status) + "): " + GattError.parseConnectionError(status)); - mCallbacks.onError(ERROR_CONNECTION_STATE_CHANGE, status); + mCallbacks.onError(gatt.getDevice(), ERROR_CONNECTION_STATE_CHANGE, status); } } @@ -696,7 +738,7 @@ public abstract class BleManager { Logger.v(mLogSession, "Secondary service found"); // Notify the parent activity - mCallbacks.onServicesDiscovered(optionalServicesFound); + mCallbacks.onServicesDiscovered(gatt.getDevice(), optionalServicesFound); // Obtain the queue of initialization requests mInitInProgress = true; @@ -713,12 +755,12 @@ public abstract class BleManager { nextRequest(); } else { Logger.w(mLogSession, "Device is not supported"); - mCallbacks.onDeviceNotSupported(); + mCallbacks.onDeviceNotSupported(gatt.getDevice()); disconnect(); } } else { DebugLogger.e(TAG, "onServicesDiscovered error " + status); - onError(ERROR_DISCOVERY_SERVICE, status); + onError(gatt.getDevice(), ERROR_DISCOVERY_SERVICE, status); } } @@ -730,7 +772,7 @@ public abstract class BleManager { if (isBatteryLevelCharacteristic(characteristic)) { final int batteryValue = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); Logger.a(mLogSession, "Battery level received: " + batteryValue + "%"); - mCallbacks.onBatteryValueReceived(batteryValue); + mCallbacks.onBatteryValueReceived(gatt.getDevice(), batteryValue); // The Battery Level value has been read. Let's try to enable Battery Level notifications. // If the Battery Level characteristic does not have the NOTIFY property, proceed with the initialization queue. @@ -744,16 +786,16 @@ public abstract class BleManager { } else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) { if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) { DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED); - mCallbacks.onError(ERROR_AUTH_ERROR_WHILE_BONDED, status); + mCallbacks.onError(gatt.getDevice(), ERROR_AUTH_ERROR_WHILE_BONDED, status); } } else { DebugLogger.e(TAG, "onCharacteristicRead error " + status); - onError(ERROR_READ_CHARACTERISTIC, status); + onError(gatt.getDevice(), ERROR_READ_CHARACTERISTIC, status); } } @Override - public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) { + public final void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) { if (status == BluetoothGatt.GATT_SUCCESS) { Logger.i(mLogSession, "Data written to " + characteristic.getUuid() + ", value: " + ParserUtils.parse(characteristic.getValue())); // The value has been written. Notify the manager and proceed with the initialization queue. @@ -762,11 +804,11 @@ public abstract class BleManager { } else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) { if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) { DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED); - mCallbacks.onError(ERROR_AUTH_ERROR_WHILE_BONDED, status); + mCallbacks.onError(gatt.getDevice(), ERROR_AUTH_ERROR_WHILE_BONDED, status); } } else { DebugLogger.e(TAG, "onCharacteristicRead error " + status); - onError(ERROR_READ_CHARACTERISTIC, status); + onError(gatt.getDevice(), ERROR_READ_CHARACTERISTIC, status); } } @@ -792,11 +834,11 @@ public abstract class BleManager { } else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) { if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) { DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED); - mCallbacks.onError(ERROR_AUTH_ERROR_WHILE_BONDED, status); + mCallbacks.onError(gatt.getDevice(), ERROR_AUTH_ERROR_WHILE_BONDED, status); } } else { DebugLogger.e(TAG, "onDescriptorWrite error " + status); - onError(ERROR_WRITE_DESCRIPTOR, status); + onError(gatt.getDevice(), ERROR_WRITE_DESCRIPTOR, status); } } @@ -808,7 +850,7 @@ public abstract class BleManager { Logger.i(mLogSession, "Notification received from " + characteristic.getUuid() + ", value: " + data); final int batteryValue = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); Logger.a(mLogSession, "Battery level received: " + batteryValue + "%"); - mCallbacks.onBatteryValueReceived(batteryValue); + mCallbacks.onBatteryValueReceived(gatt.getDevice(), batteryValue); } else { final BluetoothGattDescriptor cccd = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID); final boolean notifications = cccd == null || cccd.getValue() == null || cccd.getValue().length != 2 || cccd.getValue()[0] == 0x01; diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManagerCallbacks.java index 107d85f3..bd0a082e 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManagerCallbacks.java @@ -21,6 +21,7 @@ */ package no.nordicsemi.android.nrftoolbox.profile; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; @@ -28,71 +29,82 @@ public interface BleManagerCallbacks { /** * Called when the device has been connected. This does not mean that the application may start communication. A service discovery will be handled automatically after this call. Service discovery - * may ends up with calling {@link #onServicesDiscovered(boolean)} or {@link #onDeviceNotSupported()} if required services have not been found. + * may ends up with calling {@link #onServicesDiscovered(BluetoothDevice, boolean)} or {@link #onDeviceNotSupported(BluetoothDevice)} if required services have not been found. + * @param device the device that got connected */ - public void onDeviceConnected(); + void onDeviceConnected(final BluetoothDevice device); /** * Called when user initialized disconnection. + * @param device the device that gets disconnecting */ - public void onDeviceDisconnecting(); + void onDeviceDisconnecting(final BluetoothDevice device); /** * Called when the device has disconnected (when the callback returned {@link BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int)} with state DISCONNECTED. + * @param device the device that got disconnected */ - public void onDeviceDisconnected(); + void onDeviceDisconnected(final BluetoothDevice device); /** - * This callback is invoked when the Ble Manager lost connection to a device that has been connected with autoConnect option. Otherwise a {@link #onDeviceDisconnected()} + * This callback is invoked when the Ble Manager lost connection to a device that has been connected with autoConnect option. Otherwise a {@link #onDeviceDisconnected(BluetoothDevice)} * method will be called on such event. + * @param device the device that got disconnected due to a link loss */ - public void onLinklossOccur(); + void onLinklossOccur(final BluetoothDevice device); /** * Called when service discovery has finished and primary services has been found. The device is ready to operate. This method is not called if the primary, mandatory services were not found * during service discovery. For example in the Blood Pressure Monitor, a Blood Pressure service is a primary service and Intermediate Cuff Pressure service is a optional secondary service. * Existence of battery service is not notified by this call. - * + * * @param optionalServicesFound * if true the secondary services were also found on the device. + * @param device the device which services got disconnected */ - public void onServicesDiscovered(final boolean optionalServicesFound); + void onServicesDiscovered(final BluetoothDevice device, final boolean optionalServicesFound); /** * Method called when all initialization requests has been completed. + * @param device the device that get ready */ - public void onDeviceReady(); + void onDeviceReady(final BluetoothDevice device); /** * Called when battery value has been received from the device. - * + * * @param value * the battery value in percent + * @param device the device frm which the battery value has changed */ - public void onBatteryValueReceived(final int value); + void onBatteryValueReceived(final BluetoothDevice device, final int value); /** * Called when an {@link BluetoothGatt#GATT_INSUFFICIENT_AUTHENTICATION} error occurred and the device bond state is NOT_BONDED + * @param device the device that requires bonding */ - public void onBondingRequired(); + void onBondingRequired(final BluetoothDevice device); /** * Called when the device has been successfully bonded. + * @param device the device that got bonded */ - public void onBonded(); + void onBonded(final BluetoothDevice device); /** * Called when a BLE error has occurred - * + * * @param message * the error message * @param errorCode * the error code + * @param device the device that caused an error */ - public void onError(final String message, final int errorCode); + void onError(final BluetoothDevice device, final String message, final int errorCode); /** * Called when service discovery has finished but the main services were not found on the device. + * @param device the device that failed to connect due to lack of required services */ - public void onDeviceNotSupported(); + void onDeviceNotSupported(final BluetoothDevice device); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileActivity.java index bdb0d8ae..408ff3b1 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileActivity.java @@ -239,7 +239,7 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl } @Override - public void onDeviceConnected() { + public void onDeviceConnected(final BluetoothDevice device) { mDeviceConnected = true; runOnUiThread(new Runnable() { @Override @@ -251,12 +251,12 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl } @Override - public void onDeviceDisconnecting() { + public void onDeviceDisconnecting(final BluetoothDevice device) { // do nothing } @Override - public void onDeviceDisconnected() { + public void onDeviceDisconnected(final BluetoothDevice device) { mDeviceConnected = false; mBleManager.close(); runOnUiThread(new Runnable() { @@ -271,7 +271,7 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl } @Override - public void onLinklossOccur() { + public void onLinklossOccur(final BluetoothDevice device) { mDeviceConnected = false; runOnUiThread(new Runnable() { @Override @@ -284,7 +284,7 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl } @Override - public void onBatteryValueReceived(final int value) { + public void onBatteryValueReceived(final BluetoothDevice device, final int value) { runOnUiThread(new Runnable() { @Override public void run() { @@ -294,23 +294,23 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl } @Override - public void onBondingRequired() { + public void onBondingRequired(final BluetoothDevice device) { showToast(R.string.bonding); } @Override - public void onBonded() { + public void onBonded(final BluetoothDevice device) { showToast(R.string.bonded); } @Override - public void onError(final String message, final int errorCode) { + public void onError(final BluetoothDevice device, final String message, final int errorCode) { DebugLogger.e(TAG, "Error occurred: " + message + ", error code: " + errorCode); showToast(message + " (" + errorCode + ")"); } @Override - public void onDeviceNotSupported() { + public void onDeviceNotSupported(final BluetoothDevice device) { showToast(R.string.not_supported); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileExpandableListActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileExpandableListActivity.java index 875fef4d..e8a2d898 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileExpandableListActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileExpandableListActivity.java @@ -51,8 +51,8 @@ import no.nordicsemi.android.nrftoolbox.utility.DebugLogger; public abstract class BleProfileExpandableListActivity extends ExpandableListActivity implements BleManagerCallbacks, ScannerFragment.OnDeviceSelectedListener { private static final String TAG = "BaseProfileActivity"; - private static final String CONNECTION_STATUS = "connection_status"; - private static final String DEVICE_NAME = "device_name"; + private static final String SIS_CONNECTION_STATUS = "connection_status"; + private static final String SIS_DEVICE_NAME = "device_name"; protected static final int REQUEST_ENABLE_BT = 2; private BleManager mBleManager; @@ -127,15 +127,15 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct @Override protected void onSaveInstanceState(final Bundle outState) { super.onSaveInstanceState(outState); - outState.putBoolean(CONNECTION_STATUS, mDeviceConnected); - outState.putString(DEVICE_NAME, mDeviceName); + outState.putBoolean(SIS_CONNECTION_STATUS, mDeviceConnected); + outState.putString(SIS_DEVICE_NAME, mDeviceName); } @Override protected void onRestoreInstanceState(final Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); - mDeviceConnected = savedInstanceState.getBoolean(CONNECTION_STATUS); - mDeviceName = savedInstanceState.getString(DEVICE_NAME); + mDeviceConnected = savedInstanceState.getBoolean(SIS_CONNECTION_STATUS); + mDeviceName = savedInstanceState.getString(SIS_DEVICE_NAME); if (mDeviceConnected) { mConnectButton.setText(R.string.action_disconnect); @@ -236,7 +236,7 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct } @Override - public void onDeviceConnected() { + public void onDeviceConnected(final BluetoothDevice device) { mDeviceConnected = true; runOnUiThread(new Runnable() { @Override @@ -248,12 +248,12 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct } @Override - public void onDeviceDisconnecting() { + public void onDeviceDisconnecting(final BluetoothDevice device) { // do nothing } @Override - public void onDeviceDisconnected() { + public void onDeviceDisconnected(final BluetoothDevice device) { mDeviceConnected = false; mBleManager.close(); runOnUiThread(new Runnable() { @@ -267,7 +267,7 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct } @Override - public void onLinklossOccur() { + public void onLinklossOccur(final BluetoothDevice device) { mDeviceConnected = false; runOnUiThread(new Runnable() { @Override @@ -280,19 +280,19 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct } @Override - public void onServicesDiscovered(boolean optionalServicesFound) { + public void onServicesDiscovered(final BluetoothDevice device, boolean optionalServicesFound) { // this may notify user or show some views } /** * Called when the initialization process in completed. */ - public void onDeviceReady() { + public void onDeviceReady(final BluetoothDevice device) { // empty default implementation } @Override - public void onBatteryValueReceived(final int value) { + public void onBatteryValueReceived(final BluetoothDevice device, final int value) { runOnUiThread(new Runnable() { @Override public void run() { @@ -302,23 +302,23 @@ public abstract class BleProfileExpandableListActivity extends ExpandableListAct } @Override - public void onBondingRequired() { + public void onBondingRequired(final BluetoothDevice device) { showToast(R.string.bonding); } @Override - public void onBonded() { + public void onBonded(final BluetoothDevice device) { showToast(R.string.bonded); } @Override - public void onError(final String message, final int errorCode) { + public void onError(final BluetoothDevice device, final String message, final int errorCode) { DebugLogger.e(TAG, "Error occurred: " + message + ", error code: " + errorCode); showToast(message + " (" + errorCode + ")"); } @Override - public void onDeviceNotSupported() { + public void onDeviceNotSupported(final BluetoothDevice device) { showToast(R.string.not_supported); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileService.java index 578ea16b..621f6034 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileService.java @@ -51,6 +51,7 @@ public abstract class BleProfileService extends Service implements BleManagerCal public static final String EXTRA_DEVICE_ADDRESS = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE_ADDRESS"; /** The key for the device name that is returned in {@link #BROADCAST_CONNECTION_STATE} with state {@link #STATE_CONNECTED}. */ public static final String EXTRA_DEVICE_NAME = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE_NAME"; + public static final String EXTRA_DEVICE = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE"; public static final String EXTRA_LOG_URI = "no.nordicsemi.android.nrftoolbox.EXTRA_LOG_URI"; public static final String EXTRA_CONNECTION_STATE = "no.nordicsemi.android.nrftoolbox.EXTRA_CONNECTION_STATE"; public static final String EXTRA_BOND_STATE = "no.nordicsemi.android.nrftoolbox.EXTRA_BOND_STATE"; @@ -72,7 +73,7 @@ public abstract class BleProfileService extends Service implements BleManagerCal protected boolean mBinded; private boolean mActivityFinished; private boolean mConnected; - private String mDeviceAddress; + private BluetoothDevice mBluetoothDevice; private String mDeviceName; private ILogSession mLogSession; @@ -83,7 +84,7 @@ public abstract class BleProfileService extends Service implements BleManagerCal public final void disconnect() { if (!mConnected) { mBleManager.close(); - onDeviceDisconnected(); + onDeviceDisconnected(mBluetoothDevice); return; } @@ -104,7 +105,7 @@ public abstract class BleProfileService extends Service implements BleManagerCal * @return device address */ public String getDeviceAddress() { - return mDeviceAddress; + return mBluetoothDevice.getAddress(); } /** @@ -116,6 +117,15 @@ public abstract class BleProfileService extends Service implements BleManagerCal return mDeviceName; } + /** + * Returns the Bluetooth device + * + * @return the Bluetooth device + */ + public BluetoothDevice getBluetoothDevice() { + return mBluetoothDevice; + } + /** * Returns true if the device is connected to the sensor. * @@ -218,7 +228,7 @@ public abstract class BleProfileService extends Service implements BleManagerCal final Uri logUri = intent.getParcelableExtra(EXTRA_LOG_URI); mLogSession = Logger.openSession(getApplicationContext(), logUri); - mDeviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS); + mDeviceName = intent.getStringExtra(EXTRA_DEVICE_NAME); Logger.i(mLogSession, "Service started"); @@ -229,11 +239,11 @@ public abstract class BleProfileService extends Service implements BleManagerCal final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); final BluetoothAdapter adapter = bluetoothManager.getAdapter(); - final BluetoothDevice device = adapter.getRemoteDevice(mDeviceAddress); - mDeviceName = device.getName(); + final String deviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS); + mBluetoothDevice = adapter.getRemoteDevice(deviceAddress); onServiceStarted(); - mBleManager.connect(device); + mBleManager.connect(mBluetoothDevice); return START_REDELIVER_INTENT; } @@ -252,27 +262,28 @@ public abstract class BleProfileService extends Service implements BleManagerCal mBleManager.close(); Logger.i(mLogSession, "Service destroyed"); mBleManager = null; - mDeviceAddress = null; + mBluetoothDevice = null; mDeviceName = null; mConnected = false; mLogSession = null; } @Override - public void onDeviceConnected() { + public void onDeviceConnected(final BluetoothDevice device) { mConnected = true; final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE); broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTED); - broadcast.putExtra(EXTRA_DEVICE_ADDRESS, mDeviceAddress); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_DEVICE_NAME, mDeviceName); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } @Override - public void onDeviceDisconnecting() { + public void onDeviceDisconnecting(final BluetoothDevice device) { // Notify user about changing the state to DISCONNECTING final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTING); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } @@ -287,12 +298,12 @@ public abstract class BleProfileService extends Service implements BleManagerCal } @Override - public void onDeviceDisconnected() { + public void onDeviceDisconnected(final BluetoothDevice device) { mConnected = false; - mDeviceAddress = null; - mDeviceName = null; + // Do not use the device argument here unless you change calling onDeviceDisconnected from the binder above final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTED); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); @@ -307,31 +318,35 @@ public abstract class BleProfileService extends Service implements BleManagerCal } @Override - public void onLinklossOccur() { + public void onLinklossOccur(final BluetoothDevice device) { mConnected = false; final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_LINK_LOSS); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } @Override - public void onServicesDiscovered(final boolean optionalServicesFound) { + public void onServicesDiscovered(final BluetoothDevice device, final boolean optionalServicesFound) { final Intent broadcast = new Intent(BROADCAST_SERVICES_DISCOVERED); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_SERVICE_PRIMARY, true); broadcast.putExtra(EXTRA_SERVICE_SECONDARY, optionalServicesFound); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } @Override - public void onDeviceReady() { + public void onDeviceReady(final BluetoothDevice device) { final Intent broadcast = new Intent(BROADCAST_DEVICE_READY); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } @Override - public void onDeviceNotSupported() { + public void onDeviceNotSupported(final BluetoothDevice device) { final Intent broadcast = new Intent(BROADCAST_SERVICES_DISCOVERED); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_SERVICE_PRIMARY, false); broadcast.putExtra(EXTRA_SERVICE_SECONDARY, false); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); @@ -340,33 +355,37 @@ public abstract class BleProfileService extends Service implements BleManagerCal } @Override - public void onBatteryValueReceived(final int value) { + public void onBatteryValueReceived(final BluetoothDevice device, final int value) { final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_BATTERY_LEVEL, value); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } @Override - public void onBondingRequired() { + public void onBondingRequired(final BluetoothDevice device) { showToast(no.nordicsemi.android.nrftoolbox.common.R.string.bonding); final Intent broadcast = new Intent(BROADCAST_BOND_STATE); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } @Override - public void onBonded() { + public void onBonded(final BluetoothDevice device) { showToast(no.nordicsemi.android.nrftoolbox.common.R.string.bonded); final Intent broadcast = new Intent(BROADCAST_BOND_STATE); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } @Override - public void onError(final String message, final int errorCode) { + public void onError(final BluetoothDevice device, final String message, final int errorCode) { final Intent broadcast = new Intent(BROADCAST_ERROR); + broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice); broadcast.putExtra(EXTRA_ERROR_MESSAGE, message); broadcast.putExtra(EXTRA_ERROR_CODE, errorCode); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); @@ -421,7 +440,16 @@ public abstract class BleProfileService extends Service implements BleManagerCal * @return device address */ protected String getDeviceAddress() { - return mDeviceAddress; + return mBluetoothDevice.getAddress(); + } + + /** + * Returns the Bluetooth device object + * + * @return bluetooth device + */ + protected BluetoothDevice getBluetoothDevice() { + return mBluetoothDevice; } /** diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileServiceReadyActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileServiceReadyActivity.java index 987a7f47..7ca72b44 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileServiceReadyActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileServiceReadyActivity.java @@ -24,7 +24,6 @@ package no.nordicsemi.android.nrftoolbox.profile; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -73,7 +72,8 @@ public abstract class BleProfileServiceReadyActivity 0) - onBatteryValueReceived(value); + onBatteryValueReceived(bluetoothDevice, value); break; } case BleProfileService.BROADCAST_ERROR: { final String message = intent.getStringExtra(BleProfileService.EXTRA_ERROR_MESSAGE); final int errorCode = intent.getIntExtra(BleProfileService.EXTRA_ERROR_CODE, 0); - onError(message, errorCode); + onError(bluetoothDevice, message, errorCode); break; } } @@ -167,6 +172,7 @@ public abstract class BleProfileServiceReadyActivitytrue the secondary services were also found on the device. - */ - public abstract void onServicesDiscovered(final boolean optionalServicesFound); + @Override + public abstract void onServicesDiscovered(final BluetoothDevice device, final boolean optionalServicesFound); - /** - * Called when the initialization process in completed. - */ - public void onDeviceReady() { + @Override + public void onDeviceReady(final BluetoothDevice device) { // empty default implementation } - /** - * Called when the device has started bonding process - */ - public void onBondingRequired() { + @Override + public void onBondingRequired(final BluetoothDevice device) { // empty default implementation } - /** - * Called when the device has finished bonding process successfully - */ - public void onBonded() { + @Override + public void onBonded(final BluetoothDevice device) { // empty default implementation } - /** - * Called when service discovery has finished but the main services were not found on the device. This may occur when connecting to bonded device that does - * not support required services. - */ - public void onDeviceNotSupported() { + @Override + public void onDeviceNotSupported(final BluetoothDevice device) { showToast(R.string.not_supported); } - /** - * Called when battery value has been received from the device - * - * @param value the battery value in percent - */ - public void onBatteryValueReceived(final int value) { + @Override + public void onBatteryValueReceived(final BluetoothDevice device, final int value) { if (mBatteryLevelView != null) mBatteryLevelView.setText(getString(R.string.battery, value)); } - /** - * Called when a BLE error has occurred - * - * @param message the error message - * @param errorCode the error code - */ - public void onError(final String message, final int errorCode) { + @Override + public void onError(final BluetoothDevice device, final String message, final int errorCode) { DebugLogger.e(TAG, "Error occurred: " + message + ", error code: " + errorCode); showToast(message + " (" + errorCode + ")"); } @@ -640,6 +619,16 @@ public abstract class BleProfileServiceReadyActivity { if (!preparedWrite && value != null && value.length == 1) { // small validation if (value[0] != NO_ALERT[0]) { Logger.a(mLogSession, "[Server] Immediate alarm request received: " + AlertLevelParser.parse(characteristic)); - mCallbacks.onAlarmTriggered(); + mCallbacks.onAlarmTriggered(device); } else { Logger.a(mLogSession, "[Server] Immediate alarm request received: OFF"); - mCallbacks.onAlarmStopped(); + mCallbacks.onAlarmStopped(device); } } @@ -279,7 +279,7 @@ public class ProximityManager extends BleManager { @Override protected Queue initGatt(final BluetoothGatt gatt) { final LinkedList requests = new LinkedList<>(); - requests.push(Request.newWriteRequest(mLinklossCharacteristic, HIGH_ALERT)); + requests.add(Request.newWriteRequest(mLinklossCharacteristic, HIGH_ALERT)); return requests; } @@ -306,10 +306,14 @@ public class ProximityManager extends BleManager { mAlertLevelCharacteristic = null; mLinklossCharacteristic = null; } + + @Override + protected void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + Logger.a(mLogSession, "\"" + AlertLevelParser.parse(characteristic) + "\" sent"); + } }; public void writeImmediateAlertOn() { - Logger.a(mLogSession, "Immediate alarm request: ON"); if (mAlertLevelCharacteristic != null) { mAlertLevelCharacteristic.setValue(HIGH_ALERT); writeCharacteristic(mAlertLevelCharacteristic); @@ -319,7 +323,6 @@ public class ProximityManager extends BleManager { } public void writeImmediateAlertOff() { - Logger.a(mLogSession, "Immediate alarm request: OFF"); if (mAlertLevelCharacteristic != null) { mAlertLevelCharacteristic.setValue(NO_ALERT); writeCharacteristic(mAlertLevelCharacteristic); diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityManagerCallbacks.java index dabe9b11..7fe33dc6 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityManagerCallbacks.java @@ -21,10 +21,12 @@ */ package no.nordicsemi.android.nrftoolbox.proximity; +import android.bluetooth.BluetoothDevice; + import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; public interface ProximityManagerCallbacks extends BleManagerCallbacks { - public void onAlarmTriggered(); + void onAlarmTriggered(final BluetoothDevice device); - public void onAlarmStopped(); + void onAlarmStopped(final BluetoothDevice device); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityService.java index 6504b130..50c1f2c6 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityService.java @@ -24,6 +24,7 @@ package no.nordicsemi.android.nrftoolbox.proximity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -169,19 +170,19 @@ public class ProximityService extends BleProfileService implements ProximityMana } @Override - public void onDeviceDisconnecting() { + public void onDeviceDisconnecting(final BluetoothDevice device) { stopAlarm(); } @Override - public void onDeviceDisconnected() { - super.onDeviceDisconnected(); + public void onDeviceDisconnected(final BluetoothDevice device) { + super.onDeviceDisconnected(device); isImmediateAlertOn = false; } @Override - public void onLinklossOccur() { - super.onLinklossOccur(); + public void onLinklossOccur(final BluetoothDevice device) { + super.onLinklossOccur(device); isImmediateAlertOn = false; if (!mBinded) { @@ -192,12 +193,12 @@ public class ProximityService extends BleProfileService implements ProximityMana } @Override - public void onAlarmTriggered() { + public void onAlarmTriggered(final BluetoothDevice device) { playAlarm(); } @Override - public void onAlarmStopped() { + public void onAlarmStopped(final BluetoothDevice device) { stopAlarm(); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCActivity.java index 820f8d4a..4c724d76 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCActivity.java @@ -22,6 +22,7 @@ package no.nordicsemi.android.nrftoolbox.rsc; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -33,6 +34,7 @@ import android.support.v4.content.LocalBroadcastManager; import android.view.Menu; import android.widget.TextView; +import java.util.Locale; import java.util.UUID; import no.nordicsemi.android.nrftoolbox.R; @@ -175,7 +177,7 @@ public class RSCActivity extends BleProfileServiceReadyActivity { @Override protected Queue initGatt(final BluetoothGatt gatt) { final LinkedList requests = new LinkedList<>(); - requests.push(Request.newEnableNotificationsRequest(mRSCMeasurementCharacteristic)); + requests.add(Request.newEnableNotificationsRequest(mRSCMeasurementCharacteristic)); return requests; } @@ -84,8 +84,7 @@ public class RSCManager extends BleManager { @Override public void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - if (mLogSession != null) - Logger.a(mLogSession, RSCMeasurementParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + RSCMeasurementParser.parse(characteristic) + "\" received"); // Decode the new data int offset = 0; @@ -115,8 +114,8 @@ public class RSCManager extends BleManager { } // Notify listener about the new measurement - mCallbacks.onMeasurementReceived(instantaneousSpeed, instantaneousCadence, totalDistance, instantaneousStrideLength, running ? RSCManagerCallbacks.ACTIVITY_RUNNING - : RSCManagerCallbacks.ACTIVITY_WALKING); + mCallbacks.onMeasurementReceived(gatt.getDevice(), instantaneousSpeed, instantaneousCadence, totalDistance, instantaneousStrideLength, + running ? RSCManagerCallbacks.ACTIVITY_RUNNING : RSCManagerCallbacks.ACTIVITY_WALKING); } }; } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCManagerCallbacks.java index 6fe5d8bf..0e3c4b83 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCManagerCallbacks.java @@ -21,12 +21,14 @@ */ package no.nordicsemi.android.nrftoolbox.rsc; +import android.bluetooth.BluetoothDevice; + import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; public interface RSCManagerCallbacks extends BleManagerCallbacks { - public static final int NOT_AVAILABLE = -1; - public static final int ACTIVITY_WALKING = 0; - public static final int ACTIVITY_RUNNING = 1; + int NOT_AVAILABLE = -1; + int ACTIVITY_WALKING = 0; + int ACTIVITY_RUNNING = 1; - public void onMeasurementReceived(float speed, int cadence, float distance, float strideLen, int activity); + void onMeasurementReceived(final BluetoothDevice device, float speed, int cadence, float distance, float strideLen, int activity); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCService.java index 9e36a3ad..f79f3718 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCService.java @@ -25,6 +25,7 @@ package no.nordicsemi.android.nrftoolbox.rsc; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -149,8 +150,9 @@ public class RSCService extends BleProfileService implements RSCManagerCallbacks }; @Override - public void onMeasurementReceived(final float speed, final int cadence, final float totalDistance, final float strideLen, final int activity) { + public void onMeasurementReceived(final BluetoothDevice device, final float speed, final int cadence, final float totalDistance, final float strideLen, final int activity) { final Intent broadcast = new Intent(BROADCAST_RSC_MEASUREMENT); + broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice()); broadcast.putExtra(EXTRA_SPEED, speed); broadcast.putExtra(EXTRA_CADENCE, cadence); broadcast.putExtra(EXTRA_TOTAL_DISTANCE, totalDistance); diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateActivity.java index e7197cd6..ee494253 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateActivity.java @@ -21,6 +21,7 @@ */ package no.nordicsemi.android.nrftoolbox.template; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -133,7 +134,7 @@ public class TemplateActivity extends BleProfileServiceReadyActivity { protected Queue initGatt(final BluetoothGatt gatt) { final LinkedList requests = new LinkedList<>(); // TODO initialize your device, enable required notifications and indications, write what needs to be written to start working - requests.push(Request.newEnableNotificationsRequest(mCharacteristic)); + requests.add(Request.newEnableNotificationsRequest(mCharacteristic)); return requests; } @@ -99,8 +99,7 @@ public class TemplateManager extends BleManager { // TODO this method is called when a notification has been received // This method may be removed from this class if not required - if (mLogSession != null) - Logger.a(mLogSession, TemplateParser.parse(characteristic)); + Logger.a(mLogSession, "\"" + TemplateParser.parse(characteristic) + "\" received"); int value; final int flags = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); @@ -110,7 +109,7 @@ public class TemplateManager extends BleManager { value = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1); } //This will send callback to the Activity when new value is received from HR device - mCallbacks.onSampleValueReceived(value); + mCallbacks.onSampleValueReceived(gatt.getDevice(), value); } @Override diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManagerCallbacks.java index 125c25f5..11394464 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManagerCallbacks.java @@ -21,6 +21,8 @@ */ package no.nordicsemi.android.nrftoolbox.template; +import android.bluetooth.BluetoothDevice; + import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; /** @@ -32,10 +34,11 @@ public interface TemplateManagerCallbacks extends BleManagerCallbacks { /** * Called when a value is received. - * + * + * @param device a device from which the value was obtained * @param value * the new value */ - public void onSampleValueReceived(int value); + void onSampleValueReceived(final BluetoothDevice device, int value); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateService.java index 8150586b..d2506601 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateService.java @@ -25,6 +25,7 @@ package no.nordicsemi.android.nrftoolbox.template; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -112,8 +113,9 @@ public class TemplateService extends BleProfileService implements TemplateManage } @Override - public void onSampleValueReceived(final int value) { + public void onSampleValueReceived(final BluetoothDevice device, final int value) { final Intent broadcast = new Intent(BROADCAST_TEMPLATE_MEASUREMENT); + broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice()); broadcast.putExtra(EXTRA_DATA, value); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTActivity.java index 1f8aa3e0..f78064dd 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTActivity.java @@ -271,7 +271,7 @@ public class UARTActivity extends BleProfileServiceReadyActivity { @@ -66,7 +67,7 @@ public class UARTManager extends BleManager { @Override protected Queue initGatt(final BluetoothGatt gatt) { final LinkedList requests = new LinkedList<>(); - requests.push(Request.newEnableNotificationsRequest(mTXCharacteristic)); + requests.add(Request.newEnableNotificationsRequest(mTXCharacteristic)); return requests; } @@ -106,7 +107,9 @@ public class UARTManager extends BleManager { final byte[] buffer = mOutgoingBuffer; if (mBufferOffset == buffer.length) { try { - mCallbacks.onDataSent(new String(buffer, "UTF-8")); + final String data = new String(buffer, "UTF-8"); + Logger.a(mLogSession, "\"" + data + "\" sent"); + mCallbacks.onDataSent(gatt.getDevice(), data); } catch (final UnsupportedEncodingException e) { // do nothing } @@ -124,7 +127,8 @@ public class UARTManager extends BleManager { @Override public void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { final String data = characteristic.getStringValue(0); - mCallbacks.onDataReceived(data); + Logger.a(mLogSession, "\"" + data + "\" received"); + mCallbacks.onDataReceived(gatt.getDevice(), data); } }; diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTManagerCallbacks.java index e80d212f..6520d627 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTManagerCallbacks.java @@ -22,11 +22,13 @@ package no.nordicsemi.android.nrftoolbox.uart; +import android.bluetooth.BluetoothDevice; + import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks; public interface UARTManagerCallbacks extends BleManagerCallbacks { - public void onDataReceived(final String data); + void onDataReceived(final BluetoothDevice device, final String data); - public void onDataSent(final String data); + void onDataSent(final BluetoothDevice device, final String data); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTNewConfigurationDialogFragment.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTNewConfigurationDialogFragment.java index 1c01e9a2..80ba8cd1 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTNewConfigurationDialogFragment.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTNewConfigurationDialogFragment.java @@ -22,10 +22,8 @@ package no.nordicsemi.android.nrftoolbox.uart; -import android.app.Activity; import android.app.Dialog; import android.content.Context; -import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; @@ -33,7 +31,6 @@ import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; -import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; @@ -53,21 +50,21 @@ public class UARTNewConfigurationDialogFragment extends DialogFragment implement * @param name the name * @param duplicate true if configuration is to be duplicated */ - public void onNewConfiguration(final String name, final boolean duplicate); + void onNewConfiguration(final String name, final boolean duplicate); /** * Renames the current configuration with given name. * @param newName the new name */ - public void onRenameConfiguration(final String newName); + void onRenameConfiguration(final String newName); } @Override - public void onAttach(final Activity activity) { - super.onAttach(activity); + public void onAttach(final Context context) { + super.onAttach(context); - if (activity instanceof NewConfigurationDialogListener) { - mListener = (NewConfigurationDialogListener) activity; + if (context instanceof NewConfigurationDialogListener) { + mListener = (NewConfigurationDialogListener) context; } else { throw new IllegalArgumentException("The parent activity must implement NewConfigurationDialogListener"); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTService.java index 97104f46..0b8c2068 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTService.java @@ -25,6 +25,7 @@ package no.nordicsemi.android.nrftoolbox.uart; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -143,8 +144,8 @@ public class UARTService extends BleProfileService implements UARTManagerCallbac } @Override - public void onDeviceConnected() { - super.onDeviceConnected(); + public void onDeviceConnected(final BluetoothDevice device) { + super.onDeviceConnected(device); sendMessageToWearables(Constants.UART.DEVICE_CONNECTED, notNull(getDeviceName())); } @@ -154,14 +155,14 @@ public class UARTService extends BleProfileService implements UARTManagerCallbac } @Override - public void onDeviceDisconnected() { - super.onDeviceDisconnected(); + public void onDeviceDisconnected(final BluetoothDevice device) { + super.onDeviceDisconnected(device); sendMessageToWearables(Constants.UART.DEVICE_DISCONNECTED, notNull(getDeviceName())); } @Override - public void onLinklossOccur() { - super.onLinklossOccur(); + public void onLinklossOccur(final BluetoothDevice device) { + super.onLinklossOccur(device); sendMessageToWearables(Constants.UART.DEVICE_LINKLOSS, notNull(getDeviceName())); } @@ -172,24 +173,23 @@ public class UARTService extends BleProfileService implements UARTManagerCallbac } @Override - public void onDataReceived(final String data) { - Logger.a(getLogSession(), "\"" + data + "\" received"); - + public void onDataReceived(final BluetoothDevice device, final String data) { final Intent broadcast = new Intent(BROADCAST_UART_RX); + broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice()); broadcast.putExtra(EXTRA_DATA, data); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); // send the data received to other apps, e.g. the Tasker final Intent globalBroadcast = new Intent(ACTION_RECEIVE); + globalBroadcast.putExtra(BluetoothDevice.EXTRA_DEVICE, getBluetoothDevice()); globalBroadcast.putExtra(Intent.EXTRA_TEXT, data); sendBroadcast(globalBroadcast); } @Override - public void onDataSent(final String data) { - Logger.a(getLogSession(), "\"" + data + "\" sent"); - + public void onDataSent(final BluetoothDevice device, final String data) { final Intent broadcast = new Intent(BROADCAST_UART_TX); + broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice()); broadcast.putExtra(EXTRA_DATA, data); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } diff --git a/app/src/main/res/layout-land/activity_feature_cgms.xml b/app/src/main/res/layout-land/activity_feature_cgms.xml index b06781df..19bd60a3 100644 --- a/app/src/main/res/layout-land/activity_feature_cgms.xml +++ b/app/src/main/res/layout-land/activity_feature_cgms.xml @@ -114,6 +114,7 @@ style="@style/Widget.ListTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@string/cgms_value_unit" android:layout_alignParentRight="true" android:layout_marginRight="4dp"/> diff --git a/app/src/main/res/layout/activity_feature_cgms.xml b/app/src/main/res/layout/activity_feature_cgms.xml index c92d3075..dfea085e 100644 --- a/app/src/main/res/layout/activity_feature_cgms.xml +++ b/app/src/main/res/layout/activity_feature_cgms.xml @@ -110,6 +110,7 @@ style="@style/Widget.ListTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@string/cgms_value_unit" android:layout_alignParentRight="true" android:layout_marginRight="4dp"/> diff --git a/app/src/main/res/layout/activity_feature_cgms_item.xml b/app/src/main/res/layout/activity_feature_cgms_item.xml index 2bcb68f6..1bc83dc7 100644 --- a/app/src/main/res/layout/activity_feature_cgms_item.xml +++ b/app/src/main/res/layout/activity_feature_cgms_item.xml @@ -20,52 +20,35 @@ ~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> - + android:paddingEnd="8dp" + android:paddingStart="8dp" + android:paddingTop="2dp"> - + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceListItem"/> - - - - + - - + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:textAppearance="?android:attr/textAppearanceLarge"/> - \ No newline at end of file + diff --git a/app/src/main/res/values-land/strings_cgms.xml b/app/src/main/res/values-land/strings_cgms.xml index 3707dc9f..963400dd 100644 --- a/app/src/main/res/values-land/strings_cgms.xml +++ b/app/src/main/res/values-land/strings_cgms.xml @@ -23,7 +23,5 @@ CGMS - GCM updates are notified every minute - Glucose concentration level -16dp \ No newline at end of file diff --git a/app/src/main/res/values/strings_cgms.xml b/app/src/main/res/values/strings_cgms.xml index 17228407..8f251a99 100644 --- a/app/src/main/res/values/strings_cgms.xml +++ b/app/src/main/res/values/strings_cgms.xml @@ -22,16 +22,15 @@ --> - CGMS - CONT. GLUCOSE MONITOR - -180dp + CGMS + CONT. GLUCOSE MONITOR + -180dp - DEFAULT CGMS - mg/dL - sensor position + DEFAULT CGMS + mg/dL + Sequence number: %d CGMS (Continuous Glucode Monitoring Service) profile allows you to connect to your Continuous Glucose Monitoring sensor. - It shows you the continuous glucose measurement on the table. - GCM updates are notified every minute - Glucose concentration level + It shows you the continuous glucose measurement on the table. A new record should show up on the list every minute. You may also request all, first or the last + record using buttons below the list. \ No newline at end of file diff --git a/wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTProfile.java b/wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTProfile.java index ad75a768..e0f89209 100644 --- a/wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTProfile.java +++ b/wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTProfile.java @@ -25,11 +25,8 @@ package no.nordicsemi.android.nrftoolbox.uart; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; -import android.content.Intent; -import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; -import java.util.LinkedList; import java.util.Queue; import java.util.UUID; @@ -82,7 +79,7 @@ public class UARTProfile extends BleProfile { // We don't want to enable notifications on TX characteristic as we are not showing them here. A watch may be just used to send data. At least now. // final LinkedList requests = new LinkedList<>(); -// requests.push(BleManager.Request.newEnableNotificationsRequest(mTXCharacteristic)); +// requests.add(BleManager.Request.newEnableNotificationsRequest(mTXCharacteristic)); // return requests; return null; }