├── core
├── .gitignore
├── src
│ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── nightscout
│ │ │ └── core
│ │ │ ├── dexcom
│ │ │ ├── BatteryState.java
│ │ │ ├── CRCFailError.java
│ │ │ ├── InsertionState.java
│ │ │ ├── InvalidRecordLengthException.java
│ │ │ ├── RecordType.java
│ │ │ ├── Constants.java
│ │ │ ├── CRC16.java
│ │ │ ├── TrendArrow.java
│ │ │ ├── ReadPacket.java
│ │ │ ├── SpecialValue.java
│ │ │ ├── Command.java
│ │ │ ├── PacketBuilder.java
│ │ │ ├── records
│ │ │ │ ├── GlucoseDataSet.java
│ │ │ │ ├── GenericTimestampRecord.java
│ │ │ │ ├── CalSubrecord.java
│ │ │ │ ├── SensorRecord.java
│ │ │ │ ├── PageHeader.java
│ │ │ │ ├── MeterRecord.java
│ │ │ │ └── EGVRecord.java
│ │ │ └── Utils.java
│ │ │ ├── drivers
│ │ │ ├── AbstractUploaderDevice.java
│ │ │ ├── AbstractDevice.java
│ │ │ └── DeviceTransport.java
│ │ │ ├── barcode
│ │ │ ├── NSBarcodeConfigKeys.java
│ │ │ └── NSBarcodeConfig.java
│ │ │ ├── records
│ │ │ └── DeviceStatus.java
│ │ │ ├── model
│ │ │ ├── GlucoseUnit.java
│ │ │ ├── G4Noise.java
│ │ │ ├── ReceiverStatus.java
│ │ │ ├── DownloadStatus.java
│ │ │ ├── G4Trend.java
│ │ │ ├── DownloadResults.java
│ │ │ ├── ReceiverState.java
│ │ │ ├── Download.proto
│ │ │ ├── MeterEntry.java
│ │ │ ├── SensorEntry.java
│ │ │ ├── CalibrationEntry.java
│ │ │ └── SensorGlucoseValueEntry.java
│ │ │ ├── preferences
│ │ │ ├── NightscoutPreferences.java
│ │ │ └── TestPreferences.java
│ │ │ ├── utils
│ │ │ ├── GlucoseReading.java
│ │ │ └── RestUriUtils.java
│ │ │ └── upload
│ │ │ ├── AbstractRestUploader.java
│ │ │ ├── RestLegacyUploader.java
│ │ │ ├── BaseUploader.java
│ │ │ └── RestV1Uploader.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── nightscout
│ │ └── core
│ │ ├── dexcom
│ │ ├── CRC16Test.java
│ │ ├── SpecialValueTest.java
│ │ ├── ReadPacketTest.java
│ │ ├── SensorRecordTest.java
│ │ ├── UtilsTest.java
│ │ ├── EgvRecordTest.java
│ │ ├── MeterRecordTest.java
│ │ └── PacketBuilderTest.java
│ │ ├── utils
│ │ └── RestUriUtilsTest.java
│ │ └── upload
│ │ └── RestLegacyUploaderTest.java
└── build.gradle
├── settings.gradle
├── app
├── libs
│ └── acra-4.5.0.jar
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── battery15.png
│ │ │ │ ├── battery25.png
│ │ │ │ ├── battery50.png
│ │ │ │ ├── battery75.png
│ │ │ │ ├── batteryempty.png
│ │ │ │ ├── batteryfull.png
│ │ │ │ ├── ic_clock_bad.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_clock_good.png
│ │ │ │ ├── ic_upload_fail.png
│ │ │ │ ├── ic_upload_success.png
│ │ │ │ ├── ic_usb_connected.png
│ │ │ │ ├── ic_usb_disconnected.png
│ │ │ │ └── battery.xml
│ │ │ ├── drawable-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── integers.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── strings.xml
│ │ │ ├── xml
│ │ │ │ ├── analytics_global_config.xml
│ │ │ │ ├── app_tracker.xml
│ │ │ │ └── device_filter.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ ├── menu
│ │ │ │ └── menu.xml
│ │ │ └── values-it
│ │ │ │ └── strings.xml
│ │ ├── resources
│ │ │ └── android-logger.properties
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── nightscout
│ │ │ │ └── android
│ │ │ │ ├── settings
│ │ │ │ ├── SimpleDialogPreference.java
│ │ │ │ ├── SummaryBoundEditTextPreference.java
│ │ │ │ └── CustomSwitchPreference.java
│ │ │ │ ├── barcode
│ │ │ │ └── AndroidBarcode.java
│ │ │ │ ├── OnUpgradeReceiver.java
│ │ │ │ ├── ToastReceiver.java
│ │ │ │ ├── preferences
│ │ │ │ ├── PreferenceKeys.java
│ │ │ │ └── PreferencesValidator.java
│ │ │ │ ├── drivers
│ │ │ │ ├── AndroidUploaderDevice.java
│ │ │ │ └── USB
│ │ │ │ │ ├── USBPower.java
│ │ │ │ │ └── UsbSerialProber.java
│ │ │ │ └── Nightscout.java
│ │ ├── assets
│ │ │ ├── css
│ │ │ │ └── main.css
│ │ │ └── index.html
│ │ └── AndroidManifest.xml
│ └── test
│ │ └── java
│ │ └── com
│ │ └── nightscout
│ │ ├── robolectric
│ │ └── RobolectricGradleRunner.java
│ │ └── android
│ │ ├── wearables
│ │ └── WearableTest.java
│ │ ├── OnUpgradeReceiverTest.java
│ │ ├── test
│ │ └── RobolectricTestBase.java
│ │ ├── preferences
│ │ └── PreferencesValidatorTest.java
│ │ ├── upload
│ │ └── UploaderTest.java
│ │ └── settings
│ │ └── SettingsActivityTest.java
└── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── COPYRIGHT
├── .gitignore
├── ci
└── wait_for_emulator
├── .travis.yml
├── README.md
├── gradlew.bat
└── gradlew
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':core'
2 |
--------------------------------------------------------------------------------
/app/libs/acra-4.5.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/libs/acra-4.5.0.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/battery15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/battery15.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/battery25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/battery25.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/battery50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/battery50.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/battery75.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/battery75.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/batteryempty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/batteryempty.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/batteryfull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/batteryfull.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_clock_bad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/ic_clock_bad.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_clock_good.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/ic_clock_good.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_upload_fail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/ic_upload_fail.png
--------------------------------------------------------------------------------
/app/src/main/resources/android-logger.properties:
--------------------------------------------------------------------------------
1 | root=ERROR:com.nightscout.android:%logger{-2}:
2 | logger.com.nightscout=WARN:com.nightscout.android:%logger{-2}:
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_upload_success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/ic_upload_success.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_usb_connected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/ic_usb_connected.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_usb_disconnected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nightscout/android-uploader/HEAD/app/src/main/res/drawable-hdpi/ic_usb_disconnected.png
--------------------------------------------------------------------------------
/COPYRIGHT:
--------------------------------------------------------------------------------
1 |
2 | We track contributions on a per-patch basis using git.
3 | Please see our published git log:
4 | * https://github.com/nightscout/android-uploader/commits/master
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 60000
4 | 30000
5 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/BatteryState.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | public enum BatteryState {
4 | NONE,
5 | CHARGING,
6 | NOT_CHARGING,
7 | NTC_FAULT,
8 | BAD_BATTERY
9 | }
10 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/CRCFailError.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | public class CRCFailError extends Error {
4 | public CRCFailError(String message) {
5 | super(message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/analytics_global_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | warning
4 | 7200
5 | false
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/drivers/AbstractUploaderDevice.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.drivers;
2 |
3 | /**
4 | * Represents the device used to perform the uploads
5 | */
6 | abstract public class AbstractUploaderDevice {
7 | abstract public int getBatteryLevel();
8 | }
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Nov 18 09:12:09 PST 2014
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/InsertionState.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | public enum InsertionState {
4 | NONE,
5 | REMOVED,
6 | EXPIRED,
7 | RESIDUAL_DEVIATION,
8 | COUNTS_DEVIATION,
9 | SECOND_SESSION,
10 | OFF_TIME_LOSS,
11 | STARTED,
12 | BAD_TRANSMITTER,
13 | MANUFACTURING_MODE,
14 | MAX_VALUE
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/InvalidRecordLengthException.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | public class InvalidRecordLengthException extends Error {
4 | public InvalidRecordLengthException(String message){
5 | super(message);
6 | }
7 |
8 | public InvalidRecordLengthException(String message,Throwable throwable){
9 | super(message,throwable);
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/settings/SimpleDialogPreference.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.settings;
2 |
3 | import android.content.Context;
4 | import android.preference.DialogPreference;
5 | import android.util.AttributeSet;
6 |
7 | public class SimpleDialogPreference extends DialogPreference {
8 |
9 | public SimpleDialogPreference(Context context, AttributeSet attrs) {
10 | super(context, attrs);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/RecordType.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | public enum RecordType {
4 | MANUFACTURING_DATA,
5 | FIRMWARE_PARAMETER_DATA,
6 | PC_SOFTWARE_PARAMETER,
7 | SENSOR_DATA,
8 | EGV_DATA,
9 | CAL_SET,
10 | DEVIATION,
11 | INSERTION_TIME,
12 | RECEIVER_LOG_DATA,
13 | RECEIVER_ERROR_DATA,
14 | METER_DATA,
15 | USER_EVENT_DATA,
16 | USER_SETTING_DATA,
17 | MAX_VALUE
18 | }
19 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/barcode/NSBarcodeConfigKeys.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.barcode;
2 |
3 |
4 | public class NSBarcodeConfigKeys {
5 | public static final String MONGO_CONFIG="mongo";
6 | public static final String MONGO_URI="uri";
7 | public static final String API_CONFIG="rest";
8 | public static final String API_URI="endpoint";
9 | public static final String MONGO_COLLECTION="collection";
10 | public static final String MONGO_DEVICE_STATUS_COLLECTION="status";
11 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | gen
4 | # built application files
5 | *.apk
6 | *.ap_
7 |
8 | # files for the dex VM
9 | *.dex
10 |
11 | # Java class files
12 | *.class
13 |
14 | # generated files
15 | bin/
16 | gen/
17 | out
18 | logs
19 | .DS_Store
20 |
21 | # Ignore gradle files
22 | .gradle/
23 | build/
24 |
25 | # Local configuration file (sdk path, etc)
26 | local.properties
27 |
28 | # Proguard folder generated by Eclipse
29 | proguard/
30 |
31 | # Intellij project files
32 | *.iml
33 | *.ipr
34 | *.iws
35 | .idea/
36 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/records/DeviceStatus.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.records;
2 |
3 | public class DeviceStatus {
4 | private int batteryLevel;
5 |
6 | public DeviceStatus() {
7 | batteryLevel = -1;
8 | }
9 |
10 | public int getBatteryLevel() {
11 | return batteryLevel;
12 | }
13 |
14 | public void setBatteryLevel(int batteryLevel) {
15 | this.batteryLevel = batteryLevel;
16 | }
17 |
18 | public static DeviceStatus getCurrentStatus() {
19 | return null;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/barcode/AndroidBarcode.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.barcode;
2 |
3 | import android.app.Activity;
4 |
5 | import com.google.zxing.client.android.Intents;
6 | import com.google.zxing.integration.android.IntentIntegrator;
7 |
8 | public class AndroidBarcode {
9 | public static final String SCAN_INTENT = Intents.Scan.ACTION;
10 | Activity activity;
11 |
12 | public AndroidBarcode(Activity activity){
13 | this.activity = activity;
14 | }
15 |
16 | public void scan() {
17 | new IntentIntegrator(activity).initiateScan();
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/assets/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Open Sans', Helvetica, Arial, sans-serif;
3 | fill: #fff;
4 | background: #000;
5 | color: #808080;
6 | overflow: hidden;
7 | }
8 |
9 | .container {
10 | overflow: hidden;
11 | display: block;
12 | height: 95%;
13 | width: 100%;
14 | left: 0;
15 | margin: 0;
16 | padding: 0;
17 | top: 0px;
18 | z-index: 2;
19 | }
20 |
21 | .axis path,
22 | .axis line {
23 | fill: none;
24 | stroke: #ffffff;
25 | shape-rendering: crispEdges;
26 | }
27 |
28 | .grid path, .axis line {
29 | stroke: #808080;
30 | stroke-opacity: 1;
31 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/Constants.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | public class Constants {
4 | public final static int MAX_COMMAND = 59;
5 | public final static int MAX_POSSIBLE_COMMAND = 255;
6 | public final static int EGV_VALUE_MASK = 1023;
7 | public final static int EGV_DISPLAY_ONLY_MASK = 32768;
8 | public final static int EGV_TREND_ARROW_MASK = 15;
9 | public final static int EGV_NOISE_MASK = 112;
10 | public final static float MG_DL_TO_MMOL_L = 0.05556f;
11 | public final static int CRC_LEN = 2;
12 | public static final float MMOL_L_TO_MG_DL = 18.0182f;
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/app_tracker.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 300
5 |
6 |
7 | true
8 |
9 | false
10 |
11 |
12 | Main screen
13 |
14 |
15 | UA-54765135-1
16 |
17 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/GlucoseUnit.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.ProtoEnum;
6 |
7 | public enum GlucoseUnit
8 | implements ProtoEnum {
9 | MGDL(0),
10 | MMOL(1);
11 |
12 | private final int value;
13 |
14 | private GlucoseUnit(int value) {
15 | this.value = value;
16 | }
17 |
18 | @Override
19 | public int getValue() {
20 | return value;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/OnUpgradeReceiver.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | public class OnUpgradeReceiver extends BroadcastReceiver {
8 | @Override
9 | public void onReceive(final Context context, final Intent intent) {
10 | if (intent.getDataString().contains("com.nightscout.android")) {
11 | Intent mainActivity = new Intent(context, MainActivity.class);
12 | mainActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
13 | context.startActivity(mainActivity);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/dexcom/CRC16Test.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.hamcrest.Matchers.is;
6 | import static org.junit.Assert.assertThat;
7 |
8 |
9 | public class CRC16Test {
10 |
11 | @Test
12 | public void testCRC16() {
13 | // 01 07 00 10 04 8b b8
14 | byte[] testArray = new byte[]{(byte) 0x01, (byte) 0x07, (byte) 0x00,
15 | (byte) 0x10, (byte) 0x04};
16 | byte[] expectedCrc = new byte[]{(byte) 0x8b, (byte) 0xb8};
17 | byte[] calculatedCrc = CRC16.calculate(testArray, 0, testArray.length);
18 | assertThat(calculatedCrc, is(expectedCrc));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NightScout
6 |
7 |
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/CRC16.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | public final class CRC16 {
4 | public static byte[] calculate(byte[] buff, int start, int end) {
5 | int crcShort = 0;
6 | for (int i = start; i < end; i++) {
7 | crcShort = ((crcShort >>> 8) | (crcShort << 8) )& 0xffff;
8 | crcShort ^= (buff[i] & 0xff);
9 | crcShort ^= ((crcShort & 0xff) >> 4);
10 | crcShort ^= (crcShort << 12) & 0xffff;
11 | crcShort ^= ((crcShort & 0xFF) << 5) & 0xffff;
12 | }
13 | crcShort &= 0xffff;
14 | return new byte[] {(byte) (crcShort & 0xff), (byte) ((crcShort >> 8) & 0xff)};
15 | }
16 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/G4Noise.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.ProtoEnum;
6 |
7 | public enum G4Noise
8 | implements ProtoEnum {
9 | NOISE_NONE(0),
10 | CLEAN(1),
11 | LIGHT(2),
12 | MEDIUM(3),
13 | HEAVY(4),
14 | NOT_COMPUTED(5),
15 | MAX(6);
16 |
17 | private final int value;
18 |
19 | private G4Noise(int value) {
20 | this.value = value;
21 | }
22 |
23 | @Override
24 | public int getValue() {
25 | return value;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ci/wait_for_emulator:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Originally written by Ralf Kistner , but placed in the public domain
4 |
5 | set +e
6 |
7 | bootanim=""
8 | failcounter=0
9 | timeout_in_sec=360
10 |
11 | until [[ "$bootanim" =~ "stopped" ]]; do
12 | bootanim=`adb -e shell getprop init.svc.bootanim 2>&1 &`
13 | if [[ "$bootanim" =~ "device not found" || "$bootanim" =~ "device offline"
14 | || "$bootanim" =~ "running" ]]; then
15 | let "failcounter += 1"
16 | echo "Waiting for emulator to start"
17 | if [[ $failcounter -gt timeout_in_sec ]]; then
18 | echo "Timeout ($timeout_in_sec seconds) reached; failed to start emulator"
19 | exit 1
20 | fi
21 | fi
22 | sleep 1
23 | done
24 |
25 | echo "Emulator is ready"
26 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/ReceiverStatus.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.ProtoEnum;
6 |
7 | public enum ReceiverStatus
8 | implements ProtoEnum {
9 | RECEIVER_CONNECTED(0),
10 | /**
11 | * The receiver is connected to the uploader
12 | */
13 | RECEIVER_DISCONNECTED(1);
14 |
15 | private final int value;
16 |
17 | private ReceiverStatus(int value) {
18 | this.value = value;
19 | }
20 |
21 | @Override
22 | public int getValue() {
23 | return value;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/DownloadStatus.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.ProtoEnum;
6 |
7 | public enum DownloadStatus
8 | implements ProtoEnum {
9 | SUCCESS(0),
10 | NO_DATA(1),
11 | DEVICE_NOT_FOUND(2),
12 | IO_ERROR(3),
13 | APPLICATION_ERROR(4),
14 | UNKNOWN(5),
15 | NOT_APPLICABLE(6);
16 |
17 | private final int value;
18 |
19 | private DownloadStatus(int value) {
20 | this.value = value;
21 | }
22 |
23 | @Override
24 | public int getValue() {
25 | return value;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/dexcom/SpecialValueTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.hamcrest.Matchers.is;
6 | import static org.junit.Assert.assertThat;
7 |
8 | public class SpecialValueTest {
9 |
10 | @Test
11 | public void testIsSpecialValues() {
12 | int[] values = new int[]{0, 1, 2, 3, 5, 6, 9, 10, 12};
13 | for (int aValue : values) {
14 | assertThat(SpecialValue.isSpecialValue(aValue), is(true));
15 | }
16 | }
17 |
18 | @Test
19 | public void testIsNotSpecialValue() {
20 | int[] values = new int[]{11, 39, 100, 52, 250, 401, 72, 53, 80};
21 | for (int aValue : values) {
22 | assertThat(SpecialValue.isSpecialValue(aValue), is(false));
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/battery.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/device_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 |
3 | jdk:
4 | - oraclejdk7
5 |
6 | env:
7 | matrix:
8 | - ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a
9 |
10 | cache:
11 | directories:
12 | - $HOME/.gradle
13 |
14 | android:
15 | components:
16 | - build-tools-21.1.1
17 | - android-19
18 | - sysimg-19
19 | - extra-android-support
20 |
21 | licenses:
22 | - android-sdk-license-5be876d5
23 | - ".*intel.+"
24 |
25 | script:
26 | - TERM=dumb ./gradlew clean lint test jacocoTestReport
27 |
28 | deploy:
29 | provider: releases
30 | file: "./app/build/outputs/apk/app-debug.apk"
31 | skip_cleanup: true
32 | api_key:
33 | secure: Yv7qc91hNpvFZVjnl1fjA53SfH5kXyJPCabR6IIz2SbExDpstxStSQiUZydIgwrwtXtxliG8irhBdQSfYoYrHREbIZBCX0RkY1QxFpI7r3eLwIUW3SDjfvmbU7HwtYqHqG5gpr1qe81PEj5gFi49YsJYODw9WyaYiRqgE3JFUTs=
34 | on:
35 | repo: nightscout/android-uploader
36 | tags: true
37 | all_branches: true
38 |
39 | before_install:
40 | - sudo pip install codecov
41 |
42 | after_success:
43 | - cp app/build/jacoco/jacocoTestReport/*.xml jacoco.xml
44 | - cp app/build/jacoco/jacocoTestReport/*.xml jacoco2.xml
45 | - codecov
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/ToastReceiver.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.widget.Toast;
7 |
8 | /**
9 | * Listens to Intents from non-UI threads, and shows a Toast for messages from them.
10 | */
11 | public class ToastReceiver extends BroadcastReceiver {
12 | public static final String ACTION_SEND_NOTIFICATION = "nightscout_toast_intent";
13 | public static final String TOAST_MESSAGE = "nightscout_toast_message";
14 |
15 | @Override
16 | public void onReceive(Context context, Intent intent) {
17 | if (intent.getAction().equals(ACTION_SEND_NOTIFICATION)) {
18 | Toast.makeText(context, intent.getStringExtra(TOAST_MESSAGE), Toast.LENGTH_LONG).show();
19 | }
20 | }
21 |
22 | public static Intent createIntent(Context context, int localizedErrorMessage) {
23 | Intent intent = new Intent(ACTION_SEND_NOTIFICATION);
24 | intent.putExtra(TOAST_MESSAGE, context.getString(localizedErrorMessage));
25 | return intent;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/test/java/com/nightscout/robolectric/RobolectricGradleRunner.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.robolectric;
2 |
3 | import org.junit.runners.model.InitializationError;
4 | import org.robolectric.AndroidManifest;
5 | import org.robolectric.RobolectricTestRunner;
6 | import org.robolectric.annotation.Config;
7 | import org.robolectric.res.Fs;
8 |
9 | // Shamelessly stolen from:
10 | // http://blog.blundell-apps.com/android-gradle-app-with-robolectric-junit-tests/
11 | public class RobolectricGradleRunner extends RobolectricTestRunner {
12 | private static final int MAX_SDK_SUPPORTED_BY_ROBOLECTRIC = 18;
13 |
14 | public RobolectricGradleRunner(Class> testClass) throws InitializationError {
15 | super(testClass);
16 | }
17 |
18 | @Override
19 | protected AndroidManifest getAppManifest(Config config) {
20 | String manifestProperty = "src/main/AndroidManifest.xml";
21 | String resProperty = "src/main/res";
22 | return new AndroidManifest(Fs.fileFromPath(manifestProperty), Fs.fileFromPath(resProperty)) {
23 | @Override
24 | public int getTargetSdkVersion() {
25 | return MAX_SDK_SUPPORTED_BY_ROBOLECTRIC;
26 | }
27 | };
28 | }
29 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/drivers/AbstractDevice.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.drivers;
2 |
3 | import com.nightscout.core.model.DownloadResults;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | /**
9 | * This class is a representation for a device that we want information from e.g. pump or cgm
10 | */
11 | abstract public class AbstractDevice {
12 | protected final Logger log = LoggerFactory.getLogger(this.getClass());
13 | protected String deviceName = "Unknown";
14 | protected DeviceTransport transport;
15 |
16 | public AbstractDevice(DeviceTransport transport) {
17 | this.transport = transport;
18 | }
19 |
20 | public abstract boolean isConnected();
21 |
22 | // Not sure that I'll need this in the general device. This may be required for only push based
23 | // devices.
24 | protected void onDownload() {
25 | }
26 |
27 | public final DownloadResults download() {
28 | DownloadResults download = doDownload();
29 | onDownload();
30 | return download;
31 | }
32 |
33 | public String getDeviceName() {
34 | return deviceName;
35 | }
36 |
37 | abstract protected DownloadResults doDownload();
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/settings/SummaryBoundEditTextPreference.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.settings;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.preference.EditTextPreference;
7 | import android.util.AttributeSet;
8 |
9 | public class SummaryBoundEditTextPreference extends EditTextPreference {
10 |
11 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
12 | public SummaryBoundEditTextPreference(Context context,
13 | AttributeSet attrs,
14 | int defStyleAttr, int defStyleRes) {
15 | super(context, attrs, defStyleAttr, defStyleRes);
16 | }
17 |
18 | public SummaryBoundEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
19 | super(context, attrs, defStyleAttr);
20 | }
21 |
22 | public SummaryBoundEditTextPreference(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | public SummaryBoundEditTextPreference(Context context) {
27 | super(context);
28 | }
29 |
30 | @Override
31 | public void setText(String text) {
32 | super.setText(text);
33 | setSummary(text);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/preferences/PreferenceKeys.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.preferences;
2 |
3 | public final class PreferenceKeys {
4 | public static final String DATA_DONATE = "data_donate";
5 | public static final String API_UPLOADER_ENABLED = "cloud_storage_api_enable";
6 | public static final String API_URIS = "cloud_storage_api_base";
7 | public static final String CAL_UPLOAD_ENABLED = "cloud_cal_data";
8 | public static final String SENSOR_UPLOAD_ENABLED = "cloud_sensor_data";
9 | public static final String MONGO_UPLOADER_ENABLED = "cloud_storage_mongodb_enable";
10 | public static final String MONGO_URI = "cloud_storage_mongodb_uri";
11 | public static final String MONGO_COLLECTION = "cloud_storage_mongodb_collection";
12 | public static final String MONGO_DEVICE_STATUS_COLLECTION =
13 | "cloud_storage_mongodb_device_status_collection";
14 | public static final String ROOT_ENABLED = "root_support_enabled";
15 | public static final String I_UNDERSTAND = "i_understand";
16 | public static final String PREFERRED_UNITS = "display_options_units";
17 | public static final String PWD_NAME = "pwd_name";
18 | public static final String DONATE_DATA_QUERY = "donate_data_query";
19 | }
20 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/G4Trend.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.ProtoEnum;
6 |
7 | public enum G4Trend
8 | implements ProtoEnum {
9 | TREND_NONE(0),
10 | DOUBLE_UP(1),
11 | /**
12 | * More than 3 mg/dL per minute
13 | */
14 | SINGLE_UP(2),
15 | /**
16 | * +2 to +3 mg/dL per minute
17 | */
18 | FORTY_FIVE_UP(3),
19 | /**
20 | * +1 to +2 mg/dL per minute
21 | */
22 | FLAT(4),
23 | /**
24 | * +/- 1 mg/dL per minute
25 | */
26 | FORTY_FIVE_DOWN(5),
27 | /**
28 | * -1 to -2 mg/dL per minute
29 | */
30 | SINGLE_DOWN(6),
31 | /**
32 | * -2 to -3 mg/dL per minute
33 | */
34 | DOUBLE_DOWN(7),
35 | /**
36 | * more than -3 mg/dL per minute
37 | */
38 | NOT_COMPUTABLE(8),
39 | RATE_OUT_OF_RANGE(9);
40 |
41 | private final int value;
42 |
43 | private G4Trend(int value) {
44 | this.value = value;
45 | }
46 |
47 | @Override
48 | public int getValue() {
49 | return value;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/TrendArrow.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 |
4 | import com.nightscout.core.model.G4Trend;
5 |
6 | public enum TrendArrow {
7 | NONE,
8 | DOUBLE_UP("\u21C8", "DoubleUp"),
9 | SINGLE_UP("\u2191", "SingleUp"),
10 | UP_45("\u2197", "FortyFiveUp"),
11 | FLAT("\u2192", "Flat"),
12 | DOWN_45("\u2198", "FortyFiveDown"),
13 | SINGLE_DOWN("\u2193", "SingleDown"),
14 | DOUBLE_DOWN("\u21CA", "DoubleDown"),
15 | NOT_COMPUTABLE,
16 | OUT_OF_RANGE;
17 |
18 | private String arrowSymbol;
19 | private String trendName;
20 |
21 | TrendArrow(String arrowSymbol, String trendName) {
22 | this.arrowSymbol = arrowSymbol;
23 | this.trendName = trendName;
24 | }
25 |
26 | TrendArrow() {
27 | this(null, null);
28 | }
29 |
30 | public String symbol() {
31 | if (arrowSymbol == null) {
32 | return "\u2194";
33 | } else {
34 | return arrowSymbol;
35 | }
36 | }
37 |
38 | public String friendlyTrendName() {
39 | if (trendName == null) {
40 | return this.name().replace("_", " ");
41 | } else {
42 | return this.trendName;
43 | }
44 | }
45 |
46 | public G4Trend toProtobuf() {
47 | return G4Trend.values()[ordinal()];
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/DownloadResults.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.model;
2 |
3 | import org.json.JSONArray;
4 |
5 | public class DownloadResults {
6 | private G4Download download;
7 | private long nextUploadTime;
8 | private JSONArray resultArray;
9 | private long displayTime;
10 |
11 | public DownloadResults(G4Download download, long nextUploadTime,
12 | JSONArray resultArray, long displayTime) {
13 | this.download = download;
14 | this.nextUploadTime = nextUploadTime;
15 | this.resultArray = resultArray;
16 | this.displayTime = displayTime;
17 | }
18 |
19 | public void setDownload(G4Download download) {
20 | this.download = download;
21 | }
22 |
23 | public void setNextUploadTime(long nextUploadTime) {
24 | this.nextUploadTime = nextUploadTime;
25 | }
26 |
27 | public void setResultArray(JSONArray resultArray) {
28 | this.resultArray = resultArray;
29 | }
30 |
31 | public G4Download getDownload() {
32 | return download;
33 | }
34 |
35 | public JSONArray getResultArray() {
36 | return resultArray;
37 | }
38 |
39 | public long getDisplayTime() {
40 | return displayTime;
41 | }
42 |
43 | public long getNextUploadTime() {
44 | return nextUploadTime;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/ReadPacket.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import com.google.common.base.Optional;
4 |
5 | import java.util.Arrays;
6 |
7 | public class ReadPacket {
8 | private Command command;
9 | private byte[] data;
10 | private byte[] crc;
11 | private int OFFSET_CMD = 3;
12 | private int OFFSET_DATA = 4;
13 | private int CRC_LEN = 2;
14 |
15 | public ReadPacket(byte[] readPacket) {
16 | Optional optCmd = Command.getCommandByValue(readPacket[OFFSET_CMD]);
17 | if (optCmd.isPresent()) {
18 | this.command = optCmd.get();
19 | } else {
20 | throw new IllegalArgumentException("Unknown command: " + readPacket[OFFSET_CMD]);
21 | }
22 | this.data = Arrays.copyOfRange(readPacket, OFFSET_DATA, readPacket.length - CRC_LEN);
23 | this.crc = Arrays.copyOfRange(readPacket, readPacket.length - CRC_LEN, readPacket.length);
24 | byte[] crc_calc = CRC16.calculate(readPacket, 0, readPacket.length - 2);
25 | if (!Arrays.equals(this.crc, crc_calc)) {
26 | throw new CRCFailError("CRC check failed. Was: " + Utils.bytesToHex(this.crc) + " Expected: " + Utils.bytesToHex(crc_calc));
27 | }
28 | }
29 |
30 | public Command getCommand() {
31 | return command;
32 | }
33 |
34 | public byte[] getData() {
35 | return data;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/core/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 |
3 | sourceCompatibility = JavaVersion.VERSION_1_7
4 | targetCompatibility = JavaVersion.VERSION_1_7
5 |
6 | dependencies {
7 | compile 'joda-time:joda-time:2.5'
8 | compile 'com.google.guava:guava:18.0'
9 | compile 'org.slf4j:slf4j-simple:1.7.7'
10 | compile 'org.mongodb:mongo-java-driver:2.10.1'
11 | /** Android-provided **/
12 | // this is an old version of JSON, but it is the most compatible with the android version.
13 | // We exclude it in :app, because org.json is included in the adk.
14 | compile 'org.json:json:20090211'
15 | // as with JSON, this httpclient version is old, but hopefully matches what is in android.
16 | // We exclude it in :app
17 | compile 'org.apache.httpcomponents:httpclient:4.3'
18 | testCompile 'org.slf4j:slf4j-simple:1.7.7'
19 | testCompile 'junit:junit:4.11'
20 | testCompile 'org.hamcrest:hamcrest-library:1.3'
21 | testCompile 'org.mockito:mockito-core:1.9.5'
22 | compile 'com.squareup.wire:wire-runtime:1.6.0'
23 | }
24 |
25 | apply plugin: 'jacoco'
26 |
27 | jacoco {
28 | toolVersion = "0.7.1.201405082137"
29 | reportsDir = file("build/reports/jacoco/")
30 | }
31 |
32 | jacocoTestReport {
33 | additionalSourceDirs = files(sourceSets.main.allSource.srcDirs)
34 | sourceDirectories = files(sourceSets.main.allSource.srcDirs)
35 | classDirectories = files(sourceSets.main.output)
36 | reports {
37 | html.enabled = true
38 | xml.enabled = true
39 | csv.enabled = false
40 | }
41 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/SpecialValue.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import com.google.common.base.Optional;
4 | import com.nightscout.core.utils.GlucoseReading;
5 |
6 | public enum SpecialValue {
7 | NONE("??0", 0),
8 | SENSOR_NOT_ACTIVE("?SN", 1),
9 | MINIMALLY_EGV_AB("??2", 2),
10 | NO_ANTENNA("?NA", 3),
11 | SENSOR_OUT_OF_CAL("?NC", 5),
12 | COUNTS_AB("?CD", 6),
13 | ABSOLUTE_AB("?AD", 9),
14 | POWER_AB("???", 10),
15 | RF_BAD_STATUS("?RF", 12);
16 |
17 |
18 | private String name;
19 | private int val;
20 |
21 | SpecialValue(String s, int i) {
22 | name = s;
23 | val = i;
24 | }
25 |
26 | public int getValue() {
27 | return val;
28 | }
29 |
30 | public String toString() {
31 | return name;
32 | }
33 |
34 | public static Optional getEGVSpecialValue(int val) {
35 | for (SpecialValue e : values()) {
36 | if (e.getValue() == val)
37 | return Optional.of(e);
38 | }
39 | return Optional.absent();
40 | }
41 |
42 | public static Optional getEGVSpecialValue(GlucoseReading reading) {
43 | return getEGVSpecialValue(reading.asMgdl());
44 | }
45 |
46 | public static boolean isSpecialValue(GlucoseReading reading) {
47 | return isSpecialValue(reading.asMgdl());
48 | }
49 |
50 | public static boolean isSpecialValue(int val) {
51 | return getEGVSpecialValue(val).isPresent();
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/drivers/AndroidUploaderDevice.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.drivers;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 |
8 | import com.nightscout.core.drivers.AbstractUploaderDevice;
9 |
10 | public class AndroidUploaderDevice extends AbstractUploaderDevice {
11 | private int uploaderBattery;
12 | private Context context;
13 |
14 | private AndroidUploaderDevice(Context context) {
15 | IntentFilter deviceStatusFilter = new IntentFilter();
16 | deviceStatusFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
17 | this.context = context;
18 | this.context.registerReceiver(mDeviceStatusReceiver, deviceStatusFilter);
19 | }
20 |
21 | public int getBatteryLevel() {
22 | return uploaderBattery;
23 | }
24 |
25 | // TODO: This registers everytime. Need to fix
26 | public static AndroidUploaderDevice getUploaderDevice(Context context) {
27 | return new AndroidUploaderDevice(context);
28 | }
29 |
30 | public void close() {
31 | context.unregisterReceiver(mDeviceStatusReceiver);
32 | }
33 |
34 | BroadcastReceiver mDeviceStatusReceiver = new BroadcastReceiver() {
35 | public void onReceive(Context context, Intent intent) {
36 | String action = intent.getAction();
37 | if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
38 | uploaderBattery = intent.getIntExtra("level", 0);
39 | }
40 | }
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/test/java/com/nightscout/android/wearables/WearableTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.wearables;
2 |
3 | import android.content.Intent;
4 |
5 | import com.getpebble.android.kit.util.PebbleDictionary;
6 | import com.nightscout.android.MainActivity;
7 | import com.nightscout.android.test.RobolectricTestBase;
8 | import com.nightscout.core.dexcom.TrendArrow;
9 |
10 | import org.junit.Before;
11 | import org.junit.Test;
12 | import org.robolectric.Robolectric;
13 |
14 | import static org.hamcrest.Matchers.is;
15 | import static org.junit.Assert.assertThat;
16 |
17 | public class WearableTest extends RobolectricTestBase {
18 | MainActivity activity;
19 |
20 | @Before
21 | public void setUp() {
22 | activity = Robolectric.buildActivity(MainActivity.class).create().get();
23 | }
24 |
25 | @Test
26 | public void pebbleShouldCreateDataReceiver() {
27 | Pebble pebble = new Pebble(activity.getApplicationContext());
28 | Intent pebbleIntent = new Intent("com.getpebble.action.app.RECEIVE");
29 | assertThat(getShadowApplication().hasReceiverForIntent(pebbleIntent), is(true));
30 | }
31 |
32 | private PebbleDictionary createMockPebbleDictionary() {
33 | PebbleDictionary dict = new PebbleDictionary();
34 | dict.addString(0, String.valueOf(TrendArrow.FLAT.ordinal()));
35 | dict.addString(1, "100");
36 | dict.addUint32(2, 1417990743);
37 | dict.addUint32(3, 1417990743);
38 | dict.addString(4, "0");
39 | dict.addString(5, "100");
40 | dict.addString(6, "Bob");
41 | return dict;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/preferences/NightscoutPreferences.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.preferences;
2 |
3 | import com.nightscout.core.model.GlucoseUnit;
4 |
5 | import java.util.List;
6 |
7 | public interface NightscoutPreferences {
8 | boolean isRestApiEnabled();
9 |
10 | void setRestApiEnabled(boolean restApiEnabled);
11 |
12 | List getRestApiBaseUris();
13 |
14 | void setRestApiBaseUris(List restApis);
15 |
16 | boolean isCalibrationUploadEnabled();
17 |
18 | void setCalibrationUploadEnabled(boolean calibrationUploadEnabled);
19 |
20 | boolean isSensorUploadEnabled();
21 |
22 | void setSensorUploadEnabled(boolean sensorUploadEnabled);
23 |
24 | boolean isMongoUploadEnabled();
25 |
26 | void setMongoUploadEnabled(boolean mongoUploadEnabled);
27 |
28 | boolean isDataDonateEnabled();
29 |
30 | void setDataDonateEnabled(boolean toDonate);
31 |
32 | String getMongoClientUri();
33 |
34 | void setMongoClientUri(String client);
35 |
36 | String getMongoCollection();
37 |
38 | void setMongoCollection(String mongoCollection);
39 |
40 | String getMongoDeviceStatusCollection();
41 |
42 | void setMongoDeviceStatusCollection(String deviceStatusCollection);
43 |
44 | boolean getIUnderstand();
45 |
46 | void setIUnderstand(boolean bool);
47 |
48 | GlucoseUnit getPreferredUnits();
49 |
50 | void setPreferredUnits(GlucoseUnit units);
51 |
52 | String getPwdName();
53 |
54 | void setPwdName(String pwdName);
55 |
56 | boolean hasAskedForData();
57 |
58 | void setAskedForData(boolean askedForData);
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/test/java/com/nightscout/android/OnUpgradeReceiverTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android;
2 |
3 |
4 | import android.content.Intent;
5 | import android.net.Uri;
6 |
7 | import com.nightscout.android.test.RobolectricTestBase;
8 |
9 | import org.junit.Before;
10 | import org.junit.Test;
11 | import org.robolectric.Robolectric;
12 |
13 | import static org.hamcrest.Matchers.is;
14 | import static org.junit.Assert.assertThat;
15 |
16 | public class OnUpgradeReceiverTest extends RobolectricTestBase {
17 | MainActivity activity;
18 |
19 | @Before
20 | public void setUp() {
21 | activity = Robolectric.buildActivity(MainActivity.class).create().get();
22 | }
23 |
24 | @Test
25 | public void testOnCreate_ShouldHaveUpgradeReceiver() {
26 | Intent intent = new Intent(Intent.ACTION_PACKAGE_REPLACED);
27 | assertThat(getShadowApplication().hasReceiverForIntent(intent), is(true));
28 | }
29 |
30 | @Test
31 | public void testOnUpgradeReceiverRestartsMainActivity() {
32 | Intent intent = new Intent(Intent.ACTION_PACKAGE_REPLACED);
33 | Uri dataUri = Uri.parse("package:com.nightscout.android");
34 | intent.setData(dataUri);
35 | OnUpgradeReceiver onUpgradeReceiver = new OnUpgradeReceiver();
36 | onUpgradeReceiver.onReceive(activity.getApplicationContext(),intent);
37 | Intent anIntent = getShadowApplication().getNextStartedActivity();
38 | assertThat(anIntent.getComponent().getClassName(), is(MainActivity.class.getName()));
39 | }
40 |
41 | //TODO: need a test to make sure that activity is not started if another package is replaced
42 | }
43 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/utils/GlucoseReading.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.utils;
2 |
3 | import com.nightscout.core.dexcom.Constants;
4 | import com.nightscout.core.model.GlucoseUnit;
5 |
6 | public class GlucoseReading {
7 | private int valueMgdl;
8 |
9 | public GlucoseReading(float value, GlucoseUnit units) {
10 | this.valueMgdl = (units == GlucoseUnit.MGDL) ?
11 | Math.round(value) : Math.round(value * Constants.MMOL_L_TO_MG_DL);
12 | }
13 |
14 | public float asMmol() {
15 | return valueMgdl * Constants.MG_DL_TO_MMOL_L;
16 | }
17 |
18 | public String asMmolStr() {
19 | return String.format("%.1f", asMmol());
20 | }
21 |
22 | public int asMgdl() {
23 | return valueMgdl;
24 | }
25 |
26 | public String asMgdlStr() {
27 | return String.valueOf(valueMgdl);
28 | }
29 |
30 | public float as(GlucoseUnit units) {
31 | return (units == GlucoseUnit.MGDL) ? asMgdl() : asMmol();
32 | }
33 |
34 | public String asStr(GlucoseUnit units) {
35 | return (units == GlucoseUnit.MGDL) ? asMgdlStr() : asMmolStr();
36 | }
37 |
38 | public GlucoseReading subtract(GlucoseReading reading) {
39 | return new GlucoseReading(valueMgdl - reading.asMgdl(), GlucoseUnit.MGDL);
40 | }
41 |
42 | @Override
43 | public boolean equals(Object o) {
44 | if (this == o) return true;
45 | if (o == null || getClass() != o.getClass()) return false;
46 |
47 | GlucoseReading that = (GlucoseReading) o;
48 |
49 | if (valueMgdl != that.valueMgdl) return false;
50 |
51 | return true;
52 | }
53 |
54 | @Override
55 | public int hashCode() {
56 | return valueMgdl;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | android-uploader
2 | ====================
3 | [](https://travis-ci.org/nightscout/android-uploader?branch=dev)
4 | [](https://codecov.io/github/nightscout/android-uploader?branch=dev)
5 | [](https://gitter.im/nightscout/public)
6 |
7 |
8 |
9 |
10 | Android Uploader for the Nightscout Project.
11 |
12 |
13 | ## [License - GPL V3](gpl-v3)
14 | [gpl-3]: http://www.gnu.org/licenses/gpl-3.0.txt
15 |
16 | android-uploader - Nightscout's open source MDDS CGM uploader and archiver
17 | Copyright (C) 2014 Nightscout contributors. See the COPYRIGHT file
18 | at the root directory of this distribution and at
19 | https://github.com/nightscout/android-uploader/blob/master/COPYRIGHT
20 |
21 | This program is free software: you can redistribute it and/or modify
22 | it under the terms of the GNU General Public License as published by
23 | the Free Software Foundation, either version 3 of the License, or
24 | (at your option) any later version.
25 |
26 | This program is distributed in the hope that it will be useful,
27 | but WITHOUT ANY WARRANTY; without even the implied warranty of
28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 | GNU General Public License for more details.
30 |
31 | You should have received a copy of the GNU General Public License
32 | along with this program. If not, see .
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/drivers/USB/USBPower.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.drivers.USB;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.DataOutputStream;
6 |
7 | public class USBPower {
8 |
9 | private static final String TAG = USBPower.class.getSimpleName();
10 |
11 | private static final String SET_POWER_ON_COMMAND = "echo 'on' > \"/sys/bus/usb/devices/1-1/power/control\"";
12 | private static final String SET_POWER_SUSPEND_COMMAND_A = "echo \"0\" > \"/sys/bus/usb/devices/1-1/power/autosuspend_delay_ms\"";
13 | private static final String SET_POWER_SUSPEND_COMMAND_B = "echo \"auto\" > \"/sys/bus/usb/devices/1-1/power/control\"";
14 | public static final int POWER_ON_DELAY = 5000;
15 |
16 | public static void powerOff() {
17 | try {
18 | runCommand(SET_POWER_SUSPEND_COMMAND_A);
19 | runCommand(SET_POWER_SUSPEND_COMMAND_B);
20 | Log.i(TAG, "powerOff USB complete");
21 | } catch (Exception e) {
22 | Log.e(TAG, "Unable to powerOff USB");
23 | }
24 | }
25 |
26 | public static void powerOn() {
27 | try {
28 | runCommand(SET_POWER_ON_COMMAND);
29 | Log.i(TAG, "powerOn USB complete");
30 | Thread.sleep(POWER_ON_DELAY);
31 | } catch (Exception e) {
32 | Log.e(TAG, "Unable to powerOn USB");
33 | }
34 | }
35 |
36 | private static void runCommand(String command) throws Exception {
37 | Process process = Runtime.getRuntime().exec("su");
38 | DataOutputStream os = new DataOutputStream(process.getOutputStream());
39 | os.writeBytes(command + "\n");
40 | os.flush();
41 | os.writeBytes("exit \n");
42 | os.flush();
43 | os.close();
44 | process.waitFor();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/utils/RestUriUtils.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.utils;
2 |
3 | import com.google.common.base.Charsets;
4 | import com.google.common.base.Splitter;
5 | import com.google.common.base.Strings;
6 | import com.google.common.hash.Hashing;
7 |
8 | import java.net.URI;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | import static com.google.common.base.Preconditions.checkArgument;
13 | import static com.google.common.base.Preconditions.checkNotNull;
14 |
15 | public class RestUriUtils {
16 | public static boolean isV1Uri(URI uri) {
17 | return uri != null && (uri.getPath().endsWith("v1") || uri.getPath().endsWith("v1/"));
18 | }
19 |
20 | public static boolean hasToken(URI uri) {
21 | return !Strings.isNullOrEmpty(uri.getUserInfo());
22 | }
23 |
24 | /**
25 | * Removes the token from the uri.
26 | * @param uri Non-null uri to strip the token from.
27 | * @return uri without token.
28 | */
29 | public static URI removeToken(URI uri) {
30 | checkNotNull(uri);
31 | // This is gross, but I don't know a better way to do it.
32 | return URI.create(uri.toString().replaceFirst("//[^@]+@", "//"));
33 | }
34 |
35 | /**
36 | * Generates a secret from the given token.
37 | * @param secret Non-null, non-empty secret to generate the token from.
38 | * @return The generated token.
39 | */
40 | public static String generateSecret(String secret) {
41 | checkArgument(!Strings.isNullOrEmpty(secret));
42 | return Hashing.sha1().hashBytes(secret.getBytes(Charsets.UTF_8)).toString();
43 | }
44 |
45 | public static List splitIntoMultipleUris(String combinedUris) {
46 | if (Strings.isNullOrEmpty(combinedUris)) {
47 | return new ArrayList<>();
48 | }
49 | return Splitter.onPattern("\\s+").splitToList(combinedUris.trim());
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/dexcom/ReadPacketTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import com.google.common.primitives.UnsignedBytes;
4 |
5 | import org.junit.Test;
6 |
7 | import static org.hamcrest.Matchers.is;
8 | import static org.junit.Assert.assertThat;
9 | import static org.junit.Assert.fail;
10 |
11 | public class ReadPacketTest {
12 |
13 | byte[] testPacket = new byte[]{
14 | /** HEADER **/0x1, 0x1, 0x1,
15 | /** COMMAND **/0x5,
16 | /** DATA **/0x10, 0x15,
17 | /** CRC */0x52, 0x33
18 | };
19 |
20 | byte[] testPacketNoData = new byte[]{
21 | /** HEADER **/0x1, 0x1, 0x1,
22 | /** COMMAND **/0x1A,
23 | /** CRC **/UnsignedBytes.checkedCast(0xCE), UnsignedBytes.checkedCast(0xC1)
24 | };
25 |
26 | byte[] testPacketBadCrc = new byte[]{
27 | /** HEADER **/0x1, 0x1, 0x1,
28 | /** COMMAND **/0x1A,
29 | /** CRC **/UnsignedBytes.checkedCast(0xCE), UnsignedBytes.checkedCast(0xC0)
30 | };
31 |
32 | @Test
33 | public void testReadPacket_command() {
34 | assertThat(new ReadPacket(testPacket).getCommand().getValue(), is((byte) 0x05));
35 | }
36 |
37 | @Test
38 | public void testReadPacket_data() {
39 | assertThat(new ReadPacket(testPacket).getData(), is(new byte[]{0x10, 0x15}));
40 | }
41 |
42 | @Test
43 | public void testReadPacket_noDataPacket_command() {
44 | assertThat(new ReadPacket(testPacketNoData).getCommand().getValue(), is((byte) 0x1A));
45 | }
46 |
47 | @Test
48 | public void testReadPacket_noDataPacket_emptyData() {
49 | assertThat(new ReadPacket(testPacketNoData).getData(), is(new byte[]{}));
50 | }
51 |
52 | @Test
53 | public void testReadPacket_badCrc() throws Exception {
54 | try {
55 | new ReadPacket(testPacketBadCrc);
56 | fail("Should receive CRC error");
57 | } catch (CRCFailError error) {
58 | // nom
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/test/java/com/nightscout/android/test/RobolectricTestBase.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.test;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 |
8 | import com.google.android.gms.analytics.GoogleAnalytics;
9 | import com.google.common.base.Function;
10 | import com.nightscout.robolectric.RobolectricGradleRunner;
11 |
12 | import org.junit.Before;
13 | import org.junit.runner.RunWith;
14 | import org.robolectric.Robolectric;
15 | import org.robolectric.shadows.ShadowApplication;
16 |
17 | import static org.hamcrest.Matchers.is;
18 | import static org.junit.Assert.assertThat;
19 |
20 | @RunWith(RobolectricGradleRunner.class)
21 | public class RobolectricTestBase {
22 | private final boolean[] intentSeen = {false};
23 |
24 | @Before
25 | public final void setUpBase() {
26 | // NPEs happen when using Robolectric + GA for some reason. Disable them for now.
27 | // https://github.com/robolectric/robolectric/issues/1075
28 | getShadowApplication().declareActionUnbindable("com.google.android.gms.analytics.service.START");
29 | GoogleAnalytics.getInstance(getContext()).setAppOptOut(true);
30 | }
31 |
32 | public void whenOnBroadcastReceived(String intentKey, final Function verifyCallback) {
33 | getShadowApplication().registerReceiver(new BroadcastReceiver() {
34 | @Override
35 | public void onReceive(Context context, Intent intent) {
36 | intentSeen[0] = true;
37 | verifyCallback.apply(intent);
38 | }
39 | }, new IntentFilter(intentKey));
40 | }
41 |
42 | public void assertIntentSeen() {
43 | assertThat(intentSeen[0], is(true));
44 | }
45 |
46 | public Context getContext() {
47 | return getShadowApplication().getApplicationContext();
48 | }
49 |
50 | public ShadowApplication getShadowApplication() {
51 | return Robolectric.getShadowApplication();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/Nightscout.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android;
2 |
3 | import android.app.Application;
4 | import android.util.Log;
5 |
6 | import com.google.android.gms.analytics.GoogleAnalytics;
7 | import com.google.android.gms.analytics.Logger;
8 | import com.google.android.gms.analytics.Tracker;
9 |
10 | import net.danlew.android.joda.JodaTimeAndroid;
11 |
12 | import org.acra.ACRA;
13 | import org.acra.ReportingInteractionMode;
14 | import org.acra.annotation.ReportsCrashes;
15 |
16 | @ReportsCrashes(
17 | formUri = "https://collector.tracepot.com/a64e4a51",
18 | resToastText = R.string.crash_toast_text,
19 | resDialogText = R.string.feebback_dialog_text,
20 | resDialogIcon = R.drawable.ic_launcher,
21 | resDialogTitle = R.string.feedback_dialog_title,
22 | resDialogCommentPrompt = R.string.feedback_dialog_comment_prompt,
23 | resDialogOkToast = R.string.feedback_dialog_ok_toast,
24 | excludeMatchingSharedPreferencesKeys = {"cloud_storage_mongodb_uri", "cloud_storage_api_base"},
25 | mode = ReportingInteractionMode.TOAST,
26 | logcatArguments = {"-t", "500", "-v", "time"}
27 | )
28 | public class Nightscout extends Application {
29 | private final String TAG = MainActivity.class.getSimpleName();
30 | private Tracker tracker = null;
31 |
32 | @Override
33 | public void onCreate() {
34 | super.onCreate();
35 | ACRA.init(this);
36 | JodaTimeAndroid.init(this);
37 | }
38 |
39 | synchronized public Tracker getTracker() {
40 | Log.d(TAG, "getTracker called");
41 | if (tracker == null) {
42 | Log.d(TAG, "tracker was null - returning new tracker");
43 | GoogleAnalytics analytics = GoogleAnalytics.getInstance(this);
44 | analytics.setDryRun(false);
45 | analytics.getLogger().setLogLevel(Logger.LogLevel.WARNING);
46 | analytics.setLocalDispatchPeriod(7200);
47 | tracker = analytics.newTracker(R.xml.app_tracker);
48 | return tracker;
49 | }
50 | return tracker;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/Command.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import com.google.common.base.CaseFormat;
4 | import com.google.common.base.Optional;
5 |
6 | public enum Command {
7 | NULL(0),
8 | ACK(1),
9 | NAK(2),
10 | INVALID_COMMAND(3),
11 | INVALID_PARAM(4),
12 | INCOMPLETE_PACKET_RECEIVED(5),
13 | RECEIVER_ERROR(6),
14 | INVALID_MODE(7),
15 | PING(10),
16 | READ_FIRMWARE_HEADER(11),
17 | READ_DATABASE_PARTITION_INFO(15),
18 | READ_DATABASE_PAGE_RANGE(16),
19 | READ_DATABASE_PAGES(17),
20 | READ_DATABASE_PAGE_HEADER(18),
21 | READ_TRANSMITTER_ID(25),
22 | WRITE_TRANSMITTER_ID(26),
23 | READ_LANGUAGE(27),
24 | WRITE_LANGUAGE(28),
25 | READ_DISPLAY_TIME_OFFSET(29),
26 | WRITE_DISPLAY_TIME_OFFSET(30),
27 | READ_RTC(31),
28 | RESET_RECEIVER(32),
29 | READ_BATTERY_LEVEL(33),
30 | READ_SYSTEM_TIME(34),
31 | READ_SYSTEM_TIME_OFFSET(35),
32 | WRITE_SYSTEM_TIME(36),
33 | READ_GLUCOSE_UNIT(37),
34 | WRITE_GLUCOSE_UNIT(38),
35 | READ_BLINDED_MODE(39),
36 | WRITE_BLINDED_MODE(40),
37 | READ_CLOCK_MODE(41),
38 | WRITE_CLOCK_MODE(42),
39 | READ_DEVICE_MODE(43),
40 | ERASE_DATABASE(45),
41 | SHUTDOWN_RECEIVER(46),
42 | WRITE_PC_PARAMETERS(47),
43 | READ_BATTERY_STATE(48),
44 | READ_HARDWARE_BOARD_ID(49),
45 | READ_FIRMWARE_SETTINGS(54),
46 | READ_ENABLE_SETUP_WIZARD_FLAG(55),
47 | READ_SETUP_WIZARD_STATE(57);
48 |
49 | private byte value;
50 |
51 | Command(int command){
52 | value = (byte) command;
53 | }
54 |
55 | public byte getValue() {
56 | return value;
57 | }
58 |
59 | public String toString(){
60 | return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL,this.name()).replace("_", " ");
61 | }
62 |
63 | public static Optional getCommandByValue(int value){
64 | for(Command command:values()){
65 | if (command.getValue() == value){
66 | return Optional.of(command);
67 | }
68 | }
69 | return Optional.absent();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/drivers/DeviceTransport.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.drivers;
2 |
3 | import java.io.IOException;
4 |
5 | public interface DeviceTransport {
6 | /**
7 | * Opens and initializes the device as a USB serial device. Upon success,
8 | * caller must ensure that {@link #close()} is eventually called.
9 | *
10 | * @throws java.io.IOException on error opening or initializing the device.
11 | */
12 | public void open() throws IOException;
13 |
14 | /**
15 | * Closes the serial device.
16 | *
17 | * @throws java.io.IOException on error closing the device.
18 | */
19 | public void close() throws IOException;
20 |
21 | /**
22 | * Reads as many bytes as possible into the destination buffer.
23 | *
24 | * @param dest the destination byte buffer
25 | * @param timeoutMillis the timeout for reading
26 | * @return the actual number of bytes read
27 | * @throws java.io.IOException if an error occurred during reading
28 | */
29 | public int read(final byte[] dest, final int timeoutMillis) throws IOException;
30 |
31 | /**
32 | * Reads as many bytes as possible into the destination buffer.
33 | *
34 | * @param size size to read
35 | * @param timeoutMillis the timeout for reading
36 | * @return the actual number of bytes read
37 | * @throws java.io.IOException if an error occurred during reading
38 | */
39 | public byte[] read(int size, final int timeoutMillis) throws IOException;
40 |
41 | /**
42 | * Writes as many bytes as possible from the source buffer.
43 | *
44 | * @param src the source byte buffer
45 | * @param timeoutMillis the timeout for writing
46 | * @return the actual number of bytes written
47 | * @throws java.io.IOException if an error occurred during writing
48 | */
49 | public int write(final byte[] src, final int timeoutMillis) throws IOException;
50 |
51 | public boolean isConnected(int vendorId, int productId, int deviceClass, int subClass,
52 | int protocol);
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/upload/AbstractRestUploader.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.upload;
2 |
3 | import com.google.common.base.Joiner;
4 | import com.nightscout.core.preferences.NightscoutPreferences;
5 |
6 | import org.apache.http.HttpResponse;
7 | import org.apache.http.client.HttpClient;
8 | import org.apache.http.client.methods.HttpPost;
9 | import org.apache.http.entity.StringEntity;
10 | import org.apache.http.impl.client.DefaultHttpClient;
11 | import org.apache.http.message.AbstractHttpMessage;
12 | import org.json.JSONObject;
13 |
14 | import java.io.IOException;
15 | import java.net.URI;
16 |
17 | import static com.google.common.base.Preconditions.checkNotNull;
18 |
19 | public abstract class AbstractRestUploader extends BaseUploader {
20 | private final URI uri;
21 | private HttpClient client;
22 |
23 | public AbstractRestUploader(NightscoutPreferences preferences, URI baseUri) {
24 | super(preferences);
25 | checkNotNull(baseUri);
26 | this.uri = baseUri;
27 | }
28 |
29 | protected void setExtraHeaders(AbstractHttpMessage httpMessage) { }
30 |
31 | public URI getUri() {
32 | return uri;
33 | }
34 |
35 | public HttpClient getClient() {
36 | if (client != null) {
37 | return client;
38 | }
39 | client = new DefaultHttpClient();
40 | return client;
41 | }
42 |
43 | public void setClient(HttpClient client) {
44 | this.client = client;
45 | }
46 |
47 | protected boolean doPost(String endpoint, JSONObject jsonObject) throws IOException {
48 | HttpPost httpPost = new HttpPost(Joiner.on('/').join(uri.toString(), endpoint));
49 | httpPost.addHeader("Content-Type", "application/json");
50 | httpPost.addHeader("Accept", "application/json");
51 | setExtraHeaders(httpPost);
52 | httpPost.setEntity(new StringEntity(jsonObject.toString()));
53 | HttpResponse response = getClient().execute(httpPost);
54 | int statusCodeFamily = response.getStatusLine().getStatusCode() / 100;
55 | response.getEntity().consumeContent();
56 | return statusCodeFamily == 2;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/preferences/PreferencesValidator.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.preferences;
2 |
3 | import android.content.Context;
4 |
5 | import com.google.common.base.Optional;
6 | import com.google.common.base.Strings;
7 | import com.mongodb.MongoClientURI;
8 | import com.nightscout.android.R;
9 | import com.nightscout.core.utils.RestUriUtils;
10 |
11 | import java.net.URI;
12 |
13 | public class PreferencesValidator {
14 | /**
15 | * Validate the syntax of the given mongo uri.
16 | * @param mongoUriString String to validated.
17 | * @return Optional localized validation error, if one occurs.
18 | */
19 | public static Optional validateMongoUriSyntax(Context context, String mongoUriString) {
20 | try {
21 | new MongoClientURI(mongoUriString);
22 | } catch (IllegalArgumentException e) {
23 | return Optional.of(context.getString(R.string.illegal_mongo_uri));
24 | }
25 | return Optional.absent();
26 | }
27 |
28 | /**
29 | * Validate the syntax of a single rest api uri. Can be either legacy or v1 format.
30 | * @param restApiUri Uri to validate.
31 | * @return Localized validation error, if one occurs.
32 | */
33 | public static Optional validateRestApiUriSyntax(Context context, String restApiUri) {
34 | if (Strings.isNullOrEmpty(restApiUri)) {
35 | return Optional.of(context.getString(R.string.invalid_rest_uri, restApiUri));
36 | }
37 | URI uri;
38 | try {
39 | uri = URI.create(restApiUri);
40 | } catch (NullPointerException e) {
41 | return Optional.of(context.getString(R.string.invalid_rest_uri, restApiUri));
42 | } catch (IllegalArgumentException e) {
43 | return Optional.of(context.getString(R.string.invalid_rest_uri, restApiUri));
44 | }
45 | if (RestUriUtils.isV1Uri(uri)) {
46 | if (!RestUriUtils.hasToken(uri)) {
47 | return Optional.of(context.getString(R.string.rest_uri_missing_token, restApiUri));
48 | }
49 | }
50 | return Optional.absent();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/upload/RestLegacyUploader.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.upload;
2 |
3 | import com.nightscout.core.dexcom.records.GlucoseDataSet;
4 | import com.nightscout.core.drivers.AbstractUploaderDevice;
5 | import com.nightscout.core.preferences.NightscoutPreferences;
6 |
7 | import org.json.JSONException;
8 | import org.json.JSONObject;
9 |
10 | import java.io.IOException;
11 | import java.net.URI;
12 |
13 | public class RestLegacyUploader extends AbstractRestUploader {
14 |
15 | public RestLegacyUploader(NightscoutPreferences preferences, URI uri) {
16 | super(preferences, uri);
17 | }
18 |
19 | private JSONObject toJSONObject(GlucoseDataSet record) throws JSONException {
20 | JSONObject json = new JSONObject();
21 | json.put("device", "dexcom");
22 | json.put("date", record.getDisplayTime().getTime());
23 | json.put("dateString", record.getDisplayTime().toString());
24 | json.put("sgv", Integer.parseInt(String.valueOf(record.getBgMgdl())));
25 | json.put("direction", record.getTrend().friendlyTrendName());
26 | return json;
27 | }
28 |
29 | private JSONObject toJSONObject(AbstractUploaderDevice deviceStatus) throws JSONException {
30 | JSONObject json = new JSONObject();
31 | json.put("uploaderBattery", deviceStatus.getBatteryLevel());
32 | return json;
33 | }
34 |
35 | @Override
36 | protected boolean doUpload(GlucoseDataSet glucoseDataSet) throws IOException {
37 | try {
38 | return doPost("entries", toJSONObject(glucoseDataSet));
39 | } catch (JSONException e) {
40 | log.error("Could not create JSON object for legacy rest glucose data set.", e);
41 | return false;
42 | }
43 | }
44 |
45 | // TODO(trhodeos): is devicestatus supported in legacy apis?
46 | @Override
47 | protected boolean doUpload(AbstractUploaderDevice deviceStatus) throws IOException {
48 | try {
49 | return doPost("devicestatus", toJSONObject(deviceStatus));
50 | } catch (JSONException e) {
51 | log.error("Could not create JSON object for legacy rest device status.", e);
52 | return false;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/PacketBuilder.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import com.google.common.primitives.Bytes;
4 |
5 | import java.util.ArrayList;
6 |
7 | public class PacketBuilder {
8 | public static final int MAX_PAYLOAD = 1584;
9 | public static final int MIN_LEN = 6;
10 | public static final int MAX_LEN = MAX_PAYLOAD + MIN_LEN;
11 | public static final byte SOF = 0x01;
12 | public static final int OFFSET_SOF = 0;
13 | public static final int OFFSET_LENGTH = 1;
14 | public static final int OFFSET_NULL = 2;
15 | public static final byte NULL = 0x00;
16 | public static final int OFFSET_CMD = 3;
17 | public static final int OFFSET_PAYLOAD = 4;
18 | public static final int CRC_LEN = 2;
19 | public static final int HEADER_LEN = 4;
20 | private ArrayList packet;
21 | private Command command;
22 | private ArrayList payload;
23 |
24 | public PacketBuilder(Command command){
25 | this.command = command;
26 | }
27 |
28 | public PacketBuilder(Command command, ArrayList payload) {
29 | this.command = command;
30 | this.payload = payload;
31 | }
32 |
33 | public byte[] build() {
34 | packet = new ArrayList<>();
35 | packet.add(OFFSET_SOF, SOF);
36 | packet.add(OFFSET_LENGTH, getLength());
37 | packet.add(OFFSET_NULL, NULL);
38 | packet.add(OFFSET_CMD, (byte) command.getValue());
39 | if (this.payload != null) {
40 | this.packet.addAll(OFFSET_PAYLOAD, this.payload);
41 | }
42 | byte[] crc16 = CRC16.calculate(toBytes(), 0, this.packet.size());
43 | this.packet.add(crc16[0]);
44 | this.packet.add(crc16[1]);
45 | return this.toBytes();
46 | }
47 |
48 | private byte getLength() {
49 | int packetSize = payload == null ? MIN_LEN : payload.size() + CRC_LEN + HEADER_LEN;
50 |
51 | if (packetSize > MAX_LEN) {
52 | throw new IndexOutOfBoundsException(packetSize + " bytes, but packet must between "
53 | + MIN_LEN + " and " + MAX_LEN + " bytes.");
54 | }
55 |
56 | return (byte) packetSize;
57 | }
58 |
59 | private byte[] toBytes() {
60 | return Bytes.toArray(this.packet);
61 | }
62 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/nightscout/android/preferences/PreferencesValidatorTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.preferences;
2 |
3 | import com.nightscout.android.R;
4 | import com.nightscout.android.test.RobolectricTestBase;
5 |
6 | import org.junit.Test;
7 |
8 | import static org.hamcrest.Matchers.is;
9 | import static org.junit.Assert.assertThat;
10 |
11 | public class PreferencesValidatorTest extends RobolectricTestBase {
12 | @Test
13 | public void testValidateMongoUriSyntax_Empty() {
14 | assertThat(PreferencesValidator.validateMongoUriSyntax(getContext(), "").get(),
15 | is(getContext().getString(R.string.illegal_mongo_uri, "")));
16 | }
17 |
18 | @Test
19 | public void testValidateMongoUriSyntax_Invalid() {
20 | assertThat(PreferencesValidator.validateMongoUriSyntax(getContext(), "test/db").get(),
21 | is(getContext().getString(R.string.illegal_mongo_uri, "test/db")));
22 | }
23 |
24 | @Test
25 | public void testValidateMongoUriSyntax_Valid() {
26 | assertThat(PreferencesValidator.validateMongoUriSyntax(getContext(), "mongodb://test/db")
27 | .isPresent(),
28 | is(false));
29 | }
30 |
31 | @Test
32 | public void testValidateRestApiUriSyntax_Empty() {
33 | assertThat(PreferencesValidator.validateRestApiUriSyntax(getContext(), "").get(),
34 | is(getContext().getString(R.string.invalid_rest_uri, "")));
35 | }
36 |
37 | @Test
38 | public void testValidateRestApiUriSyntax_Invalid() {
39 | assertThat(PreferencesValidator.validateRestApiUriSyntax(getContext(), "\\invalid").get(),
40 | is(getContext().getString(R.string.invalid_rest_uri, "\\invalid")));
41 | }
42 |
43 | @Test
44 | public void testValidateRestApiUriSyntax_Valid() {
45 | assertThat(PreferencesValidator.validateRestApiUriSyntax(getContext(), "http://test.com")
46 | .isPresent(),
47 | is(false));
48 | }
49 |
50 | @Test
51 | public void testValidateRestApiUriSyntax_V1NoToken() {
52 | assertThat(PreferencesValidator.validateRestApiUriSyntax(getContext(), "http://test.com/v1")
53 | .get(),
54 | is(getContext().getString(R.string.rest_uri_missing_token, "http://test.com/v1")));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/dexcom/SensorRecordTest.java:
--------------------------------------------------------------------------------
1 | // Sensor Record: 56301B0BF3DB1A0BC03B020050FD0100A600C7
2 | // Record Sensor filtered: 130384 unfiltered: 146368 RSSI: 166 display time: 1417102819000 system time: 186331222
3 | // Sensor Record: 82311B0B1FDD1A0B2058020080220200A30092
4 | // Record Sensor filtered: 139904 unfiltered: 153632 RSSI: 163 display time: 1417103119000 system time: 186331522
5 |
6 |
7 | package com.nightscout.core.dexcom;
8 |
9 | import com.nightscout.core.dexcom.records.SensorRecord;
10 |
11 | import org.junit.Test;
12 |
13 | import static org.hamcrest.Matchers.is;
14 | import static org.junit.Assert.assertThat;
15 |
16 |
17 | public class SensorRecordTest {
18 |
19 | @Test
20 | public void shouldParseSensorRecord() throws Exception {
21 | byte[] record = new byte[]{(byte) 0x56, (byte) 0x30, (byte) 0x1B, (byte) 0x0B, (byte) 0xF3,
22 | (byte) 0xDB, (byte) 0x1A, (byte) 0x0B, (byte) 0xC0, (byte) 0x3B, (byte) 0x02,
23 | (byte) 0x00, (byte) 0x50, (byte) 0xFD, (byte) 0x01, (byte) 0x00, (byte) 0xA6,
24 | (byte) 0x00, (byte) 0xC7};
25 | SensorRecord sensorRecord = new SensorRecord(record);
26 | assertThat(sensorRecord.getUnfiltered(), is(146368L));
27 | assertThat(sensorRecord.getFiltered(), is(130384L));
28 | assertThat(sensorRecord.getRssi(), is(166));
29 | assertThat(sensorRecord.getRawDisplayTimeSeconds(), is(186309619L));
30 | assertThat(sensorRecord.getRawSystemTimeSeconds(), is(186331222L));
31 | }
32 |
33 | @Test(expected = InvalidRecordLengthException.class)
34 | public void shouldNotParseSmallSensorRecord() throws Exception {
35 | byte[] record = new byte[]{(byte) 0x56, (byte) 0x30, (byte) 0x1B, (byte) 0x0B, (byte) 0xF3,
36 | (byte) 0xDB, (byte) 0x1A, (byte) 0x0B, (byte) 0xC0, (byte) 0x3B, (byte) 0x02,
37 | (byte) 0x00, (byte) 0x50, (byte) 0xFD, (byte) 0x01, (byte) 0x00, (byte) 0xA6,
38 | (byte) 0x00};
39 | SensorRecord sensorRecord = new SensorRecord(record);
40 | }
41 |
42 | @Test(expected = InvalidRecordLengthException.class)
43 | public void shouldNotParseLargeSensorRecord() throws Exception {
44 | byte[] record = new byte[]{(byte) 0x56, (byte) 0x30, (byte) 0x1B, (byte) 0x0B, (byte) 0xF3,
45 | (byte) 0xDB, (byte) 0x1A, (byte) 0x0B, (byte) 0xC0, (byte) 0x3B, (byte) 0x02,
46 | (byte) 0x00, (byte) 0x50, (byte) 0xFD, (byte) 0x01, (byte) 0x00, (byte) 0xA6,
47 | (byte) 0x00, (byte) 0xC7, (byte) 0x00};
48 | SensorRecord sensorRecord = new SensorRecord(record);
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/dexcom/UtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import com.google.common.primitives.UnsignedBytes;
4 |
5 | import org.joda.time.DateTime;
6 | import org.joda.time.DateTimeZone;
7 | import org.joda.time.Period;
8 | import org.junit.Test;
9 |
10 | import static org.hamcrest.Matchers.is;
11 | import static org.junit.Assert.assertThat;
12 |
13 | public class UtilsTest {
14 |
15 | @Test
16 | public void testReceiverTimeToDateTime_epoch() {
17 | assertThat(Utils.receiverTimeToDateTime(0),
18 | is(Utils.DEXCOM_EPOCH.withZone(DateTimeZone.UTC)));
19 | }
20 |
21 | @Test
22 | public void testReceiverTimeToDateTime_positiveDelta() {
23 | int secondsDelta = 10;
24 | assertThat(Utils.receiverTimeToDateTime(secondsDelta),
25 | is(Utils.DEXCOM_EPOCH.plusSeconds(secondsDelta).withZone(DateTimeZone.UTC)));
26 | }
27 |
28 | @Test
29 | public void testReceiverTimeToDateTime_negativeDelta() {
30 | int secondsDelta = -10;
31 | assertThat(Utils.receiverTimeToDateTime(secondsDelta),
32 | is(Utils.DEXCOM_EPOCH.minusSeconds(10).withZone(DateTimeZone.UTC)));
33 | }
34 |
35 | @Test
36 | public void testGetTimeAgoString_ZeroDelta() {
37 | DateTime now = new DateTime();
38 | assertThat(Utils.getTimeAgoString(new Period(now, now)), is("0 seconds ago"));
39 | }
40 |
41 | @Test
42 | public void testGetTimeAgoString_SecDelta() {
43 | DateTime now = new DateTime();
44 | assertThat(Utils.getTimeAgoString(new Period(now, now.plusSeconds(1))),
45 | is("1 seconds ago"));
46 | }
47 |
48 | @Test
49 | public void testGetTimeAgoString_DayDelta() {
50 | DateTime now = new DateTime();
51 | assertThat(Utils.getTimeAgoString(new Period(now, now.plusDays(1))),
52 | is("1 days ago"));
53 | }
54 |
55 | @Test
56 | public void testGetTimeAgoString_Multiple() {
57 | DateTime now = new DateTime();
58 | assertThat(Utils.getTimeAgoString(new Period(now,
59 | now.plusMonths(1).plusDays(1).plusSeconds(3))),
60 | is("3 seconds, 1 days, and 1 months ago"));
61 | }
62 |
63 | @Test
64 | public void testBytesToHex_Simple() {
65 | assertThat(Utils.bytesToHex(new byte[]{0xA}), is("0A"));
66 | }
67 |
68 | @Test
69 | public void testBytesToHex_Multiple() {
70 | assertThat(Utils.bytesToHex(new byte[]{
71 | UnsignedBytes.checkedCast(0xDE),
72 | UnsignedBytes.checkedCast(0xAD),
73 | UnsignedBytes.checkedCast(0xBE),
74 | UnsignedBytes.checkedCast(0xEF)}),
75 | is("DEADBEEF"));
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/settings/CustomSwitchPreference.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.settings;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.preference.SwitchPreference;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.Switch;
11 |
12 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
13 | public class CustomSwitchPreference extends SwitchPreference {
14 |
15 | /**
16 | * Construct a new SwitchPreference with the given style options.
17 | *
18 | * @param context The Context that will style this preference
19 | * @param attrs Style attributes that differ from the default
20 | * @param defStyle Theme attribute defining the default style options
21 | */
22 | public CustomSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
23 | super(context, attrs, defStyle);
24 | }
25 |
26 | /**
27 | * Construct a new SwitchPreference with the given style options.
28 | *
29 | * @param context The Context that will style this preference
30 | * @param attrs Style attributes that differ from the default
31 | */
32 | public CustomSwitchPreference(Context context, AttributeSet attrs) {
33 | super(context, attrs);
34 | }
35 |
36 | /**
37 | * Construct a new SwitchPreference with default style options.
38 | *
39 | * @param context The Context that will style this preference
40 | */
41 | public CustomSwitchPreference(Context context) {
42 | super(context, null);
43 | }
44 |
45 | @Override
46 | protected void onBindView(View view) {
47 | // Clean listener before invoke SwitchPreference.onBindView
48 | ViewGroup viewGroup= (ViewGroup)view;
49 | clearListenerInViewGroup(viewGroup);
50 | super.onBindView(view);
51 | }
52 |
53 | /**
54 | * Clear listener in Switch for specify ViewGroup.
55 | *
56 | * @param viewGroup The ViewGroup that will need to clear the listener.
57 | */
58 | private void clearListenerInViewGroup(ViewGroup viewGroup) {
59 | if (null == viewGroup) {
60 | return;
61 | }
62 |
63 | int count = viewGroup.getChildCount();
64 | for(int n = 0; n < count; ++n) {
65 | View childView = viewGroup.getChildAt(n);
66 | if(childView instanceof Switch) {
67 | final Switch switchView = (Switch) childView;
68 | switchView.setOnCheckedChangeListener(null);
69 | return;
70 | } else if (childView instanceof ViewGroup){
71 | ViewGroup childGroup = (ViewGroup)childView;
72 | clearListenerInViewGroup(childGroup);
73 | }
74 | }
75 | }
76 |
77 | }
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/dexcom/EgvRecordTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import com.nightscout.core.dexcom.records.EGVRecord;
4 | import com.nightscout.core.model.G4Noise;
5 |
6 | import org.json.JSONObject;
7 | import org.junit.Test;
8 |
9 | import static org.hamcrest.Matchers.is;
10 | import static org.junit.Assert.assertThat;
11 |
12 | public class EgvRecordTest {
13 | // EGV Record: C4881A0B61341A0B0500583E
14 | // EGV: 5 Trend: NOT_COMPUTABLE display time: 1417056321000, system time: 186288324, display time offset: 186266721, noise level: None
15 | //
16 | // EGV Record: 80BD1A0B1D691A0B7800217D
17 | // EGV: 120 Trend: DOUBLE_UP display time: 1417069821000 system time: 186301824 noise level: None
18 |
19 | @Test
20 | public void shouldParseEgvRecord() throws Exception {
21 | byte[] record = new byte[]{(byte) 0xC4, (byte) 0x88, (byte) 0x1A, (byte) 0x0B, (byte) 0x61,
22 | (byte) 0x34, (byte) 0x1A, (byte) 0x0B, (byte) 0x05, (byte) 0x00, (byte) 0x58,
23 | (byte) 0x3E};
24 | EGVRecord egvRecord = new EGVRecord(record);
25 | assertThat(egvRecord.getBgMgdl(), is(5));
26 | assertThat(egvRecord.getTrend(), is(TrendArrow.NOT_COMPUTABLE));
27 | assertThat(egvRecord.getRawDisplayTimeSeconds(), is(186266721L));
28 | assertThat(egvRecord.getRawSystemTimeSeconds(), is(186288324L));
29 | assertThat(egvRecord.getNoiseMode(), is(G4Noise.NOT_COMPUTED));
30 | }
31 |
32 | @Test(expected = InvalidRecordLengthException.class)
33 | public void shouldNotParseSmallEgvRecord() throws Exception {
34 | byte[] record = new byte[]{(byte) 0xC4, (byte) 0x88, (byte) 0x1A, (byte) 0x0B, (byte) 0x61,
35 | (byte) 0x34, (byte) 0x1A, (byte) 0x0B, (byte) 0x05, (byte) 0x00, (byte) 0x58};
36 | EGVRecord egvRecord = new EGVRecord(record);
37 | }
38 |
39 | @Test(expected = InvalidRecordLengthException.class)
40 | public void shouldNotParseLargeEgvRecord() throws Exception {
41 | byte[] record = new byte[]{(byte) 0xC4, (byte) 0x88, (byte) 0x1A, (byte) 0x0B, (byte) 0x61,
42 | (byte) 0x34, (byte) 0x1A, (byte) 0x0B, (byte) 0x05, (byte) 0x00, (byte) 0x58,
43 | (byte) 0x3E, (byte) 0x00, (byte) 0x00};
44 | EGVRecord egvRecord = new EGVRecord(record);
45 | }
46 |
47 | @Test
48 | public void shouldConvertToJsonString() throws Exception {
49 | byte[] record = new byte[]{(byte) 0xC4, (byte) 0x88, (byte) 0x1A, (byte) 0x0B, (byte) 0x61,
50 | (byte) 0x34, (byte) 0x1A, (byte) 0x0B, (byte) 0x05, (byte) 0x00, (byte) 0x58,
51 | (byte) 0x3E};
52 | JSONObject obj = new JSONObject();
53 | obj.put("sgv", 5);
54 | obj.put("date", Utils.receiverTimeToDate(186266721));
55 | EGVRecord egvRecord = new EGVRecord(record);
56 | assertThat(egvRecord.toJSON().toString(), is(obj.toString()));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/ReceiverState.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.Message;
6 | import com.squareup.wire.ProtoField;
7 |
8 | import java.util.Collections;
9 | import java.util.List;
10 |
11 | import static com.squareup.wire.Message.Datatype.ENUM;
12 | import static com.squareup.wire.Message.Datatype.UINT64;
13 | import static com.squareup.wire.Message.Label.REPEATED;
14 | import static com.squareup.wire.Message.Label.REQUIRED;
15 |
16 | public final class ReceiverState extends Message {
17 |
18 | public static final Long DEFAULT_TIMESTAMP_MS = 0L;
19 | public static final List DEFAULT_EVENT = Collections.emptyList();
20 |
21 | @ProtoField(tag = 1, type = UINT64, label = REQUIRED)
22 | public final Long timestamp_ms;
23 |
24 | @ProtoField(tag = 2, type = ENUM, label = REPEATED)
25 | public final List event;
26 |
27 | public ReceiverState(Long timestamp_ms, List event) {
28 | this.timestamp_ms = timestamp_ms;
29 | this.event = immutableCopyOf(event);
30 | }
31 |
32 | private ReceiverState(Builder builder) {
33 | this(builder.timestamp_ms, builder.event);
34 | setBuilder(builder);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object other) {
39 | if (other == this) return true;
40 | if (!(other instanceof ReceiverState)) return false;
41 | ReceiverState o = (ReceiverState) other;
42 | return equals(timestamp_ms, o.timestamp_ms)
43 | && equals(event, o.event);
44 | }
45 |
46 | @Override
47 | public int hashCode() {
48 | int result = hashCode;
49 | if (result == 0) {
50 | result = timestamp_ms != null ? timestamp_ms.hashCode() : 0;
51 | result = result * 37 + (event != null ? event.hashCode() : 1);
52 | hashCode = result;
53 | }
54 | return result;
55 | }
56 |
57 | public static final class Builder extends Message.Builder {
58 |
59 | public Long timestamp_ms;
60 | public List event;
61 |
62 | public Builder() {
63 | }
64 |
65 | public Builder(ReceiverState message) {
66 | super(message);
67 | if (message == null) return;
68 | this.timestamp_ms = message.timestamp_ms;
69 | this.event = copyOf(message.event);
70 | }
71 |
72 | public Builder timestamp_ms(Long timestamp_ms) {
73 | this.timestamp_ms = timestamp_ms;
74 | return this;
75 | }
76 |
77 | public Builder event(List event) {
78 | this.event = checkForNulls(event);
79 | return this;
80 | }
81 |
82 | @Override
83 | public ReceiverState build() {
84 | checkRequiredFields();
85 | return new ReceiverState(this);
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Nightscout
4 |
5 | Settings
6 |
7 | ---
8 |
9 | Oops! A crash has occurred and is being reported to nightscout so it can be fixed!
10 | Nightscout feedback
11 | Please let us know what you think.
12 | Please add your comments below:
13 | Thank you!
14 |
15 | Reading from device is disabled until you review and accept “I understand” in preferences
16 | Force sync
17 | Crash reporting is enabled
18 | Crash reporting is disabled
19 | Email
20 | This is the email address included in bug reports and automatic crash reports. This field is optional
21 |
22 |
23 |
24 | Close
25 | Feedback
26 | Gap sync
27 | Preferences
28 | USB
29 | Upload
30 | Dex Battery
31 | Time
32 | Error resolving Mongo host. Double check mongo url.
33 | Could not create API uploader. Check your settings.
34 |
35 | Donate data?
36 | Will you anonymously donate your CGM data for research in order to speed up diabetes innovation?
37 | Yes
38 | No
39 | Auto configure
40 |
41 | Time change detected
42 | Mongo uri has invalid syntax. Please double check.
43 | Cannot connect to %s, please check that you are connected to the internet, and that the url is correct.
44 | Invalid rest url %s, please double check formatting.
45 | Missing token for rest uri %s.
46 | Invalid Input
47 | OK
48 | Unknown version
49 | Camping
50 | Git hash
51 |
52 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/records/GlucoseDataSet.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom.records;
2 |
3 | import com.nightscout.core.dexcom.TrendArrow;
4 | import com.nightscout.core.dexcom.Utils;
5 | import com.nightscout.core.model.GlucoseUnit;
6 | import com.nightscout.core.model.SensorEntry;
7 | import com.nightscout.core.model.SensorGlucoseValueEntry;
8 | import com.nightscout.core.utils.GlucoseReading;
9 |
10 | import java.util.Date;
11 |
12 | public class GlucoseDataSet {
13 |
14 | private Date systemTime;
15 | private Date displayTime;
16 | private GlucoseReading reading;
17 | private TrendArrow trend;
18 | private int noise;
19 | private long unfiltered;
20 | private long filtered;
21 | private int rssi;
22 |
23 | public GlucoseDataSet(SensorGlucoseValueEntry egvRecord) {
24 | systemTime = Utils.receiverTimeToDate(egvRecord.sys_timestamp_sec);
25 | displayTime = Utils.receiverTimeToDate(egvRecord.disp_timestamp_sec);
26 | reading = new GlucoseReading(egvRecord.sgv_mgdl, GlucoseUnit.MGDL);
27 | trend = TrendArrow.values()[egvRecord.trend.ordinal()];
28 | noise = egvRecord.noise.ordinal();
29 | }
30 |
31 | public GlucoseDataSet(EGVRecord egvRecord) {
32 | systemTime = egvRecord.getSystemTime();
33 | displayTime = egvRecord.getDisplayTime();
34 | reading = egvRecord.getReading();
35 | trend = egvRecord.getTrend();
36 | noise = egvRecord.getNoiseMode().ordinal();
37 | }
38 |
39 | public GlucoseDataSet(EGVRecord egvRecord, SensorRecord sensorRecord) {
40 | this(egvRecord);
41 | // TODO check times match between record
42 | unfiltered = sensorRecord.getUnfiltered();
43 | filtered = sensorRecord.getFiltered();
44 | rssi = sensorRecord.getRssi();
45 | }
46 |
47 | public GlucoseDataSet(SensorGlucoseValueEntry egvRecord, SensorEntry sensorRecord) {
48 | this.systemTime = Utils.receiverTimeToDate(egvRecord.sys_timestamp_sec);
49 | this.displayTime = Utils.receiverTimeToDate(egvRecord.disp_timestamp_sec);
50 | this.reading = new GlucoseReading(egvRecord.sgv_mgdl, GlucoseUnit.MGDL);
51 | this.trend = TrendArrow.values()[egvRecord.trend.ordinal()];
52 | this.noise = egvRecord.noise.ordinal();
53 | this.unfiltered = sensorRecord.unfiltered;
54 | this.filtered = sensorRecord.filtered;
55 | this.rssi = sensorRecord.rssi;
56 | }
57 |
58 | public Date getSystemTime() {
59 | return systemTime;
60 | }
61 |
62 | public Date getDisplayTime() {
63 | return displayTime;
64 | }
65 |
66 | public int getBgMgdl() {
67 | return reading.asMgdl();
68 | }
69 |
70 | public GlucoseReading getReading() {
71 | return reading;
72 | }
73 |
74 | public TrendArrow getTrend() {
75 | return trend;
76 | }
77 |
78 | public String getTrendSymbol() {
79 | return trend.symbol();
80 | }
81 |
82 | public int getNoise() {
83 | return noise;
84 | }
85 |
86 | public long getUnfiltered() {
87 | return unfiltered;
88 | }
89 |
90 | public long getFiltered() {
91 | return filtered;
92 | }
93 |
94 | public int getRssi() {
95 | return rssi;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/records/GenericTimestampRecord.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom.records;
2 |
3 | import com.nightscout.core.dexcom.Utils;
4 | import com.squareup.wire.Message;
5 |
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.nio.ByteBuffer;
10 | import java.nio.ByteOrder;
11 | import java.util.ArrayList;
12 | import java.util.Date;
13 | import java.util.List;
14 |
15 | abstract public class GenericTimestampRecord {
16 | protected final Logger log = LoggerFactory.getLogger(this.getClass());
17 | protected final int OFFSET_SYS_TIME = 0;
18 | protected final int OFFSET_DISPLAY_TIME = 4;
19 | protected Date systemTime;
20 | protected long rawSystemTimeSeconds;
21 | protected Date displayTime;
22 | protected long rawDisplayTimeSeconds;
23 |
24 | public GenericTimestampRecord(byte[] packet) {
25 | rawSystemTimeSeconds = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_SYS_TIME);
26 | systemTime = Utils.receiverTimeToDate(rawSystemTimeSeconds);
27 | rawDisplayTimeSeconds = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_DISPLAY_TIME);
28 | displayTime = Utils.receiverTimeToDate(rawDisplayTimeSeconds);
29 | }
30 |
31 | public GenericTimestampRecord(Date displayTime, Date systemTime) {
32 | this.displayTime = displayTime;
33 | this.systemTime = systemTime;
34 | }
35 |
36 | public GenericTimestampRecord(long rawDisplayTimeSeconds, long rawSystemTimeSeconds) {
37 | this.rawDisplayTimeSeconds = rawDisplayTimeSeconds;
38 | this.rawSystemTimeSeconds = rawSystemTimeSeconds;
39 | this.systemTime = Utils.receiverTimeToDate(rawSystemTimeSeconds);
40 | this.displayTime = Utils.receiverTimeToDate(rawDisplayTimeSeconds);
41 | }
42 |
43 | public Date getSystemTime() {
44 | return systemTime;
45 | }
46 |
47 | public long getRawSystemTimeSeconds() {
48 | return rawSystemTimeSeconds;
49 | }
50 |
51 | public Date getDisplayTime() {
52 | return displayTime;
53 | }
54 |
55 | public long getRawDisplayTimeSeconds() {
56 | return rawDisplayTimeSeconds;
57 | }
58 |
59 | abstract protected Message toProtobuf();
60 |
61 | public static List toProtobufList(
62 | List list, Class clazz) {
63 | List results = new ArrayList<>();
64 |
65 | for (GenericTimestampRecord record : list) {
66 | results.add(clazz.cast(record.toProtobuf()));
67 | }
68 | return results;
69 | }
70 |
71 | @Override
72 | public boolean equals(Object o) {
73 | if (this == o) return true;
74 | if (o == null || getClass() != o.getClass()) return false;
75 |
76 | GenericTimestampRecord that = (GenericTimestampRecord) o;
77 |
78 | if (rawDisplayTimeSeconds != that.rawDisplayTimeSeconds) return false;
79 | if (rawSystemTimeSeconds != that.rawSystemTimeSeconds) return false;
80 |
81 | return true;
82 | }
83 |
84 | @Override
85 | public int hashCode() {
86 | int result = (int) (rawSystemTimeSeconds ^ (rawSystemTimeSeconds >>> 32));
87 | result = 31 * result + (int) (rawDisplayTimeSeconds ^ (rawDisplayTimeSeconds >>> 32));
88 | return result;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/res/values-it/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Nightscout
4 |
5 | Configurazione
6 |
7 | ---
8 |
9 | Oops! È crashato il software. Sto mandando un rapporto a Nightscout così magari lo possono sistemare!
10 | Feedback a Nightscout
11 | Per favore facci sapere cosa ne pensi
12 | Aggiungi i tuoi commenti qui sotto:
13 | Grazie!
14 |
15 | La lettura dal device è disabilitata finchè non leggi e accetti \"HO CAPITO\" nelle preferenze
16 | Forza invio dati ultimi 2 giorni
17 | Crash reporting is enabled
18 | Crash reporting is disabled
19 | Email
20 | This is the email address included in bug reports and automatic crash reports. This field is optional
21 |
22 |
23 |
24 | Esci
25 | Feedback
26 | Forza sincronizzazione
27 | Preferenze
28 | USB
29 | Upload
30 |
31 | Donate dati?
32 | Vuoi anonimo donare i vostri dati CGM per la ricerca , al fine di accelerare l\'innovazione del diabete ?
33 | Sì
34 | No
35 |
36 | Error resolving Mongo host. Double check mongo url.
37 | Could not create API uploader. Check your settings.
38 | Auto configure
39 |
40 | Batteria Dex
41 | Ora
42 | Trovata modifica temporale
43 | Mongo uri ha sintassi non valida. Si prega di doppio controllo.
44 | Invalid Input
45 | Invalid rest url %s, si prega di controllare due volte la formattazione.
46 | Ok
47 | Missing token for rest uri %s.
48 | Impossibile connettersi a %s, si prega di verificare che si è connessi a Internet, e che l\'URL sia corretto.
49 | Versione sconosciuta
50 | Camping
51 | Git hash
52 |
53 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/records/CalSubrecord.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom.records;
2 |
3 | import com.nightscout.core.dexcom.Utils;
4 |
5 | import java.nio.ByteBuffer;
6 | import java.nio.ByteOrder;
7 | import java.util.Date;
8 |
9 | public class CalSubrecord {
10 | private Date dateEntered;
11 | private int rawDateEntered;
12 | private int calBGL;
13 | private int calRaw;
14 | private Date dateApplied;
15 | private int rawDateApplied;
16 | private byte unk;
17 |
18 | public CalSubrecord(byte[] packet, long displayTimeOffset) {
19 | int delta = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt();
20 | rawDateEntered = delta;
21 | dateEntered = Utils.receiverTimeToDate(delta + displayTimeOffset);
22 | calBGL = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(4);
23 | calRaw = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(8);
24 | delta = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(12);
25 | rawDateApplied = delta;
26 | dateApplied = Utils.receiverTimeToDate(delta + displayTimeOffset);
27 | unk = packet[16];
28 | }
29 |
30 | public CalSubrecord(int calBGL, int calRaw, Date dateApplied, Date dateEntered) {
31 | this.calBGL = calBGL;
32 | this.calRaw = calRaw;
33 | this.dateEntered = dateEntered;
34 | this.dateApplied = dateApplied;
35 | }
36 |
37 | public CalSubrecord(int calBGL, int calRaw, int dateApplied, int dateEntered) {
38 | this.calBGL = calBGL;
39 | this.calRaw = calRaw;
40 | this.rawDateEntered = dateEntered;
41 | this.rawDateApplied = dateApplied;
42 | }
43 |
44 |
45 | public Date getDateEntered() {
46 | return dateEntered;
47 | }
48 |
49 | public int getCalBGL() {
50 | return calBGL;
51 | }
52 |
53 | public int getCalRaw() {
54 | return calRaw;
55 | }
56 |
57 | public Date getDateApplied() {
58 | return dateApplied;
59 | }
60 |
61 | public byte getUnk() {
62 | return unk;
63 | }
64 |
65 | public int getRawDateEntered() {
66 | return rawDateEntered;
67 | }
68 |
69 | public void setRawDateEntered(int rawDateEntered) {
70 | this.rawDateEntered = rawDateEntered;
71 | }
72 |
73 | public int getRawDateApplied() {
74 | return rawDateApplied;
75 | }
76 |
77 | public void setRawDateApplied(int rawDateApplied) {
78 | this.rawDateApplied = rawDateApplied;
79 | }
80 |
81 | @Override
82 | public boolean equals(Object o) {
83 | if (this == o) return true;
84 | if (o == null || getClass() != o.getClass()) return false;
85 |
86 | CalSubrecord that = (CalSubrecord) o;
87 |
88 | if (calBGL != that.calBGL) return false;
89 | if (calRaw != that.calRaw) return false;
90 | if (rawDateApplied != that.rawDateApplied) return false;
91 | if (rawDateEntered != that.rawDateEntered) return false;
92 |
93 | return true;
94 | }
95 |
96 | @Override
97 | public int hashCode() {
98 | int result = rawDateEntered;
99 | result = 31 * result + calBGL;
100 | result = 31 * result + calRaw;
101 | result = 31 * result + rawDateApplied;
102 | return result;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/dexcom/MeterRecordTest.java:
--------------------------------------------------------------------------------
1 | // Meter Record: 2880180BC52B180B71000A80180BAC
2 | // Record EGV: 113 Meter time: 186155018 display time: 1416926645000 system time: 186155048
3 | // Meter Record: 4CB51A0BE9601A0B46002EB51A0B73
4 | // Record EGV: 70 Meter time: 186299694 display time: 1417071321000 system time: 186299724
5 | // Meter Record: 63B51A0B00611A0B480045B51A0BB1
6 | // Record EGV: 72 Meter time: 186299717 display time: 1417071344000 system time: 186299747
7 | // Meter Record: 7CD01A0B1A7C1A0B06015ED01A0B06
8 | // Record EGV: 262 Meter time: 186306654 display time: 1417078282000 system time: 186306684
9 | // Meter Record: 2880180BC52B180B71000A80180BAC
10 | // Record EGV: 113 Meter time: 186155018 display time: 1416926645000 system time: 186155048
11 | // Meter Record: 4CB51A0BE9601A0B46002EB51A0B73
12 | // Record EGV: 70 Meter time: 186299694 display time: 1417071321000 system time: 186299724
13 | // Meter Record: 63B51A0B00611A0B480045B51A0BB1
14 | // Record EGV: 72 Meter time: 186299717 display time: 1417071344000 system time: 186299747
15 | // Meter Record: 7CD01A0B1A7C1A0B06015ED01A0B06
16 | // Record EGV: 262 Meter time: 186306654 display time: 1417078282000 system time: 186306684
17 |
18 | package com.nightscout.core.dexcom;
19 |
20 | import com.nightscout.core.dexcom.records.MeterRecord;
21 |
22 | import org.junit.Test;
23 |
24 | import static org.hamcrest.Matchers.is;
25 | import static org.junit.Assert.assertThat;
26 |
27 | public class MeterRecordTest {
28 | // Meter Record: 28 80 18 0B C5 2B 18 0B 71 00 0A 80 18 0B AC
29 | // Record EGV: 113 Meter time: 186155018 display time: 1416926645000 system time: 186155048
30 | // Meter Record: 7CD01A0B1A7C1A0B06015ED01A0B06
31 | // Record EGV: 262 Meter time: 186306654 display time: 1417078282000 system time: 186306684
32 |
33 | @Test
34 | public void shouldParseMeterRecord() throws Exception {
35 | byte[] record = new byte[]{(byte) 0x28, (byte) 0x80, (byte) 0x18, (byte) 0x0B, (byte) 0xC5,
36 | (byte) 0x2B, (byte) 0x18, (byte) 0x0B, (byte) 0x71, (byte) 0x00, (byte) 0x0A,
37 | (byte) 0x80, (byte) 0x18, (byte) 0x0B, (byte) 0xAC};
38 | MeterRecord meterRecord = new MeterRecord(record);
39 | assertThat(meterRecord.getBgMgdl(), is(113));
40 | assertThat(meterRecord.getRawDisplayTimeSeconds(), is(186133445L));
41 | assertThat(meterRecord.getRawSystemTimeSeconds(), is(186155048L));
42 | assertThat(meterRecord.getMeterTime(), is(186155018));
43 | }
44 |
45 | @Test(expected = InvalidRecordLengthException.class)
46 | public void shouldNotParseSmallMeterRecord() throws Exception {
47 | byte[] record = new byte[]{(byte) 0x28, (byte) 0x80, (byte) 0x18, (byte) 0x0B, (byte) 0xC5,
48 | (byte) 0x2B, (byte) 0x18, (byte) 0x0B, (byte) 0x71, (byte) 0x00, (byte) 0x0A,
49 | (byte) 0x80, (byte) 0x18, (byte) 0x0B};
50 | MeterRecord meterRecord = new MeterRecord(record);
51 | }
52 |
53 | @Test(expected = InvalidRecordLengthException.class)
54 | public void shouldNotParseLargeMeterRecord() throws Exception {
55 | byte[] record = new byte[]{(byte) 0x28, (byte) 0x80, (byte) 0x18, (byte) 0x0B, (byte) 0xC5,
56 | (byte) 0x2B, (byte) 0x18, (byte) 0x0B, (byte) 0x71, (byte) 0x00, (byte) 0x0A,
57 | (byte) 0x80, (byte) 0x18, (byte) 0x0B, (byte) 0x00, (byte) 0x00};
58 | MeterRecord meterRecord = new MeterRecord(record);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
26 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
52 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
67 |
68 |
72 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/records/SensorRecord.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom.records;
2 |
3 | import com.nightscout.core.dexcom.InvalidRecordLengthException;
4 | import com.nightscout.core.dexcom.Utils;
5 | import com.nightscout.core.model.SensorEntry;
6 |
7 | import java.nio.ByteBuffer;
8 | import java.nio.ByteOrder;
9 | import java.util.List;
10 |
11 | public class SensorRecord extends GenericTimestampRecord {
12 | public static final int RECORD_SIZE = 19;
13 | private long unfiltered;
14 | private long filtered;
15 | private int rssi;
16 | private int OFFSET_UNFILTERED = 8;
17 | private int OFFSET_FILTERED = 12;
18 | private int OFFSET_RSSI = 16;
19 |
20 | public SensorRecord(byte[] packet) {
21 | super(packet);
22 | if (packet.length != RECORD_SIZE) {
23 | throw new InvalidRecordLengthException("Unexpected record size: " + packet.length +
24 | ". Expected size: " + RECORD_SIZE + ". Unparsed record: " + Utils.bytesToHex(packet));
25 | }
26 | unfiltered = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_UNFILTERED);
27 | filtered = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_FILTERED);
28 | rssi = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(OFFSET_RSSI);
29 | }
30 |
31 | public SensorRecord(int filtered, int unfiltered, int rssi, long displayTime, int systemTime) {
32 | super(displayTime, systemTime);
33 | this.filtered = filtered;
34 | this.unfiltered = unfiltered;
35 | this.rssi = rssi;
36 | }
37 |
38 | public SensorRecord(SensorEntry sensor) {
39 | super(sensor.disp_timestamp_sec, sensor.sys_timestamp_sec);
40 | this.filtered = sensor.filtered;
41 | this.unfiltered = sensor.unfiltered;
42 | this.rssi = sensor.rssi;
43 | }
44 |
45 | public long getUnfiltered() {
46 | return unfiltered;
47 | }
48 |
49 | public long getFiltered() {
50 | return filtered;
51 | }
52 |
53 | public int getRssi() {
54 | return rssi;
55 | }
56 |
57 | @Override
58 | public SensorEntry toProtobuf() {
59 | SensorEntry.Builder builder = new SensorEntry.Builder();
60 | return builder.sys_timestamp_sec(rawSystemTimeSeconds)
61 | .disp_timestamp_sec(rawDisplayTimeSeconds)
62 | .rssi(rssi)
63 | .filtered(filtered)
64 | .unfiltered(unfiltered)
65 | .build();
66 |
67 | }
68 |
69 | public static List toProtobufList(List list) {
70 | return toProtobufList(list, SensorEntry.class);
71 | }
72 |
73 | @Override
74 | public boolean equals(Object o) {
75 | if (this == o) return true;
76 | if (o == null || getClass() != o.getClass()) return false;
77 | if (!super.equals(o)) return false;
78 |
79 | SensorRecord that = (SensorRecord) o;
80 |
81 | if (filtered != that.filtered) return false;
82 | if (rssi != that.rssi) return false;
83 | if (unfiltered != that.unfiltered) return false;
84 |
85 | return true;
86 | }
87 |
88 | @Override
89 | public int hashCode() {
90 | int result = super.hashCode();
91 | result = 31 * result + (int) (unfiltered ^ (unfiltered >>> 32));
92 | result = 31 * result + (int) (filtered ^ (filtered >>> 32));
93 | result = 31 * result + rssi;
94 | return result;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/records/PageHeader.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom.records;
2 |
3 | import com.nightscout.core.dexcom.CRC16;
4 | import com.nightscout.core.dexcom.CRCFailError;
5 | import com.nightscout.core.dexcom.Constants;
6 | import com.nightscout.core.dexcom.InvalidRecordLengthException;
7 | import com.nightscout.core.dexcom.RecordType;
8 | import com.nightscout.core.dexcom.Utils;
9 |
10 | import java.nio.ByteBuffer;
11 | import java.nio.ByteOrder;
12 | import java.util.Arrays;
13 |
14 | public class PageHeader {
15 | public static final int HEADER_SIZE=28;
16 | protected final int FIRSTRECORDINDEX_OFFSET=0;
17 | protected final int NUMRECS_OFFSET=4;
18 | protected final int RECTYPE_OFFSET=8;
19 | protected final int REV_OFFSET=9;
20 | protected final int PAGENUMBER_OFFSET=10;
21 | protected final int RESERVED2_OFFSET=14;
22 | protected final int RESERVED3_OFFSET=18;
23 | protected final int RESERVED4_OFFSET=22;
24 |
25 | protected int firstRecordIndex;
26 | protected int numOfRecords;
27 | protected RecordType recordType;
28 | protected byte revision;
29 | protected int pageNumber;
30 | protected int reserved2;
31 | protected int reserved3;
32 | protected int reserved4;
33 | protected byte[] crc=new byte[2];
34 |
35 |
36 | public PageHeader(byte[] packet) {
37 | if (packet.length < HEADER_SIZE){
38 | throw new InvalidRecordLengthException("Unexpected record size: " + packet.length +
39 | ". Expected size: " + HEADER_SIZE + ". Unparsed record: " +
40 | Utils.bytesToHex(packet));
41 | }
42 | firstRecordIndex = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN)
43 | .getInt(FIRSTRECORDINDEX_OFFSET);
44 | numOfRecords = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(NUMRECS_OFFSET);
45 | recordType = RecordType.values()[packet[RECTYPE_OFFSET]];
46 | revision = packet[REV_OFFSET];
47 | pageNumber = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN)
48 | .getInt(PAGENUMBER_OFFSET);
49 | reserved2 = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(RESERVED2_OFFSET);
50 | reserved3 = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(RESERVED3_OFFSET);
51 | reserved4 = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(RESERVED4_OFFSET);
52 | System.arraycopy(packet, HEADER_SIZE-Constants.CRC_LEN, crc, 0, Constants.CRC_LEN);
53 | byte[] crc_calc = CRC16.calculate(packet, 0, HEADER_SIZE - Constants.CRC_LEN);
54 | if (!Arrays.equals(this.crc, crc_calc)) {
55 | throw new CRCFailError("CRC check failed. Was:" + Utils.bytesToHex(this.crc) +
56 | " Expected: " + Utils.bytesToHex(crc_calc));
57 | }
58 |
59 | }
60 |
61 | public byte getRevision() {
62 | return revision;
63 | }
64 |
65 | public RecordType getRecordType() {
66 | return recordType;
67 | }
68 |
69 | public int getFirstRecordIndex() {
70 | return firstRecordIndex;
71 | }
72 |
73 | public int getNumOfRecords() {
74 | return numOfRecords;
75 | }
76 |
77 | public int getPageNumber() {
78 | return pageNumber;
79 | }
80 |
81 | public int getReserved2() {
82 | return reserved2;
83 | }
84 |
85 | public int getReserved3() {
86 | return reserved3;
87 | }
88 |
89 | public int getReserved4() {
90 | return reserved4;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/records/MeterRecord.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom.records;
2 |
3 | import com.nightscout.core.dexcom.InvalidRecordLengthException;
4 | import com.nightscout.core.dexcom.Utils;
5 | import com.nightscout.core.model.GlucoseUnit;
6 | import com.nightscout.core.model.MeterEntry;
7 | import com.nightscout.core.utils.GlucoseReading;
8 |
9 | import java.nio.ByteBuffer;
10 | import java.nio.ByteOrder;
11 | import java.util.Date;
12 | import java.util.List;
13 |
14 | public class MeterRecord extends GenericTimestampRecord {
15 | public final static int RECORD_SIZE = 15;
16 | private int meterTime;
17 | private GlucoseReading reading;
18 |
19 | public MeterRecord(byte[] packet) {
20 | super(packet);
21 | if (packet.length != RECORD_SIZE) {
22 | throw new InvalidRecordLengthException("Unexpected record size: " + packet.length +
23 | ". Expected size: " + RECORD_SIZE + " record: " + Utils.bytesToHex(packet));
24 | }
25 | int meterBG = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(8);
26 | reading = new GlucoseReading(meterBG, GlucoseUnit.MGDL);
27 | meterTime = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(10);
28 | }
29 |
30 | public MeterRecord(int meterBgMgdl, int meterTime, Date displayTime, Date systemTime) {
31 | super(displayTime, systemTime);
32 | this.reading = new GlucoseReading(meterBgMgdl, GlucoseUnit.MGDL);
33 | this.meterTime = meterTime;
34 | }
35 |
36 | public MeterRecord(int meterBgMgdl, int meterTime, long displayTime, int systemTime) {
37 | super(displayTime, systemTime);
38 | this.reading = new GlucoseReading(meterBgMgdl, GlucoseUnit.MGDL);
39 | this.meterTime = meterTime;
40 | }
41 |
42 | public MeterRecord(MeterEntry meter) {
43 | super(meter.disp_timestamp_sec, meter.sys_timestamp_sec);
44 | this.reading = new GlucoseReading(meter.meter_bg_mgdl, GlucoseUnit.MGDL);
45 | this.meterTime = meter.meter_time;
46 | }
47 |
48 | public GlucoseReading getMeterBG() {
49 | return reading;
50 | }
51 |
52 | public int getBgMgdl() {
53 | return reading.asMgdl();
54 | }
55 |
56 | public int getMeterTime() {
57 | return meterTime;
58 | }
59 |
60 | @Override
61 | public MeterEntry toProtobuf() {
62 | MeterEntry.Builder builder = new MeterEntry.Builder();
63 | return builder.sys_timestamp_sec(rawSystemTimeSeconds)
64 | .disp_timestamp_sec(rawDisplayTimeSeconds)
65 | .meter_time(meterTime)
66 | .meter_bg_mgdl(reading.asMgdl())
67 | .build();
68 | }
69 |
70 | public static List toProtobufList(List list) {
71 | return toProtobufList(list, MeterEntry.class);
72 | }
73 |
74 | @Override
75 | public boolean equals(Object o) {
76 | if (this == o) return true;
77 | if (o == null || getClass() != o.getClass()) return false;
78 | if (!super.equals(o)) return false;
79 |
80 | MeterRecord that = (MeterRecord) o;
81 |
82 | if (meterTime != that.meterTime) return false;
83 | if (!reading.equals(that.reading)) return false;
84 |
85 | return true;
86 | }
87 |
88 | @Override
89 | public int hashCode() {
90 | int result = super.hashCode();
91 | result = 31 * result + meterTime;
92 | result = 31 * result + reading.hashCode();
93 | return result;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/test/java/com/nightscout/android/upload/UploaderTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.upload;
2 |
3 | import android.content.Intent;
4 |
5 | import com.google.common.base.Function;
6 | import com.google.common.collect.Lists;
7 | import com.nightscout.android.R;
8 | import com.nightscout.android.ToastReceiver;
9 | import com.nightscout.android.test.RobolectricTestBase;
10 | import com.nightscout.core.preferences.TestPreferences;
11 |
12 | import org.junit.Test;
13 |
14 | import static org.hamcrest.Matchers.is;
15 | import static org.hamcrest.Matchers.not;
16 | import static org.hamcrest.Matchers.nullValue;
17 | import static org.junit.Assert.assertThat;
18 |
19 | public class UploaderTest extends RobolectricTestBase {
20 |
21 | @Test
22 | public void shouldSendToastIntentOnInvalidMongoUri() throws Exception {
23 | TestPreferences prefs = new TestPreferences();
24 | prefs.setMongoUploadEnabled(true);
25 | prefs.setMongoClientUri(null);
26 |
27 | whenOnBroadcastReceived(ToastReceiver.ACTION_SEND_NOTIFICATION,
28 | new Function() {
29 | @Override
30 | public Void apply(Intent input) {
31 | assertThat(input.getStringExtra(ToastReceiver.TOAST_MESSAGE), is(not(nullValue())));
32 | assertThat(input.getStringExtra(ToastReceiver.TOAST_MESSAGE),
33 | is(getContext().getString(R.string.unknown_mongo_host)));
34 | return null;
35 | }
36 | });
37 |
38 | new Uploader(getContext(), prefs);
39 | assertIntentSeen();
40 | }
41 |
42 | @Test
43 | public void initializeShouldFailOnInvalidMongoUri() throws Exception {
44 | TestPreferences prefs = new TestPreferences();
45 | prefs.setMongoUploadEnabled(true);
46 | prefs.setMongoClientUri("http://test.com");
47 | Uploader uploader = new Uploader(getContext(), prefs);
48 | assertThat(uploader.areAllUploadersInitialized(), is(false));
49 | }
50 |
51 | @Test
52 | public void shouldSendToastIntentOnInvalidRestv1Uri() throws Exception {
53 | TestPreferences prefs = new TestPreferences();
54 | prefs.setRestApiEnabled(true);
55 | prefs.setRestApiBaseUris(Lists.newArrayList("http://test/v1"));
56 |
57 | whenOnBroadcastReceived(ToastReceiver.ACTION_SEND_NOTIFICATION,
58 | new Function() {
59 | @Override
60 | public Void apply(Intent input) {
61 | assertThat(input.getStringExtra(ToastReceiver.TOAST_MESSAGE), is(not(nullValue())));
62 | assertThat(input.getStringExtra(ToastReceiver.TOAST_MESSAGE),
63 | is(getContext().getString(R.string.illegal_rest_url)));
64 | return null;
65 | }
66 | });
67 | new Uploader(getContext(), prefs);
68 | assertIntentSeen();
69 | }
70 |
71 | @Test
72 | public void shouldSendToastIntentOnInvalidRestUri() throws Exception {
73 | TestPreferences prefs = new TestPreferences();
74 | prefs.setRestApiEnabled(true);
75 | prefs.setRestApiBaseUris(Lists.newArrayList("\\invalid"));
76 |
77 | whenOnBroadcastReceived(ToastReceiver.ACTION_SEND_NOTIFICATION,
78 | new Function() {
79 | @Override
80 | public Void apply(Intent input) {
81 | assertThat(input.getStringExtra(ToastReceiver.TOAST_MESSAGE), is(not(nullValue())));
82 | assertThat(input.getStringExtra(ToastReceiver.TOAST_MESSAGE),
83 | is(getContext().getString(R.string.illegal_rest_url)));
84 | return null;
85 | }
86 | });
87 | new Uploader(getContext(), prefs);
88 | assertIntentSeen();
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/Download.proto:
--------------------------------------------------------------------------------
1 | option java_package = "com.nightscout.core.model";
2 |
3 | option java_outer_classname = "G4Download";
4 |
5 | message G4Download {
6 | repeated SensorGlucoseValueEntry sgv = 1; // Glucose records as reported by the G4
7 | optional GlucoseUnit units = 2; // Units reported by the device
8 | required string download_timestamp = 3; // ISO8601 timestamp reported by uploader
9 | optional uint64 receiver_system_time_sec = 4; // Raw value of the receiver's system time
10 | optional DownloadStatus download_status = 5 [default = NOT_APPLICABLE]; // Status of the download
11 | optional uint32 receiver_battery = 6; // Battery level as reported by the receiver
12 | optional uint32 uploader_battery = 7; // Battery level as reported by the uploader
13 | repeated MeterEntry meter = 8;
14 | repeated SensorEntry sensor = 9;
15 | repeated CalibrationEntry cal = 10;
16 | }
17 |
18 | enum GlucoseUnit {
19 | MGDL = 0;
20 | MMOL = 1;
21 | }
22 |
23 | enum DownloadStatus {
24 | SUCCESS = 0;
25 | NO_DATA = 1;
26 | DEVICE_NOT_FOUND = 2;
27 | IO_ERROR = 3;
28 | APPLICATION_ERROR = 4;
29 | UNKNOWN = 5;
30 | NOT_APPLICABLE = 6;
31 | }
32 |
33 | enum G4Trend {
34 | TREND_NONE = 0;
35 | DOUBLE_UP = 1; // More than 3 mg/dL per minute
36 | SINGLE_UP = 2; // +2 to +3 mg/dL per minute
37 | FORTY_FIVE_UP = 3; // +1 to +2 mg/dL per minute
38 | FLAT = 4; // +/- 1 mg/dL per minute
39 | FORTY_FIVE_DOWN = 5; // -1 to -2 mg/dL per minute
40 | SINGLE_DOWN = 6; // -2 to -3 mg/dL per minute
41 | DOUBLE_DOWN = 7; // more than -3 mg/dL per minute
42 | NOT_COMPUTABLE = 8;
43 | RATE_OUT_OF_RANGE = 9;
44 | }
45 |
46 | enum G4Noise {
47 | NOISE_NONE = 0;
48 | CLEAN = 1;
49 | LIGHT = 2;
50 | MEDIUM = 3;
51 | HEAVY = 4;
52 | NOT_COMPUTED = 5;
53 | MAX = 6;
54 | }
55 |
56 | enum ReceiverStatus {
57 | RECEIVER_CONNECTED = 0; // The receiver is connected to the uploader
58 | RECEIVER_DISCONNECTED = 1; // The receiver is not connected to the uploader
59 | }
60 |
61 | message SensorGlucoseValueEntry {
62 | required uint32 sgv_mgdl = 1; // Sensor Glucose Value
63 | optional uint64 sys_timestamp_sec = 2; // System timestamp - Timestamp representing the internal clock of the receiver
64 | optional uint64 disp_timestamp_sec = 3; // Display timestamp - Timestamp representing the user configured time displayed on the receiver
65 | optional G4Trend trend = 4; // G4 Glucose trend arrow
66 | optional G4Noise noise = 5; // Noise level that potentially affects the G4 sensor readings
67 | }
68 |
69 | message MeterEntry {
70 | required uint32 meter_bg_mgdl = 1;
71 | optional uint32 meter_time = 2;
72 | optional uint64 sys_timestamp_sec = 3; // System timestamp - Timestamp representing the internal clock of the receiver
73 | optional uint64 disp_timestamp_sec = 4; // Display timestamp - Timestamp representing the user configured time displayed on the receiver
74 |
75 | }
76 |
77 | message SensorEntry {
78 | required uint64 filtered = 1;
79 | optional uint64 unfiltered = 2;
80 | optional uint32 rssi = 3;
81 | optional uint64 sys_timestamp_sec = 4; // System timestamp - Timestamp representing the internal clock of the receiver
82 | optional uint64 disp_timestamp_sec = 5; // Display timestamp - Timestamp representing the user configured time displayed on the receiver
83 |
84 | }
85 |
86 | message CalibrationEntry {
87 | required double slope = 1;
88 | optional double intercept = 2;
89 | optional double scale = 3;
90 | optional double decay = 4;
91 | optional uint64 sys_timestamp_sec = 5; // System timestamp - Timestamp representing the internal clock of the receiver
92 | optional uint64 disp_timestamp_sec = 6; // Display timestamp - Timestamp representing the user configured time displayed on the receiver
93 | }
94 |
95 | message ReceiverState {
96 | required uint64 timestamp_ms = 1;
97 | repeated ReceiverStatus event = 2;
98 | }
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/utils/RestUriUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.utils;
2 |
3 | import org.junit.Test;
4 |
5 | import java.net.URI;
6 | import java.util.List;
7 |
8 | import static org.hamcrest.Matchers.is;
9 | import static org.junit.Assert.assertThat;
10 |
11 | public class RestUriUtilsTest {
12 |
13 | @Test
14 | public void testIsV1Uri_withV1Uri() {
15 | assertThat(RestUriUtils.isV1Uri(URI.create("http://example.com/v1")), is(true));
16 | }
17 |
18 | @Test
19 | public void testIsV1Uri_withV1UriTrailingSlash() {
20 | assertThat(RestUriUtils.isV1Uri(URI.create("http://example.com/v1/")), is(true));
21 | }
22 |
23 | @Test
24 | public void testIsV1Uri_withV1InPathButNotEnding() {
25 | assertThat(RestUriUtils.isV1Uri(URI.create("http://example.com/v1/test")), is(false));
26 | }
27 |
28 | @Test
29 | public void testIsV1Uri_withV1NotInPath() {
30 | assertThat(RestUriUtils.isV1Uri(URI.create("http://example.v1/")), is(false));
31 | }
32 |
33 | @Test
34 | public void testIsV1Uri_withLegacyUri() {
35 | assertThat(RestUriUtils.isV1Uri(URI.create("http://example.com/foo")), is(false));
36 | }
37 |
38 | @Test
39 | public void testIsV1Uri_withNull() {
40 | assertThat(RestUriUtils.isV1Uri(null), is(false));
41 | }
42 |
43 | @Test
44 | public void testHasToken_withNone() {
45 | assertThat(RestUriUtils.hasToken(URI.create("http://example.com")), is(false));
46 | }
47 |
48 | @Test
49 | public void testHasToken_withOne() {
50 | assertThat(RestUriUtils.hasToken(URI.create("http://token@example.com")), is(true));
51 | }
52 |
53 | @Test
54 | public void testRemoveToken_withToken() {
55 | assertThat(RestUriUtils.removeToken(URI.create("http://token@example.com")),
56 | is(URI.create("http://example.com")));
57 | }
58 |
59 | @Test
60 | public void testRemoveToken_withoutToken() {
61 | assertThat(RestUriUtils.removeToken(URI.create("http://example.com")),
62 | is(URI.create("http://example.com")));
63 | }
64 |
65 | @Test(expected = IllegalArgumentException.class)
66 | public void testGenerateSecret_withNull() {
67 | RestUriUtils.generateSecret(null);
68 | }
69 |
70 | @Test(expected = IllegalArgumentException.class)
71 | public void testGenerateSecret_withEmpty() {
72 | RestUriUtils.generateSecret("");
73 | }
74 |
75 | @Test
76 | public void testGenerateSecret_withString() {
77 | assertThat(RestUriUtils.generateSecret("testingtesting"), is("b0212be2cc6081fba3e0b6f3dc6e0109d6f7b4cb"));
78 | }
79 |
80 | @Test
81 | public void testSplitIntoMultipleUris_Empty() {
82 | assertThat(RestUriUtils.splitIntoMultipleUris("").size(), is(0));
83 | }
84 |
85 | @Test
86 | public void testSplitIntoMultipleUris_One() {
87 | List urls = RestUriUtils.splitIntoMultipleUris("one");
88 | assertThat(urls.size(), is(1));
89 | assertThat(urls.get(0), is("one"));
90 | }
91 |
92 | @Test
93 | public void testSplitIntoMultipleUris_ExtraWhitespace() {
94 | List urls = RestUriUtils.splitIntoMultipleUris("one \t\n");
95 | assertThat(urls.size(), is(1));
96 | assertThat(urls.get(0), is("one"));
97 | }
98 |
99 | @Test
100 | public void testSplitIntoMultipleUris_Multiple() {
101 | List urls = RestUriUtils.splitIntoMultipleUris("one two");
102 | assertThat(urls.size(), is(2));
103 | assertThat(urls.get(0), is("one"));
104 | assertThat(urls.get(1), is("two"));
105 | }
106 |
107 | @Test
108 | public void testSplitIntoMultipleUris_Whitespace() {
109 | List urls = RestUriUtils.splitIntoMultipleUris("one \t\ntwo");
110 | assertThat(urls.size(), is(2));
111 | assertThat(urls.get(0), is("one"));
112 | assertThat(urls.get(1), is("two"));
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/preferences/TestPreferences.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.preferences;
2 |
3 | import com.nightscout.core.model.GlucoseUnit;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class TestPreferences implements NightscoutPreferences {
9 | private boolean restApiEnabled = false;
10 | private List restApiBaseUris = new ArrayList<>();
11 | private boolean calibrationUploadEnabled = false;
12 | private boolean sensorUploadEnabled = false;
13 | private boolean mongoUploadEnabled = false;
14 | private String mongoClientUri = null;
15 | private String mongoCollection = "entries";
16 | private String mongoDeviceStatusCollection = "devicestatus";
17 | private boolean dataDonateEnabled;
18 | private GlucoseUnit units;
19 | private String pwdName;
20 | private boolean understand;
21 | private boolean askedForData;
22 |
23 | @Override
24 | public boolean isRestApiEnabled() {
25 | return restApiEnabled;
26 | }
27 |
28 | @Override
29 | public void setRestApiEnabled(boolean restApiEnabled) {
30 | this.restApiEnabled = restApiEnabled;
31 | }
32 |
33 | @Override
34 | public GlucoseUnit getPreferredUnits() {
35 | return units;
36 | }
37 |
38 | @Override
39 | public void setPreferredUnits(GlucoseUnit units) {
40 | this.units = units;
41 | }
42 |
43 | @Override
44 | public List getRestApiBaseUris() {
45 | return restApiBaseUris;
46 | }
47 |
48 | @Override
49 | public void setRestApiBaseUris(List restApiBaseUris) {
50 | this.restApiBaseUris = restApiBaseUris;
51 | }
52 |
53 | @Override
54 | public boolean isCalibrationUploadEnabled() {
55 | return calibrationUploadEnabled;
56 | }
57 |
58 | @Override
59 | public void setCalibrationUploadEnabled(boolean calibrationUploadEnabled) {
60 | this.calibrationUploadEnabled = calibrationUploadEnabled;
61 | }
62 |
63 | @Override
64 | public boolean isSensorUploadEnabled() {
65 | return sensorUploadEnabled;
66 | }
67 |
68 | @Override
69 | public void setSensorUploadEnabled(boolean sensorUploadEnabled) {
70 | this.sensorUploadEnabled = sensorUploadEnabled;
71 | }
72 |
73 | @Override
74 | public boolean isMongoUploadEnabled() {
75 | return mongoUploadEnabled;
76 | }
77 |
78 | @Override
79 | public boolean isDataDonateEnabled() {
80 | return dataDonateEnabled;
81 | }
82 |
83 | @Override
84 | public void setDataDonateEnabled(boolean toDonate) {
85 | this.dataDonateEnabled = toDonate;
86 | }
87 |
88 | @Override
89 | public void setMongoUploadEnabled(boolean mongoUploadEnabled) {
90 | this.mongoUploadEnabled = mongoUploadEnabled;
91 | }
92 |
93 | @Override
94 | public String getMongoClientUri() {
95 | return mongoClientUri;
96 | }
97 |
98 | @Override
99 | public void setMongoClientUri(String mongoClientUri) {
100 | this.mongoClientUri = mongoClientUri;
101 | }
102 |
103 | @Override
104 | public String getMongoCollection() {
105 | return mongoCollection;
106 | }
107 |
108 | @Override
109 | public void setMongoCollection(String mongoCollection) {
110 | this.mongoCollection = mongoCollection;
111 | }
112 |
113 | @Override
114 | public String getMongoDeviceStatusCollection() {
115 | return mongoDeviceStatusCollection;
116 | }
117 |
118 | @Override
119 | public boolean getIUnderstand() {
120 | return understand;
121 | }
122 |
123 | @Override
124 | public void setIUnderstand(boolean bool) {
125 | understand = bool;
126 | }
127 |
128 | @Override
129 | public void setMongoDeviceStatusCollection(String mongoDeviceStatusCollection) {
130 | this.mongoDeviceStatusCollection = mongoDeviceStatusCollection;
131 | }
132 |
133 | @Override
134 | public void setPwdName(String pwdName) {
135 | this.pwdName = pwdName;
136 | }
137 |
138 | @Override
139 | public boolean hasAskedForData() {
140 | return askedForData;
141 | }
142 |
143 | @Override
144 | public void setAskedForData(boolean askedForData) {
145 | this.askedForData = askedForData;
146 | }
147 |
148 | @Override
149 | public String getPwdName() {
150 | return pwdName;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nightscout/android/drivers/USB/UsbSerialProber.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011 Google Inc.
2 | *
3 | * This library is free software; you can redistribute it and/or
4 | * modify it under the terms of the GNU Lesser General Public
5 | * License as published by the Free Software Foundation; either
6 | * version 2.1 of the License, or (at your option) any later version.
7 | *
8 | * This library is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 | * Lesser General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU Lesser General Public
14 | * License along with this library; if not, write to the Free Software
15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
16 | * USA.
17 | *
18 | * Project home page: http://code.google.com/p/usb-serial-for-android/
19 | */
20 |
21 | package com.nightscout.android.drivers.USB;
22 |
23 | import android.hardware.usb.UsbDevice;
24 | import android.hardware.usb.UsbDeviceConnection;
25 | import android.hardware.usb.UsbManager;
26 | import android.util.Log;
27 |
28 | /**
29 | * Helper class to assist in detecting and building {@link UsbSerialDriver}
30 | * instances from available hardware.
31 | *
32 | * @author mike wakerly (opensource@hoho.com)
33 | */
34 | public enum UsbSerialProber {
35 |
36 | CDC_ACM_SERIAL {
37 | @Override
38 | public UsbSerialDriver getDevice(UsbManager manager, UsbDevice usbDevice) {
39 | final UsbDeviceConnection connection = manager.openDevice(usbDevice);
40 | if (connection == null) {
41 | return null;
42 | }
43 | return new CdcAcmSerialDriver(usbDevice, connection, manager);
44 | }
45 | };
46 |
47 | private static final String TAG = UsbSerialProber.class.getSimpleName();
48 |
49 | /**
50 | * Builds a new {@link UsbSerialDriver} instance from the raw device, or
51 | * returns null if it could not be built (for example, if the
52 | * probe failed).
53 | *
54 | * @param manager the {@link android.hardware.usb.UsbManager} to use
55 | * @param usbDevice the raw {@link android.hardware.usb.UsbDevice} to use
56 | * @return the first available {@link UsbSerialDriver}, or {@code null} if
57 | * no devices could be acquired
58 | */
59 | public abstract UsbSerialDriver getDevice(final UsbManager manager, final UsbDevice usbDevice);
60 |
61 | /**
62 | * Acquires and returns the first available serial device among all
63 | * available {@link android.hardware.usb.UsbDevice}s, or returns {@code null} if no device could
64 | * be acquired.
65 | *
66 | * @param usbManager the {@link android.hardware.usb.UsbManager} to use
67 | * @return the first available {@link UsbSerialDriver}, or {@code null} if
68 | * no devices could be acquired
69 | */
70 | public static UsbSerialDriver acquire(final UsbManager usbManager) {
71 | for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
72 | final UsbSerialDriver probedDevice = acquire(usbManager, usbDevice);
73 | if (probedDevice != null) {
74 | return probedDevice;
75 | }
76 | }
77 | return null;
78 | }
79 |
80 | /**
81 | * Builds and returns a new {@link UsbSerialDriver} from the given
82 | * {@link android.hardware.usb.UsbDevice}, or returns {@code null} if no drivers supported this
83 | * device.
84 | *
85 | * @param usbManager the {@link android.hardware.usb.UsbManager} to use
86 | * @param usbDevice the {@link android.hardware.usb.UsbDevice} to use
87 | * @return a new {@link UsbSerialDriver}, or {@code null} if no devices
88 | * could be acquired
89 | */
90 | public static UsbSerialDriver acquire(final UsbManager usbManager, final UsbDevice usbDevice) {
91 | if (!usbManager.hasPermission(usbDevice)) {
92 | Log.i(TAG, "No permission for " + usbDevice.getVendorId() + " " + usbDevice.getProductId());
93 | return null;
94 | }
95 | for (final UsbSerialProber prober : values()) {
96 | final UsbSerialDriver probedDevice = prober.getDevice(usbManager, usbDevice);
97 | if (probedDevice != null) {
98 | return probedDevice;
99 | }
100 | }
101 | return null;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/dexcom/PacketBuilderTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import com.google.common.collect.Lists;
4 |
5 | import org.junit.Test;
6 |
7 | import java.util.ArrayList;
8 |
9 | import static org.hamcrest.Matchers.is;
10 | import static org.junit.Assert.assertThat;
11 |
12 | public class PacketBuilderTest {
13 |
14 | @Test
15 | public void testPingCommand() {
16 | byte[] pingCommand = new byte[]{0x01, 0x06, 0x00, 0x0A, 0x5E, 0x65};
17 | PacketBuilder builder = new PacketBuilder(Command.PING);
18 | assertThat(builder.build(), is(pingCommand));
19 | }
20 |
21 | // Command: READ_DATABASE_PAGE_RANGE
22 | // Packet: 01070010048BB8
23 | // Command: READ_DATABASE_PAGE_RANGE
24 | // Packet: 010700100A4559
25 | // Command: READ_DATABASE_PAGE_RANGE
26 | // Packet: 01070010036CC8
27 | // Command: READ_DATABASE_PAGE_RANGE
28 | // Packet: 0107001005AAA8
29 | // Command: READ_DATABASE_PAGE_RANGE
30 | // Packet: 01070010048BB8
31 | // Command: READ_DATABASE_PAGE_RANGE
32 | // Packet: 010700100A4559
33 | // Command: READ_DATABASE_PAGE_RANGE
34 | // Packet: 01070010036CC8
35 | // Command: READ_DATABASE_PAGE_RANGE
36 | // Packet: 0107001005AAA8
37 | @Test
38 | public void testReadDatabasePageRangeCommand() {
39 | byte[] readDatabasePageRangeCommand = new byte[]{0x01, 0x07, 0x00, 0x10, 0x04, (byte) 0x8B, (byte) 0xB8};
40 | PacketBuilder builder = new PacketBuilder(Command.READ_DATABASE_PAGE_RANGE, Lists.newArrayList((byte) 0x04));
41 | assertThat(builder.build(), is(readDatabasePageRangeCommand));
42 | }
43 |
44 | // Command: READ_DATABASE_PAGES
45 | // Packet: 010C00110488000000013263
46 | // Command: READ_DATABASE_PAGES
47 | // Packet: 010C001104890000000163C9
48 | // Command: READ_DATABASE_PAGES
49 | // Packet: 010C00110A01000000013D69
50 | // Command: READ_DATABASE_PAGES
51 | // Packet: 010C001103CE000000019E77
52 | // Command: READ_DATABASE_PAGES
53 | // Packet: 010C001103CF00000001CFDD
54 | // Command: READ_DATABASE_PAGES
55 | // Packet: 010C00110526000000015EC3
56 | // Command: READ_DATABASE_PAGES
57 | // Packet: 010C00110488000000013263
58 | // Command: READ_DATABASE_PAGES
59 | // Packet: 010C001104890000000163C9
60 | // Command: READ_DATABASE_PAGES
61 | // Packet: 010C00110A01000000013D69
62 | // Command: READ_DATABASE_PAGES
63 | // Packet: 010C001103CE000000019E77
64 | // Command: READ_DATABASE_PAGES
65 | // Packet: 010C001103CF00000001CFDD
66 | // Command: READ_DATABASE_PAGES
67 | // Packet: 010C00110526000000015EC3
68 | @Test
69 | public void testReadDatabasePagesCommand() {
70 | byte[] readDatabasePages = new byte[]{0x01, 0x0C, 0x00, 0x11, 0x05, 0x26, 0x00, 0x00, 0x00, 0x01, 0x5E, (byte) 0xC3};
71 | ArrayList payload = Lists.newArrayList((byte) 0x05, (byte) 0x26, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01);
72 | PacketBuilder builder = new PacketBuilder(Command.READ_DATABASE_PAGES, payload);
73 | assertThat(builder.build(), is(readDatabasePages));
74 | }
75 |
76 | // Command: READ_DISPLAY_TIME_OFFSET
77 | // Packet: 0106001D8807
78 | // Command: READ_DISPLAY_TIME_OFFSET
79 | // Packet: 0106001D8807
80 | @Test
81 | public void testReadDisplayTimeOffsetCommand() {
82 | byte[] readDatabasePages = new byte[]{0x01, 0x06, 0x00, 0x1D, (byte) 0x88, 0x07};
83 | PacketBuilder builder = new PacketBuilder(Command.READ_DISPLAY_TIME_OFFSET);
84 | assertThat(builder.build(), is(readDatabasePages));
85 | }
86 |
87 | // Command: READ_BATTERY_LEVEL
88 | // Packet: 0106002157F0
89 | @Test
90 | public void testReadBatteryLevelCommand() {
91 | byte[] readBatteryLevel = new byte[]{0x01, 0x06, 0x00, 0x21, 0x57, (byte) 0xF0};
92 | PacketBuilder builder = new PacketBuilder(Command.READ_BATTERY_LEVEL);
93 | assertThat(builder.build(), is(readBatteryLevel));
94 | }
95 |
96 | // Command: READ_SYSTEM_TIME
97 | // Packet: 0106002234C0
98 | // Command: READ_SYSTEM_TIME
99 | // Packet: 0106002234C0
100 | // Command: READ_SYSTEM_TIME
101 | // Packet: 0106002234C0
102 | // Command: READ_SYSTEM_TIME
103 | // Packet: 0106002234C0
104 | @Test
105 | public void testReadSystemTimeCommand() {
106 | byte[] readSystemTime = new byte[]{0x01, 0x06, 0x00, 0x22, 0x34, (byte) 0xC0};
107 | PacketBuilder builder = new PacketBuilder(Command.READ_SYSTEM_TIME);
108 | assertThat(builder.build(), is(readSystemTime));
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/MeterEntry.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.Message;
6 | import com.squareup.wire.ProtoField;
7 |
8 | import static com.squareup.wire.Message.Datatype.UINT32;
9 | import static com.squareup.wire.Message.Datatype.UINT64;
10 | import static com.squareup.wire.Message.Label.REQUIRED;
11 |
12 | public final class MeterEntry extends Message {
13 |
14 | public static final Integer DEFAULT_METER_BG_MGDL = 0;
15 | public static final Integer DEFAULT_METER_TIME = 0;
16 | public static final Long DEFAULT_SYS_TIMESTAMP_SEC = 0L;
17 | public static final Long DEFAULT_DISP_TIMESTAMP_SEC = 0L;
18 |
19 | @ProtoField(tag = 1, type = UINT32, label = REQUIRED)
20 | public final Integer meter_bg_mgdl;
21 |
22 | @ProtoField(tag = 2, type = UINT32)
23 | public final Integer meter_time;
24 |
25 | @ProtoField(tag = 3, type = UINT64)
26 | public final Long sys_timestamp_sec;
27 |
28 | /**
29 | * System timestamp - Timestamp representing the internal clock of the receiver
30 | */
31 | @ProtoField(tag = 4, type = UINT64)
32 | public final Long disp_timestamp_sec;
33 |
34 | public MeterEntry(Integer meter_bg_mgdl, Integer meter_time, Long sys_timestamp_sec, Long disp_timestamp_sec) {
35 | this.meter_bg_mgdl = meter_bg_mgdl;
36 | this.meter_time = meter_time;
37 | this.sys_timestamp_sec = sys_timestamp_sec;
38 | this.disp_timestamp_sec = disp_timestamp_sec;
39 | }
40 |
41 | private MeterEntry(Builder builder) {
42 | this(builder.meter_bg_mgdl, builder.meter_time, builder.sys_timestamp_sec, builder.disp_timestamp_sec);
43 | setBuilder(builder);
44 | }
45 |
46 | @Override
47 | public boolean equals(Object other) {
48 | if (other == this) return true;
49 | if (!(other instanceof MeterEntry)) return false;
50 | MeterEntry o = (MeterEntry) other;
51 | return equals(meter_bg_mgdl, o.meter_bg_mgdl)
52 | && equals(meter_time, o.meter_time)
53 | && equals(sys_timestamp_sec, o.sys_timestamp_sec)
54 | && equals(disp_timestamp_sec, o.disp_timestamp_sec);
55 | }
56 |
57 | @Override
58 | public int hashCode() {
59 | int result = hashCode;
60 | if (result == 0) {
61 | result = meter_bg_mgdl != null ? meter_bg_mgdl.hashCode() : 0;
62 | result = result * 37 + (meter_time != null ? meter_time.hashCode() : 0);
63 | result = result * 37 + (sys_timestamp_sec != null ? sys_timestamp_sec.hashCode() : 0);
64 | result = result * 37 + (disp_timestamp_sec != null ? disp_timestamp_sec.hashCode() : 0);
65 | hashCode = result;
66 | }
67 | return result;
68 | }
69 |
70 | public static final class Builder extends Message.Builder {
71 |
72 | public Integer meter_bg_mgdl;
73 | public Integer meter_time;
74 | public Long sys_timestamp_sec;
75 | public Long disp_timestamp_sec;
76 |
77 | public Builder() {
78 | }
79 |
80 | public Builder(MeterEntry message) {
81 | super(message);
82 | if (message == null) return;
83 | this.meter_bg_mgdl = message.meter_bg_mgdl;
84 | this.meter_time = message.meter_time;
85 | this.sys_timestamp_sec = message.sys_timestamp_sec;
86 | this.disp_timestamp_sec = message.disp_timestamp_sec;
87 | }
88 |
89 | public Builder meter_bg_mgdl(Integer meter_bg_mgdl) {
90 | this.meter_bg_mgdl = meter_bg_mgdl;
91 | return this;
92 | }
93 |
94 | public Builder meter_time(Integer meter_time) {
95 | this.meter_time = meter_time;
96 | return this;
97 | }
98 |
99 | public Builder sys_timestamp_sec(Long sys_timestamp_sec) {
100 | this.sys_timestamp_sec = sys_timestamp_sec;
101 | return this;
102 | }
103 |
104 | /**
105 | * System timestamp - Timestamp representing the internal clock of the receiver
106 | */
107 | public Builder disp_timestamp_sec(Long disp_timestamp_sec) {
108 | this.disp_timestamp_sec = disp_timestamp_sec;
109 | return this;
110 | }
111 |
112 | @Override
113 | public MeterEntry build() {
114 | checkRequiredFields();
115 | return new MeterEntry(this);
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/records/EGVRecord.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom.records;
2 |
3 | import com.nightscout.core.dexcom.Constants;
4 | import com.nightscout.core.dexcom.InvalidRecordLengthException;
5 | import com.nightscout.core.dexcom.TrendArrow;
6 | import com.nightscout.core.dexcom.Utils;
7 | import com.nightscout.core.model.G4Noise;
8 | import com.nightscout.core.model.GlucoseUnit;
9 | import com.nightscout.core.model.SensorGlucoseValueEntry;
10 | import com.nightscout.core.utils.GlucoseReading;
11 |
12 | import org.json.JSONException;
13 | import org.json.JSONObject;
14 |
15 | import java.nio.ByteBuffer;
16 | import java.nio.ByteOrder;
17 | import java.util.Date;
18 | import java.util.List;
19 |
20 | public class EGVRecord extends GenericTimestampRecord {
21 | public final static int RECORD_SIZE = 12;
22 | private GlucoseReading reading;
23 | private TrendArrow trend;
24 | private G4Noise noiseMode;
25 |
26 | public EGVRecord(byte[] packet) {
27 | super(packet);
28 | if (packet.length != RECORD_SIZE) {
29 | throw new InvalidRecordLengthException("Unexpected record size: " + packet.length +
30 | ". Expected size: " + RECORD_SIZE + ". Unparsed record: " + Utils.bytesToHex(packet));
31 | }
32 | int bGValue = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(8) & Constants.EGV_VALUE_MASK;
33 | reading = new GlucoseReading(bGValue, GlucoseUnit.MGDL);
34 | byte trendAndNoise = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).get(10);
35 | int trendValue = trendAndNoise & Constants.EGV_TREND_ARROW_MASK;
36 | byte noiseValue = (byte) ((trendAndNoise & Constants.EGV_NOISE_MASK) >> 4);
37 | trend = TrendArrow.values()[trendValue];
38 | noiseMode = G4Noise.values()[noiseValue];
39 | }
40 |
41 | public EGVRecord(int bGValueMgdl, TrendArrow trend, Date displayTime, Date systemTime, G4Noise noise) {
42 | super(displayTime, systemTime);
43 | this.reading = new GlucoseReading(bGValueMgdl, GlucoseUnit.MGDL);
44 | this.trend = trend;
45 | this.noiseMode = noise;
46 | }
47 |
48 | public EGVRecord(int bGValueMgdl, TrendArrow trend, long displayTime, long systemTime, G4Noise noise) {
49 | super(displayTime, systemTime);
50 | this.reading = new GlucoseReading(bGValueMgdl, GlucoseUnit.MGDL);
51 | this.trend = trend;
52 | this.noiseMode = noise;
53 | }
54 |
55 | public EGVRecord(SensorGlucoseValueEntry sgv) {
56 | super(sgv.disp_timestamp_sec, sgv.sys_timestamp_sec);
57 | this.reading = new GlucoseReading(sgv.sgv_mgdl, GlucoseUnit.MGDL);
58 | this.trend = TrendArrow.values()[sgv.trend.ordinal()];
59 | this.noiseMode = sgv.noise;
60 | }
61 |
62 | public int getBgMgdl() {
63 | return reading.asMgdl();
64 | }
65 |
66 | public TrendArrow getTrend() {
67 | return trend;
68 | }
69 |
70 | public G4Noise getNoiseMode() {
71 | return noiseMode;
72 | }
73 |
74 | public JSONObject toJSON() throws JSONException {
75 | JSONObject obj = new JSONObject();
76 | obj.put("sgv", getBgMgdl());
77 | obj.put("date", getDisplayTime());
78 | return obj;
79 | }
80 |
81 | @Override
82 | public SensorGlucoseValueEntry toProtobuf() {
83 | SensorGlucoseValueEntry.Builder builder = new SensorGlucoseValueEntry.Builder();
84 | return builder.sys_timestamp_sec(rawSystemTimeSeconds)
85 | .disp_timestamp_sec(rawDisplayTimeSeconds)
86 | .sgv_mgdl(reading.asMgdl())
87 | .trend(trend.toProtobuf())
88 | .noise(noiseMode)
89 | .build();
90 | }
91 |
92 | public static List toProtobufList(List list) {
93 | return toProtobufList(list, SensorGlucoseValueEntry.class);
94 | }
95 |
96 | public GlucoseReading getReading() {
97 | return reading;
98 | }
99 |
100 | @Override
101 | public boolean equals(Object o) {
102 | if (this == o) return true;
103 | if (o == null || getClass() != o.getClass()) return false;
104 | if (!super.equals(o)) return false;
105 |
106 | EGVRecord egvRecord = (EGVRecord) o;
107 |
108 | if (noiseMode != egvRecord.noiseMode) return false;
109 | if (!reading.equals(egvRecord.reading)) return false;
110 | if (trend != egvRecord.trend) return false;
111 |
112 | return true;
113 | }
114 |
115 | @Override
116 | public int hashCode() {
117 | int result = super.hashCode();
118 | result = 31 * result + reading.hashCode();
119 | result = 31 * result + trend.hashCode();
120 | result = 31 * result + noiseMode.hashCode();
121 | return result;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/barcode/NSBarcodeConfig.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.barcode;
2 |
3 | import com.google.common.base.Optional;
4 |
5 | import org.json.JSONArray;
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | /**
15 | * A class to manage barcode configuration of the uploader
16 | */
17 | public class NSBarcodeConfig {
18 | protected static final Logger log = LoggerFactory.getLogger(NSBarcodeConfig.class);
19 | private JSONObject config = new JSONObject();
20 |
21 | public NSBarcodeConfig(String decodeResults) {
22 | configureBarcode(decodeResults);
23 | }
24 |
25 | public void configureBarcode(String jsonConfig){
26 | if (jsonConfig == null){
27 | throw new IllegalArgumentException("Null barcode");
28 | }
29 | try {
30 | this.config = new JSONObject(jsonConfig);
31 | } catch (JSONException e) {
32 | return;
33 | }
34 | }
35 |
36 | public Optional getMongoUri() {
37 | String mongoUri = null;
38 | try {
39 | if (hasMongoConfig()) {
40 | mongoUri = config.getJSONObject(NSBarcodeConfigKeys.MONGO_CONFIG).getString(NSBarcodeConfigKeys.MONGO_URI);
41 | } else {
42 | return Optional.absent();
43 | }
44 | } catch (JSONException e) {
45 | return Optional.absent();
46 | }
47 | return Optional.of(mongoUri);
48 | }
49 |
50 | public List getApiUris() {
51 | List apiUris = new ArrayList<>();
52 | if (hasApiConfig()){
53 | JSONArray jsonArray = null;
54 | try {
55 | jsonArray = config.getJSONObject(NSBarcodeConfigKeys.API_CONFIG)
56 | .getJSONArray(NSBarcodeConfigKeys.API_URI);
57 | } catch (JSONException e) {
58 | log.error("Invalid json array: " + config.toString());
59 | return apiUris;
60 | }
61 | for (int index = 0; index < jsonArray.length(); index++) {
62 | try {
63 | apiUris.add(jsonArray.getString(index));
64 | } catch (JSONException e) {
65 | log.error("Invalid child json object: " + config.toString());
66 | }
67 | }
68 | }
69 | return apiUris;
70 | }
71 |
72 | public Optional getMongoCollection() {
73 | if (!hasMongoConfig()) {
74 | return Optional.absent();
75 | }
76 | String mongoCollection = null;
77 | try {
78 | if (config.getJSONObject(NSBarcodeConfigKeys.MONGO_CONFIG).has(NSBarcodeConfigKeys.MONGO_COLLECTION)) {
79 | mongoCollection = config.getJSONObject(NSBarcodeConfigKeys.MONGO_CONFIG)
80 | .getString(NSBarcodeConfigKeys.MONGO_COLLECTION);
81 | }
82 | } catch (JSONException e) {
83 | // Should not see this
84 | log.warn("JSON exception: ", e);
85 | }
86 | return Optional.fromNullable(mongoCollection);
87 | }
88 |
89 | public Optional getMongoDeviceStatusCollection(){
90 | if (! config.has(NSBarcodeConfigKeys.MONGO_CONFIG)) {
91 | return Optional.absent();
92 | }
93 | String deviceStatusCollection = null;
94 | try {
95 | if (config.has(NSBarcodeConfigKeys.MONGO_CONFIG) &&
96 | config.getJSONObject(NSBarcodeConfigKeys.MONGO_CONFIG).has(NSBarcodeConfigKeys.MONGO_COLLECTION)) {
97 | deviceStatusCollection = config.getJSONObject(NSBarcodeConfigKeys.MONGO_CONFIG)
98 | .getString(NSBarcodeConfigKeys.MONGO_DEVICE_STATUS_COLLECTION);
99 | }
100 | } catch (JSONException e) {
101 | // Should not see this
102 | log.warn("JSON exception: ", e);
103 | }
104 | return Optional.fromNullable(deviceStatusCollection);
105 | }
106 |
107 | public boolean hasMongoConfig(){
108 | try {
109 | return config.has(NSBarcodeConfigKeys.MONGO_CONFIG) &&
110 | config.getJSONObject(NSBarcodeConfigKeys.MONGO_CONFIG).has(NSBarcodeConfigKeys.MONGO_URI);
111 | } catch (JSONException e) {
112 | return false;
113 | }
114 | }
115 |
116 | public boolean hasApiConfig(){
117 | try {
118 | return config.has(NSBarcodeConfigKeys.API_CONFIG) &&
119 | config.getJSONObject(NSBarcodeConfigKeys.API_CONFIG)
120 | .getJSONArray(NSBarcodeConfigKeys.API_URI).length() > 0;
121 | } catch (JSONException e) {
122 | return false;
123 | }
124 | }
125 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/SensorEntry.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.Message;
6 | import com.squareup.wire.ProtoField;
7 |
8 | import static com.squareup.wire.Message.Datatype.UINT32;
9 | import static com.squareup.wire.Message.Datatype.UINT64;
10 | import static com.squareup.wire.Message.Label.REQUIRED;
11 |
12 | public final class SensorEntry extends Message {
13 |
14 | public static final Long DEFAULT_FILTERED = 0L;
15 | public static final Long DEFAULT_UNFILTERED = 0L;
16 | public static final Integer DEFAULT_RSSI = 0;
17 | public static final Long DEFAULT_SYS_TIMESTAMP_SEC = 0L;
18 | public static final Long DEFAULT_DISP_TIMESTAMP_SEC = 0L;
19 |
20 | @ProtoField(tag = 1, type = UINT64, label = REQUIRED)
21 | public final Long filtered;
22 |
23 | @ProtoField(tag = 2, type = UINT64)
24 | public final Long unfiltered;
25 |
26 | @ProtoField(tag = 3, type = UINT32)
27 | public final Integer rssi;
28 |
29 | @ProtoField(tag = 4, type = UINT64)
30 | public final Long sys_timestamp_sec;
31 |
32 | /**
33 | * System timestamp - Timestamp representing the internal clock of the receiver
34 | */
35 | @ProtoField(tag = 5, type = UINT64)
36 | public final Long disp_timestamp_sec;
37 |
38 | public SensorEntry(Long filtered, Long unfiltered, Integer rssi, Long sys_timestamp_sec, Long disp_timestamp_sec) {
39 | this.filtered = filtered;
40 | this.unfiltered = unfiltered;
41 | this.rssi = rssi;
42 | this.sys_timestamp_sec = sys_timestamp_sec;
43 | this.disp_timestamp_sec = disp_timestamp_sec;
44 | }
45 |
46 | private SensorEntry(Builder builder) {
47 | this(builder.filtered, builder.unfiltered, builder.rssi, builder.sys_timestamp_sec, builder.disp_timestamp_sec);
48 | setBuilder(builder);
49 | }
50 |
51 | @Override
52 | public boolean equals(Object other) {
53 | if (other == this) return true;
54 | if (!(other instanceof SensorEntry)) return false;
55 | SensorEntry o = (SensorEntry) other;
56 | return equals(filtered, o.filtered)
57 | && equals(unfiltered, o.unfiltered)
58 | && equals(rssi, o.rssi)
59 | && equals(sys_timestamp_sec, o.sys_timestamp_sec)
60 | && equals(disp_timestamp_sec, o.disp_timestamp_sec);
61 | }
62 |
63 | @Override
64 | public int hashCode() {
65 | int result = hashCode;
66 | if (result == 0) {
67 | result = filtered != null ? filtered.hashCode() : 0;
68 | result = result * 37 + (unfiltered != null ? unfiltered.hashCode() : 0);
69 | result = result * 37 + (rssi != null ? rssi.hashCode() : 0);
70 | result = result * 37 + (sys_timestamp_sec != null ? sys_timestamp_sec.hashCode() : 0);
71 | result = result * 37 + (disp_timestamp_sec != null ? disp_timestamp_sec.hashCode() : 0);
72 | hashCode = result;
73 | }
74 | return result;
75 | }
76 |
77 | public static final class Builder extends Message.Builder {
78 |
79 | public Long filtered;
80 | public Long unfiltered;
81 | public Integer rssi;
82 | public Long sys_timestamp_sec;
83 | public Long disp_timestamp_sec;
84 |
85 | public Builder() {
86 | }
87 |
88 | public Builder(SensorEntry message) {
89 | super(message);
90 | if (message == null) return;
91 | this.filtered = message.filtered;
92 | this.unfiltered = message.unfiltered;
93 | this.rssi = message.rssi;
94 | this.sys_timestamp_sec = message.sys_timestamp_sec;
95 | this.disp_timestamp_sec = message.disp_timestamp_sec;
96 | }
97 |
98 | public Builder filtered(Long filtered) {
99 | this.filtered = filtered;
100 | return this;
101 | }
102 |
103 | public Builder unfiltered(Long unfiltered) {
104 | this.unfiltered = unfiltered;
105 | return this;
106 | }
107 |
108 | public Builder rssi(Integer rssi) {
109 | this.rssi = rssi;
110 | return this;
111 | }
112 |
113 | public Builder sys_timestamp_sec(Long sys_timestamp_sec) {
114 | this.sys_timestamp_sec = sys_timestamp_sec;
115 | return this;
116 | }
117 |
118 | /**
119 | * System timestamp - Timestamp representing the internal clock of the receiver
120 | */
121 | public Builder disp_timestamp_sec(Long disp_timestamp_sec) {
122 | this.disp_timestamp_sec = disp_timestamp_sec;
123 | return this;
124 | }
125 |
126 | @Override
127 | public SensorEntry build() {
128 | checkRequiredFields();
129 | return new SensorEntry(this);
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/dexcom/Utils.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.dexcom;
2 |
3 | import com.google.common.base.Strings;
4 | import com.google.common.hash.HashCode;
5 | import com.nightscout.core.dexcom.records.GlucoseDataSet;
6 | import com.nightscout.core.model.SensorEntry;
7 | import com.nightscout.core.model.SensorGlucoseValueEntry;
8 |
9 | import org.joda.time.DateTime;
10 | import org.joda.time.DateTimeZone;
11 | import org.joda.time.Instant;
12 | import org.joda.time.Period;
13 | import org.joda.time.format.PeriodFormatter;
14 | import org.joda.time.format.PeriodFormatterBuilder;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import java.util.ArrayList;
19 | import java.util.Date;
20 | import java.util.List;
21 |
22 | import static com.google.common.base.Preconditions.checkNotNull;
23 | import static org.joda.time.Duration.standardSeconds;
24 |
25 | public final class Utils {
26 | protected static final Logger log = LoggerFactory.getLogger(Utils.class);
27 |
28 | public static final DateTime DEXCOM_EPOCH = new DateTime(2009, 1, 1, 0, 0, 0, 0).withZone(DateTimeZone.UTC);
29 |
30 | private static final String PRIMARY_SEPARATOR = ", ";
31 | private static final String SECONDARY_SEPARATOR = ", and ";
32 | private static final PeriodFormatter FORMATTER = new PeriodFormatterBuilder()
33 | .appendSeconds().appendSuffix(" seconds").appendSeparator(PRIMARY_SEPARATOR, SECONDARY_SEPARATOR)
34 | .appendMinutes().appendSuffix(" minutes").appendSeparator(PRIMARY_SEPARATOR, SECONDARY_SEPARATOR)
35 | .appendHours().appendSuffix(" hours").appendSeparator(PRIMARY_SEPARATOR, SECONDARY_SEPARATOR)
36 | .appendDays().appendSuffix(" days").appendSeparator(PRIMARY_SEPARATOR, SECONDARY_SEPARATOR)
37 | .appendWeeks().appendSuffix(" weeks").appendSeparator(PRIMARY_SEPARATOR, SECONDARY_SEPARATOR)
38 | .appendMonths().appendSuffix(" months").appendSeparator(PRIMARY_SEPARATOR, SECONDARY_SEPARATOR)
39 | .appendYears().appendSuffix(" years").appendLiteral(" ago")
40 | .printZeroNever()
41 | .toFormatter();
42 |
43 | // TODO: probably not the right way to do this but it seems to do the trick. Need to revisit this to fully understand what is going on during DST change
44 | public static DateTime receiverTimeToDateTime(long deltaInSeconds) {
45 | int offset = DateTimeZone.getDefault().getOffset(DEXCOM_EPOCH) - DateTimeZone.getDefault().getOffset(Instant.now());
46 | return DEXCOM_EPOCH.plus(offset).plus(standardSeconds(deltaInSeconds)).withZone(DateTimeZone.UTC);
47 | }
48 |
49 | public static Date receiverTimeToDate(long delta) {
50 | return receiverTimeToDateTime(delta).toDate();
51 | }
52 |
53 | /**
54 | * Returns human-friendly string for the length of this duration, e.g. 4 seconds ago
55 | * or 4 days ago.
56 | *
57 | * @param period Non-null Period instance.
58 | * @return String human-friendly Period string, e.g. 4 seconds ago.
59 | */
60 | public static String getTimeAgoString(Period period) {
61 | checkNotNull(period);
62 | String output = FORMATTER.print(period);
63 | if (Strings.isNullOrEmpty(output)) {
64 | return "--";
65 | }
66 | return output;
67 | }
68 |
69 | public static String getTimeString(long timeDeltaMS) {
70 | long minutes = (timeDeltaMS / 1000) / 60;
71 | long hours = minutes / 60;
72 | long days = hours / 24;
73 | long weeks = days / 7;
74 | minutes = minutes - hours * 60;
75 | hours = hours - days * 24;
76 | days = days - weeks * 7;
77 |
78 | String timeAgoString = "";
79 | if (weeks > 0) {
80 | timeAgoString += weeks + " weeks ";
81 | }
82 | if (days > 0) {
83 | timeAgoString += days + " days ";
84 | }
85 | if (hours > 0) {
86 | timeAgoString += hours + " hours ";
87 | }
88 | if (minutes >= 0) {
89 | timeAgoString += minutes + " min ";
90 | }
91 |
92 | return (timeAgoString.equals("") ? "--" : timeAgoString + "ago");
93 | }
94 |
95 | public static List mergeGlucoseDataRecords(List egvRecords,
96 | List sensorRecords) {
97 | int egvLength = egvRecords.size();
98 | int sensorLength = sensorRecords.size();
99 | List glucoseDataSets = new ArrayList<>();
100 | if (egvLength >= 0 && sensorLength == 0) {
101 | for (int i = 1; i <= egvLength; i++) {
102 | glucoseDataSets.add(new GlucoseDataSet(egvRecords.get(egvLength - i)));
103 | }
104 | return glucoseDataSets;
105 | }
106 | int smallerLength = egvLength < sensorLength ? egvLength : sensorLength;
107 | for (int i = 1; i <= smallerLength; i++) {
108 | glucoseDataSets.add(new GlucoseDataSet(egvRecords.get(egvLength - i),
109 | sensorRecords.get(sensorLength - i)));
110 | }
111 | return glucoseDataSets;
112 | }
113 |
114 |
115 | public static String bytesToHex(byte[] bytes) {
116 | return HashCode.fromBytes(bytes).toString().toUpperCase();
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/upload/BaseUploader.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.upload;
2 |
3 | import com.nightscout.core.dexcom.records.GlucoseDataSet;
4 | import com.nightscout.core.drivers.AbstractUploaderDevice;
5 | import com.nightscout.core.model.CalibrationEntry;
6 | import com.nightscout.core.model.MeterEntry;
7 | import com.nightscout.core.preferences.NightscoutPreferences;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import java.io.IOException;
13 | import java.util.List;
14 |
15 | import static com.google.common.base.Preconditions.checkNotNull;
16 |
17 | public abstract class BaseUploader {
18 | protected final Logger log = LoggerFactory.getLogger(this.getClass());
19 |
20 | private final NightscoutPreferences preferences;
21 |
22 | protected abstract boolean doUpload(GlucoseDataSet glucoseDataSet) throws IOException;
23 |
24 | protected boolean doUpload(MeterEntry meterRecord) throws IOException {
25 | log.info("Meter record upload not supported.");
26 | return true;
27 | }
28 |
29 | protected boolean doUpload(CalibrationEntry calRecord) throws IOException {
30 | log.info("Cal record upload not supported.");
31 | return true;
32 | }
33 |
34 | protected boolean doUpload(AbstractUploaderDevice deviceStatus) throws IOException {
35 | log.info("Device status upload not supported.");
36 | return true;
37 | }
38 |
39 | public BaseUploader(NightscoutPreferences preferences) {
40 | checkNotNull(preferences);
41 | this.preferences = preferences;
42 | }
43 |
44 | // TODO(trhodeos): implement some sort of retry logic in all of these public functions.
45 | public final boolean uploadGlucoseDataSets(List glucoseDataSets) {
46 | if (glucoseDataSets == null) {
47 | return true;
48 | }
49 | boolean output = true;
50 | for (GlucoseDataSet glucoseDataSet : glucoseDataSets) {
51 | try {
52 | output &= doUpload(glucoseDataSet);
53 | } catch (IOException e) {
54 | log.error("Error uploading glucose data set.", e);
55 | output = false;
56 | }
57 | }
58 | return output;
59 | }
60 |
61 | /**
62 | * Uploads the meter records
63 | *
64 | * @param meterRecords
65 | * @return True if the upload was successful, false if the upload was unsuccessful
66 | */
67 | public final boolean uploadMeterRecords(List meterRecords) {
68 | if (meterRecords == null) {
69 | return true;
70 | }
71 | boolean output = true;
72 | for (MeterEntry meterRecord : meterRecords) {
73 | try {
74 | output &= doUpload(meterRecord);
75 | } catch (IOException e) {
76 | log.error("Error uploading meter record.", e);
77 | output = false;
78 | }
79 | }
80 | return output;
81 | }
82 |
83 | /**
84 | * Uploads the calibration records
85 | *
86 | * @param calRecords
87 | * @return True if the upload was successful, false if the upload was unsuccessful
88 | */
89 | public final boolean uploadCalRecords(List calRecords) {
90 | if (calRecords == null) {
91 | return true;
92 | }
93 | boolean output = true;
94 | if (getPreferences().isCalibrationUploadEnabled()) {
95 | for (CalibrationEntry calRecord : calRecords) {
96 | try {
97 | output &= doUpload(calRecord);
98 | } catch (IOException e) {
99 | log.error("Error uploading calibration record.", e);
100 | output = false;
101 | }
102 | }
103 | }
104 | return output;
105 | }
106 |
107 | /**
108 | * Uploads the device status
109 | *
110 | * @param deviceStatus
111 | * @return True if the upload was successful or False if the upload was unsuccessful
112 | */
113 | public final boolean uploadDeviceStatus(AbstractUploaderDevice deviceStatus) {
114 | if (deviceStatus == null) {
115 | return true;
116 | }
117 | try {
118 | return doUpload(deviceStatus);
119 | } catch (IOException e) {
120 | log.error("Error uploading device status", e);
121 | return false;
122 | }
123 | }
124 |
125 | protected NightscoutPreferences getPreferences() {
126 | return this.preferences;
127 | }
128 |
129 | /**
130 | * Upload records, can be overridden to send all data in one batch.
131 | * @param glucoseDataSets
132 | * @param meterRecords
133 | * @param calRecords
134 | * @param deviceStatus
135 | * @return True if the (all) uploads was successful or False if at least one upload was unsuccessful.
136 | */
137 | public boolean uploadRecords(List glucoseDataSets, List meterRecords, List calRecords, AbstractUploaderDevice deviceStatus) {
138 | boolean allSuccessful = uploadGlucoseDataSets(glucoseDataSets);
139 | allSuccessful &= uploadMeterRecords(meterRecords);
140 | allSuccessful &= uploadCalRecords(calRecords);
141 | allSuccessful &= uploadDeviceStatus(deviceStatus);
142 | return allSuccessful;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/upload/RestV1Uploader.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.upload;
2 |
3 | import com.nightscout.core.dexcom.Utils;
4 | import com.nightscout.core.dexcom.records.GlucoseDataSet;
5 | import com.nightscout.core.drivers.AbstractUploaderDevice;
6 | import com.nightscout.core.model.CalibrationEntry;
7 | import com.nightscout.core.model.MeterEntry;
8 | import com.nightscout.core.preferences.NightscoutPreferences;
9 | import com.nightscout.core.utils.RestUriUtils;
10 |
11 | import org.apache.http.message.AbstractHttpMessage;
12 | import org.json.JSONException;
13 | import org.json.JSONObject;
14 |
15 | import java.io.IOException;
16 | import java.net.URI;
17 | import java.util.Date;
18 |
19 | import static com.google.common.base.Preconditions.checkArgument;
20 |
21 | public class RestV1Uploader extends AbstractRestUploader {
22 | private final String secret;
23 |
24 | public RestV1Uploader(NightscoutPreferences preferences, URI uri) {
25 | super(preferences, RestUriUtils.removeToken(uri));
26 | checkArgument(RestUriUtils.hasToken(uri), "Rest API v1 requires a token.");
27 | secret = RestUriUtils.generateSecret(uri.getUserInfo());
28 | }
29 |
30 | protected String getSecret() {
31 | return secret;
32 | }
33 |
34 | @Override
35 | protected void setExtraHeaders(AbstractHttpMessage post) {
36 | post.setHeader("api-secret", secret);
37 | }
38 |
39 | private JSONObject toJSONObject(GlucoseDataSet record) throws JSONException {
40 | JSONObject json = new JSONObject();
41 | json.put("device", "dexcom");
42 | json.put("date", record.getDisplayTime().getTime());
43 | json.put("dateString", record.getDisplayTime().toString());
44 | json.put("sgv", Integer.parseInt(String.valueOf(record.getBgMgdl())));
45 | json.put("direction", record.getTrend().friendlyTrendName());
46 | json.put("type", "sgv");
47 | if (getPreferences().isSensorUploadEnabled()) {
48 | json.put("filtered", record.getFiltered());
49 | json.put("unfiltered", record.getUnfiltered());
50 | json.put("rssi", record.getRssi());
51 | json.put("noise", record.getNoise());
52 | }
53 | return json;
54 | }
55 |
56 | private JSONObject toJSONObject(MeterEntry record) throws JSONException {
57 | JSONObject json = new JSONObject();
58 | Date timestamp = Utils.receiverTimeToDate(record.disp_timestamp_sec);
59 | json.put("device", "dexcom");
60 | json.put("type", "mbg");
61 | json.put("date", timestamp.getTime());
62 | json.put("dateString", timestamp.toString());
63 | json.put("mbg", Integer.parseInt(String.valueOf(record.meter_bg_mgdl)));
64 | return json;
65 | }
66 |
67 | private JSONObject toJSONObject(CalibrationEntry record) throws JSONException {
68 | JSONObject json = new JSONObject();
69 | Date timestamp = Utils.receiverTimeToDate(record.disp_timestamp_sec);
70 | json.put("device", "dexcom");
71 | json.put("type", "cal");
72 | json.put("date", timestamp.getTime());
73 | json.put("dateString", timestamp.toString());
74 | json.put("slope", record.slope);
75 | json.put("intercept", record.intercept);
76 | json.put("scale", record.scale);
77 | return json;
78 | }
79 |
80 | private JSONObject toJSONObject(AbstractUploaderDevice deviceStatus) throws JSONException {
81 | JSONObject json = new JSONObject();
82 | json.put("uploaderBattery", deviceStatus.getBatteryLevel());
83 | return json;
84 | }
85 |
86 | @Override
87 | protected boolean doUpload(GlucoseDataSet glucoseDataSet) throws IOException {
88 | try {
89 | return doPost("entries", toJSONObject(glucoseDataSet));
90 | } catch (JSONException e) {
91 | log.error("Could not create JSON object for rest v1 glucose data set.", e);
92 | return false;
93 | }
94 | }
95 |
96 | @Override
97 | protected boolean doUpload(MeterEntry meterRecord) throws IOException {
98 | try {
99 | // TODO(trhodeos): in Uploader.java, this method still used 'entries' as the endpoint,
100 | // but this seems like a bug to me.
101 | return doPost("entries", toJSONObject(meterRecord));
102 | } catch (JSONException e) {
103 | log.error("Could not create JSON object for rest v1 meter record.", e);
104 | return false;
105 | }
106 | }
107 |
108 | @Override
109 | protected boolean doUpload(CalibrationEntry calRecord) throws IOException {
110 | try {
111 | // TODO(trhodeos): in Uploader.java, this method still used 'entries' as the endpoint,
112 | // but this seems like a bug to me.
113 | return doPost("entries", toJSONObject(calRecord));
114 | } catch (JSONException e) {
115 | log.error("Could not create JSON object for rest v1 cal record.", e);
116 | return false;
117 | }
118 | }
119 |
120 | @Override
121 | protected boolean doUpload(AbstractUploaderDevice deviceStatus) throws IOException {
122 | try {
123 | return doPost("devicestatus", toJSONObject(deviceStatus));
124 | } catch (JSONException e) {
125 | log.error("Could not create JSON object for rest v1 device status.", e);
126 | return false;
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/core/src/test/java/com/nightscout/core/upload/RestLegacyUploaderTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.core.upload;
2 |
3 | import com.google.common.collect.Lists;
4 | import com.google.common.io.CharStreams;
5 | import com.nightscout.core.drivers.AbstractUploaderDevice;
6 | import com.nightscout.core.preferences.TestPreferences;
7 |
8 | import org.apache.http.HttpResponse;
9 | import org.apache.http.ProtocolVersion;
10 | import org.apache.http.client.HttpClient;
11 | import org.apache.http.client.methods.HttpPost;
12 | import org.apache.http.client.methods.HttpUriRequest;
13 | import org.apache.http.entity.StringEntity;
14 | import org.apache.http.message.BasicHttpResponse;
15 | import org.apache.http.message.BasicStatusLine;
16 | import org.json.JSONException;
17 | import org.json.JSONObject;
18 | import org.junit.Before;
19 | import org.junit.Test;
20 | import org.mockito.ArgumentCaptor;
21 |
22 | import java.io.IOException;
23 | import java.io.InputStreamReader;
24 | import java.net.URI;
25 |
26 | import static com.nightscout.core.test.MockFactory.mockCalRecord;
27 | import static com.nightscout.core.test.MockFactory.mockDeviceStatus;
28 | import static com.nightscout.core.test.MockFactory.mockGlucoseDataSet;
29 | import static com.nightscout.core.test.MockFactory.mockMeterRecord;
30 | import static org.hamcrest.MatcherAssert.assertThat;
31 | import static org.hamcrest.Matchers.containsString;
32 | import static org.hamcrest.Matchers.is;
33 | import static org.hamcrest.Matchers.not;
34 | import static org.hamcrest.Matchers.nullValue;
35 | import static org.mockito.Mockito.mock;
36 | import static org.mockito.Mockito.reset;
37 | import static org.mockito.Mockito.verifyNoMoreInteractions;
38 | import static org.mockito.Mockito.when;
39 |
40 | public class RestLegacyUploaderTest {
41 | RestLegacyUploader restUploader;
42 | HttpClient mockHttpClient;
43 | ArgumentCaptor captor;
44 | private TestPreferences preferences;
45 |
46 | @Before
47 | public void setUp() throws Exception {
48 | preferences = new TestPreferences();
49 | restUploader = new RestLegacyUploader(preferences, URI.create("http://test.com/"));
50 | mockHttpClient = mock(HttpClient.class);
51 | restUploader.setClient(mockHttpClient);
52 | setUpExecuteCaptor();
53 | }
54 |
55 | public void setUpExecuteCaptor() throws IOException {
56 | setUpExecuteCaptor(200);
57 | }
58 |
59 | public void setUpExecuteCaptor(int status) throws IOException {
60 | captor = ArgumentCaptor.forClass(HttpUriRequest.class);
61 | HttpResponse response = new BasicHttpResponse(
62 | new BasicStatusLine(new ProtocolVersion("mock", 1, 2), status, ""));
63 | response.setEntity(new StringEntity(""));
64 | when(mockHttpClient.execute(captor.capture())).thenReturn(response);
65 | }
66 |
67 | public static void verifyGlucoseDataSet(JSONObject jsonObject)
68 | throws JSONException {
69 | assertThat(jsonObject.getString("device"), is("dexcom"));
70 | assertThat(jsonObject.get("date"), is(not(nullValue())));
71 | assertThat(jsonObject.get("dateString"), is(not(nullValue())));
72 | assertThat(jsonObject.get("sgv"), is(not(nullValue())));
73 | assertThat(jsonObject.get("direction"), is(not(nullValue())));
74 | }
75 |
76 | public static void verifyDeviceStatus(JSONObject jsonObject, AbstractUploaderDevice deviceStatus)
77 | throws JSONException {
78 | assertThat(jsonObject.getInt("uploaderBattery"), is(deviceStatus.getBatteryLevel()));
79 | }
80 |
81 | @Test
82 | public void testGlucoseDataSet_Endpoint() throws Exception {
83 | restUploader.uploadGlucoseDataSets(Lists.newArrayList(mockGlucoseDataSet()));
84 | assertThat(captor.getValue().getURI().toString(), containsString("entries"));
85 | }
86 |
87 | @Test
88 | public void testGlucoseDataSet_Entity() throws Exception {
89 | restUploader.uploadGlucoseDataSets(Lists.newArrayList(mockGlucoseDataSet()));
90 | HttpPost post = (HttpPost) captor.getValue();
91 | String entity = CharStreams.toString(new InputStreamReader(post.getEntity().getContent()));
92 | verifyGlucoseDataSet(new JSONObject(entity));
93 | }
94 |
95 | @Test
96 | public void testMeterRecord_NoPost() throws Exception {
97 | reset(mockHttpClient);
98 | verifyNoMoreInteractions(mockHttpClient);
99 | restUploader.uploadMeterRecords(Lists.newArrayList(mockMeterRecord()));
100 | }
101 |
102 | @Test
103 | public void testCalRecord_NoPost() throws Exception {
104 | preferences.setCalibrationUploadEnabled(true);
105 | reset(mockHttpClient);
106 | verifyNoMoreInteractions(mockHttpClient);
107 | restUploader.uploadCalRecords(Lists.newArrayList(mockCalRecord()));
108 | }
109 |
110 | @Test
111 | public void testDeviceStatus_Endpoint() throws Exception {
112 | restUploader.uploadDeviceStatus(mockDeviceStatus());
113 | assertThat(captor.getValue().getURI().toString(), containsString("devicestatus"));
114 | }
115 |
116 | @Test
117 | public void testDeviceStatus_Entity() throws Exception {
118 | AbstractUploaderDevice deviceStatus = mockDeviceStatus();
119 | restUploader.uploadDeviceStatus(deviceStatus);
120 | HttpPost post = (HttpPost) captor.getValue();
121 | String entity = CharStreams.toString(new InputStreamReader(post.getEntity().getContent()));
122 | verifyDeviceStatus(new JSONObject(entity), deviceStatus);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/app/src/test/java/com/nightscout/android/settings/SettingsActivityTest.java:
--------------------------------------------------------------------------------
1 | package com.nightscout.android.settings;
2 |
3 | import android.preference.EditTextPreference;
4 | import android.preference.PreferenceFragment;
5 |
6 | import com.nightscout.android.R;
7 | import com.nightscout.android.preferences.PreferenceKeys;
8 | import com.nightscout.android.test.RobolectricTestBase;
9 |
10 | import org.junit.Test;
11 | import org.robolectric.shadows.ShadowAlertDialog;
12 | import org.robolectric.util.FragmentTestUtil;
13 |
14 | import static org.hamcrest.Matchers.is;
15 | import static org.hamcrest.Matchers.not;
16 | import static org.hamcrest.Matchers.nullValue;
17 | import static org.junit.Assert.assertThat;
18 |
19 | public class SettingsActivityTest extends RobolectricTestBase {
20 |
21 | private PreferenceFragment setUpPreferenceFragment(Class extends PreferenceFragment> clazz) {
22 | PreferenceFragment instance;
23 | try {
24 | instance = clazz.newInstance();
25 | } catch (InstantiationException | IllegalAccessException e) {
26 | throw new RuntimeException(e);
27 | }
28 | FragmentTestUtil.startVisibleFragment(instance);
29 | return instance;
30 | }
31 |
32 | @Test
33 | public void testValidation_RestApi_Invalid() {
34 | EditTextPreference editTextPreference = (EditTextPreference) setUpPreferenceFragment(
35 | SettingsActivity.MainPreferenceFragment.class)
36 | .findPreference(PreferenceKeys.API_URIS);
37 | assertThat(editTextPreference.getOnPreferenceChangeListener()
38 | .onPreferenceChange(editTextPreference, "\\invalidUri"), is(false));
39 | }
40 |
41 | @Test
42 | public void testValidation_RestApi_MultipleInvalid() {
43 | EditTextPreference editTextPreference = (EditTextPreference) setUpPreferenceFragment(
44 | SettingsActivity.MainPreferenceFragment.class)
45 | .findPreference(PreferenceKeys.API_URIS);
46 | assertThat(editTextPreference.getOnPreferenceChangeListener()
47 | .onPreferenceChange(editTextPreference, "http://example.com \\invalidUri"),
48 | is(false));
49 | }
50 |
51 | @Test
52 | public void testValidation_RestApi_MultipleValid() {
53 | EditTextPreference editTextPreference = (EditTextPreference) setUpPreferenceFragment(
54 | SettingsActivity.MainPreferenceFragment.class)
55 | .findPreference(PreferenceKeys.API_URIS);
56 | assertThat(editTextPreference.getOnPreferenceChangeListener()
57 | .onPreferenceChange(editTextPreference, "http://example.com validUri.com"),
58 | is(true));
59 | }
60 |
61 | @Test
62 | public void testValidation_RestApi_Valid() {
63 | EditTextPreference editTextPreference = (EditTextPreference) setUpPreferenceFragment(
64 | SettingsActivity.MainPreferenceFragment.class)
65 | .findPreference(PreferenceKeys.API_URIS);
66 | assertThat(editTextPreference.getOnPreferenceChangeListener()
67 | .onPreferenceChange(editTextPreference, "http://example.com"), is(true));
68 | }
69 |
70 | @Test
71 | public void testAlert_RestApi_InvalidShowsDialog() {
72 | EditTextPreference editTextPreference = (EditTextPreference) setUpPreferenceFragment(
73 | SettingsActivity.MainPreferenceFragment.class)
74 | .findPreference(PreferenceKeys.API_URIS);
75 | editTextPreference.getOnPreferenceChangeListener()
76 | .onPreferenceChange(editTextPreference, "\\invalidUri");
77 | ShadowAlertDialog alertDialog = getShadowApplication().getLatestAlertDialog();
78 | assertThat(alertDialog, is(not(nullValue())));
79 | assertThat(alertDialog.getMessage().toString(),
80 | is(getContext().getString(R.string.invalid_rest_uri, "\\invalidUri")));
81 | }
82 |
83 | @Test
84 | public void testValidation_Mongo_Invalid() {
85 | EditTextPreference editTextPreference = (EditTextPreference) setUpPreferenceFragment(
86 | SettingsActivity.MainPreferenceFragment.class)
87 | .findPreference(PreferenceKeys.MONGO_URI);
88 | assertThat(editTextPreference.getOnPreferenceChangeListener()
89 | .onPreferenceChange(editTextPreference, "invalidMongo"), is(false));
90 | }
91 |
92 | @Test
93 | public void testValidation_Mongo_Valid() {
94 | EditTextPreference editTextPreference = (EditTextPreference) setUpPreferenceFragment(
95 | SettingsActivity.MainPreferenceFragment.class)
96 | .findPreference(PreferenceKeys.MONGO_URI);
97 | assertThat(editTextPreference.getOnPreferenceChangeListener()
98 | .onPreferenceChange(editTextPreference, "mongodb://example.com"), is(true));
99 | }
100 |
101 | @Test
102 | public void testAlert_Mongo_InvalidShowsDialog() {
103 | EditTextPreference editTextPreference = (EditTextPreference) setUpPreferenceFragment(
104 | SettingsActivity.MainPreferenceFragment.class)
105 | .findPreference(PreferenceKeys.MONGO_URI);
106 | editTextPreference.getOnPreferenceChangeListener()
107 | .onPreferenceChange(editTextPreference, "invalidMongo");
108 | ShadowAlertDialog alertDialog = getShadowApplication().getLatestAlertDialog();
109 | assertThat(alertDialog, is(not(nullValue())));
110 | assertThat(alertDialog.getMessage().toString(),
111 | is(getContext().getString(R.string.illegal_mongo_uri)));
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/CalibrationEntry.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.Message;
6 | import com.squareup.wire.ProtoField;
7 |
8 | import static com.squareup.wire.Message.Datatype.DOUBLE;
9 | import static com.squareup.wire.Message.Datatype.UINT64;
10 | import static com.squareup.wire.Message.Label.REQUIRED;
11 |
12 | public final class CalibrationEntry extends Message {
13 |
14 | public static final Double DEFAULT_SLOPE = 0D;
15 | public static final Double DEFAULT_INTERCEPT = 0D;
16 | public static final Double DEFAULT_SCALE = 0D;
17 | public static final Double DEFAULT_DECAY = 0D;
18 | public static final Long DEFAULT_SYS_TIMESTAMP_SEC = 0L;
19 | public static final Long DEFAULT_DISP_TIMESTAMP_SEC = 0L;
20 |
21 | @ProtoField(tag = 1, type = DOUBLE, label = REQUIRED)
22 | public final Double slope;
23 |
24 | @ProtoField(tag = 2, type = DOUBLE)
25 | public final Double intercept;
26 |
27 | @ProtoField(tag = 3, type = DOUBLE)
28 | public final Double scale;
29 |
30 | @ProtoField(tag = 4, type = DOUBLE)
31 | public final Double decay;
32 |
33 | @ProtoField(tag = 5, type = UINT64)
34 | public final Long sys_timestamp_sec;
35 |
36 | /**
37 | * System timestamp - Timestamp representing the internal clock of the receiver
38 | */
39 | @ProtoField(tag = 6, type = UINT64)
40 | public final Long disp_timestamp_sec;
41 |
42 | public CalibrationEntry(Double slope, Double intercept, Double scale, Double decay, Long sys_timestamp_sec, Long disp_timestamp_sec) {
43 | this.slope = slope;
44 | this.intercept = intercept;
45 | this.scale = scale;
46 | this.decay = decay;
47 | this.sys_timestamp_sec = sys_timestamp_sec;
48 | this.disp_timestamp_sec = disp_timestamp_sec;
49 | }
50 |
51 | private CalibrationEntry(Builder builder) {
52 | this(builder.slope, builder.intercept, builder.scale, builder.decay, builder.sys_timestamp_sec, builder.disp_timestamp_sec);
53 | setBuilder(builder);
54 | }
55 |
56 | @Override
57 | public boolean equals(Object other) {
58 | if (other == this) return true;
59 | if (!(other instanceof CalibrationEntry)) return false;
60 | CalibrationEntry o = (CalibrationEntry) other;
61 | return equals(slope, o.slope)
62 | && equals(intercept, o.intercept)
63 | && equals(scale, o.scale)
64 | && equals(decay, o.decay)
65 | && equals(sys_timestamp_sec, o.sys_timestamp_sec)
66 | && equals(disp_timestamp_sec, o.disp_timestamp_sec);
67 | }
68 |
69 | @Override
70 | public int hashCode() {
71 | int result = hashCode;
72 | if (result == 0) {
73 | result = slope != null ? slope.hashCode() : 0;
74 | result = result * 37 + (intercept != null ? intercept.hashCode() : 0);
75 | result = result * 37 + (scale != null ? scale.hashCode() : 0);
76 | result = result * 37 + (decay != null ? decay.hashCode() : 0);
77 | result = result * 37 + (sys_timestamp_sec != null ? sys_timestamp_sec.hashCode() : 0);
78 | result = result * 37 + (disp_timestamp_sec != null ? disp_timestamp_sec.hashCode() : 0);
79 | hashCode = result;
80 | }
81 | return result;
82 | }
83 |
84 | public static final class Builder extends Message.Builder {
85 |
86 | public Double slope;
87 | public Double intercept;
88 | public Double scale;
89 | public Double decay;
90 | public Long sys_timestamp_sec;
91 | public Long disp_timestamp_sec;
92 |
93 | public Builder() {
94 | }
95 |
96 | public Builder(CalibrationEntry message) {
97 | super(message);
98 | if (message == null) return;
99 | this.slope = message.slope;
100 | this.intercept = message.intercept;
101 | this.scale = message.scale;
102 | this.decay = message.decay;
103 | this.sys_timestamp_sec = message.sys_timestamp_sec;
104 | this.disp_timestamp_sec = message.disp_timestamp_sec;
105 | }
106 |
107 | public Builder slope(Double slope) {
108 | this.slope = slope;
109 | return this;
110 | }
111 |
112 | public Builder intercept(Double intercept) {
113 | this.intercept = intercept;
114 | return this;
115 | }
116 |
117 | public Builder scale(Double scale) {
118 | this.scale = scale;
119 | return this;
120 | }
121 |
122 | public Builder decay(Double decay) {
123 | this.decay = decay;
124 | return this;
125 | }
126 |
127 | public Builder sys_timestamp_sec(Long sys_timestamp_sec) {
128 | this.sys_timestamp_sec = sys_timestamp_sec;
129 | return this;
130 | }
131 |
132 | /**
133 | * System timestamp - Timestamp representing the internal clock of the receiver
134 | */
135 | public Builder disp_timestamp_sec(Long disp_timestamp_sec) {
136 | this.disp_timestamp_sec = disp_timestamp_sec;
137 | return this;
138 | }
139 |
140 | @Override
141 | public CalibrationEntry build() {
142 | checkRequiredFields();
143 | return new CalibrationEntry(this);
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 |
6 | dependencies {
7 | classpath 'org.robolectric:robolectric-gradle-plugin:0.14.0'
8 | }
9 | }
10 |
11 | apply plugin: 'com.android.application'
12 | apply plugin: 'robolectric'
13 |
14 | def gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()
15 | def buildTime = new Date().format("yyyy-MM-dd'T'HH:mm'Z'", TimeZone.getTimeZone("UTC"))
16 | def codeName = "Dreamsicle"
17 |
18 | android {
19 | compileSdkVersion 21
20 | buildToolsVersion '21.1.1'
21 | defaultConfig {
22 | applicationId "com.nightscout.android"
23 | minSdkVersion 12
24 | targetSdkVersion 18
25 | versionCode 22
26 | versionName "0.1.13"
27 | buildConfigField "String", "GIT_SHA", "\"${gitSha}\""
28 | buildConfigField "String", "BUILD_TIME", "\"${buildTime}\""
29 | buildConfigField "String", "VERSION_CODENAME", "\"${codeName}\""
30 | }
31 | lintOptions {
32 | quiet false
33 | checkAllWarnings true
34 | warningsAsErrors false
35 | abortOnError true
36 | textReport true
37 | textOutput 'stdout'
38 | htmlOutput file("lint-report.html")
39 | htmlReport true
40 | checkReleaseBuilds true
41 | warning 'ProtectedPermissions', 'InvalidPackage'
42 | }
43 | packagingOptions {
44 | exclude 'META-INF/LICENSE'
45 | exclude 'META-INF/LICENSE.txt'
46 | }
47 | compileOptions {
48 | sourceCompatibility JavaVersion.VERSION_1_7
49 | targetCompatibility JavaVersion.VERSION_1_7
50 | }
51 | sourceSets {
52 | androidTest {
53 | setRoot('src/test')
54 | }
55 | }
56 | }
57 |
58 | robolectric {
59 | include '**/*Test.class'
60 | exclude '**/espresso/**/*.class'
61 | }
62 |
63 | repositories {
64 | maven { url "https://raw.github.com/embarkmobile/zxing-android-minimal/mvn-repo/maven-repository/" }
65 | maven { url "https://oss.sonatype.org/content/groups/public/" }
66 | }
67 |
68 | dependencies {
69 | compile(project(':core')) {
70 | // JSON already included by adk.
71 | exclude group: 'org.json'
72 | // HttpClient already included by adk.
73 | exclude group: 'org.apache.httpcomponents'
74 | exclude group: 'joda-time', module: 'joda-time'
75 | }
76 | compile 'ch.acra:acra:4.6.1'
77 | compile 'com.android.support:appcompat-v7:20.0.0'
78 | compile 'com.android.support:support-v4:20.0.0'
79 | compile 'com.google.android.gms:play-services-base:6.5.87'
80 | compile 'org.mongodb:mongo-java-driver:2.10.1'
81 | compile 'net.danlew:android.joda:2.6.0'
82 | compile 'com.google.guava:guava:18.0'
83 | // compile 'com.noveogroup.android:android-logger:1.3.4'
84 | compile 'com.embarkmobile:zxing-android-minimal:2.0.0@aar'
85 | compile 'com.embarkmobile:zxing-android-legacy:2.0.0@aar'
86 | compile 'com.embarkmobile:zxing-android-integration:2.0.0@aar'
87 | compile 'com.google.zxing:core:3.0.1'
88 | compile 'com.getpebble:pebblekit:2.6.0@aar'
89 | androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
90 | androidTestCompile 'org.mockito:mockito-core:1.9.5'
91 | androidTestCompile('junit:junit:4.11') {
92 | exclude module: 'hamcrest-core'
93 | }
94 | androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
95 | androidTestCompile 'org.mockito:mockito-core:1.9.5'
96 | androidTestCompile('org.robolectric:robolectric:2.4') {
97 | exclude module: 'classworlds'
98 | exclude module: 'commons-logging'
99 | exclude module: 'httpclient'
100 | exclude module: 'maven-artifact'
101 | exclude module: 'maven-artifact-manager'
102 | exclude module: 'maven-error-diagnostics'
103 | exclude module: 'maven-model'
104 | exclude module: 'maven-project'
105 | exclude module: 'maven-settings'
106 | exclude module: 'plexus-container-default'
107 | exclude module: 'plexus-interpolation'
108 | exclude module: 'plexus-utils'
109 | exclude module: 'wagon-file'
110 | exclude module: 'wagon-http-lightweight'
111 | exclude module: 'wagon-provider-api'
112 | }
113 | }
114 |
115 | apply plugin: 'idea'
116 |
117 | idea {
118 | module {
119 | testOutputDir = file('build/test-classes/debug')
120 | }
121 | }
122 |
123 | // Force 'app:test' to run after 'core:test'. This way, we fail fast if something is broken with
124 | // core.
125 | test.mustRunAfter ":core:test"
126 |
127 | apply plugin: 'jacoco'
128 |
129 | jacoco {
130 | toolVersion = "0.7.1.201405082137"
131 | reportsDir = file("$buildDir/jacoco")
132 | }
133 |
134 | sourceSets {
135 | main {
136 | java.srcDirs = ['src']
137 | }
138 | }
139 |
140 | def coverageSourceDirs = [
141 | '../app/src/main/java'
142 | ]
143 |
144 | task jacocoTestReport(type:JacocoReport, dependsOn: "testDebug") {
145 | group = "Reporting"
146 |
147 | description = "Generate Jacoco app coverage reports"
148 |
149 | classDirectories = fileTree(
150 | dir: '../app/build/intermediates/classes/debug',
151 | excludes: ['**/R.class',
152 | '**/R$*.class',
153 | '**/*$ViewInjector*.*',
154 | '**/BuildConfig.*',
155 | '**/USB/*.*',
156 | '**/proto/*.*',
157 | '**/Manifest*.*']
158 | )
159 |
160 | additionalSourceDirs = files(coverageSourceDirs)
161 | sourceDirectories = files(coverageSourceDirs)
162 | executionData = files('../app/build/jacoco/testDebug.exec')
163 |
164 | reports {
165 | xml.enabled = true
166 | html.enabled = true
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/core/src/main/java/com/nightscout/core/model/SensorGlucoseValueEntry.java:
--------------------------------------------------------------------------------
1 | // Code generated by Wire protocol buffer compiler, do not edit.
2 | // Source file: /Users/klee/Projects/Nightscout/android-uploader/core/src/main/java/com/nightscout/core/model/Download.proto
3 | package com.nightscout.core.model;
4 |
5 | import com.squareup.wire.Message;
6 | import com.squareup.wire.ProtoField;
7 |
8 | import static com.squareup.wire.Message.Datatype.ENUM;
9 | import static com.squareup.wire.Message.Datatype.UINT32;
10 | import static com.squareup.wire.Message.Datatype.UINT64;
11 | import static com.squareup.wire.Message.Label.REQUIRED;
12 |
13 | public final class SensorGlucoseValueEntry extends Message {
14 |
15 | public static final Integer DEFAULT_SGV_MGDL = 0;
16 | public static final Long DEFAULT_SYS_TIMESTAMP_SEC = 0L;
17 | public static final Long DEFAULT_DISP_TIMESTAMP_SEC = 0L;
18 | public static final G4Trend DEFAULT_TREND = G4Trend.TREND_NONE;
19 | public static final G4Noise DEFAULT_NOISE = G4Noise.NOISE_NONE;
20 |
21 | @ProtoField(tag = 1, type = UINT32, label = REQUIRED)
22 | public final Integer sgv_mgdl;
23 |
24 | /**
25 | * Sensor Glucose Value
26 | */
27 | @ProtoField(tag = 2, type = UINT64)
28 | public final Long sys_timestamp_sec;
29 |
30 | /**
31 | * System timestamp - Timestamp representing the internal clock of the receiver
32 | */
33 | @ProtoField(tag = 3, type = UINT64)
34 | public final Long disp_timestamp_sec;
35 |
36 | /**
37 | * Display timestamp - Timestamp representing the user configured time displayed on the receiver
38 | */
39 | @ProtoField(tag = 4, type = ENUM)
40 | public final G4Trend trend;
41 |
42 | /**
43 | * G4 Glucose trend arrow
44 | */
45 | @ProtoField(tag = 5, type = ENUM)
46 | public final G4Noise noise;
47 |
48 | public SensorGlucoseValueEntry(Integer sgv_mgdl, Long sys_timestamp_sec, Long disp_timestamp_sec, G4Trend trend, G4Noise noise) {
49 | this.sgv_mgdl = sgv_mgdl;
50 | this.sys_timestamp_sec = sys_timestamp_sec;
51 | this.disp_timestamp_sec = disp_timestamp_sec;
52 | this.trend = trend;
53 | this.noise = noise;
54 | }
55 |
56 | private SensorGlucoseValueEntry(Builder builder) {
57 | this(builder.sgv_mgdl, builder.sys_timestamp_sec, builder.disp_timestamp_sec, builder.trend, builder.noise);
58 | setBuilder(builder);
59 | }
60 |
61 | @Override
62 | public boolean equals(Object other) {
63 | if (other == this) return true;
64 | if (!(other instanceof SensorGlucoseValueEntry)) return false;
65 | SensorGlucoseValueEntry o = (SensorGlucoseValueEntry) other;
66 | return equals(sgv_mgdl, o.sgv_mgdl)
67 | && equals(sys_timestamp_sec, o.sys_timestamp_sec)
68 | && equals(disp_timestamp_sec, o.disp_timestamp_sec)
69 | && equals(trend, o.trend)
70 | && equals(noise, o.noise);
71 | }
72 |
73 | @Override
74 | public int hashCode() {
75 | int result = hashCode;
76 | if (result == 0) {
77 | result = sgv_mgdl != null ? sgv_mgdl.hashCode() : 0;
78 | result = result * 37 + (sys_timestamp_sec != null ? sys_timestamp_sec.hashCode() : 0);
79 | result = result * 37 + (disp_timestamp_sec != null ? disp_timestamp_sec.hashCode() : 0);
80 | result = result * 37 + (trend != null ? trend.hashCode() : 0);
81 | result = result * 37 + (noise != null ? noise.hashCode() : 0);
82 | hashCode = result;
83 | }
84 | return result;
85 | }
86 |
87 | public static final class Builder extends Message.Builder {
88 |
89 | public Integer sgv_mgdl;
90 | public Long sys_timestamp_sec;
91 | public Long disp_timestamp_sec;
92 | public G4Trend trend;
93 | public G4Noise noise;
94 |
95 | public Builder() {
96 | }
97 |
98 | public Builder(SensorGlucoseValueEntry message) {
99 | super(message);
100 | if (message == null) return;
101 | this.sgv_mgdl = message.sgv_mgdl;
102 | this.sys_timestamp_sec = message.sys_timestamp_sec;
103 | this.disp_timestamp_sec = message.disp_timestamp_sec;
104 | this.trend = message.trend;
105 | this.noise = message.noise;
106 | }
107 |
108 | public Builder sgv_mgdl(Integer sgv_mgdl) {
109 | this.sgv_mgdl = sgv_mgdl;
110 | return this;
111 | }
112 |
113 | /**
114 | * Sensor Glucose Value
115 | */
116 | public Builder sys_timestamp_sec(Long sys_timestamp_sec) {
117 | this.sys_timestamp_sec = sys_timestamp_sec;
118 | return this;
119 | }
120 |
121 | /**
122 | * System timestamp - Timestamp representing the internal clock of the receiver
123 | */
124 | public Builder disp_timestamp_sec(Long disp_timestamp_sec) {
125 | this.disp_timestamp_sec = disp_timestamp_sec;
126 | return this;
127 | }
128 |
129 | /**
130 | * Display timestamp - Timestamp representing the user configured time displayed on the receiver
131 | */
132 | public Builder trend(G4Trend trend) {
133 | this.trend = trend;
134 | return this;
135 | }
136 |
137 | /**
138 | * G4 Glucose trend arrow
139 | */
140 | public Builder noise(G4Noise noise) {
141 | this.noise = noise;
142 | return this;
143 | }
144 |
145 | @Override
146 | public SensorGlucoseValueEntry build() {
147 | checkRequiredFields();
148 | return new SensorGlucoseValueEntry(this);
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------