Skip to content
Snippets Groups Projects
Commit 69e1deb6 authored by Mikael Rinnetmäki's avatar Mikael Rinnetmäki Committed by GitHub
Browse files

Merge pull request #1 from pazaan/master

Update master
parents 8d6c623a 084cebf7
No related branches found
No related tags found
No related merge requests found
Showing
with 650 additions and 603 deletions
......@@ -13,7 +13,7 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
......@@ -14,6 +14,27 @@ This is an Android app to upload data from a MiniMed 640G insulin pump to a Nigh
<a target="blank" href="https://raw.githubusercontent.com/wiki/pazaan/640gAndroidUploader/images/kit-in-case-2.jpg"><img src="https://raw.githubusercontent.com/wiki/pazaan/640gAndroidUploader/images/kit-in-case-2.jpg" width="200"></a>
<br/><br/>
#### Development - getting started
- Install [Android Studio](https://developer.android.com/studio/index.html)
- Install [Fabric Plugin](https://fabric.io), enable Crashlytics, this should create `app/fabric.properties` with your fabric apiSecret and also add a fabric key to `app/src/AndroidManifest.xml`
```
<meta-data
android:name="io.fabric.ApiKey"
android:value="YOUR-FABRIC-KEY" />
```
(**take care not to commit this change**)
- Create a [BugFender](https://app.bugfender.com) account, create `app/bugfender.properties` and populate with
```
apiKey=YOUR-BUGFENDER-KEY
```
- Set up a virtual device in the AVD manager or connect an Android phone with USB debugging enabled.
- Use one of the run configurations, eg `installDebug`
#### App Credits
* Based on https://github.com/arbox0/MedtronicUploader *(though the internals are completely changed for the 640G)*
* Uses the [android-service-example](https://code.launchpad.net/~binwiederhier/+junk/android-service-example) by Philipp C. Heckel
......
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="640gAndroidUploader" external.system.module.version="0.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/fabric/res/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<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/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" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<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/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/23.4.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.4.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.4.0/jars" />
<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.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" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.crashlytics.sdk.android/crashlytics/2.5.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.getkeepsafe.relinker/relinker/1.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/fastadapter/1.5.2/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/google-material-typeface/2.2.0.1.original/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/iconics-core/2.6.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/materialdrawer/5.2.9/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/materialize/0.8.8/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/io.fabric.sdk.android/fabric/1.3.10/jars" />
<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-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<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/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" />
<orderEntry type="library" exported="" name="realm-annotations-1.0.0" level="project" />
<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="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="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="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" />
</component>
</module>
\ No newline at end of file
import org.ajoberstar.grgit.Grgit
buildscript {
repositories {
maven { url 'https://maven.fabric.io/public' }
......@@ -5,6 +7,8 @@ buildscript {
dependencies {
classpath 'io.fabric.tools:gradle:1.21.6'
classpath 'io.realm:realm-gradle-plugin:1.1.1'
classpath 'org.ajoberstar:grgit:1.5.0'
}
}
plugins {
......@@ -12,24 +16,41 @@ plugins {
}
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
apply plugin: 'realm-android'
repositories {
maven { url 'https://maven.fabric.io/public' }
maven { url "https://jitpack.io" }
}
apply plugin: 'io.fabric'
apply plugin: 'realm-android'
def gitVersion() {
def process = ['sh', '-c', 'git tag -l | grep -c ".*" -'].execute().text.trim()
return process.toInteger() + 1
// current dir is <your proj>/app, so it's likely that all your git repo files are in the dir
// above.
ext.repo = Grgit.open(project.file('..'))
// should result in the same value as running
// git tag -l | wc -l or git tag -l | grep -c ".*" -
def numOfTags = ext.repo.tag.list().size()
return numOfTags
}
def gitCommitId() {
//def process = ['sh', '-c', 'git tag -l | grep -c ".*" -'].execute().text.trim()
//return process.toInteger() + 1
//return 42
// current dir is <your proj>/app, so it's likely that all your git repo files are in the dir
// above.
ext.repo = Grgit.open(project.file('..'))
return ext.repo.log().first().id.substring(0, 7)
}
def getBugfenderApiKey(){
def getBugfenderApiKey() {
Properties properties = new Properties()
properties.load(new FileInputStream("app/bugfender.properties"))
return "\"" + properties.getProperty("apiKey", "") +"\""
return "\"" + properties.getProperty("apiKey", "") + "\""
}
android {
......@@ -46,7 +67,7 @@ android {
applicationId "info.nightscout.android"
minSdkVersion 14
targetSdkVersion 23
versionName project.properties['version']
versionName project.properties['version'] + "/" + gitCommitId()
versionCode gitVersion()
buildConfigField "String", "BUGFENDER_API_KEY", getBugfenderApiKey()
}
......@@ -74,7 +95,7 @@ task signRelease << {
'-digestalg',
'SHA1',
'-keystore',
'/Users/lgoedhart/keystores/nightscout_android.jks',
'/Users/lennart/keystores/nightscout_android.jks',
'app/build/outputs/apk/app-release-unsigned.apk',
'nightscoutandroidkey'
]
......@@ -94,7 +115,7 @@ task signRelease << {
task zipalignRelease << {
def command = [
'/Users/lgoedhart/Library/Android/sdk/build-tools/23.0.3/zipalign',
'/Users/lennart/Library/Android/sdk/build-tools/23.0.3/zipalign',
'-v',
'4',
'app/build/outputs/apk/app-release-unsigned.apk',
......@@ -116,14 +137,14 @@ task zipalignRelease << {
release {
tagTemplate = 'v${version}'
buildTasks = ['assembleRelease']
buildTasks = ['build']
beforeReleaseBuild.dependsOn 'clean'
afterReleaseBuild.dependsOn 'signRelease', 'zipalignRelease'
}
dependencies {
compile files('libs/slf4j-api-1.7.2.jar')
compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {
compile('com.crashlytics.sdk.android:crashlytics:2.6.5@aar') {
transitive = true;
}
compile('com.mikepenz:materialdrawer:5.2.9@aar') {
......@@ -133,8 +154,9 @@ dependencies {
compile 'org.apache.commons:commons-lang3:3.4'
compile 'com.mikepenz:google-material-typeface:2.2.0.1.original@aar'
compile 'uk.co.chrisjenx:calligraphy:2.2.0'
compile 'com.bugfender.sdk:android:0.4'
compile 'com.github.PhilJay:MPAndroidChart:v2.2.5'
compile 'com.bugfender.sdk:android:0.6.2'
compile 'com.github.PhilJay:MPAndroidChart:v3.0.0-beta1'
compile 'com.github.PhilJay:MPAndroidChart-Realm:v1.1.0@aar'
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'
......
version=0.4.0-SNAPSHOT
version=0.5.0-SNAPSHOT
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="info.nightscout.android">
<uses-sdk android:maxSdkVersion="23" />
<uses-sdk android:minSdkVersion="14" android:maxSdkVersion="23"
tools:overrideLibrary="com.github.mikephil.charting.data.realm"/>
<uses-feature android:name="android.hardware.usb.host" />
......@@ -16,6 +18,9 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USB_PERMISSION" />
<!-- allow to disable battery optimization -->
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:name=".UploaderApplication"
android:allowBackup="true"
......@@ -24,6 +29,7 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!-- I have set screenOrientation to "portrait" to avoid the restart of AsyncTasks when you rotate the phone -->
<activity
android:name=".medtronic.MainActivity"
......@@ -31,6 +37,7 @@
android:label="@string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait">
<intent-filter android:icon="@drawable/ic_launcher">
<category android:name="android.intent.category.DEFAULT" />
......@@ -61,15 +68,22 @@
android:label="@string/title_activity_login"
android:theme="@style/SettingsTheme" />
<activity android:name=".medtronic.StatusActivity" />
<service
android:name=".upload.nightscout.NightscoutUploadIntentService"
android:icon="@drawable/ic_launcher" />
<service
android:name=".xdrip_plus.XDripPlusUploadIntentService"
android:icon="@drawable/ic_launcher" />
<service
android:name=".medtronic.service.MedtronicCnlIntentService"
android:icon="@drawable/ic_launcher" />
<activity android:name=".medtronic.StatusActivity"></activity>
<receiver android:name=".medtronic.service.MedtronicCnlAlarmReceiver" />
<receiver android:name=".upload.nightscout.NightscoutUploadReceiver" />
<receiver android:name=".xdrip_plus.XDripPlusUploadReceiver" />
</application>
</manifest>
\ No newline at end of file
......@@ -9,8 +9,6 @@ import android.util.Log;
import java.io.IOException;
//import com.hoho.android.usbserial.driver.UsbId;
/**
* USB HID Driver implementation.
*
......@@ -89,14 +87,10 @@ public class UsbHidDriver extends CommonUsbDriver {
int readAmt = Math.min(dest.length, mReadBuffer.length);
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
timeoutMillis);
if (numBytesRead < 0) {
// This sucks: we get -1 on timeout, not 0 as preferred.
// We *should* use UsbRequest, except it has a bug/api oversight
// where there is no way to determine the number of bytes read
// in response :\ -- http://b.android.com/28023
return 0;
if (numBytesRead > 0) {
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
mConnection.releaseInterface(mInterface);
}
return numBytesRead;
......
......@@ -32,7 +32,7 @@ public class UploaderApplication extends Application {
Fabric.with(this, new Crashlytics());
}
if (prefs.getBoolean(getString(R.string.preferences_enable_answers), true)) {
Fabric.with(this, new Answers());
Fabric.with(this, new Answers(), new Crashlytics());
}
if (prefs.getBoolean(getString(R.string.preferences_enable_remote_logcat), false)) {
......@@ -41,7 +41,10 @@ public class UploaderApplication extends Application {
Bugfender.setDeviceString("NightscoutURL", prefs.getString(getString(R.string.preference_nightscout_url), "Not set"));
}
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this).build();
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this)
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(realmConfiguration);
}
}
......@@ -62,19 +62,8 @@ import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper;
*/
public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCallbacks<Cursor> {
/**
* Keep track of the login task to ensure we can cancel it if requested.
*/
// TODO - Replace with Rx.Java
private GetHmacAndKey mHmacAndKeyTask = null;
// UI references.
private EditText mUsernameView;
private EditText mPasswordView;
private View mProgressView;
private View mLoginFormView;
private TextView mRegisteredStickView;
private MenuItem mLoginMenuItem;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -82,25 +71,8 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
setContentView(R.layout.activity_login);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle("Register USB");
// Set up the login form.
mUsernameView = (EditText) findViewById(R.id.username);
getSupportActionBar().setTitle("Registered Devices");
mPasswordView = (EditText) findViewById(R.id.password);
mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
if (id == EditorInfo.IME_ACTION_DONE) {
attemptLogin();
return true;
}
return false;
}
});
mLoginFormView = findViewById(R.id.login_form);
mProgressView = findViewById(R.id.login_progress);
mRegisteredStickView = (TextView) findViewById(R.id.registered_usb_devices);
showRegisteredSticks();
......@@ -108,21 +80,12 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_register_usb, menu);
mLoginMenuItem = menu.findItem(R.id.action_menu_login);
mLoginMenuItem.setIcon(new IconicsDrawable(this, GoogleMaterial.Icon.gmd_cloud_download).color(Color.WHITE).actionBar());
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_menu_login:
attemptLogin();
break;
case android.R.id.home:
finish();
break;
......@@ -135,96 +98,12 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}
/**
* Attempts to sign in or register the account specified by the login form.
* If there are form errors (invalid username, missing fields, etc.), the
* errors are presented and no actual login attempt is made.
*/
private void attemptLogin() {
if (mHmacAndKeyTask != null || !checkOnline("Please connect to the Internet", "You must be online to register your USB stick.")) {
return;
}
// Reset errors.
mUsernameView.setError(null);
mPasswordView.setError(null);
// Store values at the time of the login attempt.
String username = mUsernameView.getText().toString();
String password = mPasswordView.getText().toString();
boolean cancel = false;
View focusView = null;
// Check for a valid password, if the user entered one.
if (TextUtils.isEmpty(password)) {
mPasswordView.setError(getString(R.string.error_invalid_password));
focusView = mPasswordView;
cancel = true;
}
// Check for a valid username address.
if (TextUtils.isEmpty(username)) {
mUsernameView.setError(getString(R.string.error_field_required));
focusView = mUsernameView;
cancel = true;
}
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
focusView.requestFocus();
} else {
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
showProgress(true);
mHmacAndKeyTask = new GetHmacAndKey(username, password);
mHmacAndKeyTask.execute((Void) null);
}
}
/**
* Shows the progress UI and hides the login form.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
private void showProgress(final boolean show) {
// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
// for very easy animations. If available, use these APIs to fade-in
// the progress spinner.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
mLoginFormView.animate().setDuration(shortAnimTime).alpha(
show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
});
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mProgressView.animate().setDuration(shortAnimTime).alpha(
show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
}
});
} else {
// The ViewPropertyAnimator APIs are not available, so simply show
// and hide the relevant UI components.
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
}
private void showRegisteredSticks() {
Realm realm = Realm.getDefaultInstance();
RealmResults<ContourNextLinkInfo> results = realm.where(ContourNextLinkInfo.class).findAll();
String deviceTableHtml = "<big><b>Registered Devices</b></big><br/>";
String deviceTableHtml = "";
for (ContourNextLinkInfo info : results) {
String longSerial = info.getSerialNumber();
......@@ -236,29 +115,6 @@ 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;
......@@ -271,116 +127,4 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
/**
* Represents an asynchronous login/registration task used to authenticate
* the user.
*/
public class GetHmacAndKey extends AsyncTask<Void, Void, Boolean> {
private final String mUsername;
private final String mPassword;
GetHmacAndKey(String username, String password) {
mUsername = username;
mPassword = password;
}
@Override
protected Boolean doInBackground(final Void... params) {
try {
DefaultHttpClient client = new DefaultHttpClient();
HttpPost loginPost = new HttpPost("https://carelink.minimed.eu/patient/j_security_check");
List<NameValuePair> nameValuePairs = new ArrayList<>();
nameValuePairs.add(new BasicNameValuePair("j_username", mUsername));
nameValuePairs.add(new BasicNameValuePair("j_password", mPassword));
nameValuePairs.add(new BasicNameValuePair("j_character_encoding", "UTF-8"));
loginPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
HttpResponse response = client.execute(loginPost);
if (response.getStatusLine().getStatusCode() == 200) {
// Get the HMAC/keys for every serial we have seen
Realm realm = Realm.getDefaultInstance();
RealmResults<ContourNextLinkInfo> results = realm.where(ContourNextLinkInfo.class).findAll();
for (ContourNextLinkInfo info : results) {
String longSerial = info.getSerialNumber();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream hmacRequest = new ObjectOutputStream(buffer);
hmacRequest.writeInt(0x1c);
hmacRequest.writeObject(longSerial.replaceAll("\\d+-", ""));
HttpPost hmacPost = new HttpPost("https://carelink.minimed.eu/patient/secure/SnapshotServer/");
hmacPost.setEntity(new ByteArrayEntity(buffer.toByteArray()));
hmacPost.setHeader("Content-type", "application/octet-stream");
response = client.execute(hmacPost);
ByteArrayInputStream inputBuffer = new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
ObjectInputStream hmacResponse = new ObjectInputStream(inputBuffer);
byte[] hmacBytes = (byte[]) hmacResponse.readObject();
ArrayUtils.reverse(hmacBytes);
String hmac = MessageUtils.byteArrayToHexString(hmacBytes);
buffer.reset();
inputBuffer.reset();
ObjectOutputStream keyRequest = new ObjectOutputStream(buffer);
keyRequest.writeInt(0x1f);
keyRequest.writeObject(longSerial);
HttpPost keyPost = new HttpPost("https://carelink.minimed.eu/patient/secure/SnapshotServer/");
keyPost.setEntity(new ByteArrayEntity(buffer.toByteArray()));
keyPost.setHeader("Content-type", "application/octet-stream");
response = client.execute(keyPost);
inputBuffer = new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
ObjectInputStream keyResponse = new ObjectInputStream(inputBuffer);
keyResponse.readInt(); // Throw away the first int. Not sure what it does
String key = MessageUtils.byteArrayToHexString((byte[]) keyResponse.readObject());
realm.beginTransaction();
info.setHmac(hmac);
info.setKey(key);
realm.commitTransaction();
}
return true;
}
} catch (ClientProtocolException e) {
return false;
} catch (IOException e) {
return false;
} catch (ClassNotFoundException e) {
return false;
}
return false;
}
@Override
protected void onPostExecute(final Boolean success) {
mHmacAndKeyTask = null;
if (success) {
showRegisteredSticks();
mLoginMenuItem.setVisible(false);
mLoginFormView.setVisibility(View.GONE);
mProgressView.setVisibility(View.GONE);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mLoginFormView.getWindowToken(), 0);
} else {
showProgress(false);
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
@Override
protected void onCancelled() {
mHmacAndKeyTask = null;
showProgress(false);
}
}
}
\ No newline at end of file
package info.nightscout.android.medtronic;
import org.apache.commons.lang3.ArrayUtils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Created by lgoedhart on 26/03/2016.
*/
public class MedtronicCNLSession {
public class MedtronicCnlSession {
private static final String HMAC_PADDING = "A4BD6CED9A42602564F413123";
private byte[] HMAC;
private byte[] key;
private String stickSerial;
private long linkMAC;
private long pumpMAC;
......@@ -14,16 +23,31 @@ public class MedtronicCNLSession {
private int bayerSequenceNumber = 1;
private int medtronicSequenceNumber = 1;
public byte[] getHMAC() {
/*public byte[] getHMAC() {
return HMAC;
}*/
public byte[] getHMAC() throws NoSuchAlgorithmException {
String shortSerial = this.stickSerial.replaceAll("\\d+-", "");
byte[] message = (shortSerial + HMAC_PADDING).getBytes();
byte[] numArray;
MessageDigest instance = MessageDigest.getInstance("SHA-256");
instance.update(message);
numArray = instance.digest();
ArrayUtils.reverse(numArray);
return numArray;
}
public byte[] getKey() {
return key;
}
public byte[] getIV() {
byte[] iv = new byte[key.length];
System.arraycopy(key,0,iv,0,key.length);
System.arraycopy(key, 0, iv, 0, key.length);
iv[0] = radioChannel;
return iv;
}
......@@ -32,7 +56,7 @@ public class MedtronicCNLSession {
return linkMAC;
}
public void setLinkMAC( long linkMAC ) {
public void setLinkMAC(long linkMAC) {
this.linkMAC = linkMAC;
}
......@@ -40,7 +64,7 @@ public class MedtronicCNLSession {
return pumpMAC;
}
public void setPumpMAC( long pumpMAC ) {
public void setPumpMAC(long pumpMAC) {
this.pumpMAC = pumpMAC;
}
......@@ -68,11 +92,39 @@ public class MedtronicCNLSession {
this.radioChannel = radioChannel;
}
public void setHMAC( byte[] hmac ) {
public void setHMAC(byte[] hmac) {
this.HMAC = hmac;
}
public void setKey( byte[] key ) {
public void setKey(byte[] key) {
this.key = key;
}
public void setPackedLinkKey(byte[] packedLinkKey) {
this.key = new byte[16];
int pos = this.stickSerial.charAt(this.stickSerial.length() - 1) & 7;
for (int i = 0; i < this.key.length; i++) {
if ((packedLinkKey[pos + 1] & 1) == 1) {
this.key[i] = (byte) ~packedLinkKey[pos];
} else {
this.key[i] = packedLinkKey[pos];
}
if (((packedLinkKey[pos + 1] >> 1) & 1) == 0) {
pos += 3;
} else {
pos += 2;
}
}
}
public String getStickSerial() {
return stickSerial;
}
public void setStickSerial(String stickSerial) {
this.stickSerial = stickSerial;
}
}
package info.nightscout.android.medtronic.message;
import info.nightscout.android.medtronic.MedtronicCNLSession;
import info.nightscout.android.medtronic.MedtronicCnlSession;
/**
* Created by lgoedhart on 26/03/2016.
*/
public class BeginEHSMMessage extends MedtronicSendMessage {
public BeginEHSMMessage(MedtronicCNLSession pumpSession) throws EncryptionException {
public BeginEHSMMessage(MedtronicCnlSession pumpSession) throws EncryptionException {
super(SendMessageType.BEGIN_EHSM_SESSION, pumpSession, buildPayload());
}
......
package info.nightscout.android.medtronic.message;
import info.nightscout.android.medtronic.MedtronicCNLSession;
import info.nightscout.android.medtronic.MedtronicCnlSession;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
......@@ -9,11 +9,11 @@ import java.nio.ByteOrder;
* Created by lgoedhart on 26/03/2016.
*/
public class ChannelNegotiateMessage extends MedtronicMessage {
public ChannelNegotiateMessage(MedtronicCNLSession pumpSession) {
public ChannelNegotiateMessage(MedtronicCnlSession pumpSession) {
super(CommandType.SEND_MESSAGE, CommandAction.CHANNEL_NEGOTIATE, pumpSession, buildPayload(pumpSession));
}
protected static byte[] buildPayload( MedtronicCNLSession pumpSession ) {
protected static byte[] buildPayload( MedtronicCnlSession pumpSession ) {
ByteBuffer payload = ByteBuffer.allocate(26);
payload.order(ByteOrder.LITTLE_ENDIAN);
// The MedtronicMessage sequence number is always sent as 1 for this message,
......
package info.nightscout.android.medtronic.message;
import info.nightscout.android.medtronic.MedtronicCNLSession;
import info.nightscout.android.medtronic.MedtronicCnlSession;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
......@@ -34,13 +34,17 @@ public class ContourNextLinkBinaryMessage extends ContourNextLinkMessage{
CommandType(int commandType) {
value = (byte) commandType;
}
public int getValue() {
return value;
}
}
public ContourNextLinkBinaryMessage(CommandType commandType, MedtronicCNLSession pumpSession, byte[] payload) {
public ContourNextLinkBinaryMessage(CommandType commandType, MedtronicCnlSession pumpSession, byte[] payload) {
super(buildPayload(commandType, pumpSession, payload));
}
protected static byte[] buildPayload(CommandType commandType, MedtronicCNLSession pumpSession, byte[] payload) {
protected static byte[] buildPayload(CommandType commandType, MedtronicCnlSession pumpSession, byte[] payload) {
int payloadLength = payload == null ? 0 : payload.length;
ByteBuffer payloadBuffer = ByteBuffer.allocate( ENVELOPE_SIZE + payloadLength );
......@@ -48,7 +52,7 @@ public class ContourNextLinkBinaryMessage extends ContourNextLinkMessage{
payloadBuffer.put((byte) 0x51);
payloadBuffer.put((byte) 0x3);
payloadBuffer.put("000000".getBytes()); // Text of Pump serial, but 000000 for 640g
payloadBuffer.put("000000".getBytes()); // Text of PumpInfo serial, but 000000 for 640g
byte[] unknownBytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
payloadBuffer.put(unknownBytes);
payloadBuffer.put(commandType.value);
......
package info.nightscout.android.medtronic.message;
import info.nightscout.android.medtronic.MedtronicCNLSession;
import info.nightscout.android.medtronic.MedtronicCnlSession;
/**
* Created by lgoedhart on 26/03/2016.
*/
public class EndEHSMMessage extends MedtronicSendMessage {
public EndEHSMMessage(MedtronicCNLSession pumpSession) throws EncryptionException {
public EndEHSMMessage(MedtronicCnlSession pumpSession) throws EncryptionException {
super(SendMessageType.END_EHSM_SESSION, pumpSession, buildPayload());
}
......
package info.nightscout.android.medtronic.message;
import info.nightscout.android.medtronic.MedtronicCNLSession;
import info.nightscout.android.medtronic.MedtronicCnlSession;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
......@@ -29,7 +29,7 @@ public class MedtronicMessage extends ContourNextLinkBinaryMessage {
}
}
protected MedtronicMessage(CommandType commandType, CommandAction commandAction, MedtronicCNLSession pumpSession, byte[] payload) {
protected MedtronicMessage(CommandType commandType, CommandAction commandAction, MedtronicCnlSession pumpSession, byte[] payload) {
super(commandType, pumpSession, buildPayload(commandAction, payload));
}
......
package info.nightscout.android.medtronic.message;
import info.nightscout.android.medtronic.MedtronicCNLSession;
import info.nightscout.android.medtronic.MedtronicCnlSession;
import java.nio.ByteBuffer;
......@@ -12,7 +12,7 @@ public class MedtronicReceiveMessage extends MedtronicMessage {
static int ENCRYPTED_ENVELOPE_SIZE = 3;
static int CRC_SIZE = 2;
protected MedtronicReceiveMessage(CommandType commandType, CommandAction commandAction, MedtronicCNLSession pumpSession, byte[] payload) {
protected MedtronicReceiveMessage(CommandType commandType, CommandAction commandAction, MedtronicCnlSession pumpSession, byte[] payload) {
super(commandType, commandAction, pumpSession, payload);
}
......@@ -38,14 +38,17 @@ public class MedtronicReceiveMessage extends MedtronicMessage {
* | byte receiveSequenceNumber | BE short receiveMessageType | byte[] Payload bytes | BE short CCITT CRC |
* +----------------------------+-----------------------------+----------------------+--------------------+
*/
public static ContourNextLinkMessage fromBytes(MedtronicCNLSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
// TODO - turn this into a factory
ContourNextLinkMessage message = MedtronicMessage.fromBytes(bytes);
// TODO - Validate the message, inner CCITT, serial numbers, etc
// If there's not 57 bytes, then we got back a bad message. Not sure how to process these yet.
if( bytes.length >= 57 ) {
// Also, READ_INFO and REQUEST_LINK_KEY are not encrypted
if (bytes.length >= 57 &&
(bytes[18] != CommandType.READ_INFO.getValue()) &&
(bytes[18] != CommandType.REQUEST_LINK_KEY_RESPONSE.getValue())) {
// Replace the encrypted bytes by their decrypted equivalent (same block size)
byte encryptedPayloadSize = bytes[56];
......
package info.nightscout.android.medtronic.message;
import info.nightscout.android.medtronic.MedtronicCNLSession;
import info.nightscout.android.medtronic.MedtronicCnlSession;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
......@@ -18,6 +18,7 @@ public class MedtronicSendMessage extends MedtronicMessage {
BEGIN_EHSM_SESSION(0x412),
TIME_REQUEST(0x0403),
READ_PUMP_STATUS_REQUEST(0x0112),
READ_BASAL_PATTERN_REQUEST(0x0112),
END_EHSM_SESSION(0x412);
private short value;
......@@ -27,7 +28,7 @@ public class MedtronicSendMessage extends MedtronicMessage {
}
}
protected MedtronicSendMessage(SendMessageType sendMessageType, MedtronicCNLSession pumpSession, byte[] payload) throws EncryptionException {
protected MedtronicSendMessage(SendMessageType sendMessageType, MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException {
super(CommandType.SEND_MESSAGE, CommandAction.PUMP_REQUEST, pumpSession, buildPayload(sendMessageType, pumpSession, payload));
}
......@@ -42,7 +43,7 @@ public class MedtronicSendMessage extends MedtronicMessage {
* | byte sendSequenceNumber | BE short sendMessageType | byte[] Payload bytes | BE short CCITT CRC |
* +-------------------------+----------------------+----------------------+--------------------+
*/
protected static byte[] buildPayload(SendMessageType sendMessageType, MedtronicCNLSession pumpSession, byte[] payload) throws EncryptionException {
protected static byte[] buildPayload(SendMessageType sendMessageType, MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException {
byte payloadLength = (byte) (payload == null ? 0 : payload.length);
ByteBuffer sendPayloadBuffer = ByteBuffer.allocate(ENCRYPTED_ENVELOPE_SIZE + payloadLength + CRC_SIZE);
......
package info.nightscout.android.medtronic.message;
import info.nightscout.android.medtronic.MedtronicCnlSession;
/**
* Created by lgoedhart on 26/03/2016.
*/
public class PumpBasalPatternRequestMessage extends MedtronicSendMessage {
public PumpBasalPatternRequestMessage(MedtronicCnlSession pumpSession) throws EncryptionException {
super(SendMessageType.READ_BASAL_PATTERN_REQUEST, pumpSession, null);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment