diff --git a/app/build.gradle b/app/build.gradle index 027d93daafacb3fecbcc0d36f3fc6073a5eeb215..742d781f311bc64d8087b7db300892313e22c6af 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,6 +7,7 @@ buildscript { dependencies { classpath 'io.fabric.tools:gradle:1.21.6' + classpath 'io.realm:realm-gradle-plugin:2.3.1' classpath 'org.ajoberstar:grgit:1.5.0' } } @@ -154,11 +155,16 @@ dependencies { compile 'org.apache.commons:commons-lang3:3.4' compile 'com.mikepenz:google-material-typeface:2.2.0.1.original@aar' compile 'uk.co.chrisjenx:calligraphy:2.2.0' - compile 'com.bugfender.sdk:android:0.6.2' + compile 'com.bugfender.sdk:android:0.7.2' compile 'com.jjoe64:graphview:4.2.1' compile 'com.android.support:support-v4:23.4.0' compile 'com.google.code.gson:gson:2.7' compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' + compile 'com.squareup.okhttp3:okhttp:3.3.1' + compile 'com.squareup.okhttp3:logging-interceptor:3.3.1' compile 'com.google.android.gms:play-services-appindexing:8.4.0' + + + } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4f075c37ba2f7387d322e3ed95c22c04eb2b6368..2ea896197d19abb365b6bbd2baaa8357cdd3692d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -82,6 +82,11 @@ <receiver android:name=".upload.nightscout.NightscoutUploadReceiver" /> <receiver android:name=".xdrip_plus.XDripPlusUploadReceiver" /> + + <receiver android:name=".medtronic.service.MedtronicCnlAlarmReceiver"></receiver> + + + </application> </manifest> \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java index cfe52c2cf6dd3ea601171455a3b5ca1adf67b851..87adfa0667c404458262d91ce4f503e29dc9e0a1 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java @@ -81,6 +81,10 @@ public class PumpStatusEvent extends RealmObject { return CGM_TREND.valueOf(cgmTrend); } + public String getCgmTrendString() { + return cgmTrend; + } + public void setCgmTrend(CGM_TREND cgmTrend) { if (cgmTrend != null) this.cgmTrend = cgmTrend.name(); diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java new file mode 100644 index 0000000000000000000000000000000000000000..55a2120df7189c2faa87076382db1906fdd93f91 --- /dev/null +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java @@ -0,0 +1,169 @@ +package info.nightscout.android.upload.nightscout; + +import android.util.Log; + +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import info.nightscout.android.model.medtronicNg.PumpStatusEvent; +import info.nightscout.android.upload.nightscout.serializer.EntriesSerializer; + +import android.support.annotation.NonNull; +import info.nightscout.api.UploadApi; +import info.nightscout.api.GlucoseEndpoints; +import info.nightscout.api.BolusEndpoints.BolusEntry; +import info.nightscout.api.GlucoseEndpoints.GlucoseEntry; +import info.nightscout.api.BolusEndpoints; +import info.nightscout.api.DeviceEndpoints; +import info.nightscout.api.DeviceEndpoints.Iob; +import info.nightscout.api.DeviceEndpoints.Battery; +import info.nightscout.api.DeviceEndpoints.PumpStatus; +import info.nightscout.api.DeviceEndpoints.PumpInfo; +import info.nightscout.api.DeviceEndpoints.DeviceStatus; + +public class NightScoutUpload { + + private static final String TAG = NightscoutUploadIntentService.class.getSimpleName(); + private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); + + public NightScoutUpload () { + + } + + public Boolean doRESTUpload(String url, + String secret, + int uploaderBatteryLevel, + List<PumpStatusEvent> records) { + Boolean success = false; + try { + success = isUploaded(records, url, secret, uploaderBatteryLevel); + } catch (Exception e) { + Log.e(TAG, "Unable to do REST API Upload to: " + url, e); + } + return success; + } + + + private boolean isUploaded(List<PumpStatusEvent> records, + String baseURL, + String secret, + int uploaderBatteryLevel) throws Exception { + + UploadApi uploadApi = new UploadApi(baseURL, formToken(secret)); + + boolean eventsUploaded = uploadEvents(uploadApi.getGlucoseEndpoints(), + uploadApi.getBolusApi(), + records ); + + boolean deviceStatusUploaded = uploadDeviceStatus(uploadApi.getDeviceEndpoints(), + uploaderBatteryLevel, records); + + return eventsUploaded && deviceStatusUploaded; + } + + private boolean uploadEvents(GlucoseEndpoints glucoseEndpoints, + BolusEndpoints bolusEndpoints, + List<PumpStatusEvent> records ) throws Exception { + + + List<GlucoseEntry> glucoseEntries = new ArrayList<>(); + List<BolusEntry> bolusEntries = new ArrayList<>(); + + for (PumpStatusEvent record : records) { + + GlucoseEntry glucoseEntry = new GlucoseEntry(); + + glucoseEntry.setType("sgv"); + glucoseEntry.setDirection(EntriesSerializer.getDirectionStringStatus(record.getCgmTrend())); + glucoseEntry.setDevice(record.getDeviceName()); + glucoseEntry.setSgv(record.getSgv()); + glucoseEntry.setDate(record.getEventDate().getTime()); + glucoseEntry.setDateString(record.getEventDate().toString()); + + glucoseEntries.add(glucoseEntry); + + BolusEntry bolusEntry = new BolusEntry(); + + bolusEntry.setType("mbg"); + bolusEntry.setDate(record.getEventDate().getTime()); + bolusEntry.setDateString(record.getEventDate().toString()); + bolusEntry.setDevice(record.getDeviceName()); + bolusEntry.setMbg(record.getBolusWizardBGL()); + + bolusEntries.add(bolusEntry); + + } + + glucoseEndpoints.sendEntries(glucoseEntries).execute(); + bolusEndpoints.sendEntries(bolusEntries).execute(); + + + + return true; + } + + private boolean uploadDeviceStatus(DeviceEndpoints deviceEndpoints, + int uploaderBatteryLevel, + List<PumpStatusEvent> records) throws Exception { + + + List<DeviceStatus> deviceEntries = new ArrayList<>(); + for (PumpStatusEvent record : records) { + + Iob iob = new Iob(record.getPumpDate(), record.getActiveInsulin()); + Battery battery = new Battery(record.getBatteryPercentage()); + PumpStatus pumpstatus; + if (record.isBolusing()) { + pumpstatus = new PumpStatus(true, false, ""); + + } else if (record.isSuspended()) { + pumpstatus = new PumpStatus(false, true, ""); + } else { + pumpstatus = new PumpStatus(false, false, "normal"); + } + + PumpInfo pumpInfo = new PumpInfo( + ISO8601_DATE_FORMAT.format(record.getPumpDate()), + new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP), + iob, + battery, + pumpstatus + ); + + DeviceStatus deviceStatus = new DeviceStatus( + uploaderBatteryLevel, + record.getDeviceName(), + ISO8601_DATE_FORMAT.format(record.getPumpDate()), + pumpInfo + ); + + deviceEntries.add(deviceStatus); + } + + for (DeviceStatus status: deviceEntries) { + deviceEndpoints.sendDeviceStatus(status).execute(); + } + + return true ; + } + + @NonNull + private String formToken(String secret) throws NoSuchAlgorithmException, UnsupportedEncodingException { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + byte[] bytes = secret.getBytes("UTF-8"); + digest.update(bytes, 0, bytes.length); + bytes = digest.digest(); + StringBuilder sb = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + sb.append(String.format("%02x", b & 0xff)); + } + return sb.toString(); + } + +} diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutApi.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutApi.java index c613dfbb5112dad61676c85cd04ebe520c9b56af..07cba2e8fdbe2f7bc0ae100d436fdb619b78d910 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutApi.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutApi.java @@ -2,7 +2,6 @@ package info.nightscout.android.upload.nightscout; import retrofit2.Call; import retrofit2.http.GET; - /** * Created by lgoedhart on 26/06/2016. */ diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java index 596c6a480293b6aa6ea555085fcf3bb4905c1688..51174e3cfe59108f0b24fc827e0f620f6363e424 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java @@ -10,40 +10,19 @@ import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.BasicResponseHandler; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.json.JSONArray; -import org.json.JSONObject; - -import java.math.BigDecimal; -import java.net.URL; -import java.security.MessageDigest; -import java.text.SimpleDateFormat; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import info.nightscout.android.R; import info.nightscout.android.medtronic.MainActivity; import info.nightscout.android.model.medtronicNg.PumpStatusEvent; -import info.nightscout.android.upload.nightscout.serializer.EntriesSerializer; import io.realm.Realm; import io.realm.RealmResults; public class NightscoutUploadIntentService extends IntentService { private static final String TAG = NightscoutUploadIntentService.class.getSimpleName(); - private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); - private static final int SOCKET_TIMEOUT = 60 * 1000; - private static final int CONNECTION_TIMEOUT = 30 * 1000; + private Context mContext; private Realm mRealm; + private NightScoutUpload mNightScoutUpload; public NightscoutUploadIntentService() { super(NightscoutUploadIntentService.class.getName()); @@ -63,6 +42,8 @@ public class NightscoutUploadIntentService extends IntentService { Log.i(TAG, "onCreate called"); mContext = this.getBaseContext(); + mNightScoutUpload = new NightScoutUpload(); + } @Override @@ -84,7 +65,18 @@ public class NightscoutUploadIntentService extends IntentService { if (enableRESTUpload) { long start = System.currentTimeMillis(); Log.i(TAG, String.format("Starting upload of %s record using a REST API", records.size())); - doRESTUpload(prefs, records); + String urlSetting = prefs.getString(mContext.getString(R.string.preference_nightscout_url), ""); + String secretSetting = prefs.getString(mContext.getString(R.string.preference_api_secret), "YOURAPISECRET"); + int uploaderBatteryLevel = MainActivity.batLevel; + Boolean uploadSuccess = mNightScoutUpload.doRESTUpload(urlSetting, + secretSetting, uploaderBatteryLevel, records); + if (uploadSuccess) { + mRealm.beginTransaction(); + for (PumpStatusEvent updateRecord : records) { + updateRecord.setUploaded(true); + } + mRealm.commitTransaction(); + } Log.i(TAG, String.format("Finished upload of %s record using a REST API in %s ms", records.size(), System.currentTimeMillis() - start)); } } catch (Exception e) { @@ -98,217 +90,6 @@ public class NightscoutUploadIntentService extends IntentService { NightscoutUploadReceiver.completeWakefulIntent(intent); } - private void doRESTUpload(SharedPreferences prefs, RealmResults<PumpStatusEvent> records) { - String apiScheme = "https://"; - String apiUrl = ""; - String apiSecret = prefs.getString(mContext.getString(R.string.preference_api_secret), "YOURAPISECRET"); - - // TODO - this code needs to go to the Settings Activity. - // Add the extra match for "KEY@" to support the previous single field - Pattern p = Pattern.compile("(.*\\/\\/)?(.*@)?([^\\/]*)(.*)"); - Matcher m = p.matcher(prefs.getString(mContext.getString(R.string.preference_nightscout_url), "")); - - if (m.find()) { - apiUrl = m.group(3); - - // Only override apiSecret from URL (the "old" way), if the API secret preference is empty - if (apiSecret.equals("YOURAPISECRET") || apiSecret.equals("")) { - apiSecret = (m.group(2) == null) ? "" : m.group(2).replace("@", ""); - } - - // Override the URI scheme if it's been provided in the preference) - if (m.group(1) != null && !m.group(1).equals("")) { - apiScheme = m.group(1); - } - } - - // Update the preferences to match what we expect. Only really used from converting from the - // old format to the new format. Aren't we nice for managing backward compatibility? - prefs.edit().putString(mContext.getString(R.string.preference_api_secret), apiSecret).apply(); - prefs.edit().putString(mContext.getString(R.string.preference_nightscout_url), String.format("%s%s", apiScheme, apiUrl)).apply(); - - String uploadUrl = String.format("%s%s@%s/api/v1/", apiScheme, apiSecret, apiUrl); - - try { - doRESTUploadTo(uploadUrl, records); - } catch (Exception e) { - Log.e(TAG, "Unable to do REST API Upload to: " + uploadUrl, e); - } - } - - private void doRESTUploadTo(String baseURI, RealmResults<PumpStatusEvent> records) { - try { - String baseURL; - String secret = null; - String[] uriParts = baseURI.split("@"); - - if (uriParts.length == 1) { - throw new Exception("Starting with API v1, a pass phase is required"); - } else if (uriParts.length == 2) { - secret = uriParts[0]; - baseURL = uriParts[1]; - - // new format URL! - if (secret.contains("http")) { - if (secret.contains("https")) { - baseURL = "https://" + baseURL; - } else { - baseURL = "http://" + baseURL; - } - String[] uriParts2 = secret.split("//"); - secret = uriParts2[1]; - } - } else { - throw new Exception(String.format("Unexpected baseURI: %s, uriParts.length: %s", baseURI, uriParts.length)); - } - - JSONArray devicestatusBody = new JSONArray(); - JSONArray entriesBody = new JSONArray(); - - for (PumpStatusEvent record : records) { - addDeviceStatus(devicestatusBody, record); - addSgvEntry(entriesBody, record); - addMbgEntry(entriesBody, record); - } - - boolean isUploaded = uploadToNightscout(new URL(baseURL + "/entries"), secret, entriesBody); - - for(int i = 0; isUploaded && i < devicestatusBody.length(); i++) { - isUploaded &= uploadToNightscout(new URL(baseURL + "/devicestatus"), secret, devicestatusBody.getJSONObject(i)); - } - - if (isUploaded) { - // Yay! We uploaded. Tell Realm - // FIXME - check the upload succeeded! - mRealm.beginTransaction(); - for (PumpStatusEvent updateRecord : records) { - updateRecord.setUploaded(true); - } - mRealm.commitTransaction(); - } - - } catch (Exception e) { - Log.e(TAG, "Unable to post data", e); - } - } - - private boolean uploadToNightscout(URL endpoint, String secret, JSONObject httpBody) throws Exception { - return uploadToNightscout(endpoint, secret, httpBody.toString()); - } - - private boolean uploadToNightscout(URL endpoint, String secret, JSONArray httpBody) throws Exception { - return uploadToNightscout(endpoint, secret, httpBody.toString()); - } - - private boolean uploadToNightscout(URL endpoint, String secret, String httpBody) throws Exception { - Log.i(TAG, "postURL: " + endpoint.toString()); - - HttpPost post = new HttpPost(endpoint.toString()); - - if (secret == null || secret.isEmpty()) { - throw new Exception("Starting with API v1, a pass phase is required"); - } else { - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - byte[] bytes = secret.getBytes("UTF-8"); - digest.update(bytes, 0, bytes.length); - bytes = digest.digest(); - StringBuilder sb = new StringBuilder(bytes.length * 2); - for (byte b : bytes) { - sb.append(String.format("%02x", b & 0xff)); - } - String token = sb.toString(); - post.setHeader("api-secret", token); - } - - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT); - HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT); - - DefaultHttpClient httpclient = new DefaultHttpClient(params); - - Log.i(TAG, "Upload JSON: " + httpBody); - - try { - StringEntity se = new StringEntity(httpBody); - post.setEntity(se); - post.setHeader("Accept", "application/json"); - post.setHeader("Content-type", "application/json"); - - ResponseHandler responseHandler = new BasicResponseHandler(); - httpclient.execute(post, responseHandler); - } catch (Exception e) { - Log.w(TAG, "Unable to post data to: '" + post.getURI().toString() + "'", e); - return false; - } - - return true; - } - - private void addDeviceStatus(JSONArray devicestatusArray, PumpStatusEvent record) throws Exception { - JSONObject json = new JSONObject(); - json.put("uploaderBattery", MainActivity.batLevel); - json.put("device", record.getDeviceName()); - json.put("created_at", ISO8601_DATE_FORMAT.format(record.getPumpDate())); - - JSONObject pumpInfo = new JSONObject(); - pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(record.getPumpDate())); - pumpInfo.put("reservoir", new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP)); - - JSONObject iob = new JSONObject(); - iob.put("timestamp", record.getPumpDate()); - iob.put("bolusiob", record.getActiveInsulin()); - - JSONObject status = new JSONObject(); - if (record.isBolusing()) { - status.put("bolusing", true); - } else if (record.isSuspended()) { - status.put("suspended", true); - } else { - status.put("status", "normal"); - } - - JSONObject battery = new JSONObject(); - battery.put("percent", record.getBatteryPercentage()); - - pumpInfo.put("iob", iob); - pumpInfo.put("battery", battery); - pumpInfo.put("status", status); - - json.put("pump", pumpInfo); - String jsonString = json.toString(); - Log.i(TAG, "Device Status JSON: " + jsonString); - - devicestatusArray.put(json); - } - - private void addSgvEntry(JSONArray entriesArray, PumpStatusEvent pumpRecord) throws Exception { - JSONObject json = new JSONObject(); - // TODO replace with Retrofit/EntriesSerializer - json.put("sgv", pumpRecord.getSgv()); - json.put("direction", EntriesSerializer.getDirectionString(pumpRecord.getCgmTrend())); - json.put("device", pumpRecord.getDeviceName()); - json.put("type", "sgv"); - json.put("date", pumpRecord.getEventDate().getTime()); - json.put("dateString", pumpRecord.getEventDate()); - - entriesArray.put(json); - } - - private void addMbgEntry(JSONArray entriesArray, PumpStatusEvent pumpRecord) throws Exception { - if (pumpRecord.hasRecentBolusWizard()) { - JSONObject json = new JSONObject(); - - // TODO replace with Retrofit/EntriesSerializer - json.put("type", "mbg"); - json.put("mbg", pumpRecord.getBolusWizardBGL()); - json.put("device", pumpRecord.getDeviceName()); - json.put("date", pumpRecord.getEventDate().getTime()); - json.put("dateString", pumpRecord.getEventDate()); - - entriesArray.put(json); - } - } - private boolean isOnline() { ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); @@ -320,4 +101,6 @@ public class NightscoutUploadIntentService extends IntentService { public static final String EXTENDED_DATA = "info.nightscout.android.upload.nightscout.DATA"; } + + } diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/serializer/EntriesSerializer.java b/app/src/main/java/info/nightscout/android/upload/nightscout/serializer/EntriesSerializer.java index 260b8c3b2acab0a24e81dd6c35dee9f9872f4e21..ce79ab7900ef741ecb3c8f35550eebd019f34d69 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/serializer/EntriesSerializer.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/serializer/EntriesSerializer.java @@ -42,6 +42,35 @@ public class EntriesSerializer implements JsonSerializer<PumpStatusEvent> { } } + public static String getDirectionStringStatus(PumpStatusEvent.CGM_TREND trend) { + switch( trend ) { + case NONE: + return "NONE"; + case DOUBLE_UP: + return "DoubleUp"; + case SINGLE_UP: + return "SingleUp"; + case FOURTY_FIVE_UP: + return "FortyFiveUp"; + case FLAT: + return "Flat"; + case FOURTY_FIVE_DOWN: + return "FortyFiveDown"; + case SINGLE_DOWN: + return "SingleDown"; + case DOUBLE_DOWN: + return "DoubleDown"; + case NOT_COMPUTABLE: + return "NOT COMPUTABLE"; + case RATE_OUT_OF_RANGE: + return "RATE OUT OF RANGE"; + case NOT_SET: + return "NONE"; + default: + return "NOT COMPUTABLE"; // TODO - should this be something else? + } + } + @Override public JsonElement serialize(PumpStatusEvent src, Type typeOfSrc, JsonSerializationContext context) { final JsonObject jsonObject = new JsonObject(); diff --git a/app/src/main/java/info/nightscout/api/BolusEndpoints.java b/app/src/main/java/info/nightscout/api/BolusEndpoints.java new file mode 100644 index 0000000000000000000000000000000000000000..67d2291198532976c64d25eae16718087cb28f43 --- /dev/null +++ b/app/src/main/java/info/nightscout/api/BolusEndpoints.java @@ -0,0 +1,76 @@ +package info.nightscout.api; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.Headers; +import retrofit2.http.POST; + +public interface BolusEndpoints { + + class BolusEntry { + String type; + String dateString; + float date; + float mbg; + String device; + + public BolusEntry() { } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDateString() { + return dateString; + } + + public void setDateString(String dateString) { + this.dateString = dateString; + } + + public float getDate() { + return date; + } + + public void setDate(float date) { + this.date = date; + } + + public float getMbg() { + return mbg; + } + + public void setMbg(float mbg) { + this.mbg = mbg; + } + + public String getDevice() { + return device; + } + + public void setDevice(String device) { + this.device = device; + } + + } + + @Headers({ + "Accept: application/json", + "Content-type: application/json" + }) + @POST("/api/v1/entries") + Call<ResponseBody> sendEntries(@Body List<BolusEntry> entries); + +} + + + diff --git a/app/src/main/java/info/nightscout/api/DeviceEndpoints.java b/app/src/main/java/info/nightscout/api/DeviceEndpoints.java new file mode 100644 index 0000000000000000000000000000000000000000..1bf66b24336e790d69c464ed1acb419c029d433d --- /dev/null +++ b/app/src/main/java/info/nightscout/api/DeviceEndpoints.java @@ -0,0 +1,97 @@ +package info.nightscout.api; + +import java.math.BigDecimal; +import java.util.Date; + +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.Header; +import retrofit2.http.Headers; +import retrofit2.http.POST; + +public interface DeviceEndpoints { + + class Iob { + final Date timestamp; + final float bolusiob; + public Iob (Date timestamp, + float bolusiob) { + this.timestamp = timestamp; + this.bolusiob = bolusiob; + } + } + + class Battery { + final short percent; + public Battery(short percent) { + this.percent = percent; + } + } + + class PumpStatus { + final Boolean bolusing; + final Boolean suspended; + final String status; + public PumpStatus( + Boolean bolusing, + Boolean suspended, + String status + + ) { + this.bolusing = bolusing; + this.suspended = suspended; + this.status = status; + + } + } + + class PumpInfo { + final String clock; + final BigDecimal reservoir; + final Iob iob; + final Battery battery; + final PumpStatus status; + + public PumpInfo(String clock, + BigDecimal reservoir, + Iob iob, + Battery battery, + PumpStatus status) { + this.clock = clock; + this.reservoir = reservoir; + this.iob = iob; + this.battery = battery; + this.status = status; + + } + } + + class DeviceStatus { + final Integer uploaderBattery; + final String device; + final String created_at; + final PumpInfo pump; + + public DeviceStatus(Integer uploaderBattery, + String device, + String created_at, + PumpInfo pump) { + this.uploaderBattery = uploaderBattery; + this.device = device; + this.created_at = created_at; + this.pump = pump; + } + } + + @Headers({ + "Accept: application/json", + "Content-type: application/json" + }) + @POST("/api/v1/devicestatus") + Call<ResponseBody> sendDeviceStatus(@Body DeviceStatus deviceStatus); + +} + + + diff --git a/app/src/main/java/info/nightscout/api/GlucoseEndpoints.java b/app/src/main/java/info/nightscout/api/GlucoseEndpoints.java new file mode 100644 index 0000000000000000000000000000000000000000..07c1c10a8c2e60705ba348d7c9a0758216f74b1a --- /dev/null +++ b/app/src/main/java/info/nightscout/api/GlucoseEndpoints.java @@ -0,0 +1,89 @@ +package info.nightscout.api; + + + +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.Headers; +import retrofit2.http.POST; + +public interface GlucoseEndpoints { + + class GlucoseEntry { + + String type; + String dateString; + float date; + float sgv; + String direction; + String device; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDateString() { + return dateString; + } + + public void setDateString(String dateString) { + this.dateString = dateString; + } + + public float getDate() { + return date; + } + + public void setDate(float date) { + this.date = date; + } + + public float getSgv() { + return sgv; + } + + public void setSgv(float sgv) { + this.sgv = sgv; + } + + public String getDirection() { + return direction; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + public String getDevice() { + return device; + } + + public void setDevice(String device) { + this.device = device; + } + + public GlucoseEntry() { } + } + + @Headers({ + "Accept: application/json", + "Content-type: application/json" + }) + @POST("/api/v1/entries") + Call<ResponseBody> sendEntries(@Body List<GlucoseEntry> entries); + +} + + + diff --git a/app/src/main/java/info/nightscout/api/UploadApi.java b/app/src/main/java/info/nightscout/api/UploadApi.java new file mode 100644 index 0000000000000000000000000000000000000000..1c429b7379d21f7342a199b7c50d1ff87aebe043 --- /dev/null +++ b/app/src/main/java/info/nightscout/api/UploadApi.java @@ -0,0 +1,78 @@ +package info.nightscout.api; + + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public class UploadApi { + private Retrofit retrofit; + private GlucoseEndpoints glucoseEndpoints; + private BolusEndpoints bolusApi; + private DeviceEndpoints deviceEndpoints; + + public GlucoseEndpoints getGlucoseEndpoints() { + return glucoseEndpoints; + } + + public BolusEndpoints getBolusApi() { + return bolusApi; + } + + public DeviceEndpoints getDeviceEndpoints() { + return deviceEndpoints; + } + + public UploadApi(String baseURL, String token) { + + class AddAuthHeader implements Interceptor { + + private String token; + + public AddAuthHeader(String token) { + this.token = token; + } + + @Override + public Response intercept(Interceptor.Chain chain) throws IOException { + Request original = chain.request(); + + Request.Builder requestBuilder = original.newBuilder() + .header("api-secret", token) + .method( original.method(), original.body()); + + Request request = requestBuilder.build(); + return chain.proceed(request); + } + }; + + OkHttpClient.Builder okHttpClient = new OkHttpClient().newBuilder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(60, TimeUnit.SECONDS) + .writeTimeout(60, TimeUnit.SECONDS); + + okHttpClient.addInterceptor(new AddAuthHeader(token)); + + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + logging.setLevel(HttpLoggingInterceptor.Level.BODY); + okHttpClient.addInterceptor(logging); + + retrofit = new Retrofit.Builder() + .baseUrl(baseURL) + .client(okHttpClient.build()) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + + glucoseEndpoints = retrofit.create(GlucoseEndpoints.class); + bolusApi = retrofit.create(BolusEndpoints.class); + deviceEndpoints = retrofit.create(DeviceEndpoints.class); + + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 580e85d04a9bbe67a3b112aa026ec9c260224707..b403419f414cf3f94125d7f47ae87e0e7f14e5b7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,9 +25,11 @@ <string name="action_sign_in_short">Retrieve keys</string> <string name="error_invalid_password">Password is required</string> <string name="error_incorrect_password">The Username or password is incorrect</string> + <string name="error_client_protocol_exception">Could not communicate with server.</string> + <string name="error_io_exception">Could not connect to server.</string> + <string name="error_class_not_found_exception">Application code error.</string> + <string name="error_http_response">Server responded with error. Could be username or password problem.</string> <string name="error_field_required">This field is required</string> - <string name="preference_nightscout_url">Nightscout URL</string> - <string name="preference_api_secret">API SECRET</string> <string name="prompt_carelink_username_password">Please enter your CareLink details.\nThey will not be stored.</string> <string name="close">Close</string> <string name="register_contour_next_link">Registered Devices</string> @@ -51,6 +53,8 @@ <string name="dummy_content">DUMMY\nCONTENT</string> <string name="menu_name_status">Status</string> <string name="menu_name_battery_status">unknown</string> + <string name="preference_api_secret">YOUR.API.SECRET</string> + <string name="preference_nightscout_url">YOUR.NIGHTSCOUT.URL</string> <string name="preferences_poll_interval">Poll interval</string> <string name="preferences_low_battery_poll_interval">Poll interval on low pump battery</string> @@ -58,4 +62,5 @@ <string name="to_register_a_contour_next_link_you_must_first_plug_it_in_and_get_a_reading_from_the_pump">To register a Contour Next Link you must first plug it in, and get a reading from the pump.</string> <string name="serial_number">Serial number</string> <string name="preferences_chart_interval">Chart Zoom</string> + </resources>