diff --git a/.gitignore b/.gitignore
index fefd7fedd471e9bc46cb9cf599d499b9a0b394ee..a0722a1d135612b37f3243c980473c0408b138e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ workspace.xml
 fabric.properties
 local.properties
 .idea
+bugfender.properties
diff --git a/app/app.iml b/app/app.iml
index e21e32e18a1d39dd95c1e160e391b59f001dde94..841fa46dd6f3926ebd21f377de95524506efdd97 100644
--- a/app/app.iml
+++ b/app/app.iml
@@ -67,14 +67,6 @@
       <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
@@ -83,8 +75,17 @@
       <sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
@@ -94,7 +95,7 @@
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.4.0/jars" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.4.0/jars" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/23.4.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.bugfender.sdk/android/0.3.5/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.bugfender.sdk/android/0.4/jars" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.crashlytics.sdk.android/answers/1.3.6/jars" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.crashlytics.sdk.android/beta/1.1.4/jars" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.crashlytics.sdk.android/crashlytics-core/2.3.8/jars" />
@@ -109,19 +110,31 @@
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/io.realm/realm-android-library/1.0.0/jars" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/uk.co.chrisjenx/calligraphy/2.2.0/jars" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
       <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
       <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
     </content>
     <orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
     <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" exported="" name="okio-1.8.0" level="project" />
     <orderEntry type="library" exported="" name="support-annotations-23.4.0" level="project" />
     <orderEntry type="library" exported="" name="google-material-typeface-2.2.0.1.original" level="project" />
+    <orderEntry type="library" exported="" name="MPAndroidChart-v2.2.5" level="project" />
     <orderEntry type="library" exported="" name="okhttp-2.4.0" level="project" />
     <orderEntry type="library" exported="" name="relinker-1.2.1" level="project" />
     <orderEntry type="library" exported="" name="retrofit-1.9.0" level="project" />
@@ -129,24 +142,25 @@
     <orderEntry type="library" exported="" name="animated-vector-drawable-23.4.0" level="project" />
     <orderEntry type="library" exported="" name="commons-lang3-3.4" level="project" />
     <orderEntry type="library" exported="" name="support-v4-23.4.0" level="project" />
+    <orderEntry type="library" exported="" name="android-0.4" level="project" />
     <orderEntry type="library" exported="" name="recyclerview-v7-23.4.0" level="project" />
     <orderEntry type="library" exported="" name="slf4j-api-1.7.2" level="project" />
     <orderEntry type="library" exported="" name="support-vector-drawable-23.4.0" level="project" />
     <orderEntry type="library" exported="" name="materialize-0.8.8" level="project" />
-    <orderEntry type="library" exported="" name="okio-1.4.0" level="project" />
     <orderEntry type="library" exported="" name="realm-android-library-1.0.0" level="project" />
     <orderEntry type="library" exported="" name="appcompat-v7-23.4.0" level="project" />
     <orderEntry type="library" exported="" name="fabric-1.3.10" level="project" />
     <orderEntry type="library" exported="" name="design-23.4.0" level="project" />
-    <orderEntry type="library" exported="" name="android-0.3.5" level="project" />
-    <orderEntry type="library" exported="" name="gson-2.4" level="project" />
     <orderEntry type="library" exported="" name="crashlytics-2.5.5" level="project" />
     <orderEntry type="library" exported="" name="crashlytics-core-2.3.8" level="project" />
     <orderEntry type="library" exported="" name="beta-1.1.4" level="project" />
     <orderEntry type="library" exported="" name="fastadapter-1.5.2" level="project" />
-    <orderEntry type="library" exported="" name="logback-android-1.1.1-3" level="project" />
+    <orderEntry type="library" exported="" name="retrofit-2.1.0" level="project" />
+    <orderEntry type="library" exported="" name="gson-2.7" level="project" />
+    <orderEntry type="library" exported="" name="converter-gson-2.1.0" level="project" />
     <orderEntry type="library" exported="" name="materialdrawer-5.2.9" level="project" />
     <orderEntry type="library" exported="" name="calligraphy-2.2.0" level="project" />
+    <orderEntry type="library" exported="" name="okhttp-3.3.0" level="project" />
     <orderEntry type="library" exported="" name="answers-1.3.6" level="project" />
     <orderEntry type="library" exported="" name="iconics-core-2.6.0" level="project" />
     <orderEntry type="library" exported="" name="org.apache.http.legacy-android-23" level="project" />
diff --git a/app/build.gradle b/app/build.gradle
index c767a6020438f130ef40e37285812e9b075fbf3a..0fa95d999428d38e08ad5f488c7cb844285ca4c7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,6 +17,7 @@ apply plugin: 'realm-android'
 
 repositories {
     maven { url 'https://maven.fabric.io/public' }
+    maven { url "https://jitpack.io" }
 }
 
 
@@ -25,6 +26,12 @@ def gitVersion() {
     return process.toInteger() + 1
 }
 
+def getBugfenderApiKey(){
+    Properties properties = new Properties()
+    properties.load(new FileInputStream("app/bugfender.properties"))
+    return "\"" + properties.getProperty("apiKey", "") +"\""
+}
+
 android {
     compileSdkVersion 23
     buildToolsVersion "23.0.3"
@@ -41,6 +48,7 @@ android {
         targetSdkVersion 23
         versionName project.properties['version']
         versionCode gitVersion()
+        buildConfigField "String", "BUGFENDER_API_KEY", getBugfenderApiKey()
     }
 
     buildTypes {
@@ -51,8 +59,9 @@ android {
     }
 
     lintOptions {
-        // TODO - 'InvalidPackage' is here because of logback. We can remove this if we remove logback.
-        disable 'InvalidPackage', 'TrulyRandom'
+        // Dag nabbit :(
+        // Because of http://stackoverflow.com/questions/35492259/lint-error-on-okio
+        warning 'InvalidPackage'
     }
 }
 
@@ -113,9 +122,6 @@ release {
 }
 
 dependencies {
-    compile 'com.android.support:appcompat-v7:23.4.0'
-    compile 'org.apache.commons:commons-lang3:3.4'
-    compile files('libs/logback-android-1.1.1-3.jar')
     compile files('libs/slf4j-api-1.7.2.jar')
     compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {
         transitive = true;
@@ -123,7 +129,14 @@ dependencies {
     compile('com.mikepenz:materialdrawer:5.2.9@aar') {
         transitive = true
     }
+    compile 'com.android.support:appcompat-v7:23.4.0'
+    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.3.5'
+    compile 'com.bugfender.sdk:android:0.4'
+    compile 'com.github.PhilJay:MPAndroidChart:v2.2.5'
+    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'
 }
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c067681495750701d9a78af923eceb2d77fca1a0..b875e849aaf3675d64cbee090851630aa761a929 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -32,13 +32,10 @@
             android:launchMode="singleTask"
             android:screenOrientation="portrait">
             <intent-filter android:icon="@drawable/ic_launcher">
-                <action android:name="android.intent.action.MAIN" />
-
                 <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-            <intent-filter android:icon="@drawable/ic_launcher">
+
                 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+                <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
             </intent-filter>
 
             <meta-data
@@ -49,17 +46,30 @@
             android:name=".settings.SettingsActivity"
             android:icon="@drawable/ic_launcher"
             android:label="Settings"
-            android:theme="@style/SettingsTheme"/>
+            android:theme="@style/SettingsTheme" />
+        <activity
+            android:name=".SplashActivity"
+            android:theme="@style/SplashTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
 
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
         <activity
             android:name=".medtronic.GetHmacAndKeyActivity"
             android:label="@string/title_activity_login"
             android:theme="@style/SettingsTheme" />
 
+        <service
+            android:name=".upload.nightscout.NightscoutUploadIntentService"
+            android:icon="@drawable/ic_launcher" />
+
         <service
             android:name=".medtronic.service.MedtronicCnlIntentService"
-            android:icon="@drawable/ic_launcher"></service>
+            android:icon="@drawable/ic_launcher" />
 
+        <activity android:name=".medtronic.StatusActivity"></activity>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/assets/logback.xml b/app/src/main/assets/logback.xml
deleted file mode 100644
index c6459d39ae7475308c13b18602fdbd9fbd217d83..0000000000000000000000000000000000000000
--- a/app/src/main/assets/logback.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<configuration>
-  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
-      <file>/sdcard/640gAndroidUploader.log</file>
-    <append>true</append>
-    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
-      <fileNamePattern>/sdcard/640gAndroidUploader.%i.log.zip</fileNamePattern>
-      <minIndex>1</minIndex>
-      <maxIndex>3</maxIndex>
-    </rollingPolicy>
-
-    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
-      <maxFileSize>5MB</maxFileSize>
-    </triggeringPolicy>
-   
-    <encoder>
-      <pattern>%-5p[%d{yyyy-MM-dd HH:mm:ss}]:%msg%n</pattern>
-    </encoder>
-  </appender>
-
-  <root level="ERROR">
-    <appender-ref ref="FILE" />
-  </root>
-</configuration>
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/android/SplashActivity.java b/app/src/main/java/info/nightscout/android/SplashActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..e84fa6f22e10025788b7c2573205af93fc67855a
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/SplashActivity.java
@@ -0,0 +1,21 @@
+package info.nightscout.android;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+import info.nightscout.android.medtronic.MainActivity;
+
+/**
+ * Created by lgoedhart on 18/06/2016.
+ */
+public class SplashActivity extends AppCompatActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = new Intent(this, MainActivity.class);
+        startActivity(intent);
+        finish();
+    }
+}
diff --git a/app/src/main/java/info/nightscout/android/USB/UsbHidDriver.java b/app/src/main/java/info/nightscout/android/USB/UsbHidDriver.java
index a8d625678b39a3d0d990534027dc84e48274d430..ea63fa44d6f6f2019bfae1345156a09cd1a114ab 100644
--- a/app/src/main/java/info/nightscout/android/USB/UsbHidDriver.java
+++ b/app/src/main/java/info/nightscout/android/USB/UsbHidDriver.java
@@ -34,20 +34,27 @@ public class UsbHidDriver extends CommonUsbDriver {
         super(device, connection);
     }
 
-    public static UsbHidDriver acquire(UsbManager usbManager, int vendorId, int productId) {
-
+    public static UsbDevice getUsbDevice(UsbManager usbManager, int vendorId, int productId) {
         // Iterate all the available devices and find ours.
         for (UsbDevice device : usbManager.getDeviceList().values()) {
             if (device.getProductId() == productId && device.getVendorId() == vendorId) {
-                final UsbDeviceConnection mConnection = usbManager.openDevice(device);
-
-                return new UsbHidDriver(device, mConnection);
+                return device;
             }
         }
 
         return null;
     }
 
+    public static UsbHidDriver acquire(UsbManager usbManager, UsbDevice device) {
+        if (device != null) {
+            final UsbDeviceConnection mConnection = usbManager.openDevice(device);
+
+            return new UsbHidDriver(device, mConnection);
+        }
+
+        return null;
+    }
+
     @Override
     public void open() throws IOException {
         Log.d(TAG, "Claiming HID interface.");
diff --git a/app/src/main/java/info/nightscout/android/UploaderApplication.java b/app/src/main/java/info/nightscout/android/UploaderApplication.java
index 384ab6f2f39319ba3fcbeebf4b3232d9e266d141..13fe3d456d7a3afbd0368ca2e10a47d1dbd5ec53 100644
--- a/app/src/main/java/info/nightscout/android/UploaderApplication.java
+++ b/app/src/main/java/info/nightscout/android/UploaderApplication.java
@@ -1,9 +1,14 @@
 package info.nightscout.android;
 
 import android.app.Application;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
 
 import com.bugfender.sdk.Bugfender;
+import com.crashlytics.android.Crashlytics;
+import com.crashlytics.android.answers.Answers;
 
+import io.fabric.sdk.android.Fabric;
 import io.realm.Realm;
 import io.realm.RealmConfiguration;
 import uk.co.chrisjenx.calligraphy.CalligraphyConfig;
@@ -21,8 +26,20 @@ public class UploaderApplication extends Application {
                 .build()
         );
 
-        //Bugfender.init(this, "TSwASSlwqOGXFb86RadI7EEJZSr2jGeT", BuildConfig.DEBUG);
-        //Bugfender.enableLogcatLogging();
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
+
+        if (prefs.getBoolean(getString(R.string.preferences_enable_crashlytics), true)) {
+            Fabric.with(this, new Crashlytics());
+        }
+        if (prefs.getBoolean(getString(R.string.preferences_enable_answers), true)) {
+            Fabric.with(this, new Answers());
+        }
+
+        if (prefs.getBoolean(getString(R.string.preferences_enable_remote_logcat), false)) {
+            Bugfender.init(this, BuildConfig.BUGFENDER_API_KEY, BuildConfig.DEBUG);
+            Bugfender.enableLogcatLogging();
+            Bugfender.setDeviceString("NightscoutURL", prefs.getString(getString(R.string.preference_nightscout_url), "Not set"));
+        }
 
         RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this).build();
         Realm.setDefaultConfiguration(realmConfiguration);
diff --git a/app/src/main/java/info/nightscout/android/medtronic/GetHmacAndKeyActivity.java b/app/src/main/java/info/nightscout/android/medtronic/GetHmacAndKeyActivity.java
index e96266b00e2f6b9b9225ff02dc8f3262462070ca..64c30b3aea050802499e70eac5b49b00e373fbea 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/GetHmacAndKeyActivity.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/GetHmacAndKeyActivity.java
@@ -5,12 +5,16 @@ import android.animation.AnimatorListenerAdapter;
 import android.annotation.TargetApi;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Loader;
 import android.database.Cursor;
 import android.graphics.Color;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.text.Html;
 import android.text.TextUtils;
@@ -87,7 +91,7 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
         mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
             @Override
             public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
-                if (id == R.id.login || id == EditorInfo.IME_NULL) {
+                if (id == EditorInfo.IME_ACTION_DONE) {
                     attemptLogin();
                     return true;
                 }
@@ -137,7 +141,7 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
      * errors are presented and no actual login attempt is made.
      */
     private void attemptLogin() {
-        if (mHmacAndKeyTask != null) {
+        if (mHmacAndKeyTask != null || !checkOnline("Please connect to the Internet", "You must be online to register your USB stick.")) {
             return;
         }
 
@@ -232,6 +236,29 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
         mRegisteredStickView.setText(Html.fromHtml(deviceTableHtml));
     }
 
+    private boolean checkOnline(String title, String message) {
+        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+        NetworkInfo netInfo = cm.getActiveNetworkInfo();
+
+        boolean isOnline = (netInfo != null && netInfo.isConnectedOrConnecting());
+
+        if (!isOnline) {
+            new AlertDialog.Builder(this, R.style.AppTheme)
+                    .setTitle(title)
+                    .setMessage(message)
+                    .setCancelable(false)
+                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            dialog.dismiss();
+                        }
+                    })
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .show();
+        }
+
+        return isOnline;
+    }
+
     @Override
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
         return null;
diff --git a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
index 887ab956f4f73d4615bb8e599afaa6bc5037ac20..9c632ee91d22a1c65128f9215a2e42ae3eb1c11c 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
@@ -1,7 +1,7 @@
 package info.nightscout.android.medtronic;
 
-import android.app.ActivityManager;
 import android.app.AlarmManager;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -10,29 +10,32 @@ import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
 import android.os.BatteryManager;
 import android.os.Bundle;
+import android.os.Handler;
 import android.preference.PreferenceManager;
+import android.support.v4.app.TaskStackBuilder;
 import android.support.v4.content.LocalBroadcastManager;
 import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
+import android.support.v7.app.NotificationCompat;
 import android.support.v7.widget.Toolbar;
 import android.text.Html;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
 import android.widget.TextView;
 import android.widget.TextView.BufferType;
 
-import com.crashlytics.android.Crashlytics;
-import com.crashlytics.android.answers.Answers;
+import com.github.mikephil.charting.data.realm.implementation.RealmLineData;
+import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet;
+import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
+import com.github.mikephil.charting.utils.ColorTemplate;
 import com.mikepenz.google_material_typeface_library.GoogleMaterial;
 import com.mikepenz.materialdrawer.AccountHeaderBuilder;
 import com.mikepenz.materialdrawer.Drawer;
@@ -40,78 +43,67 @@ import com.mikepenz.materialdrawer.DrawerBuilder;
 import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
 import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.Locale;
 
 import info.nightscout.android.R;
+import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.eula.Eula;
 import info.nightscout.android.eula.Eula.OnEulaAgreedTo;
 import info.nightscout.android.medtronic.service.MedtronicCnlIntentService;
+import info.nightscout.android.model.CgmStatusEvent;
+import info.nightscout.android.model.medtronicNg.ContourNextLinkInfo;
 import info.nightscout.android.settings.SettingsActivity;
-import info.nightscout.android.upload.MedtronicNG.CGMRecord;
 import info.nightscout.android.upload.MedtronicNG.PumpStatusRecord;
-import io.fabric.sdk.android.Fabric;
+import info.nightscout.android.upload.nightscout.NightscoutUploadIntentService;
+import io.realm.Realm;
+import io.realm.RealmResults;
+import io.realm.Sort;
 import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper;
 
 /* Main activity for the MainActivity program */
 public class MainActivity extends AppCompatActivity implements OnSharedPreferenceChangeListener, OnEulaAgreedTo {
     private static final String TAG = MainActivity.class.getSimpleName();
+
     public static int batLevel = 0;
     public static PumpStatusRecord pumpStatusRecord = new PumpStatusRecord();
-    final Object mHandlerActiveLock = new Object();
-    boolean keepServiceAlive = true;
-    Boolean mHandlerActive = false;
-    ActivityManager manager = null;
+    boolean mEnableCgmService = true;
     SharedPreferences prefs = null;
-    private Logger log = (Logger) LoggerFactory.getLogger(MainActivity.class.getName());
-    private Toolbar toolbar;
-    private TextView mTextViewBg;
-    private TextView mTextViewBgTime;
-    private TextView mTextViewLog;
-    private TextView mTextViewUnits;
-    private TextView mTextViewTrend;
-    private TextView mTextViewIOB;
-    private Button buttonStopService;
+    private TextView mTextViewLog; // This will eventually move to a status page.
     private Intent mCnlIntentService;
+    private Intent mNightscoutUploadService;
+    private Handler mUiRefreshHandler = new Handler();
+    private Runnable mUiRefreshRunnable = new RefreshDisplayRunnable();
+    private Realm mRealm;
 
-    //Look for and launch the service, mTextViewLog status to user
     @Override
     public void onCreate(Bundle savedInstanceState) {
         Log.i(TAG, "onCreate called");
         super.onCreate(savedInstanceState);
 
+        mRealm = Realm.getDefaultInstance();
         mCnlIntentService = new Intent(this, MedtronicCnlIntentService.class);
+        mNightscoutUploadService = new Intent(this, NightscoutUploadIntentService.class);
 
         setContentView(R.layout.activity_main);
 
         PreferenceManager.getDefaultSharedPreferences(getBaseContext()).registerOnSharedPreferenceChangeListener(this);
         prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
 
-        if (prefs.getBoolean(getString(R.string.preferences_enable_crashlytics), true)) {
-            Fabric.with(this, new Crashlytics());
-        }
-        if (prefs.getBoolean(getString(R.string.preferences_enable_answers), true)) {
-            Fabric.with(this, new Answers());
-        }
-
-        if (!prefs.getBoolean("IUNDERSTAND", false)) {
+        if (!prefs.getBoolean(getString(R.string.preference_eula_accepted), false)) {
             stopCgmService();
-        } else {
-            mHandlerActive = true;
         }
 
-        // Registers the DownloadStateReceiver and its intent filters
         LocalBroadcastManager.getInstance(this).registerReceiver(
                 new StatusMessageReceiver(),
                 new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_STATUS_MESSAGE));
         LocalBroadcastManager.getInstance(this).registerReceiver(
-                new CgmRecordReceiver(),
-                new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_CGM_DATA));
+                new RefreshDataReceiver(),
+                new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_REFRESH_DATA));
 
-        keepServiceAlive = Eula.show(this, prefs);
+        mEnableCgmService = Eula.show(this, prefs);
 
         IntentFilter batteryIntentFilter = new IntentFilter();
         batteryIntentFilter.addAction(Intent.ACTION_BATTERY_LOW);
@@ -119,13 +111,20 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         batteryIntentFilter.addAction(Intent.ACTION_BATTERY_OKAY);
         registerReceiver(new BatteryReceiver(), batteryIntentFilter);
 
+        UsbReceiver usbReceiver = new UsbReceiver();
         IntentFilter usbIntentFilter = new IntentFilter();
         usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
         usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
-        registerReceiver(new UsbReceiver(), usbIntentFilter);
+        usbIntentFilter.addAction(MedtronicCnlIntentService.Constants.ACTION_USB_PERMISSION);
+        registerReceiver(usbReceiver, usbIntentFilter);
+        LocalBroadcastManager.getInstance(this).registerReceiver(
+                usbReceiver,
+                new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_NO_USB_PERMISSION));
+        LocalBroadcastManager.getInstance(this).registerReceiver(
+                usbReceiver,
+                new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_USB_REGISTER));
 
-        manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
-        toolbar = (Toolbar) findViewById(R.id.toolbar);
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
 
         if (toolbar != null) {
             setSupportActionBar(toolbar);
@@ -134,12 +133,28 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             getSupportActionBar().setTitle("Nightscout");
         }
 
-
-        final PrimaryDrawerItem itemHome = new PrimaryDrawerItem().withName("Home").withIcon(GoogleMaterial.Icon.gmd_home).withSelectable(false);
-        final PrimaryDrawerItem itemSettings = new PrimaryDrawerItem().withName("Settings").withIcon(GoogleMaterial.Icon.gmd_settings).withSelectable(false);
-        final PrimaryDrawerItem itemRegisterUsb = new PrimaryDrawerItem().withName("Register Contour Next Link").withIcon(GoogleMaterial.Icon.gmd_usb).withSelectable(false);
-
-        Drawer drawer = new DrawerBuilder()
+        final PrimaryDrawerItem itemSettings = new PrimaryDrawerItem()
+                .withName("Settings")
+                .withIcon(GoogleMaterial.Icon.gmd_settings)
+                .withSelectable(false);
+        final PrimaryDrawerItem itemRegisterUsb = new PrimaryDrawerItem()
+                .withName("Register Contour Next Link")
+                .withIcon(GoogleMaterial.Icon.gmd_usb)
+                .withSelectable(false);
+        final PrimaryDrawerItem itemStopCollecting = new PrimaryDrawerItem()
+                .withName("Stop collecting data")
+                .withIcon(GoogleMaterial.Icon.gmd_stop)
+                .withSelectable(false);
+        final PrimaryDrawerItem itemGetNow = new PrimaryDrawerItem()
+                .withName("Read data now")
+                .withIcon(GoogleMaterial.Icon.gmd_play_arrow)
+                .withSelectable(false);
+        final PrimaryDrawerItem itemClearLog = new PrimaryDrawerItem()
+                .withName("Clear Log")
+                .withIcon(GoogleMaterial.Icon.gmd_clear_all)
+                .withSelectable(false);
+
+        new DrawerBuilder()
                 .withActivity(this)
                 .withAccountHeader(new AccountHeaderBuilder()
                         .withActivity(this)
@@ -151,9 +166,11 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 .withActionBarDrawerToggle(true)
                 .withSelectedItem(-1)
                 .addDrawerItems(
-                        itemHome,
                         itemSettings,
-                        itemRegisterUsb
+                        itemRegisterUsb,
+                        itemStopCollecting,
+                        itemGetNow,
+                        itemClearLog
                 )
                 .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                     @Override
@@ -162,74 +179,29 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                             openSettings();
                         } else if (drawerItem.equals(itemRegisterUsb)) {
                             openUsbRegistration();
+                        } else if (drawerItem.equals(itemStopCollecting)) {
+                            mEnableCgmService = false;
+                            stopCgmService();
+                            finish();
+                        } else if (drawerItem.equals(itemGetNow)) {
+                            startCgmService();
+                        } else if (drawerItem.equals(itemClearLog)) {
+                            mTextViewLog.setText("", BufferType.EDITABLE);
                         }
+
                         return false;
                     }
                 })
                 .build();
 
-        // UI elements - TODO do these need to be members?
-        mTextViewBg = (TextView) findViewById(R.id.textview_bg);
-        mTextViewBgTime = (TextView) findViewById(R.id.textview_bg_time);
         mTextViewLog = (TextView) findViewById(R.id.textview_log);
-        mTextViewUnits = (TextView) findViewById(R.id.textview_units);
-        if (prefs.getBoolean("mmolxl", false)) {
-            mTextViewUnits.setText(R.string.text_unit_mmolxl);
-        } else {
-            mTextViewUnits.setText(R.string.text_unit_mgxdl);
-        }
-        mTextViewTrend = (TextView) findViewById(R.id.textview_trend);
-        mTextViewIOB = (TextView) findViewById(R.id.textview_iob);
-
-        buttonStopService = (Button) findViewById(R.id.button_stop_service);
-
-        Button buttonClearLog = (Button) findViewById(R.id.button_clear_log);
-        Button buttonStartService = (Button) findViewById(R.id.button_start_service);
-
-        buttonClearLog.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mTextViewLog.setText("", BufferType.EDITABLE);
-            }
-        });
-
-        buttonStartService.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // Get an immediate reading.
-                startCgmService();
-            }
-        });
-
-        buttonStopService.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                synchronized (mHandlerActiveLock) {
-                    mHandlerActive = false;
-                    keepServiceAlive = false;
-                    stopCgmService();
-                    finish();
-                }
-            }
-        });
     }
 
     @Override
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
         startCgmService();
-    }
-
-    @Override
-    protected void onPause() {
-        log.info("ON PAUSE!");
-        super.onPause();
-    }
-
-    @Override
-    protected void onResume() {
-        log.info("ON RESUME!");
-        super.onResume();
+        startDisplayRefreshLoop();
     }
 
     @Override
@@ -239,22 +211,28 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        //MenuInflater inflater = getMenuInflater();
-        //inflater.inflate(R.menu.menu, menu);
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.menu, menu);
 
         return true;
     }
 
-    private boolean checkOnline(String title, String message) {
-        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo netInfo = cm.getActiveNetworkInfo();
-
-        boolean isOnline = (netInfo != null && netInfo.isConnectedOrConnecting());
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_menu_status:
+                Intent intent = new Intent(this, StatusActivity.class);
+                startActivity(intent);
+                break;
+        }
+        return true;
+    }
 
-        if (!isOnline) {
+    private boolean hasDetectedCnl() {
+        if (mRealm.where(ContourNextLinkInfo.class).count() == 0) {
             new AlertDialog.Builder(this, R.style.AppTheme)
-                    .setTitle(title)
-                    .setMessage(message)
+                    .setTitle("Contour Next Link not detected")
+                    .setMessage("To register a Contour Next Link you must first plug it in.")
                     .setCancelable(false)
                     .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                         public void onClick(DialogInterface dialog, int which) {
@@ -263,38 +241,80 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                     })
                     .setIcon(android.R.drawable.ic_dialog_alert)
                     .show();
+            return false;
         }
+        return true;
+    }
+
+    private boolean hasUsbPermission() {
+        UsbManager usbManager = (UsbManager) this.getSystemService(Context.USB_SERVICE);
+        UsbDevice cnlDevice = UsbHidDriver.getUsbDevice(usbManager, MedtronicCnlIntentService.USB_VID, MedtronicCnlIntentService.USB_PID);
+
+        return !(usbManager != null && cnlDevice != null && !usbManager.hasPermission(cnlDevice));
 
-        return isOnline;
     }
 
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
+    private void waitForUsbPermission() {
+        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+        Intent permissionIntent = new Intent(MedtronicCnlIntentService.Constants.ACTION_USB_PERMISSION);
+        permissionIntent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, hasUsbPermission());
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, permissionIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+        alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000L, pendingIntent);
+    }
 
-        switch (item.getItemId()) {
-            case R.id.menu_settings:
-                Intent settingsIntent = new Intent(this, SettingsActivity.class);
-                startActivity(settingsIntent);
-                break;
-            case R.id.registerCNL:
-                if (checkOnline("Please connect to the Internet", "You must be online to register your USB stick.")) {
-                    Intent loginIntent = new Intent(this, GetHmacAndKeyActivity.class);
-                    startActivity(loginIntent);
-                }
-                break;
-            default:
-                break;
+    private void requestUsbPermission() {
+        if (!hasUsbPermission()) {
+            UsbManager usbManager = (UsbManager) this.getSystemService(Context.USB_SERVICE);
+            UsbDevice cnlDevice = UsbHidDriver.getUsbDevice(usbManager, MedtronicCnlIntentService.USB_VID, MedtronicCnlIntentService.USB_PID);
+            PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(MedtronicCnlIntentService.Constants.ACTION_USB_PERMISSION), 0);
+            usbManager.requestPermission(cnlDevice, permissionIntent);
         }
-        return super.onOptionsItemSelected(item);
+    }
+
+    private void refreshDisplay() {
+        cancelDisplayRefreshLoop();
+        startDisplayRefreshLoop();
+    }
+
+    private void startDisplayRefreshLoop() {
+        mUiRefreshHandler.post(mUiRefreshRunnable);
+    }
+
+    private void cancelDisplayRefreshLoop() {
+        mUiRefreshHandler.removeCallbacks(mUiRefreshRunnable);
     }
 
     private void startCgmService() {
+        startCgmService(0L);
+    }
+
+    private void startCgmService(long runAtTime) {
         Log.i(TAG, "startCgmService called");
-        startService(mCnlIntentService);
+
+        if (!mEnableCgmService) {
+            return;
+        }
+
+        if (runAtTime == 0L) {
+            startService(mCnlIntentService);
+        } else {
+            AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+            PendingIntent pending = PendingIntent.getService(this, 0, mCnlIntentService, 0);
+
+            alarmManager.set(AlarmManager.RTC_WAKEUP, runAtTime, pending);
+        }
     }
 
     private void startCgmServicePolling(long initialPoll) {
         Log.i(TAG, "startCgmServicePolling called");
+
+        if (!mEnableCgmService) {
+            return;
+        }
+
+        // Cancel any existing polling.
+        stopCgmService();
+
         AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
         PendingIntent pending = PendingIntent.getService(this, 0, mCnlIntentService, 0);
 
@@ -302,6 +322,10 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 initialPoll, MedtronicCnlIntentService.POLL_PERIOD_MS, pending);
     }
 
+    private void uploadCgmData() {
+        startService(mNightscoutUploadService);
+    }
+
     private void stopCgmService() {
         Log.i(TAG, "stopCgmService called");
 
@@ -311,21 +335,49 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         alarmManager.cancel(pending);
     }
 
+    private void showDisconnectionNotification(String title, String message) {
+        int notifyId = 1;
+
+        NotificationCompat.Builder mBuilder =
+                (NotificationCompat.Builder) new NotificationCompat.Builder(this)
+                        .setPriority(NotificationCompat.PRIORITY_MAX)
+                        .setSmallIcon(R.drawable.ic_launcher) // FIXME - this icon doesn't follow the standards (ie, it has black in it)
+                        .setContentTitle(title)
+                        .setContentText(message)
+                        .setTicker(message)
+                        .setVibrate(new long[]{1000, 1000, 1000, 1000, 1000});
+        // Creates an explicit intent for an Activity in your app
+        Intent resultIntent = new Intent(this, MainActivity.class);
+
+        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+        // Adds the back stack for the Intent (but not the Intent itself)
+        stackBuilder.addParentStack(MainActivity.class);
+        // Adds the Intent that starts the Activity to the top of the stack
+        stackBuilder.addNextIntent(resultIntent);
+        PendingIntent resultPendingIntent =
+                stackBuilder.getPendingIntent(
+                        0,
+                        PendingIntent.FLAG_UPDATE_CURRENT
+                );
+        mBuilder.setContentIntent(resultPendingIntent);
+        NotificationManager mNotificationManager =
+                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        // notifyId allows you to update the notification later on.
+        mNotificationManager.notify(notifyId, mBuilder.build());
+    }
+
     @Override
     protected void onDestroy() {
         Log.i(TAG, "onDestroy called");
-        log.info("onDestroy called");
         PreferenceManager.getDefaultSharedPreferences(getBaseContext()).unregisterOnSharedPreferenceChangeListener(this);
-        synchronized (mHandlerActiveLock) {
-            if (!keepServiceAlive) {
-                stopCgmService();
-            }
-            mHandlerActive = false;
-            SharedPreferences.Editor editor = getBaseContext().getSharedPreferences(MedtronicConstants.PREFS_NAME, 0).edit();
-            editor.putLong("lastDestroy", System.currentTimeMillis());
-            editor.apply();
-            super.onDestroy();
+        cancelDisplayRefreshLoop();
+
+        if (!mEnableCgmService) {
+            stopCgmService();
         }
+
+        mRealm.close();
+        super.onDestroy();
     }
 
     @Override
@@ -333,25 +385,25 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                                           String key) {
         if (key.equals(getString(R.string.preference_eula_accepted))) {
             if (!sharedPreferences.getBoolean(getString(R.string.preference_eula_accepted), false)) {
-                synchronized (mHandlerActiveLock) {
-                    mHandlerActive = false;
-                }
+                mEnableCgmService = false;
                 stopCgmService();
             } else {
+                mEnableCgmService = true;
                 startCgmService();
-                mHandlerActive = true;
             }
+        } else if (key.equals("mmolxl")) {
+            refreshDisplay();
         }
     }
 
     @Override
     public void onEulaAgreedTo() {
-        keepServiceAlive = true;
+        mEnableCgmService = true;
     }
 
     @Override
     public void onEulaRefusedTo() {
-        keepServiceAlive = false;
+        mEnableCgmService = false;
     }
 
     public void openSettings() {
@@ -360,13 +412,13 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     }
 
     public void openUsbRegistration() {
-        if (checkOnline("Please connect to the Internet", "You must be online to register your USB stick.")) {
+        if (hasDetectedCnl()) {
             Intent loginIntent = new Intent(this, GetHmacAndKeyActivity.class);
             startActivity(loginIntent);
         }
     }
 
-    private String renderTrendHtml(CGMRecord.TREND trend) {
+    private String renderTrendHtml(CgmStatusEvent.TREND trend) {
         switch (trend) {
             case DOUBLE_UP:
                 return "&#x21c8;";
@@ -392,18 +444,41 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         public void onReceive(Context context, Intent intent) {
             String message = intent.getStringExtra(MedtronicCnlIntentService.Constants.EXTENDED_DATA);
 
+            Log.i(TAG, "Message Receiver: " + message);
             mTextViewLog.setText(mTextViewLog.getText() + "\n" + message, BufferType.EDITABLE);
         }
     }
 
-    private class CgmRecordReceiver extends BroadcastReceiver {
+    private class RefreshDisplayRunnable implements Runnable {
         @Override
-        public void onReceive(Context context, Intent intent) {
-            CGMRecord cgmRecord = (CGMRecord) intent.getSerializableExtra(MedtronicCnlIntentService.Constants.EXTENDED_DATA);
+        public void run() {
+            // UI elements - TODO do these need to be members?
+            TextView textViewBg = (TextView) findViewById(R.id.textview_bg);
+            TextView textViewBgTime = (TextView) findViewById(R.id.textview_bg_time);
+            TextView textViewUnits = (TextView) findViewById(R.id.textview_units);
+            if (prefs.getBoolean("mmolxl", false)) {
+                textViewUnits.setText(R.string.text_unit_mmolxl);
+            } else {
+                textViewUnits.setText(R.string.text_unit_mgxdl);
+            }
+            TextView textViewTrend = (TextView) findViewById(R.id.textview_trend);
+            TextView textViewIOB = (TextView) findViewById(R.id.textview_iob);
+            //LineChart chart = (LineChart) findViewById(R.id.chart);
+
+            // Get the most recently written CGM record.
+            RealmResults<CgmStatusEvent> results =
+                    mRealm.where(CgmStatusEvent.class)
+                            .findAllSorted("eventDate", Sort.ASCENDING);
 
-            // FIXME - replace initial polling time with the next expected polling period
-            // i.e. Next 5 minute CGM polling increment to occur after now, + POLL_GRACE_PERIOD_MS
-            startCgmServicePolling(System.currentTimeMillis() + MedtronicCnlIntentService.POLL_PERIOD_MS);
+            CgmStatusEvent cgmRecord = null;
+
+            if (results.size() > 0) {
+                cgmRecord = results.last();
+            }
+
+            if (cgmRecord == null) {
+                return;
+            }
 
             DecimalFormat df;
             if (prefs.getBoolean("mmolDecimals", false))
@@ -413,22 +488,88 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
             String sgvString, units;
             if (prefs.getBoolean("mmolxl", false)) {
-                float fBgValue = (float) cgmRecord.sgv;
+                float fBgValue = (float) cgmRecord.getSgv();
                 sgvString = df.format(fBgValue / 18.016f);
                 units = "mmol/L";
-                log.info("mmolxl true --> " + sgvString);
+                Log.d(TAG, "mmolxl true --> " + sgvString);
 
             } else {
-                sgvString = String.valueOf(cgmRecord.sgv);
+                sgvString = String.valueOf(cgmRecord.getSgv());
                 units = "mg/dL";
-                log.info("mmolxl false --> " + sgvString);
+                Log.d(TAG, "mmolxl false --> " + sgvString);
             }
 
-            mTextViewBg.setText(sgvString);
-            mTextViewUnits.setText(units);
-            mTextViewBgTime.setText(DateUtils.formatDateTime(getBaseContext(), cgmRecord.sgvDate.getTime(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME));
-            mTextViewTrend.setText(Html.fromHtml(renderTrendHtml(cgmRecord.getTrend())));
-            mTextViewIOB.setText(String.format(Locale.getDefault(), "%.2f", pumpStatusRecord.activeInsulin));
+            textViewBg.setText(sgvString);
+            textViewUnits.setText(units);
+            textViewBgTime.setText(DateUtils.getRelativeTimeSpanString(cgmRecord.getEventDate().getTime()));
+            textViewTrend.setText(Html.fromHtml(renderTrendHtml(cgmRecord.getTrend())));
+            textViewIOB.setText(String.format(Locale.getDefault(), "%.2f", pumpStatusRecord.activeInsulin));
+
+            // TODO - waiting for MPAndroidCharts 3.0.0. This will fix:
+            // Date support
+            // Realm v1.0.0 support
+            // updateChart(results);
+
+            // Run myself again in 60 seconds;
+            mUiRefreshHandler.postDelayed(this, 60000L);
+        }
+
+        private void updateChart(RealmResults<CgmStatusEvent> results) {
+            RealmLineDataSet<CgmStatusEvent> lineDataSet = new RealmLineDataSet<>(results, "sgv", "eventDate");
+
+            lineDataSet.setDrawCircleHole(false);
+            lineDataSet.setColor(ColorTemplate.rgb("#FF5722"));
+            lineDataSet.setCircleColor(ColorTemplate.rgb("#FF5722"));
+            lineDataSet.setLineWidth(1.8f);
+            lineDataSet.setCircleSize(3.6f);
+
+            ArrayList<ILineDataSet> dataSets = new ArrayList<ILineDataSet>();
+            dataSets.add(lineDataSet);
+
+            RealmLineData lineData = new RealmLineData(results, "eventDate", dataSets);
+
+            // set data
+            //chart.setMinimumHeight(200);
+            //chart.setData(lineData);
+        }
+    }
+
+    private class RefreshDataReceiver extends BroadcastReceiver {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            CgmStatusEvent record = mRealm.where(CgmStatusEvent.class)
+                    .findAll()
+                    .last();
+
+            long nextPoll = record.getEventDate().getTime() + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS + MedtronicCnlIntentService.POLL_PERIOD_MS;
+            startCgmServicePolling(nextPoll);
+            Log.d(TAG, "Next Poll at " + new Date(nextPoll).toString());
+
+            // Delete invalid or old records from Realm
+            // TODO - show an error message if the valid records haven't been uploaded
+            final RealmResults<CgmStatusEvent> results =
+                    mRealm.where(CgmStatusEvent.class)
+                            .equalTo("sgv", 0)
+                            .or()
+                            .lessThan("eventDate", new Date(System.currentTimeMillis() - (24 * 60 * 60 * 1000)))
+                            .findAll();
+
+            if (results.size() > 0) {
+                mRealm.executeTransaction(new Realm.Transaction() {
+                    @Override
+                    public void execute(Realm realm) {
+                        // Delete all matches
+                        Log.d(TAG, "Deleting " + results.size() + " records from realm");
+                        results.deleteAllFromRealm();
+                    }
+                });
+            }
+
+            // TODO - handle isOffline in NightscoutUploadIntentService?
+            uploadCgmData();
+
+            refreshDisplay();
         }
     }
 
@@ -436,13 +577,36 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
-                mTextViewLog.setText(mTextViewLog.getText() + "\nUSB plugged in", BufferType.EDITABLE);
-                startCgmService();
+            if (MedtronicCnlIntentService.Constants.ACTION_USB_PERMISSION.equals(action)) {
+                boolean permissionGranted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
+                if (permissionGranted) {
+                    Log.d(TAG, "Got permission to access USB");
+                    startCgmService();
+                } else {
+                    Log.d(TAG, "Still no permission for USB. Waiting...");
+                    waitForUsbPermission();
+                }
+            } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
+                Log.d(TAG, "USB plugged in");
+
+                if (hasUsbPermission()) {
+                    // Give the USB a little time to warm up first
+                    startCgmService(System.currentTimeMillis() + MedtronicCnlIntentService.USB_WARMUP_TIME_MS);
+                } else {
+                    Log.d(TAG, "No permission for USB. Waiting.");
+                    waitForUsbPermission();
+                }
             } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
-                mTextViewLog.setText(mTextViewLog.getText() + "\nUSB unplugged", BufferType.EDITABLE);
+                Log.d(TAG, "USB unplugged");
+                if (mEnableCgmService) {
+                    showDisconnectionNotification("USB Error", "Contour Next Link unplugged.");
+                }
+            } else if (MedtronicCnlIntentService.Constants.ACTION_NO_USB_PERMISSION.equals(action)) {
+                Log.d(TAG, "No permission to read the USB device.");
+                requestUsbPermission();
+            } else if (MedtronicCnlIntentService.Constants.ACTION_USB_REGISTER.equals(action)) {
+                openUsbRegistration();
             }
-
         }
     }
 
@@ -452,7 +616,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             if (arg1.getAction().equalsIgnoreCase(Intent.ACTION_BATTERY_LOW)
                     || arg1.getAction().equalsIgnoreCase(Intent.ACTION_BATTERY_CHANGED)
                     || arg1.getAction().equalsIgnoreCase(Intent.ACTION_BATTERY_OKAY)) {
-                Log.i("BatteryReceived", "BatteryReceived");
                 batLevel = arg1.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
             }
         }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java
index 94658af861e75b153215d42d197e9ff1ca1fab26..8b56ea37e359278c03f25173894a2bd12f9b5ea0 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java
@@ -32,7 +32,7 @@ import info.nightscout.android.medtronic.message.PumpTimeResponseMessage;
 import info.nightscout.android.medtronic.message.ReadInfoResponseMessage;
 import info.nightscout.android.medtronic.message.UnexpectedMessageException;
 import info.nightscout.android.medtronic.service.MedtronicCnlIntentService;
-import info.nightscout.android.upload.MedtronicNG.CGMRecord;
+import info.nightscout.android.model.CgmStatusEvent;
 import info.nightscout.android.utils.HexDump;
 
 /**
@@ -182,6 +182,27 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
         }
     }
 
+    private static CgmStatusEvent.TREND fromMessageByte(byte messageByte) {
+        switch( messageByte ) {
+            case (byte) 0x60:
+                return CgmStatusEvent.TREND.FLAT;
+            case (byte) 0xc0:
+                return CgmStatusEvent.TREND.DOUBLE_UP;
+            case (byte) 0xa0:
+                return CgmStatusEvent.TREND.SINGLE_UP;
+            case (byte) 0x80:
+                return CgmStatusEvent.TREND.FOURTY_FIVE_UP;
+            case (byte) 0x40:
+                return CgmStatusEvent.TREND.FOURTY_FIVE_DOWN;
+            case (byte) 0x20:
+                return CgmStatusEvent.TREND.SINGLE_DOWN;
+            case (byte) 0x00:
+                return CgmStatusEvent.TREND.DOUBLE_DOWN;
+            default:
+                return CgmStatusEvent.TREND.NOT_COMPUTABLE;
+        }
+    }
+
     public void enterControlMode() throws IOException, TimeoutException, UnexpectedMessageException {
         boolean doRetry = false;
 
@@ -290,7 +311,7 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
         return MessageUtils.decodeDateTime(rtc, offset);
     }
 
-    public void getPumpStatus(CGMRecord cgmRecord) throws IOException, EncryptionException, ChecksumException, TimeoutException {
+    public void getPumpStatus(CgmStatusEvent cgmRecord, long pumpTimeOffset) throws IOException, EncryptionException, ChecksumException, TimeoutException {
         // FIXME - throw if not in EHSM mode (add a state machine)
 
         new PumpStatusRequestMessage(mPumpSession).send(this);
@@ -313,21 +334,21 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
         // Read the data into the record
         long rawActiveInsulin = statusBuffer.getShort(0x33) & 0x0000ffff;
         MainActivity.pumpStatusRecord.activeInsulin = new BigDecimal(rawActiveInsulin / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP);
-        cgmRecord.sgv = statusBuffer.getShort(0x35) & 0x0000ffff; // In mg/DL. 0 means no CGM reading
+        cgmRecord.setSgv(statusBuffer.getShort(0x35) & 0x0000ffff); // In mg/DL. 0 means no CGM reading
         long rtc;
         long offset;
-        if ((cgmRecord.sgv & 0x200) == 0x200) {
+        if ((cgmRecord.getSgv() & 0x200) == 0x200) {
             // Sensor error. Let's reset. FIXME - solve this more elegantly later
-            cgmRecord.sgv = 0;
+            cgmRecord.setSgv(0);
             rtc = 0;
             offset = 0;
-            cgmRecord.setTrend(CGMRecord.TREND.NOT_SET);
+            cgmRecord.setTrend(CgmStatusEvent.TREND.NOT_SET);
         } else {
             rtc = statusBuffer.getInt(0x37) & 0x00000000ffffffffL;
             offset = statusBuffer.getInt(0x3b);
-            cgmRecord.setTrend(CGMRecord.fromMessageByte(statusBuffer.get(0x40)));
+            cgmRecord.setTrend(fromMessageByte(statusBuffer.get(0x40)));
         }
-        cgmRecord.sgvDate = MessageUtils.decodeDateTime(rtc, offset);
+        cgmRecord.setEventDate(new Date(MessageUtils.decodeDateTime(rtc, offset).getTime() - pumpTimeOffset));
         MainActivity.pumpStatusRecord.recentBolusWizard = statusBuffer.get(0x48) != 0;
         MainActivity.pumpStatusRecord.bolusWizardBGL = statusBuffer.getShort(0x49); // In mg/DL
         long rawReservoirAmount = statusBuffer.getInt(0x2b);
diff --git a/app/src/main/java/info/nightscout/android/medtronic/StatusActivity.java b/app/src/main/java/info/nightscout/android/medtronic/StatusActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..2729d102872e24cf16c1540d69800104e78b7d3c
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/medtronic/StatusActivity.java
@@ -0,0 +1,45 @@
+package info.nightscout.android.medtronic;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+
+import com.mikepenz.google_material_typeface_library.GoogleMaterial;
+import com.mikepenz.iconics.IconicsDrawable;
+
+import info.nightscout.android.R;
+
+public class StatusActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_status);
+
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+
+        if (toolbar != null) {
+            setSupportActionBar(toolbar);
+            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+            getSupportActionBar().setHomeAsUpIndicator(
+                    new IconicsDrawable(this)
+                            .icon(GoogleMaterial.Icon.gmd_close)
+                            .color(Color.WHITE)
+                            .sizeDp(24)
+            );
+            getSupportActionBar().setElevation(0);
+            getSupportActionBar().setTitle("Status");
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+}
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 bdab9b3c1c0124f55f182e7568da1f2209217da1..d39f3246dadb395b61d450476ec7e65335f8eca8 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
@@ -4,24 +4,17 @@ import android.app.IntentService;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
 import android.support.v4.app.NotificationManagerCompat;
 import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
+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;
@@ -29,15 +22,15 @@ import info.nightscout.android.medtronic.message.ChecksumException;
 import info.nightscout.android.medtronic.message.EncryptionException;
 import info.nightscout.android.medtronic.message.MessageUtils;
 import info.nightscout.android.medtronic.message.UnexpectedMessageException;
+import info.nightscout.android.model.CgmStatusEvent;
 import info.nightscout.android.model.medtronicNg.ContourNextLinkInfo;
-import info.nightscout.android.upload.MedtronicNG.CGMRecord;
-import info.nightscout.android.upload.MedtronicNG.PumpStatusRecord;
-import info.nightscout.android.upload.UploadHelper;
 import io.realm.Realm;
+import io.realm.RealmResults;
 
 public class MedtronicCnlIntentService extends IntentService {
     public final static int USB_VID = 0x1a79;
     public final static int USB_PID = 0x6210;
+    public final static long USB_WARMUP_TIME_MS = 5000L;
     public final static long POLL_PERIOD_MS = 300000L;
     // Number of additional seconds to wait after the next expected CGM poll, so that we don't interfere with CGM radio comms.
     public final static long POLL_GRACE_PERIOD_MS = 30000L;
@@ -51,12 +44,6 @@ public class MedtronicCnlIntentService extends IntentService {
         super(MedtronicCnlIntentService.class.getName());
     }
 
-    public final class Constants {
-        public static final String ACTION_STATUS_MESSAGE = "info.nightscout.android.medtronic.service.STATUS_MESSAGE";
-        public static final String ACTION_CGM_DATA = "info.nightscout.android.medtronic.service.CGM_DATA";
-        public static final String EXTENDED_DATA = "info.nightscout.android.medtronic.service.DATA";
-    }
-
     protected void sendStatus(String message) {
         Intent localIntent =
                 new Intent(Constants.ACTION_STATUS_MESSAGE)
@@ -64,10 +51,9 @@ public class MedtronicCnlIntentService extends IntentService {
         LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
     }
 
-    protected void sendCgmRecord(Serializable cgmRecord) {
+    protected void sendMessage(String action) {
         Intent localIntent =
-                new Intent(Constants.ACTION_CGM_DATA)
-                        .putExtra(Constants.EXTENDED_DATA, cgmRecord);
+                new Intent(action);
         LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
     }
 
@@ -99,198 +85,174 @@ public class MedtronicCnlIntentService extends IntentService {
     }
 
     protected void onHandleIntent(Intent intent) {
-        Log.i(TAG, "onHandleIntent called");
+        Log.d(TAG, "onHandleIntent called");
 
         if (!hasUsbHostFeature()) {
             sendStatus("It appears that this device doesn't support USB OTG.");
+            Log.w(TAG, "Device does not support USB OTG");
+            return;
+        }
+
+        UsbDevice cnlStick = UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID);
+        if (cnlStick == null) {
+            sendStatus("USB connection error. Is the Bayer Contour Next Link plugged in?");
+            Log.w(TAG, "USB connection error. Is the CNL plugged in?");
+            return;
+        }
+
+        if (!mUsbManager.hasPermission(UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID))) {
+            sendMessage(Constants.ACTION_NO_USB_PERMISSION);
             return;
         }
+        mHidDevice = UsbHidDriver.acquire(mUsbManager, cnlStick);
 
-        UploadHelper mUploader = new UploadHelper(getBaseContext());
-        mHidDevice = UsbHidDriver.acquire(mUsbManager, USB_VID, USB_PID);
         Realm realm = Realm.getDefaultInstance();
 
-        // Load the initial data to the display
-        CGMRecord cgmRecord = loadData();
-        PumpStatusRecord pumpRecord = MainActivity.pumpStatusRecord;
+        try {
+            mHidDevice.open();
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to open serial device", e);
+            return;
+        }
 
-        sendCgmRecord(cgmRecord);
+        MedtronicCNLReader cnlReader = new MedtronicCNLReader(mHidDevice);
 
-        if (mHidDevice == null) {
-            String title = "USB connection error";
-            String msg = "Is the Bayer Contour NextLink plugged in?";
-            //showNotification(title, msg);
-            sendStatus(title + "\n" + msg);
-        } else {
-            try {
-                mHidDevice.open();
-            } catch (Exception e) {
-                Log.e(TAG, "Unable to open serial device", e);
-                return;
-            }
+        realm.beginTransaction();
 
-            // Go get the data
-            MedtronicCNLReader cnlReader = new MedtronicCNLReader(mHidDevice);
+        try {
+            sendStatus("Connecting to the Contour Next Link...");
+            Log.d(TAG, "Connecting to the Contour Next Link.");
+            cnlReader.requestDeviceInfo();
 
-            try {
-                sendStatus("Connecting to the Contour Next Link...");
-                cnlReader.requestDeviceInfo();
+            // Is the device already configured?
+            ContourNextLinkInfo info = realm
+                    .where(ContourNextLinkInfo.class)
+                    .equalTo("serialNumber", cnlReader.getStickSerial())
+                    .findFirst();
 
-                // Is the device already configured?
-                ContourNextLinkInfo info = realm
-                        .where(ContourNextLinkInfo.class).equalTo("serialNumber", cnlReader.getStickSerial())
-                        .findFirst();
+            if (info == null) {
+                info = new ContourNextLinkInfo();
+                info.setSerialNumber(cnlReader.getStickSerial());
 
-                if (info == null) {
-                    info = new ContourNextLinkInfo();
-                    info.setSerialNumber(cnlReader.getStickSerial());
+                info = realm.copyToRealm(info);
+            }
 
-                    realm.beginTransaction();
-                    info = realm.copyToRealm(info);
-                    realm.commitTransaction();
-                }
+            String hmac = info.getHmac();
+            String key = info.getKey();
+
+            if (hmac == null || key == null) {
+                // Must commit the transaction before we send the Registration activation message
+                realm.commitTransaction();
 
-                String hmac = info.getHmac();
-                String key = info.getKey();
+                sendMessage(Constants.ACTION_USB_REGISTER);
+                return;
+            }
 
-                String deviceName = String.format("medtronic-640g://%s", cnlReader.getStickSerial());
-                cgmRecord.setDeviceName(deviceName);
-                pumpRecord.setDeviceName(deviceName);
+            cnlReader.getPumpSession().setHMAC(MessageUtils.hexStringToByteArray(hmac));
+            cnlReader.getPumpSession().setKey(MessageUtils.hexStringToByteArray(key));
 
-                if (hmac == null || key == null) {
-                    sendStatus(String.format("Before you can use the Contour Next Link, you need to register it with the app. Select '%s' from the menu.", getString(R.string.register_contour_next_link)));
-                    return;
-                }
+            cnlReader.enterControlMode();
+
+            try {
+                cnlReader.enterPassthroughMode();
+                cnlReader.openConnection();
+                cnlReader.requestReadInfo();
+                byte radioChannel = cnlReader.negotiateChannel();
+                if (radioChannel == 0) {
+                    sendStatus("Could not communicate with the 640g. Are you near the pump?");
+                    Log.i(TAG, "Could not communicate with the 640g. Are you near the pump?");
+                } else {
+                    sendStatus(String.format(Locale.getDefault(), "Connected to Contour Next Link on channel %d.", (int) radioChannel));
+                    Log.d(TAG, String.format("Connected to Contour Next Link on channel %d.", (int) radioChannel));
+                    cnlReader.beginEHSMSession();
+
+                    //PumpStatusEvent pumpRecord = realm.createObject(PumpStatusEvent.class);
+                    CgmStatusEvent cgmRecord = realm.createObject(CgmStatusEvent.class);
+
+                    String deviceName = String.format("medtronic-640g://%s", cnlReader.getStickSerial());
+                    cgmRecord.setDeviceName(deviceName);
+                    //pumpRecord.setDeviceName(deviceName);
+                    // TODO - legacy. Remove once we've plumbed in pumpRecord.
+                    MainActivity.pumpStatusRecord.setDeviceName(deviceName);
+
+                    //pumpRecord.setPumpDate(cnlReader.getPumpTime());
+                    long pumpTime = cnlReader.getPumpTime().getTime();
+                    long pumpOffset = pumpTime - System.currentTimeMillis();
+                    // TODO - send ACTION to MainActivity to show offset between pump and uploader.
+                    MainActivity.pumpStatusRecord.pumpDate = new Date(pumpTime - pumpOffset);
+                    cnlReader.getPumpStatus(cgmRecord, pumpOffset);
+
+                    cnlReader.endEHSMSession();
+
+                    boolean cancelTransaction = true;
+                    if (cgmRecord.getSgv() != 0) {
+                        // Check that the record doesn't already exist before committing
+                        RealmResults<CgmStatusEvent> checkExistingRecords = realm
+                                .where(CgmStatusEvent.class)
+                                .equalTo("eventDate", cgmRecord.getEventDate())
+                                .equalTo("deviceName", cgmRecord.getDeviceName())
+                                .equalTo("sgv", cgmRecord.getSgv())
+                                .findAll();
+
+                        // There should be the 1 record we've already added in this transaction.
+                        if (checkExistingRecords.size() <= 1) {
+                            realm.commitTransaction();
+                            cancelTransaction = false;
+                        }
+
+                        // Tell the Main Activity we have new data
+                        sendMessage(Constants.ACTION_REFRESH_DATA);
+                    }
 
-                cnlReader.getPumpSession().setHMAC(MessageUtils.hexStringToByteArray(hmac));
-                cnlReader.getPumpSession().setKey(MessageUtils.hexStringToByteArray(key));
-
-                cnlReader.enterControlMode();
-                try {
-                    cnlReader.enterPassthroughMode();
-                    cnlReader.openConnection();
-                    cnlReader.requestReadInfo();
-                    byte radioChannel = cnlReader.negotiateChannel();
-                    if (radioChannel == 0) {
-                        sendStatus("Could not communicate with the 640g. Are you near the pump?");
-                        Log.i(TAG, "Could not communicate with the 640g. Are you near the pump?");
-                    } else {
-                        sendStatus(String.format(Locale.getDefault(), "Connected to Contour Next Link on channel %d.", (int) radioChannel));
-                        Log.d(TAG, String.format("Connected to Contour Next Link on channel %d.", (int) radioChannel));
-                        cnlReader.beginEHSMSession();
-
-                        pumpRecord.pumpDate = cnlReader.getPumpTime();
-                        cnlReader.getPumpStatus(cgmRecord);
-
-                        writeData(cgmRecord);
-                        sendCgmRecord(cgmRecord);
-                        cnlReader.endEHSMSession();
+                    if (cancelTransaction) {
+                        realm.cancelTransaction();
                     }
-                    cnlReader.closeConnection();
-                } catch (UnexpectedMessageException e) {
-                    Log.e(TAG, "Unexpected Message", e);
-                    sendStatus("Communication Error: " + e.getMessage());
-
-                } finally {
-                    cnlReader.endPassthroughMode();
-                    cnlReader.endControlMode();
                 }
-            } catch (IOException e) {
-                Log.e(TAG, "Error getting SGVs", e);
-                sendStatus("Error connecting to Contour Next Link.");
-            } catch (ChecksumException e) {
-                Log.e(TAG, "Checksum error", e);
-                sendStatus("Checksum error getting message from the Contour Next Link.");
-            } catch (EncryptionException e) {
-                Log.e(TAG, "Encryption exception", e);
-                sendStatus("Error decrypting messages from Contour Next Link.");
-            } catch (TimeoutException e) {
-                Log.e(TAG, "Timeout communicating with Contour", e);
-                sendStatus("Timeout communicating with the Contour Next Link.");
+
+                cnlReader.closeConnection();
             } catch (UnexpectedMessageException e) {
                 Log.e(TAG, "Unexpected Message", e);
-                sendStatus("Could not close connection: " + e.getMessage());
+                sendStatus("Communication Error: " + e.getMessage());
+            } finally {
+                cnlReader.endPassthroughMode();
+                cnlReader.endControlMode();
             }
-
-            // TODO - add retries.
-            if (!isOnline()) {
-                String title = "Cannot upload data";
-                String msg = "Please check that you're connected to the Internet";
-                //showNotification(title, msg);
-                sendStatus(title + "\n" + msg);
-            } else {
-                // FIXME - DO THE UPLOAD!
-                //mUploader.execute(cgmRecord);
+        } catch (IOException e) {
+            Log.e(TAG, "Error connecting to Contour Next Link.", e);
+            sendStatus("Error connecting to Contour Next Link.");
+        } catch (ChecksumException e) {
+            Log.e(TAG, "Checksum error getting message from the Contour Next Link.", e);
+            sendStatus("Checksum error getting message from the Contour Next Link.");
+        } catch (EncryptionException e) {
+            Log.e(TAG, "Error decrypting messages from Contour Next Link.", e);
+            sendStatus("Error decrypting messages from Contour Next Link.");
+        } catch (TimeoutException e) {
+            Log.e(TAG, "Timeout communicating with the Contour Next Link.", e);
+            sendStatus("Timeout communicating with the Contour Next Link.");
+        } catch (UnexpectedMessageException e) {
+            Log.e(TAG, "Could not close connection.", e);
+            sendStatus("Could not close connection: " + e.getMessage());
+        } finally {
+            if (realm.isInTransaction()) {
+                // If we didn't commit the transaction, we've run into an error. Let's roll it back
+                realm.cancelTransaction();
             }
-
-            realm.close();
         }
-    }
 
-    private boolean isOnline() {
-        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo netInfo = cm.getActiveNetworkInfo();
-        return netInfo != null && netInfo.isConnectedOrConnecting();
+        realm.close();
     }
 
     private boolean hasUsbHostFeature() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_HOST);
     }
 
-    // FIXME - replace this with writing to the SQLite DB.
-    private void writeData(CGMRecord cgmRecord) {
-        //Write most recent data
-        try {
-            Context context = getBaseContext();
-            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(context.getFilesDir(), "save.bin"))); //Select where you wish to save the file...
-            oos.writeObject(cgmRecord); // write the class as an 'object'
-            oos.flush(); // flush the stream to insure all of the information was written to 'save.bin'
-            oos.close();// close the stream
-        } catch (Exception e) {
-            Log.e(TAG, "write to OutputStream failed", e);
-        }
-    }
-
-    /*
-    // FIXME - when we want to enable notifications, start with this. We'll need to fix the icon to match
-    // the Android standards (linter will fail anyway)
-    private void showNotification(String title, String message) {
-        NotificationManagerCompat nm = NotificationManagerCompat.from(mContext);
-
-        // The PendingIntent to launch our activity if the user selects this notification
-        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
-        nm.notify(R.string.app_name, new NotificationCompat.Builder(mContext)
-                .setDefaults(NotificationCompat.DEFAULT_ALL)
-                .setContentTitle(title)
-                .setStyle(new NotificationCompat.BigTextStyle()
-                        .bigText(message))
-                .setContentText(message)
-                .setTicker(message)
-                //.setSmallIcon(R.drawable.ic_action_warning)
-                .setSmallIcon(R.drawable.ic_launcher)
-                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher))
-                .setContentIntent(contentIntent)
-                .build());
-    }
-    */
+    public final class Constants {
+        public static final String ACTION_STATUS_MESSAGE = "info.nightscout.android.medtronic.service.STATUS_MESSAGE";
+        public static final String ACTION_NO_USB_PERMISSION = "info.nightscout.android.medtronic.service.NO_USB_PERMISSION";
+        public static final String ACTION_USB_PERMISSION = "info.nightscout.android.medtronic.USB_PERMISSION";
+        public static final String ACTION_REFRESH_DATA = "info.nightscout.android.medtronic.service.CGM_DATA";
+        public static final String ACTION_USB_REGISTER = "info.nightscout.android.medtronic.USB_REGISTER";
 
-    private CGMRecord loadData() {
-        ObjectInputStream ois = null;
-        try {
-            Context context = getBaseContext();
-            ois = new ObjectInputStream(new FileInputStream(new File(context.getFilesDir(), "save.bin")));
-            Object o = ois.readObject();
-            ois.close();
-            return (CGMRecord) o;
-        } catch (Exception ex) {
-            Log.w(TAG, " unable to load Medtronic640g data");
-            try {
-                if (ois != null)
-                    ois.close();
-            } catch (Exception e) {
-                Log.e(TAG, " Error closing ObjectInputStream");
-            }
-        }
-        return new CGMRecord();
+        public static final String EXTENDED_DATA = "info.nightscout.android.medtronic.service.DATA";
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/model/CgmStatusEvent.java b/app/src/main/java/info/nightscout/android/model/CgmStatusEvent.java
index c8759eeb61102ddd8c9b0dc5d24c04050d6b7a56..1100d3270879dfb38dd38311cde16f46235899b9 100644
--- a/app/src/main/java/info/nightscout/android/model/CgmStatusEvent.java
+++ b/app/src/main/java/info/nightscout/android/model/CgmStatusEvent.java
@@ -9,25 +9,14 @@ import io.realm.annotations.Index;
  * Created by lgoedhart on 4/06/2016.
  */
 public class CgmStatusEvent extends RealmObject {
-    public enum TREND {
-        NONE,
-        DOUBLE_UP,
-        SINGLE_UP,
-        FOURTY_FIVE_UP,
-        FLAT,
-        FOURTY_FIVE_DOWN,
-        SINGLE_DOWN,
-        DOUBLE_DOWN,
-        NOT_COMPUTABLE,
-        RATE_OUT_OF_RANGE,
-        NOT_SET
-    }
-
     @Index
     private Date eventDate; // The actual time of the event (assume the capture device eventDate/time is accurate)
     private Date pumpDate; // The eventDate/time on the pump at the time of the event
+    private String deviceName;
     private int sgv;
     private String trend;
+    @Index
+    private boolean uploaded = false;
 
     public Date getEventDate() {
         return eventDate;
@@ -45,6 +34,14 @@ public class CgmStatusEvent extends RealmObject {
         this.pumpDate = pumpDate;
     }
 
+    public String getDeviceName() {
+        return deviceName;
+    }
+
+    public void setDeviceName(String deviceName) {
+        this.deviceName = deviceName;
+    }
+
     public int getSgv() {
         return sgv;
     }
@@ -60,4 +57,26 @@ public class CgmStatusEvent extends RealmObject {
     public void setTrend(TREND trend) {
         this.trend = trend.name();
     }
+
+    public boolean isUploaded() {
+        return uploaded;
+    }
+
+    public void setUploaded(boolean uploaded) {
+        this.uploaded = uploaded;
+    }
+
+    public enum TREND {
+        NONE,
+        DOUBLE_UP,
+        SINGLE_UP,
+        FOURTY_FIVE_UP,
+        FLAT,
+        FOURTY_FIVE_DOWN,
+        SINGLE_DOWN,
+        DOUBLE_DOWN,
+        NOT_COMPUTABLE,
+        RATE_OUT_OF_RANGE,
+        NOT_SET
+    }
 }
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 741f1a68891a94ba73e1a374bfff69e2a953c3c6..1a11543456646e2b9040966c3b545085c1f6e704 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
@@ -12,8 +12,65 @@ public class PumpStatusEvent extends RealmObject {
     @Index
     private Date eventDate; // The actual time of the event (assume the capture device eventDate/time is accurate)
     private Date pumpDate; // The eventDate/time on the pump at the time of the event
+    private String deviceName;
     private float activeInsulin;
     private float reservoirAmount;
     private boolean recentBolusWizard; // Whether a bolus wizard has been run recently
     private int bolusWizardBGL; // in mg/dL. 0 means no recent bolus wizard reading.
+
+    public Date getEventDate() {
+        return eventDate;
+    }
+
+    public void setEventDate(Date eventDate) {
+        this.eventDate = eventDate;
+    }
+
+    public Date getPumpDate() {
+        return pumpDate;
+    }
+
+    public void setPumpDate(Date pumpDate) {
+        this.pumpDate = pumpDate;
+    }
+
+    public String getDeviceName() {
+        return deviceName;
+    }
+
+    public void setDeviceName(String deviceName) {
+        this.deviceName = deviceName;
+    }
+
+    public float getActiveInsulin() {
+        return activeInsulin;
+    }
+
+    public void setActiveInsulin(float activeInsulin) {
+        this.activeInsulin = activeInsulin;
+    }
+
+    public float getReservoirAmount() {
+        return reservoirAmount;
+    }
+
+    public void setReservoirAmount(float reservoirAmount) {
+        this.reservoirAmount = reservoirAmount;
+    }
+
+    public boolean isRecentBolusWizard() {
+        return recentBolusWizard;
+    }
+
+    public void setRecentBolusWizard(boolean recentBolusWizard) {
+        this.recentBolusWizard = recentBolusWizard;
+    }
+
+    public int getBolusWizardBGL() {
+        return bolusWizardBGL;
+    }
+
+    public void setBolusWizardBGL(int bolusWizardBGL) {
+        this.bolusWizardBGL = bolusWizardBGL;
+    }
 }
diff --git a/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java b/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java
index 7519a5c7f75cc4e177012ca37b2ec08d272d0b8a..e070ac229cd308a3e7f82ae50528aa789bfcbb21 100644
--- a/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java
+++ b/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java
@@ -10,7 +10,13 @@ import android.preference.Preference;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceFragment;
 
+import java.io.IOException;
+
 import info.nightscout.android.R;
+import info.nightscout.android.upload.nightscout.NightscoutApi;
+import retrofit2.Call;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
 
 public class SettingsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener {
 
diff --git a/app/src/main/java/info/nightscout/android/upload/UploadHelper.java b/app/src/main/java/info/nightscout/android/upload/UploadHelper.java
deleted file mode 100644
index d6c06f609a458b297eb2df36aeb33c29fed4ee2a..0000000000000000000000000000000000000000
--- a/app/src/main/java/info/nightscout/android/upload/UploadHelper.java
+++ /dev/null
@@ -1,478 +0,0 @@
-package info.nightscout.android.upload;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.AsyncTask;
-import android.preference.PreferenceManager;
-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 org.slf4j.LoggerFactory;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.security.MessageDigest;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import ch.qos.logback.classic.Logger;
-import info.nightscout.android.R;
-import info.nightscout.android.medtronic.MainActivity;
-import info.nightscout.android.medtronic.MedtronicConstants;
-import info.nightscout.android.upload.MedtronicNG.CGMRecord;
-
-public class UploadHelper extends AsyncTask<Record, Integer, Long> {
-
-	private Logger log = (Logger) LoggerFactory.getLogger(UploadHelper.class.getName());
-    private static final String TAG = "DexcomUploadHelper";
-    private SharedPreferences settings = null;// common application preferences
-    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss aa", Locale.getDefault());
-    private static final int SOCKET_TIMEOUT = 60 * 1000;
-    private static final int CONNECTION_TIMEOUT = 30 * 1000;
-
-    Context context;
-	private List<JSONObject> recordsNotUploadedList = new ArrayList<JSONObject>();
-    private List<JSONObject> recordsNotUploadedListJson = new ArrayList<JSONObject>();
-
-    public static final Object isModifyingRecordsLock = new Object();
-
-    public UploadHelper(Context context) {
-        this.context = context;
-		settings = context.getSharedPreferences(MedtronicConstants.PREFS_NAME, 0);
-        synchronized (isModifyingRecordsLock) {
-	        try {
-	        	long currentTime = System.currentTimeMillis();
-	    		long diff = currentTime - settings.getLong("lastDestroy", 0);
-	    		if (diff != currentTime && diff > (6*MedtronicConstants.TIME_60_MIN_IN_MS)) {
-	    			log.debug("Remove older records");
-	    			SharedPreferences.Editor editor = settings.edit();
-	    			if (settings.contains("recordsNotUploaded"))
-	    				editor.remove("recordsNotUploaded");
-	    			if (settings.contains("recordsNotUploadedJson"))
-	    				editor.remove("recordsNotUploadedJson");
-	            	editor.apply();
-	    		}
-	        	if (settings.contains("recordsNotUploaded")){
-	        		JSONArray recordsNotUploaded = new JSONArray(settings.getString("recordsNotUploaded","[]"));
-	        		for (int i = 0; i < recordsNotUploaded.length(); i++){
-	        			recordsNotUploadedList.add(recordsNotUploaded.getJSONObject(i));
-	        		}
-	        		log.debug("retrieve older json records -->" +recordsNotUploaded.length());
-	            	SharedPreferences.Editor editor = settings.edit();
-	            	editor.remove("recordsNotUploaded");
-	            	editor.apply();
-	            }	
-	        	if (settings.contains("recordsNotUploadedJson")){
-	        		JSONArray recordsNotUploadedJson = new JSONArray(settings.getString("recordsNotUploadedJson","[]"));
-	        		for (int i = 0; i < recordsNotUploadedJson.length(); i++){
-	        			recordsNotUploadedListJson.add(recordsNotUploadedJson.getJSONObject(i));
-	        		}
-	        		log.debug("retrieve older json records -->" +recordsNotUploadedJson.length());
-	            	SharedPreferences.Editor editor = settings.edit();
-	            	editor.remove("recordsNotUploadedJson");
-	            	editor.apply();
-	            }	
-			} catch (Exception e) {
-				log.debug("ERROR Retrieving older list, I have lost them");
-				recordsNotUploadedList = new ArrayList<>();
-				recordsNotUploadedListJson = new ArrayList<>();
-				SharedPreferences.Editor editor = settings.edit();
-				if (settings.contains("recordsNotUploaded"))
-					editor.remove("recordsNotUploaded");
-				if (settings.contains("recordsNotUploadedJson"))
-					editor.remove("recordsNotUploadedJson");
-	        	editor.apply();
-			}
-        }
-    }
-
-	/**
-     * doInBackground
-     */
-    protected Long doInBackground(Record... records) {
-
-        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.context);
-        Boolean enableRESTUpload = prefs.getBoolean("EnableRESTUpload", false);
-        try{
-        	if (enableRESTUpload) {
-                long start = System.currentTimeMillis();
-                Log.i(TAG, String.format("Starting upload of %s record using a REST API", records.length));
-                log.info(String.format("Starting upload of %s record using a REST API", records.length));
-                doRESTUpload(prefs, records);
-                Log.i(TAG, String.format("Finished upload of %s record using a REST API in %s ms", records.length, System.currentTimeMillis() - start));
-                log.info(String.format("Finished upload of %s record using a REST API in %s ms", records.length, System.currentTimeMillis() - start));
-            }
-        }catch(Exception e){
-        	log.error("ERROR uploading data!!!!!", e);
-        }
-        
-        
-        return 1L;
-    }
-
-    protected void onPostExecute(Long result) {
-        super.onPostExecute(result);
-        Log.i(TAG, "Post execute, Result: " + result + ", Status: FINISHED");
-        log.info("Post execute, Result: " + result + ", Status: FINISHED");
-
-    }
-
-    private void doRESTUpload(SharedPreferences prefs, Record... records) {
-        String apiScheme = "https://";
-		String apiUrl = "";
-		String apiSecret = prefs.getString(context.getString(R.string.preference_api_secret), "YOURAPISECRET");
-
-		// Add the extra match for "KEY@" to support the previous single field
-		Pattern p = Pattern.compile("(.*\\/\\/)?(.*@)?([^\\/]*)(.*)");
-		Matcher m = p.matcher(prefs.getString(context.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(context.getString(R.string.preference_api_secret), apiSecret ).apply();
-		prefs.edit().putString(context.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);
-			log.error("Unable to do REST API Upload to: " + uploadUrl, e);
-		}
-    }
-
-    private void doRESTUploadTo(String baseURI, Record[] records) {
-		try {
-            int apiVersion = 0;
-            if (baseURI.endsWith("/v1/")) apiVersion = 1;
-
-            String baseURL;
-            String secret = null;
-            String[] uriParts = baseURI.split("@");
-
-            if (uriParts.length == 1 && apiVersion == 0) {
-                baseURL = uriParts[0];
-            } else if (uriParts.length == 1) {
-            	if (recordsNotUploadedListJson.size() > 0){
-                 	JSONArray jsonArray = new JSONArray(recordsNotUploadedListJson);
-                 	SharedPreferences.Editor editor = settings.edit();
-                 	editor.putString("recordsNotUploaded", jsonArray.toString());
-                 	editor.apply();
-                 }
-                throw new Exception("Starting with API v1, a pass phase is required");
-            } else if (uriParts.length == 2 && apiVersion > 0) {
-                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 {
-            	if (recordsNotUploadedListJson.size() > 0){
-                 	JSONArray jsonArray = new JSONArray(recordsNotUploadedListJson);
-                 	SharedPreferences.Editor editor = settings.edit();
-                 	editor.putString("recordsNotUploadedJson", jsonArray.toString());
-                 	editor.apply();
-                 }
-                throw new Exception(String.format("Unexpected baseURI: %s, uriParts.length: %s, apiVersion: %s", baseURI, uriParts.length, apiVersion));
-            }
-
-            
-
-            HttpParams params = new BasicHttpParams();
-            HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
-            HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
-
-            DefaultHttpClient httpclient = new DefaultHttpClient(params);
-
-            postDeviceStatus(baseURL,httpclient);
-
-            if (recordsNotUploadedListJson.size() > 0){
-            	List<JSONObject> auxList = new ArrayList<JSONObject>(recordsNotUploadedListJson);
-            	recordsNotUploadedListJson.clear();
-        		for (int i = 0; i < auxList.size(); i++){
-        			JSONObject json = auxList.get(i);
-        			String postURL = baseURL; 
-                	postURL += "entries";
-                    Log.i(TAG, "postURL: " + postURL);
-
-                    HttpPost post = new HttpPost(postURL);
-
-                    if (apiVersion > 0) {
-                        if (secret == null || secret.isEmpty()) {
-                        	 if (auxList.size() > 0){
-                             	JSONArray jsonArray = new JSONArray(auxList);
-                             	SharedPreferences.Editor editor = settings.edit();
-                             	editor.putString("recordsNotUploaded", jsonArray.toString());
-                             	editor.apply();
-                             }
-                            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);
-                        }
-                    }
-
-                    String jsonString = json.toString();
-
-                    Log.i(TAG, "Upload JSON: " + jsonString);
-                    log.debug("JSON to Upload "+ jsonString);
-
-                    try {
-                        StringEntity se = new StringEntity(jsonString);
-                        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);
-                        log.warn("Unable to post data to: '" + post.getURI().toString() + "'", e);
-                    }
-        		}
-        	}
-            
-            for (Record record : records) {
-            	String postURL = baseURL; 
-            	if (record instanceof GlucometerRecord){
-					postURL +=  "entries";
-            	}else{
-					postURL += "entries";
-            	}
-                Log.i(TAG, "postURL: " + postURL);
-                log.info( "postURL: " + postURL);
-
-                HttpPost post = new HttpPost(postURL);
-
-                if (apiVersion > 0) {
-                    if (secret == null || secret.isEmpty()) {
-                    	 if (recordsNotUploadedListJson.size() > 0){
-                          	JSONArray jsonArray = new JSONArray(recordsNotUploadedListJson);
-                          	SharedPreferences.Editor editor = settings.edit();
-                          	editor.putString("recordsNotUploadedJson", jsonArray.toString());
-                          	editor.apply();
-                          }
-                        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);
-                    }
-                }
-
-                JSONObject json = new JSONObject();
-
-                try {
-					populateV1APIEntry(json, record);
-                } catch (Exception e) {
-                    Log.w(TAG, "Unable to populate entry, apiVersion: " + apiVersion, e);
-                    log.warn("Unable to populate entry, apiVersion: " + apiVersion, e);
-                    continue;
-                }
-
-                String jsonString = json.toString();
-
-                Log.i(TAG, "Upload JSON: " + jsonString);
-                log.info("Upload JSON: " + jsonString);
-
-                try {
-                    StringEntity se = new StringEntity(jsonString);
-                    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) {
-					//Only EGV records are important enough.
-					if (recordsNotUploadedListJson.size() > 49){
-                        recordsNotUploadedListJson.remove(0);
-                        recordsNotUploadedListJson.add(49,json);
-                    }else{
-                        recordsNotUploadedListJson.add(json);
-                    }
-
-					Log.w(TAG, "Unable to post data to: '" + post.getURI().toString() + "'", e);
-                    log.warn( "Unable to post data to: '" + post.getURI().toString() + "'", e);
-                }
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "Unable to post data", e);
-            log.error("Unable to post data", e);
-        }
-        if (recordsNotUploadedListJson.size() > 0){
-        	synchronized (isModifyingRecordsLock) {
-    	        try {
-	        		JSONArray recordsNotUploadedJson = new JSONArray(settings.getString("recordsNotUploadedJson","[]"));
-	        		if (recordsNotUploadedJson.length() > 0 && recordsNotUploadedJson.length() < recordsNotUploadedListJson.size()){
-    	        		for (int i = 0; i < recordsNotUploadedJson.length(); i++){
-    	        			if (recordsNotUploadedListJson.size() > 49){
-    	        				recordsNotUploadedListJson.remove(0);
-    	        				recordsNotUploadedListJson.add(49, recordsNotUploadedJson.getJSONObject(i));
-							}else{
-								recordsNotUploadedListJson.add(recordsNotUploadedJson.getJSONObject(i));
-							}
-    	        		}
-	        		}else{
-	        			for (int i = 0; i < recordsNotUploadedListJson.size(); i++){
-							recordsNotUploadedJson.put(recordsNotUploadedListJson.get(i));
-    	        		}
-	        			int start = 0;
-	        			if (recordsNotUploadedJson.length() > 50){
-	        				start = recordsNotUploadedJson.length() - 51;
-	        			}
-	        			recordsNotUploadedListJson.clear();
-	        			for (int i = start; i < recordsNotUploadedJson.length(); i++){
-							recordsNotUploadedListJson.add(recordsNotUploadedJson.getJSONObject(i));
-    	        		}
-	        		}
-	        		log.debug("retrieve older json records -->" +recordsNotUploadedJson.length());
-	            	SharedPreferences.Editor editor = settings.edit();
-	            	editor.remove("recordsNotUploadedJson");
-	            	editor.apply();
-    			} catch (Exception e) {
-    				log.debug("ERROR RETRIEVING OLDER LISTs, I HAVE LOST THEM");	
-    				SharedPreferences.Editor editor = settings.edit();
-    				if (settings.contains("recordsNotUploadedJson"))
-    					editor.remove("recordsNotUploadedJson");
-    	        	editor.apply();
-    			}
-    	        JSONArray jsonArray = new JSONArray(recordsNotUploadedListJson);
-            	SharedPreferences.Editor editor = settings.edit();
-            	editor.putString("recordsNotUploadedJson", jsonArray.toString());
-            	editor.apply();
-            }
-        }
-    }
-
-	private void postDeviceStatus(String baseURL, DefaultHttpClient httpclient) throws Exception {
-        String devicestatusURL = baseURL + "devicestatus";
-        Log.i(TAG, "devicestatusURL: " + devicestatusURL);
-        log.info("devicestatusURL: " + devicestatusURL);
-
-        JSONObject json = new JSONObject();
-        json.put("uploaderBattery", MainActivity.batLevel);
-		json.put("device", MainActivity.pumpStatusRecord.getDeviceName() );
-
-		JSONObject pumpInfo = new JSONObject();
-		pumpInfo.put( "clock", MainActivity.pumpStatusRecord.pumpDate );
-		pumpInfo.put( "reservoir", MainActivity.pumpStatusRecord.reservoirAmount);
-
-		JSONObject iob = new JSONObject();
-		iob.put( "timestamp", MainActivity.pumpStatusRecord.pumpDate );
-		iob.put( "bolusiob", MainActivity.pumpStatusRecord.activeInsulin );
-
-		JSONObject battery = new JSONObject();
-		battery.put( "percent", MainActivity.pumpStatusRecord.batteryPercentage );
-
-		pumpInfo.put( "iob", iob );
-		pumpInfo.put( "battery", battery );
-		json.put( "pump", pumpInfo );
-        String jsonString = json.toString();
-		Log.i(TAG, "Device Status JSON: " + jsonString);
-		log.debug("Device Status JSON: "+ jsonString);
-
-        HttpPost post = new HttpPost(devicestatusURL);
-        StringEntity se = new StringEntity(jsonString);
-        post.setEntity(se);
-        post.setHeader("Accept", "application/json");
-        post.setHeader("Content-type", "application/json");
-
-        ResponseHandler responseHandler = new BasicResponseHandler();
-        httpclient.execute(post, responseHandler);
-    }
-    
-    private void populateV1APIEntry(JSONObject json, Record oRecord) throws Exception {
-		if (oRecord instanceof CGMRecord) {
-			CGMRecord pumpRecord = (CGMRecord) oRecord;
-			json.put("sgv", pumpRecord.sgv);
-			json.put("direction", pumpRecord.direction);
-			json.put("device", pumpRecord.getDeviceName());
-			json.put("type", "sgv");
-			json.put("date", pumpRecord.sgvDate.getTime());
-			json.put("dateString",  pumpRecord.sgvDate);
-		} else {
-			Date date = DATE_FORMAT.parse(oRecord.displayTime);
-			json.put("date", date.getTime());
-		}
-    }
-
-	private static String convertStreamToString(InputStream is) {
-
-	    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
-	    StringBuilder sb = new StringBuilder();
-
-	    String line = null;
-	    try {
-	        while ((line = reader.readLine()) != null) {
-	            sb.append(line).append("\n");
-	        }
-	    } catch (IOException e) {
-	        e.printStackTrace();
-	    } finally {
-	        try {
-	            is.close();
-	        } catch (IOException e) {
-	            e.printStackTrace();
-	        }
-	    }
-	    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
new file mode 100644
index 0000000000000000000000000000000000000000..c613dfbb5112dad61676c85cd04ebe520c9b56af
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutApi.java
@@ -0,0 +1,33 @@
+package info.nightscout.android.upload.nightscout;
+
+import retrofit2.Call;
+import retrofit2.http.GET;
+
+/**
+ * Created by lgoedhart on 26/06/2016.
+ */
+public interface NightscoutApi {
+    class LoginStatus {
+        public final String status;
+
+        public LoginStatus(String status) {
+            this.status = status;
+        }
+    }
+
+    class SiteStatus {
+        public final String status;
+        public final String name;
+
+        public SiteStatus(String status, String name) {
+            this.status = status;
+            this.name = name;
+        }
+    }
+
+    @GET("/api/v1/status.json")
+    Call<SiteStatus> getStatus();
+
+    @GET("/api/v1/experiments/test")
+    Call<LoginStatus> testLogin();
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..6ba4b5c8d65b8f71dfa44fcef38cadad02e4c87b
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java
@@ -0,0 +1,289 @@
+package info.nightscout.android.upload.nightscout;
+
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+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.JSONObject;
+
+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.CgmStatusEvent;
+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;
+    Context mContext;
+    private Realm mRealm;
+
+    public NightscoutUploadIntentService() {
+        super(NightscoutUploadIntentService.class.getName());
+    }
+
+    protected void sendStatus(String message) {
+        Intent localIntent =
+                new Intent(Constants.ACTION_STATUS_MESSAGE)
+                        .putExtra(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<CgmStatusEvent> records = mRealm
+                .where(CgmStatusEvent.class)
+                .equalTo("uploaded", false)
+                .notEqualTo("sgv", 0)
+                .findAll();
+
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+
+        Boolean enableRESTUpload = prefs.getBoolean("EnableRESTUpload", false);
+        try {
+            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);
+                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) {
+            Log.e(TAG, "ERROR uploading data!!!!!", e);
+        }
+    }
+
+    private void doRESTUpload(SharedPreferences prefs, RealmResults<CgmStatusEvent> 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<CgmStatusEvent> 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));
+            }
+
+            HttpParams params = new BasicHttpParams();
+            HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
+            HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
+
+            DefaultHttpClient httpclient = new DefaultHttpClient(params);
+
+            postDeviceStatus(baseURL, httpclient);
+
+            for (CgmStatusEvent record : records) {
+                String postURL = baseURL + "entries";
+
+                Log.i(TAG, "postURL: " + postURL);
+
+                HttpPost post = new HttpPost(postURL);
+
+                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);
+                }
+
+                JSONObject json = new JSONObject();
+
+                try {
+                    // FIXME - Change this to bulk uploads
+                    populateV1APIEntry(json, record);
+                } catch (Exception e) {
+                    Log.w(TAG, "Unable to populate entry", e);
+                    continue;
+                }
+
+                String jsonString = json.toString();
+
+                Log.i(TAG, "Upload JSON: " + jsonString);
+
+                try {
+                    StringEntity se = new StringEntity(jsonString);
+                    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);
+                }
+
+                // Yay! We uploaded. Tell Realm
+                mRealm.beginTransaction();
+                // TODO - does realm have auto incrementing keys?
+                // Turns out not yet (https://github.com/realm/realm-java/issues/469),
+                // but in the meantime, use this: https://gist.github.com/carloseduardosx/a7bd88d7337660cd10a2c5dcc580ebd0
+                RealmResults<CgmStatusEvent> updateRecordResults = mRealm
+                        .where(CgmStatusEvent.class)
+                        .equalTo("eventDate", record.getEventDate())
+                        .equalTo("deviceName", record.getDeviceName())
+                        .equalTo("sgv", record.getSgv())
+                        .findAll();
+                // FIXME - We shouldn't need this after we remove insertion of duplicates
+                for (CgmStatusEvent updateRecord : updateRecordResults) {
+                    updateRecord.setUploaded(true);
+                }
+                mRealm.commitTransaction();
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to post data", e);
+        }
+    }
+
+    private void postDeviceStatus(String baseURL, DefaultHttpClient httpclient) throws Exception {
+        String devicestatusURL = baseURL + "devicestatus";
+        Log.i(TAG, "devicestatusURL: " + devicestatusURL);
+
+        JSONObject json = new JSONObject();
+        json.put("uploaderBattery", MainActivity.batLevel);
+        json.put("device", MainActivity.pumpStatusRecord.getDeviceName());
+
+        JSONObject pumpInfo = new JSONObject();
+        pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(MainActivity.pumpStatusRecord.pumpDate));
+        pumpInfo.put("reservoir", MainActivity.pumpStatusRecord.reservoirAmount);
+
+        JSONObject iob = new JSONObject();
+        iob.put("timestamp", MainActivity.pumpStatusRecord.pumpDate);
+        iob.put("bolusiob", MainActivity.pumpStatusRecord.activeInsulin);
+
+        JSONObject battery = new JSONObject();
+        battery.put("percent", MainActivity.pumpStatusRecord.batteryPercentage);
+
+        pumpInfo.put("iob", iob);
+        pumpInfo.put("battery", battery);
+        json.put("pump", pumpInfo);
+        String jsonString = json.toString();
+        Log.i(TAG, "Device Status JSON: " + jsonString);
+
+        HttpPost post = new HttpPost(devicestatusURL);
+        StringEntity se = new StringEntity(jsonString);
+        post.setEntity(se);
+        post.setHeader("Accept", "application/json");
+        post.setHeader("Content-type", "application/json");
+
+        ResponseHandler responseHandler = new BasicResponseHandler();
+        httpclient.execute(post, responseHandler);
+    }
+
+    private void populateV1APIEntry(JSONObject json, CgmStatusEvent pumpRecord) throws Exception {
+        // TODO replace with Retrofit/EntriesSerializer
+        json.put("sgv", pumpRecord.getSgv());
+        json.put("direction", EntriesSerializer.getDirectionString(pumpRecord.getTrend()));
+        json.put("device", pumpRecord.getDeviceName());
+        json.put("type", "sgv");
+        json.put("date", pumpRecord.getEventDate().getTime());
+        json.put("dateString", pumpRecord.getEventDate());
+
+    }
+
+    private boolean isOnline() {
+        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        NetworkInfo netInfo = cm.getActiveNetworkInfo();
+        return netInfo != null && netInfo.isConnectedOrConnecting();
+    }
+
+    public final class Constants {
+        public static final String ACTION_STATUS_MESSAGE = "info.nightscout.android.upload.nightscout.STATUS_MESSAGE";
+
+        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
new file mode 100644
index 0000000000000000000000000000000000000000..02da1b07766c5e7f2a3ce413f791c380ca977d7e
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/serializer/EntriesSerializer.java
@@ -0,0 +1,57 @@
+package info.nightscout.android.upload.nightscout.serializer;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+
+import java.lang.reflect.Type;
+
+import info.nightscout.android.model.CgmStatusEvent;
+
+/**
+ * Created by lgoedhart on 26/06/2016.
+ */
+public class EntriesSerializer implements JsonSerializer<CgmStatusEvent> {
+    public static String getDirectionString(CgmStatusEvent.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(CgmStatusEvent src, Type typeOfSrc, JsonSerializationContext context) {
+        final JsonObject jsonObject = new JsonObject();
+        jsonObject.addProperty("sgv", src.getSgv());
+        jsonObject.addProperty("direction", getDirectionString(src.getTrend()));
+        jsonObject.addProperty("device", src.getDeviceName());
+        jsonObject.addProperty("type", "sgv");
+        jsonObject.addProperty("date", src.getEventDate().getTime());
+        jsonObject.addProperty("dateString", String.valueOf(src.getEventDate()));
+
+        return jsonObject;
+    }
+}
diff --git a/app/src/main/res/drawable/background_splash.xml b/app/src/main/res/drawable/background_splash.xml
new file mode 100644
index 0000000000000000000000000000000000000000..161ca05574fa42f9ecff5b430f454f82b5027cd7
--- /dev/null
+++ b/app/src/main/res/drawable/background_splash.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:drawable="@color/md_black_1000"/>
+
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_launcher"/>
+    </item>
+
+</layer-list>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index f8dec0427c4c8487c39cce4a993fa624cf926080..56ac4a9c6e3a449922fac8c1bcedbd368be5f4a1 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -2,7 +2,6 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:gravity="center_horizontal"
     android:orientation="vertical"
     android:paddingBottom="@dimen/activity_vertical_margin"
@@ -47,6 +46,7 @@
                     android:layout_height="wrap_content"
                     android:hint="@string/prompt_username"
                     android:inputType="text"
+                    android:imeOptions="actionNext"
                     android:maxLines="1"
                     android:singleLine="true"/>
 
@@ -57,7 +57,7 @@
                     android:hint="@string/prompt_password"
                     android:imeActionId="@+id/login"
                     android:imeActionLabel="@string/action_sign_in_short"
-                    android:imeOptions="actionUnspecified"
+                    android:imeOptions="actionDone|actionUnspecified"
                     android:inputType="textPassword"
                     android:maxLines="1"
                     android:singleLine="true"/>
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 5ecb12545edb96666e30bb41f1e8810666aa2480..b8e7b4b93be8c470d57fcb88bf2d70fa92a9fed9 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -16,7 +16,7 @@
             android:layout_height="wrap_content"
             app:layout_scrollFlags="scroll|enterAlways"
             app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
-            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
 
         </android.support.v7.widget.Toolbar>
 
@@ -37,7 +37,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="center"
             android:layout_marginTop="5sp"
-            android:text="3 minutes ago"
+            android:text="never"
             android:textAppearance="?android:attr/textAppearanceMedium" />
 
         <LinearLayout
@@ -119,38 +119,13 @@
 
     </LinearLayout>
 
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:orientation="horizontal">
-
-        <Button
-            android:id="@+id/button_stop_service"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/button_text_stop_uploading_data" />
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="wrap_content"
+    <!--
+    <com.github.mikephil.charting.charts.LineChart
+        android:id="@+id/chart"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:orientation="horizontal">
-
-        <Button
-            android:id="@+id/button_start_service"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/button_text_get_now" />
-
-        <Button
-            android:id="@+id/button_clear_log"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/button_text_clear_log" />
-    </LinearLayout>
+        android:visibility="invisible" />
+    -->
 
     <ScrollView
         android:id="@+id/scrollView"
diff --git a/app/src/main/res/layout/activity_status.xml b/app/src/main/res/layout/activity_status.xml
new file mode 100644
index 0000000000000000000000000000000000000000..eff1081d8b651ab4bebcaae0eab44e4fb08dea67
--- /dev/null
+++ b/app/src/main/res/layout/activity_status.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    tools:context="info.nightscout.android.medtronic.StatusActivity"
+    android:orientation="vertical">
+
+    <android.support.design.widget.AppBarLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+        <android.support.v7.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_scrollFlags="scroll|enterAlways"
+            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
+            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+        </android.support.v7.widget.Toolbar>
+
+    </android.support.design.widget.AppBarLayout>
+
+    <ScrollView
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/status_scroll_view"
+        android:fillViewport="true" >
+
+        <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="Pump Status"
+                android:id="@+id/status_pump_text_view"
+                android:singleLine="true" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="Uploader Status"
+                android:id="@+id/status_uploader_text_view" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="CGM Status"
+                android:id="@+id/status_cgm_text_view" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="Nightscout Status"
+                android:id="@+id/status_nightscout_text_view" />
+
+        </LinearLayout>
+    </ScrollView>
+
+</LinearLayout>
diff --git a/app/src/main/res/menu/menu.xml b/app/src/main/res/menu/menu.xml
index ed398f07134bc58c192bc0139912bb2c2ac27c2d..3a4948d2788551963d7d0670e34f0e6c67aa5944 100644
--- a/app/src/main/res/menu/menu.xml
+++ b/app/src/main/res/menu/menu.xml
@@ -2,13 +2,9 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
     <item
-            android:id="@+id/menu_settings"
-            android:orderInCategory="100"
-            android:title="@string/menu_name_preferences">
-    </item>
-    <item
-            android:id="@+id/registerCNL"
-            android:orderInCategory="100"
-            android:title="@string/register_contour_next_link">
-    </item>
+        android:id="@+id/action_menu_status"
+        android:icon="@drawable/ic_launcher"
+        android:orderInCategory="100"
+        app:showAsAction="always"
+        android:title="@string/menu_name_status"/>
 </menu>
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7ce840eb60032b49e97e6a7569f955b7a4ba9437
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,12 @@
+<resources>
+
+    <!-- Declare custom theme attributes that allow changing which styles are
+         used for button bars depending on the API level.
+         ?android:attr/buttonBarStyle is new as of API 11 so this is
+         necessary to support previous API levels. -->
+    <declare-styleable name="ButtonBarContainerTheme">
+        <attr name="metaButtonBarStyle" format="reference" />
+        <attr name="metaButtonBarButtonStyle" format="reference" />
+    </declare-styleable>
+
+</resources>
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000000000000000000000000000000000000..327c0604ce2213706c63cb482ad34c50ec395f25
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+<resources>
+
+    <color name="black_overlay">#66000000</color>
+
+</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4622e91ea33e751de165b7f071d94a5aa9efb7ea..bbb585428d3ae24f746f6ff16c7ccdb68440663e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -7,14 +7,14 @@
     <string name="eula_accept">Accept</string>
     <string name="eula_refuse">Refuse</string>
     <string-array name="calib_types_values">
-     <item>1</item>
-     <item>2</item>
-     <item>3</item>
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
     </string-array>
     <string-array name="levelList">
-     <item>Error</item>
-     <item>Info</item>
-     <item>Debug</item>
+        <item>Error</item>
+        <item>Info</item>
+        <item>Debug</item>
     </string-array>
     <string name="title_activity_login">CareLink login</string>
 
@@ -33,6 +33,7 @@
     <string name="register_contour_next_link">Register Contour Next Link</string>
     <string name="preferences_enable_crashlytics">prefs_enable_crashlytics</string>
     <string name="preferences_enable_answers">prefs_enable_answers</string>
+    <string name="preferences_enable_remote_logcat">prefs_enable_remote_logcat</string>
     <string name="menu_name_preferences">Preferences</string>
     <string name="button_text_stop_uploading_data">Stop Uploading CGM Data</string>
     <string name="button_text_clear_log">Clear Log</string>
@@ -43,4 +44,9 @@
     <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>
+
+    <string name="title_activity_status">Uploader Status</string>
+    <string name="dummy_button">Dummy Button</string>
+    <string name="dummy_content">DUMMY\nCONTENT</string>
+    <string name="menu_name_status">Status</string>
 </resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index d5d511766e2bf455a639fbe087a72025766bf3e2..98bc8f595e27b154ad08893a68cdabb53fd4db4e 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -7,12 +7,15 @@
         <item name="material_drawer_background">@color/material_drawer_dark_background</item>
         <item name="material_drawer_primary_text">@color/material_drawer_dark_primary_text</item>
         <item name="material_drawer_primary_icon">@color/material_drawer_dark_primary_icon</item>
-        <item name="material_drawer_secondary_text">@color/material_drawer_dark_secondary_text</item>
+        <item name="material_drawer_secondary_text">@color/material_drawer_dark_secondary_text
+        </item>
         <item name="material_drawer_hint_text">@color/material_drawer_dark_hint_text</item>
         <item name="material_drawer_divider">@color/material_drawer_dark_divider</item>
         <item name="material_drawer_selected">@color/material_drawer_dark_selected</item>
         <item name="material_drawer_selected_text">@color/material_drawer_dark_selected_text</item>
-        <item name="material_drawer_header_selection_text">@color/material_drawer_dark_header_selection_text</item>
+        <item name="material_drawer_header_selection_text">
+            @color/material_drawer_dark_header_selection_text
+        </item>
     </style>
 
     <!-- Settings theme. Has an action bar. -->
@@ -20,4 +23,20 @@
 
     </style>
 
+    <style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
+        <item name="android:windowBackground">@drawable/background_splash</item>
+    </style>
+
+    <style name="FullscreenTheme" parent="AppTheme">
+        <item name="android:actionBarStyle">@style/FullscreenActionBarStyle</item>
+        <item name="android:windowActionBarOverlay">true</item>
+        <item name="android:windowBackground">@null</item>
+        <item name="metaButtonBarStyle">?android:attr/buttonBarStyle</item>
+        <item name="metaButtonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
+    </style>
+
+    <style name="FullscreenActionBarStyle" parent="Widget.AppCompat.ActionBar">
+        <item name="android:background">@color/black_overlay</item>
+    </style>
+
 </resources>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 3a039ba36f60428eed118a73ba7734eac5e66c59..8a8fb08d3852284649d8428d84e72fac42c4d557 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -66,6 +66,11 @@
             android:key="@string/preferences_enable_answers"
             android:summary="Sends usage data to the developer to help develop a better app."
             android:title="Share usage data" />
+        <SwitchPreference
+            android:defaultValue="false"
+            android:key="@string/preferences_enable_remote_logcat"
+            android:summary="Allow the developer to debug your app. Only enable if asked to by the support team."
+            android:title="Remote Debugging" />
         <ListPreference
             android:defaultValue="1"
             android:disableDependentsState="false"