diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..7ec0147 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,21 @@ +name: Android CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Gradle + run: ./gradlew build diff --git a/.gitignore b/.gitignore index 39fb081..ffae0c6 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,15 @@ /build /captures .externalNativeBuild +.idea/* +/.idea +.idea/modules.xml +.idea/misc.xml +.idea/gradle.xml +.idea/compiler.xml +/app +.idea/misc.xml +.idea/misc.xml +.idea/modules.xml +.idea/misc.xml +.idea/modules.xml diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index a946cbe..fc22fce 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 30aa626..681f41a 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,29 +1,116 @@ - - - - - - - - - - + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
\ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7ac24c7..af17e69 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,17 +1,19 @@ + diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..a5f05cd --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index e0d5b93..c59d3df 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,11 +1,18 @@ - + + + + - + diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 66a081c..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000..8aea447 --- /dev/null +++ b/Changelog.md @@ -0,0 +1,32 @@ +# Version 2.3.0 +## New Features +* RAW Data Export: For Debugging purposes RAW Data can be exported to a file in JSON format. The file is stored in the Download folder and contains the Data of the last hours as defined in "Max sync hours" + +## Bugfixes +* Smoothing: Internally switched to float values for more precise smoothing +* Smoothing: better handling of first values after gaps in measurements (e.g. because the transmitter had no connection to the sensor or has been charged) + +# Version 2.3.1 +* Added Code comments +* Fixed spelling in descriptions + +# Version 2.3.2 +## New Features +* Esel does not send future vaules +* Values can be shifted (by total days) to the future (usefull if phone time is shifted to a date in the past) + +# Version 3.0.1 +## New Features +* New icon: we are a pirate donkey now! +* Companion Mode: ESEL can read CGM values from the notifications provided by the Eversense app +* Access to [eversensedms.com](https://www.eversensedms.com/) to fill missing data +* Fixes for smoothing and calculation of direction + +# Version 3.0.2 +* Changed synchronization of datareader to one common sync + +# Version 3.0.3 +* For notification lisener: added support for com.senseonics.eversense365.us and generic "com.senseonics." + +# Version 3.0.4 +* Added support for us.eversensedms.com for backfilling data from Eversense server diff --git a/README.md b/README.md index 4079783..b7c9414 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,35 @@ # Esel +There are two different methods how to access the readings from Eversense: +* Companion mode, which reads the data from the Eversense notifications (works with the standard Eversense App, available since ESEL version 3.0.1) +* Patched mode, which requires a patched version of the Eversense App (works completely offline, including backfilling) -1. Uninstall the Eversense App (Warning: your local historical data (older than 1 week) will be lost!) -2. Install the patched Eversense app (mod_com.senseonics.gen12androidapp-release.apk) and use it as described by the vendor +First of all, you need to install ESEL: +1. Get the ESEL apk e.g. from https://github.com/BernhardRo/Esel/tree/master/apk +2. Install the apk on your phone * You need to enable installation of Apps from unknown sources - * Start the Eversense App, login, connect to your transmitter and use it just like the normal app. -3. Build https://github.com/BernhardRo/Esel and install it on your phone. -4. Configuration: +3. Configuration: * Allow ESEL to run in the background (it will ask for it) + * Allow ESEL access to the Android Notifications (it will ask for it) + * In newer Android versions, these settings may be not directly accessible due to restricted settings. In this case restricted settings must be allowed first, see "Allow restricted settings" in https://support.google.com/android/answer/12623953 * Upload to Nightscout: Activate "Send to NightScout" in the preferences. It needs a configured AndroidAPS with internal NSClient or NSClient itself installed on the same phone * Inter-App-Broadcasts: Activate "Send to AAPS and xDrip". In xDrip and/or AndroidAPS activate the input method "640g/Eversense". * "Smooth Data" applies a smoothing algorithm to the raw values and provides these smoothed values instead of the raw readings. Smoothing is per default disabled. - * For feedback contact @BernhardRo -4. For the modification of the Eversense App, see: https://github.com/BernhardRo/Esel/wiki/How-to-modify-the-Android-Eversense-App -If you run esel with a fresh installation of Eversense for the first time, it can take up to 15min until your first values appear in xDrip! \ No newline at end of file +## Companion Mode +1. Install/use the official Eversense App from the Google Play Store + * Optional, but required for backfilling: Login to your Eversense account with your login data + * In Sync, enable Auto synchronization +3. Configuration of ESEL: + * Disable the setting "Get data from patched Eversense App" + * For backfilling: Enable "Fill missing data from eversensedms.com" + * Provide as Email address and password your Eversense login data + +## Patched Eversense App +1. Uninstall the Eversense App (Warning: your local historical data (older than 1 week) will be lost!) +2. Install the patched Eversense app (e.g. get it from https://cr4ck3d3v3r53n53.club) and use it as described by the vendor + * You need to enable installation of Apps from unknown sources + * Start the Eversense App, login, connect to your transmitter and use it just like the normal app. +3. Configuration of ESEL: + * Enable the setting "Get data from patched Eversense App" + +If you run ESEL with a fresh installation of Eversense for the first time, it can take up to 15min until your first values appear in xDrip! diff --git a/apk/Eversense_CGM_v1.0.410-patched.apk b/apk/Eversense_CGM_v1.0.410-patched.apk new file mode 100644 index 0000000..01579ee Binary files /dev/null and b/apk/Eversense_CGM_v1.0.410-patched.apk differ diff --git a/apk/Eversense_com_us_v2_0_103.senseonics.androidapp-patched.apk b/apk/Eversense_com_us_v2_0_103.senseonics.androidapp-patched.apk new file mode 100644 index 0000000..8a8caf5 Binary files /dev/null and b/apk/Eversense_com_us_v2_0_103.senseonics.androidapp-patched.apk differ diff --git a/apk/README.md b/apk/README.md index 2a29650..96e9a7c 100644 --- a/apk/README.md +++ b/apk/README.md @@ -2,16 +2,16 @@ 1. Uninstall the Eversense App (Warning: your local historical data (older than 1 week) will be lost!) 2. Install the patched Eversense app (mod_com.senseonics.gen12androidapp-release.apk) and use it as described by the vendor - * You need to enable installation of Apps from unknown sources - * Start the Eversense App, login, connect to your transmitter and use it just like the normal app. + * You need to enable installation of Apps from unknown sources + * Start the Eversense App, login, connect to your transmitter and use it just like the normal app. 3. Install esel.apk on your phone. 4. Configuration: - * Allow ESEL to run in the background (it will ask for it) - * Upload to Nightscout: Activate "Send to NightScout" in the preferences. It needs a configured AndroidAPS with internal NSClient or NSClient itself installed on the same phone - * Inter-App-Broadcasts: Activate "Send to AAPS and xDrip". In xDrip and/or AndroidAPS activate the input method "640g/Eversense". - * "Smooth Data" applies a smoothing algorithm to the raw values and provides these smoothed values instead of the raw readings. Smoothing is per default disabled. + * Allow ESEL to run in the background (it will ask for it) + * Upload to Nightscout: Activate "Send to NightScout" in the preferences. It needs a configured AndroidAPS with internal NSClient or NSClient itself installed on the same phone + * Inter-App-Broadcasts: Activate "Send to AAPS and xDrip". In xDrip and/or AndroidAPS activate the input method "640g/Eversense". + * "Smooth Data" applies a smoothing algorithm to the raw values and provides these smoothed values instead of the raw readings. Smoothing is per default disabled. 5. Install xDrip: https://jamorham.github.io/#xdrip-plus (Download latest APK) - * Use as Datasource 640G / EverSense + * Use as Datasource 640G / EverSense If you run esel with a fresh installation of Eversense for the first time, it can take up to 15min until your first values appear in xDrip! diff --git a/apk/esel.apk b/apk/esel.apk deleted file mode 100644 index 7f61fcd..0000000 Binary files a/apk/esel.apk and /dev/null differ diff --git a/apk/esel_303_us.apk b/apk/esel_303_us.apk new file mode 100644 index 0000000..7471a2e Binary files /dev/null and b/apk/esel_303_us.apk differ diff --git a/apk/esel_304.apk b/apk/esel_304.apk new file mode 100644 index 0000000..ceeb9c0 Binary files /dev/null and b/apk/esel_304.apk differ diff --git a/apk/esel_305.apk b/apk/esel_305.apk new file mode 100644 index 0000000..d25e642 Binary files /dev/null and b/apk/esel_305.apk differ diff --git a/apk/esel_306.apk b/apk/esel_306.apk new file mode 100644 index 0000000..7a8c369 Binary files /dev/null and b/apk/esel_306.apk differ diff --git a/app/build.gradle b/app/build.gradle index 9873ab8..018260d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdk 30 defaultConfig { applicationId "esel.esel.esel" - minSdkVersion 22 - targetSdkVersion 25 + minSdk 26 + targetSdk 30 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -17,16 +16,23 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + namespace 'esel.esel.esel' } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.squareup.retrofit2:adapter-guava:2.9.0' + implementation 'com.google.code.gson:gson:2.10.1' + implementation 'com.squareup.retrofit2:retrofit-converters:2.8.1' + implementation 'com.squareup.retrofit2:adapter-guava:2.9.0' + implementation 'com.squareup.retrofit2:adapter-guava:2.9.0' + androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:25.3.1' - compile 'com.android.support:support-v4:25.3.1' - compile 'com.android.support:design:25.3.1' - compile 'com.android.support.constraint:constraint-layout:1.0.2' - testCompile 'junit:junit:4.12' + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:support-v4:28.0.0' + implementation 'com.android.support:design:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:2.0.4' + implementation 'com.squareup.retrofit2:converter-gson:2.5.0' + testImplementation 'junit:junit:4.13.2' } diff --git a/app/debug/app-debug.apk b/app/debug/app-debug.apk index af0b715..177031d 100644 Binary files a/app/debug/app-debug.apk and b/app/debug/app-debug.apk differ diff --git a/app/debug/esel.apk b/app/debug/esel.apk deleted file mode 100644 index 97b7f46..0000000 Binary files a/app/debug/esel.apk and /dev/null differ diff --git a/app/debug/output-metadata.json b/app/debug/output-metadata.json new file mode 100644 index 0000000..c1d9d00 --- /dev/null +++ b/app/debug/output-metadata.json @@ -0,0 +1,21 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "esel.esel.esel", + "variantName": "debug", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "app-debug.apk" + } + ], + "elementType": "File", + "minSdkVersionForDexing": 26 +} \ No newline at end of file diff --git a/app/debug/output.json b/app/debug/output.json deleted file mode 100644 index f20a39f..0000000 --- a/app/debug/output.json +++ /dev/null @@ -1 +0,0 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-debug.apk","fullName":"debug","baseName":"debug"},"path":"app-debug.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5f365eb..e538d8b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,11 +1,14 @@ - + - + + + + + - - - @@ -39,8 +40,21 @@ - - + + + + + + - + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..3ce16bb Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/esel/esel/esel/ErrorsActivity.java b/app/src/main/java/esel/esel/esel/ErrorsActivity.java deleted file mode 100644 index 3569937..0000000 --- a/app/src/main/java/esel/esel/esel/ErrorsActivity.java +++ /dev/null @@ -1,47 +0,0 @@ -package esel.esel.esel; - -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.PowerManager; -import android.os.SystemClock; -import android.provider.Settings; -import android.support.v7.app.AlertDialog; -import android.view.View; -import android.support.design.widget.NavigationView; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.view.Menu; -import android.view.MenuItem; -import android.widget.Button; -import android.widget.TextView; -import android.widget.Toast; - -import java.io.IOException; - -import esel.esel.esel.datareader.Datareader; -import esel.esel.esel.datareader.SGV; -import esel.esel.esel.preferences.Preferences; -import esel.esel.esel.preferences.PrefsFragment; -import esel.esel.esel.util.LocalBroadcaster; -import esel.esel.esel.util.ToastUtils; - -public class ErrorsActivity extends MenuActivity { - - private TextView textViewValue; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setupView(R.layout.activity_errors); - textViewValue = (TextView) findViewById(R.id.textview_main); - } - -} diff --git a/app/src/main/java/esel/esel/esel/Esel.java b/app/src/main/java/esel/esel/esel/Esel.java index 639059e..15c9b95 100644 --- a/app/src/main/java/esel/esel/esel/Esel.java +++ b/app/src/main/java/esel/esel/esel/Esel.java @@ -6,6 +6,8 @@ import esel.esel.esel.receivers.KeepAliveReceiver; import esel.esel.esel.receivers.ReadReceiver; +import esel.esel.esel.util.SP; + /** * Created by adrian on 04/08/17. */ @@ -23,8 +25,12 @@ public void onCreate() { super.onCreate(); sInstance = this; sResources = getResources(); - startReadReceiver(); - startKeepAliveService(); + + boolean use_patched_es = SP.getBoolean("use_patched_es", false); + if (use_patched_es) { + startKeepAliveService(); + startReadReceiver(); + } } public static Esel getsInstance() { @@ -48,23 +54,27 @@ public synchronized void startReadReceiver() { public synchronized void stopReadReceiver() { - if (readReceiver != null) + if (readReceiver != null) { readReceiver.cancelAlarm(this); readReceiver = null; + } } - private void startKeepAliveService() { + public synchronized void startKeepAliveService() { if (keepAliveReceiver == null) { keepAliveReceiver = new KeepAliveReceiver(); keepAliveReceiver.setAlarm(this); + } else { + keepAliveReceiver.setAlarm(this); } } - public void stopKeepAliveService() { - if (keepAliveReceiver != null) + public synchronized void stopKeepAliveService() { + if (keepAliveReceiver != null) { keepAliveReceiver.cancelAlarm(this); + keepAliveReceiver = null; + } } - } diff --git a/app/src/main/java/esel/esel/esel/LogActivity.java b/app/src/main/java/esel/esel/esel/LogActivity.java new file mode 100644 index 0000000..6a8850a --- /dev/null +++ b/app/src/main/java/esel/esel/esel/LogActivity.java @@ -0,0 +1,47 @@ +package esel.esel.esel; + +import android.os.Bundle; +import android.util.Log; +import android.widget.TextView; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import esel.esel.esel.util.SP; + +public class LogActivity extends MenuActivity { + + private static TextView textViewValue; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setupView(R.layout.activity_errors); + textViewValue = (TextView) findViewById(R.id.textview_main); + String msg = SP.getString("logging",""); + textViewValue.setText(msg); + } + public static void addLog(String type,String tag, String value){ + String msg = SP.getString("logging",""); + int lines_limit = 800; + String[] lines = msg.split("\n"); + if(lines.length>lines_limit){ + int limit_to = (int)(lines_limit * 0.7); + StringBuilder strbuild = new StringBuilder(); + for (int i = 0; i valueArray = Datareader.readDataFromContentProvider(getBaseContext(), 6, currentTime - syncTime); + boolean use_esdms = SP.getBoolean("use_esdms",false); + if(use_esdms){ + class DataHandler implements EsNowDatareader.ProcessResultI{ + @Override + public void ProcessResult(List data) { + if (data != null && data.size() > 0) { + textViewValue.setText(""); + for (int i = 0; i < data.size(); i++) { + SGV sgv = data.get(i); + textViewValue.append(sgv.toString() +" " + sgv.direction + "\n"); + //LocalBroadcaster.broadcast(sgv); + EselLog.LogI(TAG,String.valueOf(sgv.value) + " " + sgv.direction); + } + } else { + EselLog.LogE(TAG,"No access to eversensedms",true); + } + } + } - if (valueArray != null && valueArray.size() > 0) { - textViewValue.setText(""); - for (int i = 0; i < valueArray.size(); i++) { - SGV sgv = valueArray.get(i); - textViewValue.append(sgv.toString() + "\n"); - //LocalBroadcaster.broadcast(sgv); + EsNowDatareader reader = new EsNowDatareader(); + reader.queryCurrentValue(new DataHandler()); + + + + }else { + List valueArray = Datareader.readDataFromContentProvider(getBaseContext(), 6, currentTime - syncTime); + + if (valueArray != null && valueArray.size() > 0) { + textViewValue.setText(""); + for (int i = 0; i < valueArray.size(); i++) { + SGV sgv = valueArray.get(i); + textViewValue.append(sgv.toString() +" " + sgv.direction+ "\n"); + //LocalBroadcaster.broadcast(sgv); + EselLog.LogI(TAG,String.valueOf(sgv.value) + " " + sgv.direction); + } + } else { + EselLog.LogE(TAG,"DB not readable!",true); } - } else { - ToastUtils.makeToast("DB not readable!"); } - }catch (android.database.CursorIndexOutOfBoundsException eb) { - eb.printStackTrace(); - ToastUtils.makeToast("DB is empty!\nIt can take up to 15min with running Eversense App until values are available!"); + } catch (android.database.CursorIndexOutOfBoundsException eb) { + eb.printStackTrace(); + EselLog.LogW(TAG,"DB is empty!\nIt can take up to 15min with running Eversense App until values are available!",true); } catch (Exception e) { e.printStackTrace(); } @@ -101,19 +147,102 @@ public void onClick(View view) { sync = SP.getInt("max-sync-hours", sync); + } catch (Exception e) { e.printStackTrace(); } + ReadReceiver receiver = new ReadReceiver(); - int written = receiver.FullSync(getBaseContext(), sync); - textViewValue.setText("Read " + written + " values from DB\n(last " + sync + " hours)"); + receiver.FullSync(getBaseContext(), sync); + textViewValue.setText("Read values from DB\n(last " + sync + " hours)"); } }); + buttonExport.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + int sync = 8; + + try { + + sync = SP.getInt("max-sync-hours", sync); + + } catch (Exception e) { + e.printStackTrace(); + } + + + String filename = "esel_output_" + System.currentTimeMillis() + ".json"; + String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + Environment.DIRECTORY_DOWNLOADS; + File file = new File(path, filename); + + ReadReceiver receiver = new ReadReceiver(); + + receiver.FullExport(getBaseContext(),file, sync); + + textViewValue.setText("Created file " + file.getAbsoluteFile() + " containing values from DB\n(last " + sync + " hours)"); + + + } + }); + } + private void askForNotificationAccess() { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { + final String packageName = getPackageName(); + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getBaseContext()); + + boolean nlenabled = NotificationManagerCompat.getEnabledListenerPackages(getBaseContext()).contains(packageName); + + if (!nlenabled) { + final Runnable askNotificationAccessRunnable = new Runnable() { + @Override + public void run() { + try { + Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS); + startActivity(intent); + } catch (ActivityNotFoundException e) { + final String msg = "Device does not appear to support notification access!"; + runOnUiThread(new Runnable() { + @Override + public void run() { + ToastUtils.makeToast("Device does not appear to support notification access!"); + } + }); + } + } + }; + + try { + final Intent intent = new Intent(); + intent.setAction(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS); + intent.setData(Uri.parse("package:" + packageName)); + //startActivity(intent); + + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Please Allow Permission") + .setMessage("For data access in Companion Mode, ESEL needs access to the System Notifications.\n" + + "If the settings are not available due to restricted settings, see 'https://support.google.com/android/answer/12623953'.") + .setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + SystemClock.sleep(100); + MainActivity.this.runOnUiThread(askNotificationAccessRunnable); + dialog.dismiss(); + } + }).show(); + } catch (Exception e) { + ToastUtils.makeToast("Please whitelist ESEL in the phone settings."); + } + } + } + +} + private void askForBatteryOptimizationPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { final String packageName = getPackageName(); diff --git a/app/src/main/java/esel/esel/esel/MenuActivity.java b/app/src/main/java/esel/esel/esel/MenuActivity.java index 4214c46..e8eb922 100644 --- a/app/src/main/java/esel/esel/esel/MenuActivity.java +++ b/app/src/main/java/esel/esel/esel/MenuActivity.java @@ -1,8 +1,6 @@ package esel.esel.esel; import android.content.Intent; -import android.os.Bundle; -import android.support.annotation.NonNull; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; @@ -11,16 +9,8 @@ import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; -import java.io.IOException; - -import esel.esel.esel.datareader.Datareader; -import esel.esel.esel.datareader.SGV; import esel.esel.esel.preferences.Preferences; -import esel.esel.esel.util.ToastUtils; /** * Created by adrian on 07/08/17. @@ -54,8 +44,8 @@ public boolean onNavigationItemSelected(MenuItem item) { } else if (id == R.id.nav_home && !(this instanceof MainActivity)){ Intent intent = new Intent(this, MainActivity.class); startActivity(intent); - } else if (id == R.id.nav_errors && !(this instanceof ErrorsActivity)){ - Intent intent = new Intent(this, ErrorsActivity.class); + } else if (id == R.id.nav_errors && !(this instanceof LogActivity)){ + Intent intent = new Intent(this, LogActivity.class); startActivity(intent); } diff --git a/app/src/main/java/esel/esel/esel/datareader/Datareader.java b/app/src/main/java/esel/esel/esel/datareader/Datareader.java index d462df1..7e75e2b 100644 --- a/app/src/main/java/esel/esel/esel/datareader/Datareader.java +++ b/app/src/main/java/esel/esel/esel/datareader/Datareader.java @@ -1,8 +1,9 @@ -package esel.esel.esel.datareader; + package esel.esel.esel.datareader; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.util.Log; import java.io.BufferedReader; import java.io.DataOutputStream; @@ -15,6 +16,8 @@ import esel.esel.esel.R; import esel.esel.esel.util.SP; +import static android.content.ContentValues.TAG; + /** * Created by bernhard on 18-11-03. */ @@ -58,18 +61,18 @@ public static List readDataFromContentProvider(Context context, int number, do { String timestamp_str = item.getString(0); String glucoseLevel_str = item.getString(1); -// String groupId = item.getString(2); -// String recordNumber = item.getString(3); -// String glucoseRaw1 = item.getString(4); -// String glucoseRaw2 = item.getString(5); -// String glucoseRaw3 = item.getString(6); -// String glucoseRaw4 = item.getString(7); -// String glucoseRaw5 = item.getString(8); -// String glucoseRaw6 = item.getString(9); -// String glucoseRaw7 = item.getString(10); -// String glucoseRaw8 = item.getString(11); - - String line = timestamp_str + "," +glucoseLevel_str; + String groupId = item.getString(2); + String recordNumber = item.getString(3); + // String glucoseRaw1 = item.getString(4); + // String glucoseRaw2 = item.getString(5); + // String glucoseRaw3 = item.getString(6); + // String glucoseRaw4 = item.getString(7); + // String glucoseRaw5 = item.getString(8); + // String glucoseRaw6 = item.getString(9); + // String glucoseRaw7 = item.getString(10); + // String glucoseRaw8 = item.getString(11); + + String line = timestamp_str + "," +glucoseLevel_str + "," + recordNumber; sb.append(timestamp_str + "," +glucoseLevel_str + "\n"); SGV sgv = Datareader.generateSGV(line); @@ -82,13 +85,13 @@ public static List readDataFromContentProvider(Context context, int number, -// Cursor transmitter = context.getContentResolver().query(Uri.parse(uriTransmitter), null, null, null, null); + // Cursor transmitter = context.getContentResolver().query(Uri.parse(uriTransmitter), null, null, null, null); // // -// transmitter.moveToFirst(); -// String id = transmitter.getString(0); -// String name = transmitter.getString(1); -// String address = transmitter.getString(2); + // transmitter.moveToFirst(); + // String id = transmitter.getString(0); + // String name = transmitter.getString(1); + // String address = transmitter.getString(2); // String status = transmitter.getString(3); // do { // // Do work... @@ -99,15 +102,20 @@ public static List readDataFromContentProvider(Context context, int number, valueArray.remove(0); } + Log.d(TAG, "readDataFromContentProvider called, result = " + valueArray); + return valueArray; } + public static SGV generateSGV(String dataString){ String[] tokens = dataString.split(","); long timestamp = Long.parseLong(tokens[0]); int value = Integer.parseInt(tokens[1]); - return new SGV(value, timestamp); + int record = Integer.parseInt(tokens[2]); + return new SGV(value, timestamp,record); } + } diff --git a/app/src/main/java/esel/esel/esel/datareader/EsNotificationListener.java b/app/src/main/java/esel/esel/esel/datareader/EsNotificationListener.java new file mode 100644 index 0000000..b3c1977 --- /dev/null +++ b/app/src/main/java/esel/esel/esel/datareader/EsNotificationListener.java @@ -0,0 +1,105 @@ +package esel.esel.esel.datareader; + +import android.app.Notification; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.widget.RemoteViews; + +import java.util.ArrayList; +import java.util.List; + +import esel.esel.esel.util.SP; +import esel.esel.esel.receivers.ReadReceiver; + +/** + * Created by bernhard on 24-01-18. + */ +public class EsNotificationListener extends NotificationListenerService { + + private static List lastReadings = new ArrayList(); + private static final ReadReceiver rr = new ReadReceiver(); + + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + + boolean use_patched_es = SP.getBoolean("use_patched_es", true); + if(use_patched_es){ + return; + } + + if (sbn.getPackageName().equals("com.senseonics.gen12androidapp") || + sbn.getPackageName().equals("com.senseonics.androidapp") || + sbn.getPackageName().equals("com.senseonics.eversense365.us") || + sbn.getPackageName().contains("com.senseonics.") + ) { + Notification notification = sbn.getNotification(); + if (notification != null && notification.tickerText != null) { + try { + SGV sgv = generateSGV(notification,lastReadings.size()); + if(sgv != null) { + lastReadings.add(sgv); + rr.CallBroadcast(null); + } + } catch (NumberFormatException err) { + err.printStackTrace(); + } + + + + } + } + + } + + public static List getData(int number, long lastReadingTime){ + List result = new ArrayList(); + for (SGV reading:lastReadings) { + //if(reading.timestamp > lastReadingTime){ + result.add(reading); + //} + + } + + while (result.size() > number){ + result.remove(0); + } + + if(result.size() == number){ + SGV last = lastReadings.get(lastReadings.size()-1); + lastReadings.clear(); + lastReadings.add(last); + } + + return result; + } + + public static SGV generateSGV(Notification notification ,int record){ + long timestamp = notification.when; + String tickerText = (String) notification.tickerText; + int value; + if(tickerText.contains("HI")){ + value =410; + } else if(tickerText.contains(".") || tickerText.contains(",")){ //is mmol/l + float valuef = Float.parseFloat(tickerText); + value = SGV.Convert(valuef); + }else{ + value =Integer.parseInt(tickerText); + } + + if(lastReadings.size()>0) { + long five_min = 300000l; + SGV oldSgv = lastReadings.get(lastReadings.size() - 1); + long lastreadingtime = oldSgv.timestamp; // SP.getLong("lastreadingtime_nl",timestamp); + int lastreadingvalue = oldSgv.raw; //SP.getInt("lastreadingvalue_nl",value); + if (value == lastreadingvalue && (lastreadingtime + (five_min )) > timestamp ) { // no new value // 5 min 30 secs grace time + return null; + } + } + + // SP.putLong("lastreadingtime_nl",timestamp); + // SP.putInt("lastreadingvalue_nl",value); + + return new SGV(value, timestamp,record); + } + +} diff --git a/app/src/main/java/esel/esel/esel/datareader/EsNowDatareader.java b/app/src/main/java/esel/esel/esel/datareader/EsNowDatareader.java new file mode 100644 index 0000000..3bb8e8e --- /dev/null +++ b/app/src/main/java/esel/esel/esel/datareader/EsNowDatareader.java @@ -0,0 +1,338 @@ +package esel.esel.esel.datareader; + +import java.io.Reader; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import esel.esel.esel.util.CareService; +import esel.esel.esel.util.EselLog; +import esel.esel.esel.util.SP; +import esel.esel.esel.util.ToastUtils; +import esel.esel.esel.util.UserLoginService; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public final class EsNowDatareader { + + private static final String TAG = "EsNowDatareader"; + private String username; + private String password; + static final String grant_type = "password"; + static final String client_id = "eversenseMMAAndroid"; + static final String client_secret = "6ksPx#]~wQ3U"; + private String BASE_AUTH_URL = "https://ousiamapialpha.eversensedms.com/connect/"; + private String BASE_URL = "https://ousalphaapiservices.eversensedms.com/"; + static final String BASE_AUTH_URL_US = "https://apiservice.eversensedms.com/"; + static final String BASE_URL_US = "https://apiservice.eversensedms.com/"; + static final String BASE_AUTH_URL_OUS = "https://ousiamapialpha.eversensedms.com/connect/"; + static final String BASE_URL_OUS = "https://ousalphaapiservices.eversensedms.com/"; + private static DateTimeFormatter dateformat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + private static ZoneId zoneId = ZoneId.systemDefault(); + + private UserLoginService.SenseonicsTokenDto token; + private List user; + private List values; + private List events; + private LocalDateTime startDate; + private LocalDateTime endDate; + private ProcessResultI processor; + + + private int userId = 0; + + private String bearer_token = ""; + private long token_expires=0; + + public EsNowDatareader() { + + username = SP.getString("es_username",""); + password =SP.getString("es_password",""); + + BASE_AUTH_URL = BASE_AUTH_URL_OUS; + BASE_URL = BASE_URL_OUS; + + if(SP.getBoolean("esdms_us",false)){ + BASE_AUTH_URL = BASE_AUTH_URL_US; + BASE_URL = BASE_URL_US; + } + + bearer_token = SP.getString("esnow_token", bearer_token); + token_expires = SP.getLong("es_now_token_expire", token_expires); + userId = SP.getInt("esnow_userId", userId); + + } + + static public void updateLogin(){ + EsNowDatareader reader = new EsNowDatareader(); + if(reader.bearer_token == "" || reader.tokenHasExpired() ) { + reader.login(); + } + } + + private void login() { + LoginController login = new LoginController(); + login.start();; + } + + private boolean tokenHasExpired(){ + long currentTime = System.currentTimeMillis(); + //return true; + return currentTime > token_expires; + } + + private void currentUser() { + UserController data = new UserController(); + data.start(); + } + public void queryCurrentValue(ProcessResultI processor) { + + this.processor = processor; + SgvController data = new SgvController(); + data.start(); + + + } + + public void queryLastValues(ProcessResultI processor, int hours) { + + LocalDateTime.now(); + startDate = LocalDateTime.now().minusHours(hours); + endDate = LocalDateTime.now(); + this.processor = processor; + SgvHistController data = new SgvHistController(); + data.start(); + + } + + + private SGV generateSGV(CareService.UserEventDto data, int record){ + ZonedDateTime date = ZonedDateTime.parse(data.eventDate,dateformat.withZone(ZoneId.of("UTC"))); + long timestamp = (long)date.toEpochSecond() * 1000; + int sgv = data.value; + + return new SGV(sgv,timestamp,record); + } + + private SGV generateSGV(CareService.CurrentValuesDto data){ + ZonedDateTime date = ZonedDateTime.parse(data.timeStamp,dateformat.withZone(ZoneId.of("UTC"))); + long timestamp = (long)date.toEpochSecond() *1000; + int sgv = data.currentGlucose; + + return new SGV(sgv,timestamp,1); + } + + + public class LoginController implements Callback { + public void start() { + Gson gson = new GsonBuilder() + .setLenient() + .create(); + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(BASE_AUTH_URL) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build(); + + UserLoginService login = retrofit.create(UserLoginService.class); + + Call call = login.authenticate(grant_type,client_id,client_secret,username,password); + call.enqueue(this); + + } + + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + token = response.body(); + if(token != null) { + token.SetExpireDateTime(); + bearer_token = token.GetBearerToken(); + token_expires = token.expireDateTime; + SP.putString("esnow_token",bearer_token ); + SP.putLong("es_now_token_expire", token_expires); + currentUser(); + EselLog.LogI(TAG,"Eversensedms: Successfully logged in."); + } + } else { + try { + Reader reader = response.errorBody().charStream(); + char[] charBuffer = new char[500]; + int amountRead = reader.read(charBuffer); + charBuffer[amountRead] = '\0'; + String msg = new String(charBuffer, 0, amountRead); + if(msg.contains("6008")){ + EselLog.LogW(TAG,"Not authorized. Check username and password. Disabling Eversensedms...",true); + SP.putBoolean("use_esdms", false); + }else if(msg.contains("5005")) { + EselLog.LogW(TAG,"Not authorized. Check username and password. Account is locked. Try again in 30min. Disabling Eversensedms...",true); + SP.putBoolean("use_esdms", false); + }else{ + EselLog.LogW(TAG,msg,true); + } + + + }catch(Exception err){ + EselLog.LogE(TAG,err.getMessage(),true); + } + } + } + + @Override + public void onFailure(Call call, Throwable t) { + EselLog.LogE(TAG, t.getStackTrace().toString(), true); + } + } + + public class UserController implements Callback> { + public void start() { + Gson gson = new GsonBuilder() + .setLenient() + .create(); + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build(); + + CareService data = retrofit.create(CareService.class); + + Call> call = data.getUserProfile(bearer_token); + call.enqueue(this); + + } + + @Override + public void onResponse(Call> call, Response> response) { + if (response.isSuccessful()) { + user = response.body(); + if(user != null){ + userId = user.get(0).userId; + SP.putInt("esnow_userId",userId ); + EselLog.LogI(TAG,"UserId is " + userId); + } + } else { + try { + EselLog.LogE(TAG, response.errorBody().string(), true); + }catch(Exception err){ + EselLog.LogE(TAG, err.getMessage(), true); + } + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + EselLog.LogE(TAG, t.getStackTrace().toString(), true); + } + } + + + public class SgvController implements Callback> { + + + public void start() { + Gson gson = new GsonBuilder() + .setLenient() + .create(); + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build(); + + CareService data = retrofit.create(CareService.class); + + Call> call = data.getCurrentValues(bearer_token,userId); + call.enqueue(this); + + } + + @Override + public void onResponse(Call> call, Response> response) { + if (response.isSuccessful()) { + values = response.body(); + List result = new ArrayList(); + SGV value = generateSGV(values.get(0)); + result.add(value); + if(processor != null) { + processor.ProcessResult(result); + } + } else { + try { + EselLog.LogE(TAG, response.errorBody().string(), true); + }catch(Exception err){ + EselLog.LogE(TAG, err.getMessage(), true); + } + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + EselLog.LogE(TAG, t.getStackTrace().toString(), true); + } + } + + public class SgvHistController implements Callback> { + + public void start() { + Gson gson = new GsonBuilder() + .setLenient() + .create(); + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build(); + + CareService data = retrofit.create(CareService.class); + + Call> call = data.getFollowingUserSensorGlucose(bearer_token,userId,startDate.format(dateformat),endDate.format(dateformat)); + call.enqueue(this); + + } + + @Override + public void onResponse(Call> call, Response> response) { + if (response.isSuccessful()) { + events = response.body(); + List result = new ArrayList(); + for (int i = 0; i < events.size(); i++){ + SGV value = generateSGV(events.get(i),i); + result.add(value); + } + if (processor != null){ + processor.ProcessResult(result); + } + + } else { + try { + EselLog.LogE(TAG, response.errorBody().string(), true); + }catch(Exception err){ + EselLog.LogE(TAG, err.getMessage(), true); + } + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + EselLog.LogE(TAG, t.getStackTrace().toString(), true); + } + } + + public interface ProcessResultI{ + + public void ProcessResult(List data); + + } + +} \ No newline at end of file diff --git a/app/src/main/java/esel/esel/esel/datareader/SGV.java b/app/src/main/java/esel/esel/esel/datareader/SGV.java index 9af25af..db07dbe 100644 --- a/app/src/main/java/esel/esel/esel/datareader/SGV.java +++ b/app/src/main/java/esel/esel/esel/datareader/SGV.java @@ -1,23 +1,32 @@ package esel.esel.esel.datareader; +import android.util.Log; + import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import esel.esel.esel.util.SP; +import static android.content.ContentValues.TAG; +import static java.lang.Math.min; + /** * Created by adrian on 04/08/17. */ public class SGV { - public int value; - public long timestamp; + public int value; //unit: md/dl (always used internally) + public int raw; + public long timestamp; // UNIX time in ms + public int record; public String direction; - SGV(int value, long timestamp){ + public SGV(int value, long timestamp, int record){ this.value = value; + this.raw = value; this.timestamp = timestamp; + this.record = record; if (this.value < 0) { this.value = 38;} else if (this.value < 40) { this.value = 39;} @@ -25,6 +34,11 @@ public class SGV { else if (this.value > 400) { this.value = 400;} } + static public int Convert(float mmoll){ + float mgdl = mmoll * 18.0182f; + return Math.round(mgdl); + } + @Override public String toString(){ DateFormat df = SimpleDateFormat.getDateTimeInstance(); @@ -33,41 +47,77 @@ public String toString(){ public void setDirection(double slope_by_minute) { direction = "NONE"; - if (slope_by_minute <= (-3.5)) { + if (slope_by_minute <= (-3.5d)) { direction = "DoubleDown"; - } else if (slope_by_minute <= (-2)) { + } else if (slope_by_minute <= (-2d)) { direction = "SingleDown"; - } else if (slope_by_minute <= (-1)) { + } else if (slope_by_minute <= (-1d)) { direction = "FortyFiveDown"; - } else if (slope_by_minute <= (1)) { + } else if (slope_by_minute <= (1d)) { direction = "Flat"; - } else if (slope_by_minute <= (2)) { + } else if (slope_by_minute <= (2d)) { direction = "FortyFiveUp"; - } else if (slope_by_minute <= (3.5)) { + } else if (slope_by_minute <= (3.5d)) { direction = "SingleUp"; - } else if (slope_by_minute <= (40)) { + } else if (slope_by_minute <= (40d)) { direction = "DoubleUp"; } } - public void smooth(int last){ - double smooth = this.value; + /** + * Created by bernhard on 2018-11-18. + */ - double lastSmooth = SP.getInt("readingSmooth",last*1000)/1000; - double factor = SP.getDouble("smooth_factor",0.3); - double correction = SP.getDouble("correction_factor",0.5); - int lastRaw = SP.getInt("lastReadingRaw", value); + public void smooth(int last,boolean enable_smooth){ + double value = (double)this.value; + double lastSmooth = (double)last; + + if(!enable_smooth){ + SP.putInt("lastReadingRaw", this.value); + SP.putFloat("readingSmooth",(float)this.value); + return; + } + + try{ + lastSmooth = SP.getFloat("readingSmooth",(float)lastSmooth); + }catch (Exception e){ + //first time: no value available, fallbacksolution is default value + } + double factor = SP.getDouble("smooth_factor",0.3,0.0,1.0); + double correction = SP.getDouble("correction_factor",0.5,0.0,1.0); + double descent_factor = SP.getDouble("descent_factor",0.0,0.0,1.0); + float lastRaw = SP.getInt("lastReadingRaw", this.value); SP.putInt("lastReadingRaw", this.value); - double a=lastSmooth+(factor*(this.value-lastSmooth)); - smooth=a+correction*((lastRaw-lastSmooth)+(this.value-a))/2; + if(last < 39) {//no useful value, e.g. due to pause in transmitter usage + lastRaw = this.value; + lastSmooth = this.value; + } + + // exponential smoothing, see https://en.wikipedia.org/wiki/Exponential_smoothing + // y'[t]=y'[t-1] + (a*(y-y'[t-1])) = a*y+(1-a)*y'[t-1] + // factor is a, value is y, lastSmooth y'[t-1], smooth y' + // factor between 0 and 1, default 0.3 + // factor = 0: always last smooth (constant) + // factor = 1: no smoothing + double smooth=lastSmooth+(factor*(value-lastSmooth)); - SP.putInt("readingSmooth",(int)Math.round(smooth*1000)); + // correction: average of delta between raw and smooth value, added to smooth with correction factor + // correction between 0 and 1, default 0.5 + // correction = 0: no correction, full smoothing + // correction > 0: less smoothing + smooth=smooth+(correction*((lastRaw-lastSmooth)+(value-smooth))/2.0d); + + smooth = smooth - descent_factor*(smooth-min(value,smooth)); + + SP.putFloat("readingSmooth",(float)smooth); if(this.value > SP.getInt("lower_limit",65)){ this.value = (int)Math.round(smooth); } + Log.d(TAG, "readDataFromContentProvider called, result = " + this.value); + } } diff --git a/app/src/main/java/esel/esel/esel/receivers/ReadReceiver.java b/app/src/main/java/esel/esel/esel/receivers/ReadReceiver.java index 9f5ce6c..d501e99 100644 --- a/app/src/main/java/esel/esel/esel/receivers/ReadReceiver.java +++ b/app/src/main/java/esel/esel/esel/receivers/ReadReceiver.java @@ -5,22 +5,28 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; import android.os.PowerManager; -import android.util.Log; +import org.json.JSONArray; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import esel.esel.esel.Esel; -import esel.esel.esel.R; +import esel.esel.esel.LogActivity; import esel.esel.esel.datareader.Datareader; +import esel.esel.esel.datareader.EsNotificationListener; +import esel.esel.esel.datareader.EsNowDatareader; import esel.esel.esel.datareader.SGV; +import esel.esel.esel.util.EselLog; import esel.esel.esel.util.LocalBroadcaster; import esel.esel.esel.util.SP; import esel.esel.esel.util.ToastUtils; +import retrofit2.Call; /** * Created by adrian on 04/08/17. @@ -32,16 +38,25 @@ public class ReadReceiver extends BroadcastReceiver { private static final String TAG = "ReadReceiver"; + private boolean suppressBroadcast =false; + JSONArray output = new JSONArray(); + @Override public synchronized void onReceive(Context context, Intent intent) { PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, ""); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Esel:ReadReceiver:Broadcast");//Broadcast wl.acquire(); - Log.d(TAG, "onReceive called"); + EselLog.LogV(TAG, "onReceive called"); setAlarm(Esel.getsInstance()); + CallBroadcast(context); + + wl.release(); + } + + public void CallBroadcast(Context context){ int sync = 8; try { @@ -56,7 +71,7 @@ public synchronized void onReceive(Context context, Intent intent) { try { SP.putLong("readReceiver-called", System.currentTimeMillis()); - //TODO: KeepAlive und ReadReceiver bei App-Beenden stoppen. + //String datastring = Datareader.readData(); @@ -71,7 +86,9 @@ public synchronized void onReceive(Context context, Intent intent) { } catch (Exception e) { - ToastUtils.makeToast("Exception: " + e.getMessage()); + String msg = e.getMessage(); + ToastUtils.makeToast("Exception: " + msg); + EselLog.LogE(TAG,msg); } @@ -83,95 +100,189 @@ public synchronized void onReceive(Context context, Intent intent) { FullSync(context,sync); } + boolean use_esdms = SP.getBoolean("use_esdms",false); + if(use_esdms){ + EsNowDatareader.updateLogin(); + } + + } + + public void FullSync(Context context, int syncHours){ + long currentTime = System.currentTimeMillis(); + long syncTime = syncHours * 60 * 60 * 1000L; + long lastTimestamp = currentTime - syncTime; + + + boolean use_esdms = SP.getBoolean("use_esdms", false); + if(use_esdms){ + + class DataHandler implements EsNowDatareader.ProcessResultI{ + + @Override + public void ProcessResult(List data) { + try { + int written = ProcesssValues( false, data); + String msg = "Full Sync done: Read " + written + " values from DB\n(last " + syncHours + " hours)"; + //ToastUtils.makeToast(msg); + EselLog.LogI(TAG,msg,true); + } + catch(Exception e){ + //ToastUtils.makeToast("No access to eversensedms"); + EselLog.LogE(TAG,"No access to eversensedms",true); + } + } + } + + EsNowDatareader reader = new EsNowDatareader(); + reader.queryLastValues(new DataHandler(),syncHours); + + + + }else { + + //disable smoothing as historical data will be overwritten + int written = broadcastData(context, lastTimestamp, false); + String msg = "Full Sync done: Read " + written + " values from DB\n(last " + syncHours + " hours)"; + //ToastUtils.makeToast(msg); + EselLog.LogI(TAG,msg,true); + } + + SP.putLong("last_full_sync", currentTime); + + - wl.release(); } - public int FullSync(Context context, int syncHours){ + public void FullExport(Context context,File file, int syncHours){ long currentTime = System.currentTimeMillis(); long syncTime = syncHours * 60 * 60 * 1000L; long lastTimestamp = currentTime - syncTime; - //disable smoothing as historical data will be overwritten - int written = broadcastData(context, lastTimestamp, false); + int written = 0; + suppressBroadcast = true; + boolean use_esdms = SP.getBoolean("use_esdms", false); + + if (use_esdms) { + + class DataHandler implements EsNowDatareader.ProcessResultI { + + @Override + public void ProcessResult(List data) { + int written = ProcesssValues( false,data); + String msg = "Full Sync done: Read " + written + " values from DB\n(last " + syncHours + " hours)"; + //ToastUtils.makeToast(msg); + EselLog.LogI(TAG, msg,true); + WriteData(file,output.toString()); + output = new JSONArray(); + suppressBroadcast = false; + + } + } + + EsNowDatareader reader = new EsNowDatareader(); + reader.queryLastValues(new DataHandler(), syncHours); + + }else { + + written = broadcastData(context, lastTimestamp, false); + suppressBroadcast = false; + WriteData(file,output.toString()); + output = new JSONArray(); + String msg = "Full Sync done: Read " + written + " values from DB\n(last " + syncHours + " hours)"; + //ToastUtils.makeToast(msg); + EselLog.LogI(TAG, msg,true); + + } + SP.putLong("last_full_sync", currentTime); - ToastUtils.makeToast("Full Sync done: Read " + written + " values from DB\n(last " + syncHours + " hours)"); + } + + private void WriteData(File file,String data){ + + if (!file.getParentFile().exists()) { + file.getParentFile().mkdir(); + } + if (!file.getParentFile().canWrite()) { + String msg = "Error: can not write data. Please enable the storage access permission for Esel."; + //ToastUtils.makeToast(msg); + EselLog.LogE(TAG, msg,true); + } + if (!file.exists()) { + try { + file.createNewFile(); + FileWriter fileWriter = new FileWriter(file.getAbsoluteFile()); + BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); + bufferedWriter.write(data.toString()); + bufferedWriter.close(); + + } catch (IOException err) { + String msg = "Error creating file: " + err.toString() + " occured at: " + err.getStackTrace().toString(); + //ToastUtils.makeToast(msg); + EselLog.LogE(TAG, msg,true); + } + } - return written; } - public int broadcastData(Context context, long lastReadingTime, boolean smoothEnabled) { + public int broadcastData(Context context, long lastReadingTime, boolean is_continuous_run) { int result = 0; try { - + long currentTime = System.currentTimeMillis(); SP.putLong("readReceiver-called", System.currentTimeMillis()); int size = 2; long updatedReadingTime = lastReadingTime; + boolean use_patched_es = SP.getBoolean("use_patched_es", true); do { lastReadingTime = updatedReadingTime; - List valueArray = Datareader.readDataFromContentProvider(context, size, lastReadingTime); + List valueArray = new ArrayList<>(); - if (valueArray == null || valueArray.size() == 0) { - ToastUtils.makeToast("DB not readable!"); - //wl.release(); - return result; - } + if (SP.getBoolean("overwrite_bg", false)) { //send constant values e.g. for debugging purpose - if (valueArray.size() != size) { - //ToastUtils.makeToast("DB not readable!"); - //wl.release(); - return result; - } - - for (int i = 0; i < valueArray.size(); i++) { - SGV sgv = valueArray.get(i); - - long oldTime = SP.getLong("lastReadingTime", -1L); - int oldValue = SP.getInt("lastReadingValue", -1); + //if(currentTime - lastReadingTime > 30000) { + int bg = SP.getInt("bg_value", 120); + SGV sgv = new SGV(bg, currentTime, 1); + valueArray.add(new SGV(bg, lastReadingTime, 1)); + valueArray.add(new SGV(bg, currentTime, 2)); - if (oldTime != sgv.timestamp) { - - double slopeByMinute = 0d; - if (oldTime != sgv.timestamp) { - slopeByMinute = (oldValue - sgv.value) * 60000.0d / ((oldTime - sgv.timestamp) * 1.0d); - } - sgv.setDirection(slopeByMinute); + //} - if (sgv.value >= 39 && oldValue >= 39) { - //ToastUtils.makeToast(sgv.toString()); - if(SP.getBoolean("smooth_data",false) && smoothEnabled){ - sgv.smooth(oldValue); - } + }else if (use_patched_es){ + valueArray = Datareader.readDataFromContentProvider(context, size, lastReadingTime); - LocalBroadcaster.broadcast(sgv); - result++; - } else { - ToastUtils.makeToast("NOT A READING!"); - } - SP.putLong("lastReadingTime", sgv.timestamp); - SP.putInt("lastReadingValue", sgv.value); + if (valueArray.size() == 0) { + //ToastUtils.makeToast("DB not readable!"); + EselLog.LogE(TAG,"DB not readable!",true); + } + }else { + boolean read_from_nl = true; + if(read_from_nl){ + valueArray = EsNotificationListener.getData(size,lastReadingTime); } } + if (valueArray.size() != size) { + return result; + } + + result += ProcesssValues(is_continuous_run,valueArray); + updatedReadingTime = SP.getLong("lastReadingTime", lastReadingTime); } while (updatedReadingTime != lastReadingTime); - //} catch (IOException e) { - // ToastUtils.makeToast("IOException"); - //} catch (InterruptedException e) { - // ToastUtils.makeToast("InterruptedException"); } catch (android.database.CursorIndexOutOfBoundsException eb) { eb.printStackTrace(); - ToastUtils.makeToast("DB is empty!\nIt can take up to 15min with running Eversense App until values are available!"); + //ToastUtils.makeToast("DB is empty!\nIt can take up to 15min with running Eversense App until values are available!"); + EselLog.LogW(TAG,"DB is empty! It can take up to 15min with running Eversense App until values are available!",true); } catch (Exception e) { e.printStackTrace(); + SP.putInt("lastReadingValue", 120); } //wl.release(); @@ -179,7 +290,80 @@ public int broadcastData(Context context, long lastReadingTime, boolean smoothEn return result; } + private int ProcesssValues( boolean is_continuous_run, List valueArray) { + int result = 0; + + long currentTime = System.currentTimeMillis(); + long fiveMin = 5 * 60 * 1000L; + long fourMin = 4 * 60 * 1000L; + + for (int i = 0; i < valueArray.size(); i++) { + SGV sgv = valueArray.get(i); + long oldTime = SP.getLong("lastReadingTime", -1L); + boolean acceptValue = oldTime != sgv.timestamp; //is new value + + if(sgv.timestamp - currentTime > (60 * 1000)){ + //sgv is from future + long shiftValue = sgv.timestamp - currentTime; + float sec = shiftValue/1000f; + EselLog.LogW(TAG,"broadcastData called, value is in future by [sec] " + sec); + acceptValue = false; + } + + if(sgv.timestamp - oldTime < fourMin){ + EselLog.LogW(TAG,"broadcastData called, value ignored as it is not older than 4 min: " + sgv.value + ", timestamp: " + sgv.timestamp); + acceptValue = false; + } + + if (acceptValue) { + //if (!futureValue) { + int oldValue = sgv.value; + + oldValue = SP.getInt("lastReadingValue", -1); + + long sgvTime = sgv.timestamp; + //check if old value is not older than 12min + boolean hasTimeGap = (sgvTime - oldTime) > 12 * 60 *1000L; + + if (sgv.value >= 39 /*&& oldValue >= 39*/) { //check for old value to ignore first 5 min + //ToastUtils.makeToast(sgv.toString()); + if(is_continuous_run) { + boolean enable_smooth = SP.getBoolean("smooth_data", false) && !hasTimeGap; + sgv.smooth(oldValue, enable_smooth); + } + + double slopeByMinute = 0d; + if (oldTime != sgvTime) { + slopeByMinute = (sgv.value - oldValue ) * 60000.0d / (( sgvTime - oldTime) * 1.0d); + } + if(!hasTimeGap){ + sgv.setDirection(slopeByMinute); + } + + try { + if (!suppressBroadcast) { + LocalBroadcaster.broadcast(sgv,is_continuous_run); + } else { + LocalBroadcaster.addSgvEntry(output, sgv); + } + + result++; + } + catch(Exception e){ + EselLog.LogE(TAG,"LocalBroadcaster.broadcast exception, result = " + e.getMessage()); + } + } else { + ToastUtils.makeToast("NOT A READING!"); + } + SP.putLong("lastReadingTime", sgvTime); + SP.putInt("lastReadingValue", sgv.value); + //SP.putFloat("lastReadingDirection", slopeByMinute); + } + } + + return result; + } public void setAlarm(Context context) { diff --git a/app/src/main/java/esel/esel/esel/util/CareService.java b/app/src/main/java/esel/esel/esel/util/CareService.java new file mode 100644 index 0000000..bc6e1a6 --- /dev/null +++ b/app/src/main/java/esel/esel/esel/util/CareService.java @@ -0,0 +1,142 @@ +package esel.esel.esel.util; + + +import com.google.gson.annotations.SerializedName; +import java.util.List; + +import esel.esel.esel.datareader.SGV; +import retrofit2.http.Body; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Query; +import retrofit2.http.Header; +import retrofit2.Call; + +public interface CareService { + //public static final Companion Companion = Companion.$$INSTANCE; + + @GET("api/care/GetCurrentValues") + Call> getCurrentValues(@Header("Authorization") String token,@Query("FollowerUserID") int paramInt); + + @GET("api/care/GetFollowingUserSensorGlucose") + Call> getFollowingUserSensorGlucose(@Header("Authorization") String token,@Query("UserID") int paramInt, @Query("startDate") String paramString1, @Query("endDate") String paramString2); + + + @GET("api/care/GetUserProfile") + Call> getUserProfile(@Header("Authorization") String token); + + public static final class Companion { + static final Companion $$INSTANCE = new Companion(); + + private static final String CARE_BASE_PATH = "api/care/"; + } + + + public final class CurrentValuesDto { + @SerializedName("CurrentGlucose") + public final int currentGlucose; + + @SerializedName("GlucoseTrend") + public final int glucoseTrend; + + @SerializedName("TimeStamp") + public final String timeStamp; + + public CurrentValuesDto(int paramInt1, String paramString, int paramInt2) { + this.currentGlucose = paramInt1; + this.timeStamp = paramString; + this.glucoseTrend = paramInt2; + } + } + + public final class UserEventDto { + @SerializedName("EventDate") + public final String eventDate; + + @SerializedName("EventSubTypeID") + private final int eventSubTypeId; + + @SerializedName("EventTypeID") + private final int eventTypeId; + + @SerializedName("Value") + public final int value; + + public UserEventDto(int paramInt1, int paramInt2, int paramInt3, String paramString) { + this.eventTypeId = paramInt1; + this.eventSubTypeId = paramInt2; + this.value = paramInt3; + this.eventDate = paramString; + } + } + + public final class MemberDto { + @SerializedName("CurrentGlucose") + private final int currentGlucose; + + @SerializedName("FirstName") + private final String firstName; + + @SerializedName("GlucoseTrend") + private final int glucoseTrend; + + @SerializedName("LastName") + private final String lastName; + + @SerializedName("ProfileImage") + private final String profileImage; + + @SerializedName("Status") + private final int status; + + @SerializedName("CGTime") + private final String timestamp; + + @SerializedName("UserName") + private final String userEmail; + + @SerializedName("UserID") + private final int userId; + + public MemberDto(int paramInt1, String paramString1, String paramString2, String paramString3, int paramInt2, int paramInt3, String paramString4, int paramInt4, String paramString5) { + this.userId = paramInt1; + this.userEmail = paramString1; + this.firstName = paramString2; + this.lastName = paramString3; + this.currentGlucose = paramInt2; + this.glucoseTrend = paramInt3; + this.profileImage = paramString4; + this.status = paramInt4; + this.timestamp = paramString5; + } + } + + public final class UserProfileDto { + @SerializedName("FirstName") + private final String firstName; + + @SerializedName("LastName") + private final String lastName; + + @SerializedName("ProfileImage") + private final String profileImageBase64; + + @SerializedName("UserID") + public final int userId; + + @SerializedName("UserName") + private final String username; + + public UserProfileDto(int paramInt, String paramString1, String paramString2, String paramString3, String paramString4) { + this.userId = paramInt; + this.username = paramString1; + this.firstName = paramString2; + this.lastName = paramString3; + this.profileImageBase64 = paramString4; + } + } + + +} diff --git a/app/src/main/java/esel/esel/esel/util/EselLog.java b/app/src/main/java/esel/esel/esel/util/EselLog.java new file mode 100644 index 0000000..317b6b8 --- /dev/null +++ b/app/src/main/java/esel/esel/esel/util/EselLog.java @@ -0,0 +1,65 @@ +package esel.esel.esel.util; + +import android.util.Log; + +import esel.esel.esel.LogActivity; + +public class EselLog { + + + public static void LogI(String tag, String value, boolean toast) { + if(toast) { + ToastUtils.makeToast("Info: " + value); + } + LogI(tag,value); + } + public static void LogI(String tag, String value){ + String type = "Info: "; + Log.v(tag,value); + LogActivity.addLog(type,tag,value); + } + + public static void LogE(String tag, String value, boolean toast) { + if(toast) { + ToastUtils.makeToast("Error: " + value); + } + + LogE(tag,value); + } + + public static void LogE(String tag, String value){ + String type = "Error: "; + Log.v(tag,value); + LogActivity.addLog(type,tag,value); + } + + public static void LogW(String tag, String value, boolean toast) { + if(toast) { + ToastUtils.makeToast("Warning: " + value); + } + + LogW(tag,value); + } + + public static void LogW(String tag, String value){ + String type = "Warning: "; + Log.v(tag,value); + LogActivity.addLog(type,tag,value); + } + + public static void LogV(String tag, String value, boolean toast) { + if(toast) { + ToastUtils.makeToast("Message: " + value); + } + + LogV(tag,value); + } + + public static void LogV(String tag, String value){ + String type = "Message: "; + Log.v(tag,value); + //LogActivity.addLog(type,tag,value); + } + + +} diff --git a/app/src/main/java/esel/esel/esel/util/LocalBroadcaster.java b/app/src/main/java/esel/esel/esel/util/LocalBroadcaster.java index 4a305d3..a215971 100644 --- a/app/src/main/java/esel/esel/esel/util/LocalBroadcaster.java +++ b/app/src/main/java/esel/esel/esel/util/LocalBroadcaster.java @@ -3,7 +3,6 @@ import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.Bundle; -import android.util.Log; import org.json.JSONArray; import org.json.JSONObject; @@ -12,6 +11,7 @@ import java.util.List; import java.util.Locale; +import esel.esel.esel.LogActivity; import esel.esel.esel.Esel; import esel.esel.esel.datareader.SGV; @@ -25,50 +25,46 @@ public class LocalBroadcaster { public static final String ACTION_DATABASE = "info.nightscout.client.DBACCESS"; - private static final String TAG = "LocalBroadcaster"; private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US); - - - public static void broadcast(SGV sgv){ + public static void broadcast(SGV sgv, boolean log) { try { + int timeShift = SP.getInt("shift_days",0); + if(timeShift > 0){ + timeShift = timeShift * 24 * 60 * 60 * 1000; //days to ms + sgv.timestamp = sgv.timestamp + timeShift; + } - if (SP.getBoolean("send_to_AAPS", true)){ + if (SP.getBoolean("send_to_AAPS", true)) { final JSONArray entriesBody = new JSONArray(); addSgvEntry(entriesBody, sgv); - sendBundle("add", "entries", entriesBody, XDRIP_PLUS_NS_EMULATOR); + sendBundle("add", "entries", entriesBody, XDRIP_PLUS_NS_EMULATOR, log); } if (SP.getBoolean("send_to_NS", true)) { - sendBundle("dbAdd", "entries", generateSgvEntry(sgv), ACTION_DATABASE); + sendBundle("dbAdd", "entries", generateSgvEntry(sgv), ACTION_DATABASE, log); } + if(log) { + EselLog.LogI(TAG, String.valueOf(sgv.value) + " " + sgv.direction); + } } catch (Exception e) { - Log.e(TAG, "Unable to send bundle: " + e); + String msg = "Unable to send bundle: " + e; + EselLog.LogE(TAG,msg); } } - private static void addSgvEntry(JSONArray entriesArray, SGV sgv) throws Exception { - JSONObject json = new JSONObject(); - json.put("sgv", sgv.value); - if (sgv.direction == null){ - json.put("direction", "NONE"); - } else { - json.put("direction", sgv.direction); - } - json.put("device", "ESEL"); - json.put("type", "sgv"); - json.put("date", sgv.timestamp); - json.put("dateString", format.format(sgv.timestamp)); - + public static void addSgvEntry(JSONArray entriesArray, SGV sgv) throws Exception { + JSONObject json = generateSgvEntry(sgv); entriesArray.put(json); } private static JSONObject generateSgvEntry(SGV sgv) throws Exception { JSONObject json = new JSONObject(); json.put("sgv", sgv.value); - if (sgv.direction == null){ + json.put("rawbg", sgv.raw); + if (sgv.direction == null) { json.put("direction", "NONE"); } else { json.put("direction", sgv.direction); @@ -77,25 +73,32 @@ private static JSONObject generateSgvEntry(SGV sgv) throws Exception { json.put("type", "sgv"); json.put("date", sgv.timestamp); json.put("dateString", format.format(sgv.timestamp)); - + json.put("created_at", format.format(sgv.timestamp)); + json.put("source", "Eversense"); return json; } - private static void sendBundle(String action, String collection, Object json, String intentIdAction) { + private static void sendBundle(String action, String collection, Object json, String intentIdAction,boolean log) { final Bundle bundle = new Bundle(); bundle.putString("action", action); bundle.putString("collection", collection); bundle.putString("data", json.toString()); final Intent intent = new Intent(intentIdAction); intent.putExtras(bundle).addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - Esel.getsInstance().sendBroadcast(intent); + List receivers = Esel.getsInstance().getPackageManager().queryBroadcastReceivers(intent, 0); - if (receivers.size() < 1) { - Log.w(TAG, "No xDrip receivers found. "); - } else { - Log.d(TAG, receivers.size() + " xDrip receivers"); + + /* With receiving apps targeting O+ we need to explicitly send broadcasts to the packages + for them to wake from the background*/ + for (ResolveInfo resolveInfo : receivers) { + String packageName = resolveInfo.activityInfo.packageName; + if (packageName != null) { + intent.setPackage(packageName); + Esel.getsInstance().sendBroadcast(intent); + if(log) { + EselLog.LogI(TAG, "send to: " + packageName); + } + } } } - - } diff --git a/app/src/main/java/esel/esel/esel/util/SP.java b/app/src/main/java/esel/esel/esel/util/SP.java index 53d490e..2042855 100644 --- a/app/src/main/java/esel/esel/esel/util/SP.java +++ b/app/src/main/java/esel/esel/esel/util/SP.java @@ -11,7 +11,7 @@ */ public class SP { - static SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(Esel.getsInstance().getApplicationContext()); + static public SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(Esel.getsInstance().getApplicationContext()); static public boolean contains(String key) { return sharedPreferences.contains(key); @@ -46,7 +46,19 @@ static public Double getDouble(int resourceID, Double defaultValue) { } static public Double getDouble(String key, Double defaultValue) { - return SafeParse.stringToDouble(sharedPreferences.getString(key, defaultValue.toString())); + Double value = SafeParse.stringToDouble(sharedPreferences.getString(key, defaultValue.toString())); + return value; + } + + static public Double getDouble(String key, Double defaultValue, Double min, Double max) { + Double value = SafeParse.stringToDouble(sharedPreferences.getString(key, defaultValue.toString())); + if(valuemax){ + value = max; + } + return value; } static public int getInt(int resourceID, Integer defaultValue) { @@ -77,6 +89,14 @@ static public long getLong(String key, Long defaultValue) { } } + static public float getFloat(String key, Float defaultValue) { + try { + return sharedPreferences.getFloat(key, defaultValue); + } catch (Exception e) { + return SafeParse.stringToInt(sharedPreferences.getString(key, defaultValue.toString())); + } + } + static public void putBoolean(String key, boolean value) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putBoolean(key, value); @@ -119,6 +139,12 @@ static public void putInt(int resourceID, int value) { editor.apply(); } + static public void putFloat(String key, float value) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putFloat(key, value); + editor.apply(); + } + static public void putString(int resourceID, String value) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(Esel.getsResources().getString(resourceID), value); diff --git a/app/src/main/java/esel/esel/esel/util/UserLoginService.java b/app/src/main/java/esel/esel/esel/util/UserLoginService.java new file mode 100644 index 0000000..db77e99 --- /dev/null +++ b/app/src/main/java/esel/esel/esel/util/UserLoginService.java @@ -0,0 +1,69 @@ +package esel.esel.esel.util; + +import android.os.SystemClock; + +import com.google.gson.annotations.SerializedName; +import retrofit2.Call; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.POST; +import java.time.LocalDateTime; +public interface UserLoginService { + @FormUrlEncoded + @POST("token") + Call authenticate(@Field("grant_type") String paramString1, @Field("client_id") String paramString2, @Field("client_secret") String paramString3, @Field("username") String paramString4, @Field("password") String paramString5); + + + public final class SenseonicsTokenDto { + + @SerializedName("access_token") + private final String accessToken; + + @SerializedName("as:client_id") + private final String clientId; + + @SerializedName("expires") + private final String expired; + + @SerializedName("expires_in") + private final Long expiresIn; + + @SerializedName(".issued") + private final String issued; + + @SerializedName("refresh_token") + private String refresh_token; + + @SerializedName("token_type") + private final String tokenType; + + @SerializedName("userName") + private final String userName; + + public SenseonicsTokenDto(String paramString1, String paramString2, Long paramLong, String paramString3, String paramString4, String paramString5, String paramString6, String paramString7) { + this.accessToken = paramString1; + this.tokenType = paramString2; + this.expiresIn = paramLong; + this.refresh_token = paramString3; + this.clientId = paramString4; + this.userName = paramString5; + this.issued = paramString6; + this.expired = paramString7; + + SetExpireDateTime(); + } + + public void SetExpireDateTime(){ + long currentTime = System.currentTimeMillis(); + + this.expireDateTime = currentTime + (expiresIn * 1000); + } + + public long expireDateTime; + + public String GetBearerToken(){ + return "Bearer "+ accessToken; + } + + } +} diff --git a/app/src/main/res/drawable/esel_pic.png b/app/src/main/res/drawable/esel_pic.png new file mode 100644 index 0000000..c1e7bb8 Binary files /dev/null and b/app/src/main/res/drawable/esel_pic.png differ diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..ca3826a --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml index 458b4b0..e9af4f9 100644 --- a/app/src/main/res/drawable/side_nav_bar.xml +++ b/app/src/main/res/drawable/side_nav_bar.xml @@ -2,8 +2,8 @@ android:shape="rectangle"> \ No newline at end of file diff --git a/app/src/main/res/layout/app_bar_errors.xml b/app/src/main/res/layout/app_bar_errors.xml index c8ba2b2..8b3b032 100644 --- a/app/src/main/res/layout/app_bar_errors.xml +++ b/app/src/main/res/layout/app_bar_errors.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="esel.esel.esel.ErrorsActivity"> + tools:context="esel.esel.esel.LogActivity"> + layout="@layout/content_errors" + /> 16dp 16dp - 16dp + 5dp 160dp 16dp diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..6d1dc2b --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #00B1B8 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1da6957..0c59a81 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,9 @@ Esel + Version 3.0.6 Open navigation drawer Close navigation drawer Settings 24 + EsNotificationListener diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 31d787a..75d506f 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1,5 +1,36 @@ + + + + + + + + + + - + android:key="smooth_factor" + android:summary="Exponential smooth Factor.\n(1 = no smoothing, between 0 and 1, default: 0.3)" + android:title="Smooth Factor" /> + + - \ No newline at end of file + + + + + + + + diff --git a/build.gradle b/build.gradle index ae9fbaf..31f360b 100644 --- a/build.gradle +++ b/build.gradle @@ -4,9 +4,10 @@ buildscript { repositories { jcenter() google() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:8.13.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -17,6 +18,7 @@ allprojects { repositories { jcenter() google() + mavenCentral() } } diff --git a/gradle.properties b/gradle.properties index aac7c9b..8450cac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,9 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false +android.nonTransitiveRClass=false org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index eb4ef0a..25e9bd9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Nov 03 23:10:45 CET 2018 +#Sun Oct 27 11:46:34 CET 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip diff --git a/mod/Readme.md b/mod/Readme.md new file mode 100644 index 0000000..27ac9cf --- /dev/null +++ b/mod/Readme.md @@ -0,0 +1,139 @@ +# How to patch ES +## Prerequisits +You need to have the following tools available. On your computer: +* Android debugger [Adb](https://developer.android.com/studio/command-line/adb) +* A Tool to decompile Apk apps to smali code, e.g. [Apktool](https://ibotpeaches.github.io/Apktool/install) + +On your Android smartphone: +* Enable Developer options on your Android phone, e.g. by following this [User Guide](https://developer.android.com/studio/debug/dev-options) +* Enable USB-Debugging in the Developer options on your Android phone. + + +## Get the sources +1. Install the official EversenseApp from the Playstore +2. find the app with + + adb shell pm list packages + + should be com.senseonics.gen12androidapp +3. get the full path name: + + adb shell pm path com.senseonics.gen12androidapp + + should give you + > package:/data/app/com.senseonics.gen12androidapp-nU4XaDnFpSSira9EH02H9Q==/base.apk + > package:/data/app/com.senseonics.gen12androidapp-nU4XaDnFpSSira9EH02H9Q==/split_config.de.apk + > package:/data/app/com.senseonics.gen12androidapp-nU4XaDnFpSSira9EH02H9Q==/split_config.xxhdpi.apk + + Of course the language file (the second one) can be different according to your localization. +4. pull to computer: + + adb pull /data/app/com.senseonics.gen12androidapp-nU4XaDnFpSSira9EH02H9Q==/base.apk .\org\ + adb pull /data/app/com.senseonics.gen12androidapp-nU4XaDnFpSSira9EH02H9Q==/split_config.de.apk .\org\ + adb pull /data/app/com.senseonics.gen12androidapp-nU4XaDnFpSSira9EH02H9Q==/split_config.xxhdpi.apk .\org\ + +## Create the patched files +1. Decompile the APK to .smali code (e.g. with Apktool) + + apktool.bat d .\org\base.apk +2. In the decompiled app, navigate to + > base\smali_classes2\com\senseonics\db +3. Copy/duplicate + > ConnectedTransmitterContentProvider.smali + + and name the new file to + > GlucoseContentProvider.smali + + (see the file GlucoseContentProvider.smali in this repo for references) +4. Edit + > GlucoseContentProvider.smali + + and rename every instance of + + ConnectedTransmitterContentProvider + + to + + GlucoseContentProvider + + and also rename + + connectedTransmitters + + to + + glucosereadings + + (in the SQL statements) - except of line 25: for security reasons, rename the string in + + performDelete + + to something like + + deletenotsupported + +5. Modify + > com.senseonics.gen12androidapp\AndroidManifest.xml + + Replace line 74 + + + + and delete the declaration + + android:localeConfig="@xml/locales_config" + + (see the file AndroidManifest.xml in this repo for references). Modify in + + > res\values\strings.xml:630 and 631: © 2016 Senseonics, Inc. + + and add + > patched + + delete file + + > \res\xml\locales_config.xml + + open file + > \res\values\public.xml + + and delete line + + + + (appears 2 times). Instead, insert line + + + +6. Compile the code according the documentation of your tool you have used to decompile it + + apktool.bat b .\base\ + +## Install the patched files +1. Sign the compiled APK (See the Android Documentation for help). First aligne it: + + zipalign.exe -v -p -f 4 .\base\dist\base.apk .\base\dist\base-aligned.apk + + next, sign it. Here an example with an keyfile called my-release-key_123456.jks and pwd 123456: + + apksigner.bat sign --ks my-release-key_123456.jks --out .\base-patched.apk .\base\dist\base-aligned.apk + > 123456 +2. Do the same (without any modification) with the other apk files, language file: + + apksigner.bat sign --ks my-release-key_123456.jks --out .\split_config.de.apk .\org\split_config.de.apk + + and common file: + + apksigner.bat sign --ks my-release-key_123456.jks --out .\split_config.xxhdpi.apk .\org\split_config.xxhdpi.apk +3. Uninstall the original Eversense App on your phone (Warning: the local history of your CGM readings in your Eversense App will get lost) and install your new APK. The new App will behave just like the original one - except of the difference that the CGM reading can be accessed from other Apps e.g. by ESEL + + adb install-multiple base.apk split_config.de.apk split_config.xxhdpi.apk + + + +There is also a script available following these steps and running on Bitbucket in Docker: +https://bitbucket.org/norbert_bitbucket1/hackingeversense/src/master/ diff --git a/mod/my-release-key_anon.jks b/mod/my-release-key_123456.jks similarity index 100% rename from mod/my-release-key_anon.jks rename to mod/my-release-key_123456.jks diff --git a/mod/v1.2.107/apk/base.apk b/mod/v1.2.107/apk/base.apk new file mode 100644 index 0000000..8268a3d Binary files /dev/null and b/mod/v1.2.107/apk/base.apk differ diff --git a/mod/v1.2.107/apk/split_config.de.apk b/mod/v1.2.107/apk/split_config.de.apk new file mode 100644 index 0000000..f464f7c Binary files /dev/null and b/mod/v1.2.107/apk/split_config.de.apk differ diff --git a/mod/v1.2.107/apk/split_config.en.apk b/mod/v1.2.107/apk/split_config.en.apk new file mode 100644 index 0000000..c86600c Binary files /dev/null and b/mod/v1.2.107/apk/split_config.en.apk differ diff --git a/mod/v1.2.107/apk/split_config.xxhdpi.apk b/mod/v1.2.107/apk/split_config.xxhdpi.apk new file mode 100644 index 0000000..528eb37 Binary files /dev/null and b/mod/v1.2.107/apk/split_config.xxhdpi.apk differ diff --git a/mod/v1.2.107/changes/AndroidManifest.xml b/mod/v1.2.107/changes/AndroidManifest.xml new file mode 100644 index 0000000..9a65a76 --- /dev/null +++ b/mod/v1.2.107/changes/AndroidManifest.xml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mod/v1.2.107/changes/res/values/public.xml b/mod/v1.2.107/changes/res/values/public.xml new file mode 100644 index 0000000..bbe87b7 --- /dev/null +++ b/mod/v1.2.107/changes/res/values/public.xml @@ -0,0 +1,4786 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/v1.2.107/changes/smali_classes2/com/senseonics/db/GlucoseContentProvider.smali b/mod/v1.2.107/changes/smali_classes2/com/senseonics/db/GlucoseContentProvider.smali new file mode 100644 index 0000000..2ac2567 --- /dev/null +++ b/mod/v1.2.107/changes/smali_classes2/com/senseonics/db/GlucoseContentProvider.smali @@ -0,0 +1,194 @@ +.class public Lcom/senseonics/db/GlucoseContentProvider; +.super Landroid/content/ContentProvider; +.source "GlucoseContentProvider.java" + + +# instance fields +.field helper:Lcom/senseonics/db/SenseonicsDBHelper; + + +# direct methods +.method public constructor ()V + .locals 0 + + .line 9 + invoke-direct {p0}, Landroid/content/ContentProvider;->()V + + return-void +.end method + +.method private performDelete(Landroid/database/sqlite/SQLiteDatabase;)I + .locals 2 + + const/4 v0, 0x0 + + const-string v1, "deletenotsupported" + + .line 50 + invoke-virtual {p1, v1, v0, v0}, Landroid/database/sqlite/SQLiteDatabase;->delete(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)I + + move-result p1 + + return p1 +.end method + + +# virtual methods +.method public delete(Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I + .locals 0 + + .line 45 + iget-object p1, p0, Lcom/senseonics/db/GlucoseContentProvider;->helper:Lcom/senseonics/db/SenseonicsDBHelper; + + invoke-virtual {p1}, Lcom/senseonics/db/SenseonicsDBHelper;->getWritableDatabase()Landroid/database/sqlite/SQLiteDatabase; + + move-result-object p1 + + .line 46 + invoke-direct {p0, p1}, Lcom/senseonics/db/GlucoseContentProvider;->performDelete(Landroid/database/sqlite/SQLiteDatabase;)I + + move-result p1 + + return p1 +.end method + +.method public getType(Landroid/net/Uri;)Ljava/lang/String; + .locals 0 + + const/4 p1, 0x0 + + return-object p1 +.end method + +.method public insert(Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri; + .locals 4 + + .line 31 + iget-object v0, p0, Lcom/senseonics/db/GlucoseContentProvider;->helper:Lcom/senseonics/db/SenseonicsDBHelper; + + invoke-virtual {v0}, Lcom/senseonics/db/SenseonicsDBHelper;->getWritableDatabase()Landroid/database/sqlite/SQLiteDatabase; + + move-result-object v0 + + const/4 v1, 0x0 + + const-string v2, "select count(*) from glucosereadings" + + .line 33 + invoke-virtual {v0, v2, v1}, Landroid/database/sqlite/SQLiteDatabase;->rawQuery(Ljava/lang/String;[Ljava/lang/String;)Landroid/database/Cursor; + + move-result-object v2 + + .line 34 + invoke-interface {v2}, Landroid/database/Cursor;->moveToFirst()Z + + const/4 v3, 0x0 + + .line 35 + invoke-interface {v2, v3}, Landroid/database/Cursor;->getInt(I)I + + move-result v2 + + if-lez v2, :cond_0 + + .line 36 + invoke-direct {p0, v0}, Lcom/senseonics/db/GlucoseContentProvider;->performDelete(Landroid/database/sqlite/SQLiteDatabase;)I + + :cond_0 + const-string v2, "glucosereadings" + + .line 39 + invoke-virtual {v0, v2, v1, p2}, Landroid/database/sqlite/SQLiteDatabase;->insert(Ljava/lang/String;Ljava/lang/String;Landroid/content/ContentValues;)J + + move-result-wide v0 + + .line 40 + new-instance p2, Ljava/lang/StringBuilder; + + invoke-direct {p2}, Ljava/lang/StringBuilder;->()V + + const-string v2, "" + + invoke-virtual {p2, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + invoke-virtual {p2, v0, v1}, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder; + + invoke-virtual {p2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object p2 + + invoke-static {p1, p2}, Landroid/net/Uri;->withAppendedPath(Landroid/net/Uri;Ljava/lang/String;)Landroid/net/Uri; + + move-result-object p1 + + return-object p1 +.end method + +.method public onCreate()Z + .locals 2 + + .line 15 + new-instance v0, Lcom/senseonics/db/SenseonicsDBHelper; + + invoke-virtual {p0}, Lcom/senseonics/db/GlucoseContentProvider;->getContext()Landroid/content/Context; + + move-result-object v1 + + invoke-direct {v0, v1}, Lcom/senseonics/db/SenseonicsDBHelper;->(Landroid/content/Context;)V + + iput-object v0, p0, Lcom/senseonics/db/GlucoseContentProvider;->helper:Lcom/senseonics/db/SenseonicsDBHelper; + + const/4 v0, 0x1 + + return v0 +.end method + +.method public query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor; + .locals 8 + + .line 21 + iget-object p1, p0, Lcom/senseonics/db/GlucoseContentProvider;->helper:Lcom/senseonics/db/SenseonicsDBHelper; + + invoke-virtual {p1}, Lcom/senseonics/db/SenseonicsDBHelper;->getReadableDatabase()Landroid/database/sqlite/SQLiteDatabase; + + move-result-object v0 + + const-string v1, "glucosereadings" + + const/4 v5, 0x0 + + const/4 v6, 0x0 + + move-object v2, p2 + + move-object v3, p3 + + move-object v4, p4 + + move-object v7, p5 + + invoke-virtual/range {v0 .. v7}, Landroid/database/sqlite/SQLiteDatabase;->query(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor; + + move-result-object p1 + + return-object p1 +.end method + +.method public update(Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I + .locals 1 + + .line 55 + iget-object p1, p0, Lcom/senseonics/db/GlucoseContentProvider;->helper:Lcom/senseonics/db/SenseonicsDBHelper; + + invoke-virtual {p1}, Lcom/senseonics/db/SenseonicsDBHelper;->getWritableDatabase()Landroid/database/sqlite/SQLiteDatabase; + + move-result-object p1 + + const-string v0, "glucosereadings" + + invoke-virtual {p1, v0, p2, p3, p4}, Landroid/database/sqlite/SQLiteDatabase;->update(Ljava/lang/String;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I + + move-result p1 + + return p1 +.end method