CGMS profile added, ver 1.18.0

+ minor fixes
This commit is contained in:
Aleksander Nowakowski
2016-09-05 18:41:02 +02:00
parent 173de83610
commit add6bd2aeb
45 changed files with 1778 additions and 35 deletions

View File

@@ -9,6 +9,7 @@ It contains applications demonstrating Bluetooth Smart profiles:
* **Blood Pressure Monitor**,
* **Health Thermometer Monitor**,
* **Glucose Monitor**,
* **Continuous Glucose Monitor**,
* **Proximity Monitor**.
Since version 1.10.0 the *nRF Toolbox* also supports the **Nordic UART Service** which may be used for bidirectional text communication between devices.

View File

@@ -8,8 +8,8 @@ android {
applicationId "no.nordicsemi.android.nrftoolbox"
minSdkVersion 18
targetSdkVersion 24
versionCode 46
versionName "1.17.0"
versionCode 47
versionName "1.18.0"
}
buildTypes {
release {

View File

@@ -19,7 +19,7 @@
~ 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.
-->
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="no.nordicsemi.android.nrftoolbox"
android:installLocation="auto">
@@ -201,6 +201,16 @@
<category android:name="no.nordicsemi.android.nrftoolbox.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="no.nordicsemi.android.nrftoolbox.cgms.CGMSActivity"
android:icon="@drawable/ic_cgms_feature"
android:label="@string/cgms_feature_title"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="no.nordicsemi.android.nrftoolbox.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="no.nordicsemi.android.nrftoolbox.proximity.ProximityService"
@@ -228,6 +238,9 @@
</intent-filter>
</activity>
<service
android:name="no.nordicsemi.android.nrftoolbox.cgms.CGMService"
android:label="@string/cgms_feature_title" />
<service
android:name="no.nordicsemi.android.nrftoolbox.rsc.RSCService"
android:label="@string/rsc_feature_title" />

View File

@@ -0,0 +1,292 @@
/*
* Copyright (c) 2016, 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.cgms;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.SparseArray;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
import java.util.UUID;
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<CGMService.CGMSBinder> implements PopupMenu.OnMenuItemClickListener {
private View mControlPanelStd;
private View mControlPanelAbort;
private TextView mUnitView;
private ListView mRecordsListView;
private CGMSRecordsAdapter mCgmsRecordsAdapter;
private CGMService.CGMSBinder mBinder;
@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());
}
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();
}
}
});
// 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<CGMSRecord> 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 onServiceBinded(CGMService.CGMSBinder binder) {
mBinder = binder;
SparseArray<CGMSRecord> 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 Class<? extends BleProfileService> getServiceClass() {
return CGMService.class;
}
@Override
protected int getLoggerProfileTitle() {
return R.string.cgms_feature_title;
}
@Override
protected int getAboutTextId() {
return R.string.cgms_about_text;
}
@Override
protected int getDefaultDeviceName() {
return R.string.cgms_default_name;
}
@Override
protected UUID getFilterUUID() {
return CGMSManager.CGMS_UUID;
}
@Override
public void onServicesDiscovered(final boolean optionalServicesFound) {
// this may notify user or show some views
}
@Override
public void onDeviceReady() {
}
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 onDeviceDisconnected() {
super.onDeviceDisconnected();
setOperationInProgress(false);
clearRecords();
}
@Override
protected void setDefaultUI() {
}
@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;
}
}

View File

@@ -0,0 +1,383 @@
/*
* Copyright (c) 2016, 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.cgms;
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.RecordAccessControlPointParser;
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
import no.nordicsemi.android.nrftoolbox.utility.DebugLogger;
/**
* Created by rora on 10.05.2016.
*/
public class CGMSManager extends BleManager<CGMSManagerCallbacks> {
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");
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;
/**
* The filter type is used for range operators ({@link #OPERATOR_LESS_THEN_OR_EQUAL}, {@link #OPERATOR_GREATER_THEN_OR_EQUAL}, {@link #OPERATOR_WITHING_RANGE}.<br/>
* The syntax of the operand is: [Filter Type][Minimum][Maximum].<br/>
* This filter selects the records by the sequence number.
*/
private 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}.<br/>
* The syntax of the operand is: [Filter Type][Minimum][Maximum].<br/>
* This filter selects the records by the user facing time (base time + offset time).
*/
private 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 static CGMSManager managerInstance = null;
private SparseArray<CGMSRecord> 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;
}
public CGMSManager(Context context) {
super(context);
}
@Override
protected BleManagerGattCallback getGattCallback() {
return mGattCallback;
}
/**
* BluetoothGatt callbacks for connection/disconnection, service discovery, receiving notification, etc
*/
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
@Override
protected Queue<Request> initGatt(final BluetoothGatt gatt) {
final LinkedList<Request> 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 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
public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
}
@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 onCharacteristicIndicated(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
if (mLogSession != null)
Logger.a(mLogSession, RecordAccessControlPointParser.parse(characteristic));
// Record Access Control Point characteristic
int offset = 0;
final int opCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset);
offset += 2; // skip the operator
if (opCode == OP_CODE_NUMBER_OF_STORED_RECORDS_RESPONSE) {
// We've obtained the number of all records
final int number = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset);
mCallbacks.onNumberOfRecordsRequested(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();
}
} else if (opCode == OP_CODE_RESPONSE_CODE) {
final int requestedOpCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset);
final int responseCode = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset + 1);
DebugLogger.d(TAG, "Response result for: " + requestedOpCode + " is: " + responseCode);
switch (responseCode) {
case RESPONSE_SUCCESS:
if (!mAbort)
mCallbacks.onOperationCompleted();
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;
}
}
};
/**
* Writes given operation parameters to the characteristic
*
* @param characteristic
* the characteristic to write. This must be the Record Access Control Point characteristic
* @param opCode
* the operation code
* @param operator
* the operator (see {@link #OPERATOR_NULL} and others
* @param params
* optional parameters (one for >=, <=, two for the range, none for other operators)
*/
private void setOpCode(final BluetoothGattCharacteristic characteristic, final int opCode, final int operator, final Integer... params) {
final int size = 2 + ((params.length > 0) ? 1 : 0) + params.length * 2; // 1 byte for opCode, 1 for operator, 1 for filter type (if parameters exists) and 2 for each parameter
characteristic.setValue(new byte[size]);
// write the operation code
int offset = 0;
characteristic.setValue(opCode, BluetoothGattCharacteristic.FORMAT_UINT8, offset);
offset += 1;
// write the operator. This is always present but may be equal to OPERATOR_NULL
characteristic.setValue(operator, BluetoothGattCharacteristic.FORMAT_UINT8, offset);
offset += 1;
// if parameters exists, append them. Parameters should be sorted from minimum to maximum. Currently only one or two params are allowed
if (params.length > 0) {
// our implementation use only sequence number as a filer type
characteristic.setValue(FILTER_TYPE_SEQUENCE_NUMBER, BluetoothGattCharacteristic.FORMAT_UINT8, offset);
offset += 1;
for (final Integer i : params) {
characteristic.setValue(i, BluetoothGattCharacteristic.FORMAT_UINT16, offset);
offset += 2;
}
}
}
/**
* 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;
clear();
mCallbacks.onOperationStarted();
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_LAST_RECORD);
writeCharacteristic(characteristic);
}
/**
* Returns all records as a sparse array where sequence number is the key.
*
* @return the records list
*/
public SparseArray<CGMSRecord> getRecords() {
return mRecords;
}
/**
* Clears the records list locally
*/
public void clear() {
mRecords.clear();
mCallbacks.onDatasetChanged();
}
/**
* Sends abort operation signal to the device
*/
public void abort() {
if (mRecordAccessControlPointCharacteristic == null)
return;
mAbort = true;
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_ABORT_OPERATION, OPERATOR_NULL);
writeCharacteristic(characteristic);
}
/**
* 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();
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_REPORT_STORED_RECORDS, OPERATOR_FIRST_RECORD);
final LinkedList<Request> requests = new LinkedList<>();
writeCharacteristic(characteristic);
}
/**
* 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;
clear();
mCallbacks.onOperationStarted();
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_REPORT_NUMBER_OF_RECORDS, OPERATOR_ALL_RECORDS);
writeCharacteristic(characteristic);
}
public void deleteAllRecords() {
if (mRecordAccessControlPointCharacteristic == null)
return;
clear();
mCallbacks.onOperationStarted();
final BluetoothGattCharacteristic characteristic = mRecordAccessControlPointCharacteristic;
setOpCode(characteristic, OP_CODE_DELETE_STORED_RECORDS, OPERATOR_ALL_RECORDS);
writeCharacteristic(characteristic);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2016, 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.cgms;
import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks;
/**
* Created by rora on 10.05.2016.
*/
public interface CGMSManagerCallbacks extends BleManagerCallbacks {
/**
* Called when new CGM value has been obtained from the sensor
*
* @param value
* the new value
*/
public void onCGMValueReceived(float value, String timeStamp);
public void onOperationStarted();
public void onOperationCompleted();
public void onOperationFailed();
public void onOperationAborted();
public void onOperationNotSupported();
public void onDatasetChanged();
public void onNumberOfRecordsRequested(final int value);
}

View File

@@ -0,0 +1,56 @@
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 */
protected int sequenceNumber;
/** The base time of the measurement */
protected Calendar time;
/** Time offset of the record */
protected String timeStamp;
protected float reading;
protected CGMSRecord(float cgmsValue, String timeStamp) {
sequenceNumber = 0;
this.timeStamp = timeStamp;
this.reading = cgmsValue;
}
protected CGMSRecord(Parcel in) {
sequenceNumber = in.readInt();
timeStamp = in.readString();
reading = in.readFloat();
}
public static final Creator<CGMSRecord> CREATOR = new Creator<CGMSRecord>() {
@Override
public CGMSRecord createFromParcel(Parcel in) {
return new CGMSRecord(in);
}
@Override
public CGMSRecord[] newArray(int size) {
return new CGMSRecord[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(sequenceNumber);
parcel.writeString(timeStamp);
parcel.writeFloat(reading);
}
}

View File

@@ -0,0 +1,87 @@
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 no.nordicsemi.android.nrftoolbox.R;
/**
* Created by rora on 02.09.2016.
*/
public class CGMSRecordsAdapter extends BaseAdapter {
private SparseArray<CGMSRecord> mRecords;
private LayoutInflater mInflator;
private Context context;
public CGMSRecordsAdapter(Context context) {
super();
mRecords = new SparseArray<>();
this.context = context;
mInflator = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return mRecords.size();
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
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);
final String concentration = String.valueOf(cgmsRecord.reading);
if (concentration != null && concentration.length() > 0) {
viewHolder.concentration.setText(concentration /*+ " " + context.getString(R.string.cgms_value_unit*/);
viewHolder.time.setText(cgmsRecord.timeStamp);
}
return convertView;
}
public void addItem(CGMSRecord record) {
mRecords.put(mRecords.size(), record);
}
public SparseArray<CGMSRecord> getValues() {
return mRecords;
}
public void clear() {
mRecords.clear();
}
static class ViewHolder {
TextView time;
TextView details;
TextView concentration;
}
}

View File

@@ -0,0 +1,267 @@
package no.nordicsemi.android.nrftoolbox.cgms;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.NotificationCompat;
import android.util.SparseArray;
import no.nordicsemi.android.log.Logger;
import no.nordicsemi.android.nrftoolbox.FeaturesActivity;
import no.nordicsemi.android.nrftoolbox.R;
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
/**
* Created by rora on 05.09.2016.
*/
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 OPERATION_STARTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_STARTED";
public static final String OPERATION_COMPLETED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_COMPLETED";
public static final String OPERATION_SUPPORTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_SUPPORTED";
public static final String OPERATION_NOT_SUPPORTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_NOT_SUPPORTED";
public static final String OPERATION_FAILED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_FAILED";
public static final String OPERATION_ABORTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_ABORTED";
public static final String EXTRA_CGMS_RECORD = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_CGMS_RECORD";
public static final String 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;
private final static int OPEN_ACTIVITY_REQ = 0;
private final static int DISCONNECT_REQ = 1;
private CGMSManager mManager;
private final LocalBinder mBinder = new CGMSBinder();
private SparseArray<CGMSRecord> 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<CGMSRecord> 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<CGMSRecord> getRecords() {
return mRecords;
}
/**
* Clears the records list locally
*/
public void clear() {
mRecords.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
* Point indication with status code ({@link CGMSManager# RESPONSE_SUCCESS} or other in case of error.
*/
public void getFirstRecord() {
if(mManager != null)
mManager.getFirstRecord();
}
/**
* Sends the request to obtain 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.
*/
public void getAllRecords() {
clear();
if(mManager != null)
mManager.getAllRecords();
}
public void deleteAllRecords() {
if(mManager != null)
mManager.deleteAllRecords();
}
}
@Override
protected LocalBinder getBinder() {
return mBinder;
}
@Override
protected BleManager initializeManager() {
return mManager = new CGMSManager(this);
}
@Override
public void onCreate() {
super.onCreate();
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DISCONNECT);
registerReceiver(mDisconnectActionBroadcastReceiver, filter);
}
@Override
public void onDestroy() {
// when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService
cancelNotification();
unregisterReceiver(mDisconnectActionBroadcastReceiver);
super.onDestroy();
}
@Override
protected void onRebind() {
// when the activity rebinds to the service, remove the notification
cancelNotification();
}
@Override
protected void onUnbind() {
// when the activity closes we need to show the notification that user is connected to the sensor
createNotification(R.string.csc_notification_connected_message, 0);
}
@Override
protected void onServiceStarted() {
// logger is now available. Assign it to the manager
mManager.setLogger(getLogSession());
}
/**
* Creates the notification
*
* @param messageResId
* the message resource id. The message must have one String parameter,<br />
* f.e. <code>&lt;string name="name"&gt;%s is connected&lt;/string&gt;</code>
* @param defaults
* signals that will be used to notify the user
*/
private void createNotification(final int messageResId, final int defaults) {
final Intent parentIntent = new Intent(this, FeaturesActivity.class);
parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final Intent targetIntent = new Intent(this, CGMSActivity.class);
final Intent disconnect = new Intent(ACTION_DISCONNECT);
final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, DISCONNECT_REQ, disconnect, PendingIntent.FLAG_UPDATE_CURRENT);
// both activities above have launchMode="singleTask" in the AndroidManifest.xml file, so if the task is already running, it will be resumed
final PendingIntent pendingIntent = PendingIntent.getActivities(this, OPEN_ACTIVITY_REQ, new Intent[] { parentIntent, targetIntent }, PendingIntent.FLAG_UPDATE_CURRENT);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentIntent(pendingIntent);
builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName()));
builder.setSmallIcon(R.drawable.ic_stat_notify_cgms);
builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true);
builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.csc_notification_action_disconnect), disconnectAction));
final Notification notification = builder.build();
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(NOTIFICATION_ID, notification);
}
/**
* Cancels the existing notification. If there is no active notification this method does nothing
*/
private void cancelNotification() {
final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.cancel(NOTIFICATION_ID);
}
/**
* This broadcast receiver listens for {@link #ACTION_DISCONNECT} that may be fired by pressing Disconnect action button on the notification.
*/
private final BroadcastReceiver mDisconnectActionBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
Logger.i(getLogSession(), "[Notification] Disconnect action pressed");
if (isConnected())
getBinder().disconnect();
else
stopSelf();
}
};
@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);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
mRecords.put(mRecords.size(), cgmsRecord);
}
@Override
public void onOperationStarted() {
final Intent broadcast = new Intent(OPERATION_STARTED);
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationCompleted() {
final Intent broadcast = new Intent(OPERATION_COMPLETED);
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationFailed() {
final Intent broadcast = new Intent(OPERATION_FAILED);
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationAborted() {
final Intent broadcast = new Intent(OPERATION_ABORTED);
broadcast.putExtra(EXTRA_DATA, true);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onOperationNotSupported() {
final Intent broadcast = new Intent(OPERATION_NOT_SUPPORTED);
broadcast.putExtra(EXTRA_DATA, false);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
@Override
public void onDatasetChanged() {
}
@Override
public void onNumberOfRecordsRequested(int value) {
showToast(getString(R.string.gls_progress, value));
}
}

View File

@@ -265,6 +265,7 @@ public abstract class BleProfileActivity extends AppCompatActivity implements Bl
mConnectButton.setText(R.string.action_connect);
mDeviceNameView.setText(getDefaultDeviceName());
mBatteryLevelView.setText(R.string.not_available);
mConnectButton.setEnabled(true);
}
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".BPMActivity">
tools:context=".bpm.BPMActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -0,0 +1,227 @@
<!--
~ 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".cgms.CGMSActivity">
<include
android:id="@+id/toolbar_actionbar"
layout="@layout/toolbar"/>
<no.nordicsemi.android.nrftoolbox.widget.ForegroundRelativeLayout
style="@style/HeaderShadow"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The size of text below must be fixed, therefore dp are used instead of sp -->
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/cgms_feature_title_long_margin"
android:rotation="270"
android:text="@string/cgms_feature_title_long"
android:textColor="@color/darkGray"
android:textSize="32dp"
android:textStyle="bold"/>
<TextView
android:id="@+id/battery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="@dimen/feature_horizontal_margin"
android:layout_marginTop="@dimen/feature_device_name_margin_top"
android:background="@drawable/battery"
android:freezesText="true"
android:gravity="center"
android:text="@string/not_available"
android:textColor="#FFFFFF"
android:textSize="12sp"/>
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetTextView
android:id="@+id/device_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="@dimen/feature_device_name_margin_top"
android:layout_toRightOf="@+id/battery"
android:ellipsize="end"
android:freezesText="true"
android:maxLines="1"
android:text="@string/cgms_default_name"
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/action_connect"
android:layout_marginLeft="@dimen/feature_horizontal_margin"
android:layout_marginRight="@dimen/feature_horizontal_margin"
android:baselineAligned="false"
android:gravity="center_horizontal"
android:orientation="horizontal">
<!-- Application section -->
<LinearLayout
style="@style/Widget.List"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/activity_vertical_margin_bottom"
android:layout_marginLeft="4dp"
android:layout_marginTop="@dimen/feature_vertical_margin_top"
android:layout_weight="1"
android:orientation="vertical">
<RelativeLayout
style="@style/Widget.ListTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
style="@style/Widget.ListTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/gls_section_data"/>
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetTextView
android:id="@+id/unit"
style="@style/Widget.ListTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="4dp"/>
</RelativeLayout>
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"
tools:listitem="@layout/activity_feature_cgms_item"/>
<TextView
android:id="@+id/empty"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/gls_no_data"/>
</LinearLayout>
<!-- Timestamp section -->
<LinearLayout
style="@style/Widget.List"
android:layout_width="120dp"
android:layout_height="160dp"
android:layout_marginBottom="@dimen/activity_vertical_margin_bottom"
android:layout_marginLeft="14dp"
android:layout_marginRight="4dp"
android:layout_marginTop="@dimen/feature_vertical_margin_top"
android:orientation="vertical">
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
style="@style/Widget.ListTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/gls_section_actions"/>
<LinearLayout
android:id="@+id/cgms_control_std"
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<Button
android:id="@+id/action_all"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="0dp"
android:text="@string/gls_action_all"/>
<Button
android:id="@+id/action_last"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="0dp"
android:text="@string/gls_action_last"/>
<Button
android:id="@+id/action_more"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="0dp"
android:text="@string/gls_action_more"/>
</LinearLayout>
<LinearLayout
android:id="@+id/cgms_control_abort"
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<Button
android:id="@+id/action_abort"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="@string/gls_action_abort"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/action_connect"
style="@style/Widget.Connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/activity_vertical_margin_bottom"
android:onClick="onConnectClicked"
android:text="@string/action_connect"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="6dp"
android:src="@drawable/background_title"/>
</no.nordicsemi.android.nrftoolbox.widget.ForegroundRelativeLayout>
</LinearLayout>

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".CSCActivity">
tools:context=".csc.CSCActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".DfuActivity">
tools:context=".dfu.DfuActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".BPMActivity">
tools:context=".hrs.HRSActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".HTSActivity">
tools:context=".hts.HTSActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ProximityActivity">
tools:context=".proximity.ProximityActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".RSCActivity">
tools:context=".rsc.RSCActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".UARTActivity">
tools:context=".uart.UARTActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".BPMActivity">
tools:context=".bpm.BPMActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".CSCActivity">
tools:context=".csc.CSCActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".DfuActivity">
tools:context=".dfu.DfuActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".BPMActivity">
tools:context=".hrs.HRSActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".RSCActivity">
tools:context=".rsc.RSCActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".BPMActivity">
tools:context=".bpm.BPMActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -0,0 +1,225 @@
<!--
~ 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/toolbar_actionbar"
layout="@layout/toolbar"/>
<no.nordicsemi.android.nrftoolbox.widget.ForegroundRelativeLayout
style="@style/HeaderShadow"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The size of text below must be fixed, therefore dp are used instead of sp -->
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/cgms_feature_title_long_margin"
android:rotation="270"
android:text="@string/cgms_feature_title_long"
android:textColor="@color/darkGray"
android:textSize="32dp"
android:textStyle="bold"/>
<TextView
android:id="@+id/battery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="@dimen/feature_horizontal_margin"
android:layout_marginTop="@dimen/feature_device_name_margin_top"
android:background="@drawable/battery"
android:freezesText="true"
android:gravity="center"
android:text="@string/not_available"
android:textColor="#FFFFFF"
android:textSize="12sp"/>
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetTextView
android:id="@+id/device_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="@dimen/feature_device_name_margin_top"
android:layout_toRightOf="@+id/battery"
android:ellipsize="end"
android:freezesText="true"
android:maxLines="1"
android:text="@string/cgms_default_name"
android:textAllCaps="true"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/action_connect"
android:gravity="center_horizontal"
android:orientation="vertical">
<!-- Application section -->
<LinearLayout
style="@style/Widget.List"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginLeft="@dimen/feature_horizontal_margin"
android:layout_marginRight="@dimen/feature_horizontal_margin"
android:layout_marginTop="@dimen/feature_vertical_margin_top"
android:layout_weight="1"
android:orientation="vertical">
<RelativeLayout
style="@style/Widget.ListTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
style="@style/Widget.ListTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/gls_section_data"/>
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetTextView
android:id="@+id/unit"
style="@style/Widget.ListTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="4dp"/>
</RelativeLayout>
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"
tools:listitem="@layout/activity_feature_gls_item"/>
<TextView
android:id="@+id/empty"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/gls_no_data"/>
</LinearLayout>
<!-- Timestamp section -->
<LinearLayout
style="@style/Widget.List"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_vertical_margin_bottom"
android:layout_marginLeft="@dimen/feature_horizontal_margin"
android:layout_marginRight="@dimen/feature_horizontal_margin"
android:layout_marginTop="15dp"
android:orientation="vertical">
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
style="@style/Widget.ListTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/gls_section_actions"/>
<LinearLayout
android:id="@+id/cgms_control_std"
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/action_all"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="@string/gls_action_all"/>
<Button
android:id="@+id/action_last"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="@string/gls_action_last"/>
<Button
android:id="@+id/action_more"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="@string/gls_action_more"/>
</LinearLayout>
<LinearLayout
android:id="@+id/cgms_control_abort"
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<Button
android:id="@+id/action_abort"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="@string/gls_action_abort"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/action_connect"
style="@style/Widget.Connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/activity_vertical_margin_bottom"
android:onClick="onConnectClicked"
android:text="@string/action_connect"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="6dp"
android:src="@drawable/background_title"/>
</no.nordicsemi.android.nrftoolbox.widget.ForegroundRelativeLayout>
</LinearLayout>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightSmall"
android:paddingBottom="2dp"
android:paddingEnd="4dp"
android:paddingStart="?android:attr/listPreferredItemPaddingLeft"
android:paddingTop="2dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<TextView
android:id="@+id/details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="12sp"
android:text="@string/cgms_item_summary"/>
</LinearLayout>
<TextView
android:id="@+id/cgms_concentration"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_centerVertical="true"
android:gravity="right"
android:textAppearance="?android:attr/textAppearanceLarge"
android:paddingEnd="?android:attr/listPreferredItemPaddingLeft"/>
<TextView
android:id="@+id/cgms_unit"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/cgms_value_unit"/>
</LinearLayout>

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".CSCActivity">
tools:context=".csc.CSCActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".DfuActivity">
tools:context=".dfu.DfuActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".HRSActivity">
tools:context=".hrs.HRSActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".HTSActivity">
tools:context=".hts.HTSActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ProximityActivity">
tools:context=".proximity.ProximityActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".RSCActivity">
tools:context=".rsc.RSCActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".TemplateActivity">
tools:context=".template.TemplateActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".UARTActivity">
tools:context=".uart.UARTActivity">
<include
android:id="@+id/toolbar_actionbar"

View File

@@ -44,6 +44,13 @@
android:layout_height="0dp"
android:layout_weight="1">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:layout_marginBottom="6dp"
android:src="@drawable/background_title"/>
<GridView
android:id="@+id/grid"
android:layout_width="match_parent"
@@ -60,13 +67,6 @@
android:scrollbarStyle="outsideOverlay"
android:stretchMode="columnWidth"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:layout_marginBottom="6dp"
android:src="@drawable/background_title"/>
<no.nordicsemi.android.nrftoolbox.widget.TrebuchetBoldTextView
android:id="@android:id/empty"
android:layout_width="wrap_content"

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2016, 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.
-->
<resources>
<string name="cgms_feature_title_long">CGMS</string>
<string name="text_cgms_interval">GCM updates are notified every minute</string>
<string name="cgms_item_summary">Glucose concentration level</string>
<dimen name="cgms_feature_title_long_margin">-16dp</dimen>
</resources>

View File

@@ -37,7 +37,7 @@
<string name="action_select">SELECT DEVICE</string>
<string name="action_connecting">CONNECTING…</string>
<string name="action_disconnect">DISCONNECT</string>
<string name="drawer_open">Open</string>
<string name="drawer_close">Close</string>
<string name="drawer_plugins">PLUGINS</string>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2016, 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.
-->
<resources>
<string name="cgms_feature_title">CGMS</string>
<string name="cgms_feature_title_long">CONT. GLUCOSE MONITOR</string>
<dimen name="cgms_feature_title_long_margin">-180dp</dimen>
<string name="cgms_default_name">DEFAULT CGMS</string>
<string name="cgms_value_unit">mg/dL</string>
<string name="cgms_position_label">sensor position</string>
<string name="cgms_about_text">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.</string>
<string name="text_cgms_interval">GCM updates are notified every minute</string>
<string name="cgms_item_summary">Glucose concentration level</string>
</resources>

View File

@@ -9,8 +9,8 @@ android {
applicationId "no.nordicsemi.android.nrftoolbox"
minSdkVersion 20
targetSdkVersion 24
versionCode 45
versionName "1.17.0"
versionCode 47
versionName "1.18.0"
}
buildTypes {
release {