diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9f14c5b58bb70a546b4920237d9960ba062fe9e5..ac8db432fffa7643d18b7adf5e42d03a321513d0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -67,6 +67,9 @@ <service android:name=".upload.nightscout.NightscoutUploadIntentService" android:icon="@drawable/ic_launcher" /> + <service + android:name=".xdrip_plus.XDripPlusUploadIntentService" + android:icon="@drawable/ic_launcher" /> <service android:name=".medtronic.service.MedtronicCnlIntentService" @@ -76,6 +79,7 @@ <receiver android:name=".medtronic.service.MedtronicCnlAlarmReceiver" /> <receiver android:name=".upload.nightscout.NightscoutUploadReceiver" /> + <receiver android:name=".xdrip_plus.XDripPlusUploadReceiver" /> </application> diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java index 1a1f246f4f3e4cd643b14ee0c4cf1b3e9484f6e7..d21499004116e0f974647842f09f01fcfbef576b 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java +++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java @@ -5,9 +5,12 @@ import android.app.IntentService; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; +import android.os.Build; +import android.preference.PreferenceManager; import android.support.v4.app.NotificationManagerCompat; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; @@ -17,6 +20,7 @@ import java.util.Date; import java.util.Locale; import java.util.concurrent.TimeoutException; +import info.nightscout.android.R; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MainActivity; import info.nightscout.android.medtronic.MedtronicCNLReader; @@ -28,6 +32,7 @@ import info.nightscout.android.model.medtronicNg.ContourNextLinkInfo; import info.nightscout.android.model.medtronicNg.PumpInfo; import info.nightscout.android.model.medtronicNg.PumpStatusEvent; import info.nightscout.android.upload.nightscout.NightscoutUploadReceiver; +import info.nightscout.android.xdrip_plus.XDripPlusUploadReceiver; import io.realm.Realm; import io.realm.RealmResults; @@ -274,11 +279,34 @@ public class MedtronicCnlIntentService extends IntentService { } // TODO - set status if offline or Nightscout not reachable + sendToXDrip(); uploadToNightscout(); MedtronicCnlAlarmReceiver.completeWakefulIntent(intent); } } + // reliable wake alarm manager wake up for all android versions + public static void wakeUpIntent(Context context, long wakeTime, PendingIntent pendingIntent) { + final AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, wakeTime, pendingIntent); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + alarm.setExact(AlarmManager.RTC_WAKEUP, wakeTime, pendingIntent); + } else + alarm.set(AlarmManager.RTC_WAKEUP, wakeTime, pendingIntent); + } + + private void sendToXDrip() { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + if (prefs.getBoolean(getString(R.string.preference_enable_xdrip_plus), false)) { + final Intent receiverIntent = new Intent(this, XDripPlusUploadReceiver.class); + final long timestamp = System.currentTimeMillis() + 500L; + final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, (int) timestamp, receiverIntent, PendingIntent.FLAG_ONE_SHOT); + Log.d(TAG,"Scheduling xDrip+ send"); + wakeUpIntent(getApplicationContext(), timestamp, pendingIntent); + } + } + private void uploadToNightscout() { AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent receiverIntent = new Intent(this, NightscoutUploadReceiver.class); diff --git a/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadIntentService.java b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadIntentService.java new file mode 100644 index 0000000000000000000000000000000000000000..6cc1a6f91b54362685926b06cb6f9072b78704bd --- /dev/null +++ b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadIntentService.java @@ -0,0 +1,171 @@ +package info.nightscout.android.xdrip_plus; + +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; + +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; +import io.realm.Sort; + +/** + * Created by jamorham on 17/11/2016. + */ + + +public class XDripPlusUploadIntentService extends IntentService { + + private static final String TAG = XDripPlusUploadIntentService.class.getSimpleName(); + private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); + Context mContext; + private Realm mRealm; + + public XDripPlusUploadIntentService() { + super(XDripPlusUploadIntentService.class.getName()); + } + + // status unused + protected void sendStatus(String message) { + Intent localIntent = + new Intent(info.nightscout.android.xdrip_plus.XDripPlusUploadIntentService.Constants.ACTION_STATUS_MESSAGE) + .putExtra(info.nightscout.android.xdrip_plus.XDripPlusUploadIntentService.Constants.EXTENDED_DATA, message); + LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent); + } + + @Override + public void onCreate() { + super.onCreate(); + + Log.i(TAG, "onCreate called"); + mContext = this.getBaseContext(); + } + + @Override + protected void onHandleIntent(Intent intent) { + Log.d(TAG, "onHandleIntent called"); + mRealm = Realm.getDefaultInstance(); + + RealmResults<PumpStatusEvent> all_records = mRealm + .where(PumpStatusEvent.class) + .notEqualTo("sgv", 0) + .findAllSorted("eventDate", Sort.DESCENDING); + + // get the most recent record and send that + if (all_records.size() > 0) { + List<PumpStatusEvent> records = all_records.subList(0, 1); + doXDripUpload(records); + } + XDripPlusUploadReceiver.completeWakefulIntent(intent); + } + + private void doXDripUpload(List<PumpStatusEvent> records) { + try { + + final JSONArray devicestatusBody = new JSONArray(); + final JSONArray entriesBody = new JSONArray(); + + for (PumpStatusEvent record : records) { + addDeviceStatus(devicestatusBody, record); + addSgvEntry(entriesBody, record); + addMbgEntry(entriesBody, record); + } + + if (entriesBody.length() > 0) sendBundle(mContext, "add", "entries", entriesBody); + if (devicestatusBody.length() > 0) + sendBundle(mContext, "add", "devicestatus", devicestatusBody); + + } catch (Exception e) { + Log.e(TAG, "Unable to send bundle: " + e); + } + } + + private void sendBundle(Context context, String action, String collection, JSONArray json) { + final Bundle bundle = new Bundle(); + bundle.putString("action", action); + bundle.putString("collection", collection); + bundle.putString("data", json.toString()); + final Intent intent = new Intent(Constants.XDRIP_PLUS_NS_EMULATOR); + intent.putExtras(bundle).addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + context.sendBroadcast(intent); + List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(intent, 0); + if (receivers.size() < 1) { + Log.e(TAG, "No receivers"); + } else Log.e(TAG, receivers.size() + " receivers"); + } + + + 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 battery = new JSONObject(); + battery.put("percent", record.getBatteryPercentage()); + + pumpInfo.put("iob", iob); + pumpInfo.put("battery", battery); + json.put("pump", pumpInfo); + //String jsonString = json.toString(); + + 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); + } + } + + + public final class Constants { + public static final String ACTION_STATUS_MESSAGE = "info.nightscout.android.xdrip_plus.STATUS_MESSAGE"; + public static final String EXTENDED_DATA = "info.nightscout.android.xdrip_plus.DATA"; + private static final String XDRIP_PLUS_NS_EMULATOR = "com.eveningoutpost.dexdrip.NS_EMULATOR"; + } +} diff --git a/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadReceiver.java b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadReceiver.java new file mode 100644 index 0000000000000000000000000000000000000000..734cd34d65542d827f26d5b81290ccb42023db47 --- /dev/null +++ b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadReceiver.java @@ -0,0 +1,22 @@ +package info.nightscout.android.xdrip_plus; + +import android.content.Context; +import android.content.Intent; +import android.support.v4.content.WakefulBroadcastReceiver; +import android.util.Log; + + +/** + * Created by jamorham on 17/11/2016. + */ +public class XDripPlusUploadReceiver extends WakefulBroadcastReceiver { + private static final String TAG = XDripPlusUploadReceiver.class.getSimpleName(); + + @Override + public void onReceive(final Context context, Intent intent) { + // Start the IntentService + Log.d(TAG, "Received broadcast message"); + Intent service = new Intent(context, XDripPlusUploadIntentService.class); + startWakefulService(context, service); + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bbb585428d3ae24f746f6ff16c7ccdb68440663e..bfa6c9df237c0f66af2c2e04aaa26bb6508d5841 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,6 +41,7 @@ <string name="button_text_start_uploading_data">Start Uploading CGM Data</string> <string name="preference_eula_accepted">IUNDERSTAND</string> <string name="preference_enable_rest_upload">EnableRESTUpload</string> + <string name="preference_enable_xdrip_plus">EnablexDripPlusUpload</string> <string name="error_msg_api_secret_length">API Secret must be 12 characters or longer.</string> <string name="text_unit_mmolxl">mmol/L</string> <string name="text_unit_mgxdl">mg/dL</string> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 8a8fb08d3852284649d8428d84e72fac42c4d557..9e6793c85ec60101ef37b053d3af5d9caefd74b1 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -39,6 +39,10 @@ android:dialogTitle="Enter your Nightscout API secret" android:key="@string/preference_api_secret" android:title="API Secret"/> + <CheckBoxPreference + android:key="@string/preference_enable_xdrip_plus" + android:summary="Enable local broadcast of data to xDrip+" + android:title="Send to xDrip+"/> </PreferenceCategory> <PreferenceCategory android:title="Disclaimer"> <SwitchPreference