diff --git a/app/build.gradle b/app/build.gradle index 783561a599..0c5fe18a24 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -226,6 +226,8 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.google.android.gms:play-services-wearable:17.0.0' implementation 'com.google.firebase:firebase-core:17.2.1' + implementation 'com.google.firebase:firebase-auth:19.2.0' + implementation 'com.google.firebase:firebase-database:19.2.0' implementation('com.crashlytics.sdk.android:crashlytics:2.10.1@aar') { transitive = true; } diff --git a/app/google-services.json b/app/google-services.json index 42db6f4289..507c792c93 100644 --- a/app/google-services.json +++ b/app/google-services.json @@ -13,7 +13,12 @@ "package_name": "info.nightscout.aapspumpcontrol" } }, - "oauth_client": [], + "oauth_client": [ + { + "client_id": "477603612366-a925drvlvs7qn7gt73r585erbqto8c79.apps.googleusercontent.com", + "client_type": 3 + } + ], "api_key": [ { "current_key": "AIzaSyDcZpDRMaGjdhihXp531cVYM6LkEL8KbgM" @@ -37,7 +42,12 @@ "package_name": "info.nightscout.androidaps" } }, - "oauth_client": [], + "oauth_client": [ + { + "client_id": "477603612366-a925drvlvs7qn7gt73r585erbqto8c79.apps.googleusercontent.com", + "client_type": 3 + } + ], "api_key": [ { "current_key": "AIzaSyDcZpDRMaGjdhihXp531cVYM6LkEL8KbgM" @@ -61,7 +71,12 @@ "package_name": "info.nightscout.nsclient" } }, - "oauth_client": [], + "oauth_client": [ + { + "client_id": "477603612366-a925drvlvs7qn7gt73r585erbqto8c79.apps.googleusercontent.com", + "client_type": 3 + } + ], "api_key": [ { "current_key": "AIzaSyDcZpDRMaGjdhihXp531cVYM6LkEL8KbgM" @@ -85,7 +100,12 @@ "package_name": "info.nightscout.nsclient2" } }, - "oauth_client": [], + "oauth_client": [ + { + "client_id": "477603612366-a925drvlvs7qn7gt73r585erbqto8c79.apps.googleusercontent.com", + "client_type": 3 + } + ], "api_key": [ { "current_key": "AIzaSyDcZpDRMaGjdhihXp531cVYM6LkEL8KbgM" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aeeb53475d..8462fa3800 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -76,6 +76,8 @@ + + 120) { + ToastUtils.showToastInUiThread(this, R.string.invalidage) + return@setOnClickListener + } + if ((weight < 5 || weight > 150) && tdd == 0.0) { + ToastUtils.showToastInUiThread(this, R.string.invalidweight) + return@setOnClickListener + } + if ((tdd < 5 || tdd > 150) && weight == 0.0) { + ToastUtils.showToastInUiThread(this, R.string.invalidweight) + return@setOnClickListener + } + val profile = DefaultProfile().profile(age, tdd, weight, ProfileFunctions.getSystemUnits()) + val args = Bundle() + args.putLong("time", DateUtil.now()) + args.putInt("mode", ProfileViewerDialog.Mode.CUSTOM_PROFILE.ordinal) + args.putString("customProfile", profile.data.toString()) + args.putString("customProfileUnits", profile.units) + args.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight") + val pvd = ProfileViewerDialog() + pvd.arguments = args + pvd.show(supportFragmentManager, "ProfileViewDialog") + } + + survey_submit.setOnClickListener { + val r = FirebaseRecord() + r.id = InstanceId.instanceId() + r.age = SafeParse.stringToInt(survey_age.text.toString()) + r.weight = SafeParse.stringToInt(survey_weight.text.toString()) + if (r.age < 1 || r.age > 120) { + ToastUtils.showToastInUiThread(this, R.string.invalidage) + return@setOnClickListener + } + if (r.weight < 5 || r.weight > 150) { + ToastUtils.showToastInUiThread(this, R.string.invalidweight) + return@setOnClickListener + } + + if (survey_spinner.selectedItem == null) + return@setOnClickListener + val profileName = survey_spinner.selectedItem.toString() + val specificProfile = profileStore.getSpecificProfile(profileName) + + r.profileJson = specificProfile.toString() + + val auth = FirebaseAuth.getInstance() + auth.signInAnonymously() + .addOnCompleteListener(this) { task -> + if (task.isSuccessful) { + log.debug("signInAnonymously:success") + val user = auth.currentUser + + val database = FirebaseDatabase.getInstance().reference + database.child("survey").child(r.id).setValue(r) + } else { + log.error("signInAnonymously:failure", task.exception) + ToastUtils.showToastInUiThread(this, "Authentication failed.") + //updateUI(null) + } + + // ... + } + finish() + } + } + + inner class FirebaseRecord { + var id = "" + var age: Int = 0 + var weight: Int = 0 + var profileJson = "ghfg" + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index 93c95dda51..00d4bafebe 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -397,7 +397,7 @@ public class Profile { return toMgdl(getIsfTimeFromMidnight(secondsFromMidnight(time)), units); } - double getIsfTimeFromMidnight(int timeAsSeconds) { + public double getIsfTimeFromMidnight(int timeAsSeconds) { if (isf_v == null) isf_v = convertToSparseArray(isf); return getValueToTime(isf_v, timeAsSeconds); diff --git a/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java b/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java index af8451477d..66123e1ce9 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java +++ b/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java @@ -125,7 +125,8 @@ public class QuickWizardEntry { trend = true; } - return new BolusWizard(profile, profileName, tempTarget, carbs(), cob, bg, 0d, 100, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, "QuickWizard"); + double percentage = SP.getDouble(R.string.key_boluswizard_percentage, 100.0); + return new BolusWizard(profile, profileName, tempTarget, carbs(), cob, bg, 0d, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, "QuickWizard"); } public String buttonText() { diff --git a/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt b/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt new file mode 100644 index 0000000000..b121dbee02 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt @@ -0,0 +1,145 @@ +package info.nightscout.androidaps.data.defaultProfile + +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.utils.Round +import org.json.JSONArray +import org.json.JSONObject +import java.util.* + + +class DefaultProfile { + var oneToFive: TreeMap> = TreeMap() + var sixToEleven: TreeMap> = TreeMap() + var twelveToSeventeen: TreeMap> = TreeMap() + var eighteenToTwentyfor: TreeMap> = TreeMap() + + fun profile(age: Double, tdd: Double, weight: Double, units: String): Profile { + val profile = JSONObject() + if (age >= 1 && age < 6) { + val _tdd = if (tdd == 0.0) 0.6 * weight else tdd + closest(oneToFive, _tdd * 0.3)?.let { array -> profile.put("basal", arrayToJson(array)) } + val ic = Round.roundTo(250.0 / _tdd, 1.0) + profile.put("carbratio", singleValueArray(ic, arrayOf( 0.0, -4.0, -1.0, -2.0, -4.0, 0.0, -4.0))) + val isf = Round.roundTo(200.0 / _tdd, 0.1) + profile.put("sens", singleValueArray(isf, arrayOf( 0.0, -2.0, -0.0, -0.0, -2.0, 0.0, -2.0))) + } else if (age >= 6 && age < 12) { + val _tdd = if (tdd == 0.0) 0.8 * weight else tdd + closest(sixToEleven, _tdd * 0.4)?.let { array -> profile.put("basal", arrayToJson(array)) } + val ic = Round.roundTo(375.0 / _tdd, 1.0) + profile.put("carbratio", singleValueArray(ic, arrayOf( 0.0, -3.0, 0.0, -1.0, -3.0, 0.0, -2.0))) + val isf = Round.roundTo(170.0 / _tdd, 0.1) + profile.put("sens", singleValueArray(isf, arrayOf( 0.0, -1.0, -0.0, -0.0, -1.0, 0.0, -1.0))) + } else if (age >= 12 && age < 17) { + val _tdd = if (tdd == 0.0) 1.0 * weight else tdd + closest(twelveToSeventeen, _tdd * 0.5)?.let { array -> profile.put("basal", arrayToJson(array)) } + val ic = Round.roundTo(500.0 / _tdd, 1.0) + profile.put("carbratio", singleValueArray(ic, arrayOf( 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, -1.0))) + val isf = Round.roundTo(100.0 / _tdd, 0.1) + profile.put("sens", singleValueArray(isf, arrayOf( 0.2, 0.0, 0.2, 0.2, 0.0, 0.2, 0.2))) + } else if (age >= 18) { + + } + profile.put("dia", 5.0) + profile.put("carbs_hr", 20) // not used + profile.put("delay", 5.0) // not used + profile.put("timezone", TimeZone.getDefault().getID()) + profile.put("target_high", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units)))) + profile.put("target_low", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units)))) + return Profile(profile, units) + } + + init { + oneToFive[1.00] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050) + oneToFive[1.13] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050) + oneToFive[1.25] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.050, 0.050) + oneToFive[1.38] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.050, 0.050) + oneToFive[1.50] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.100, 0.100, 0.050, 0.050) + oneToFive[1.75] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.050, 0.050, 0.050, 0.060, 0.060, 0.075, 0.075, 0.050, 0.050, 0.050, 0.100, 0.125, 0.100, 0.050, 0.050) + oneToFive[2.00] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.075, 0.050, 0.050, 0.065, 0.065, 0.075, 0.075, 0.050, 0.050, 0.050, 0.100, 0.125, 0.100, 0.050, 0.050) + oneToFive[2.25] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.100, 0.100, 0.075, 0.060, 0.060, 0.070, 0.070, 0.100, 0.100, 0.050, 0.050, 0.050, 0.125, 0.150, 0.125, 0.065, 0.050) + oneToFive[2.50] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.125, 0.125, 0.100, 0.065, 0.065, 0.075, 0.075, 0.125, 0.125, 0.060, 0.060, 0.060, 0.150, 0.150, 0.150, 0.070, 0.060) + oneToFive[2.75] = arrayOf(0.075, 0.075, 0.075, 0.100, 0.100, 0.100, 0.125, 0.150, 0.125, 0.100, 0.070, 0.070, 0.080, 0.080, 0.150, 0.150, 0.070, 0.070, 0.070, 0.175, 0.175, 0.175, 0.080, 0.070) + oneToFive[3.25] = arrayOf(0.100, 0.100, 0.100, 0.125, 0.125, 0.125, 0.150, 0.150, 0.150, 0.100, 0.080, 0.080, 0.100, 0.100, 0.175, 0.175, 0.075, 0.075, 0.075, 0.200, 0.200, 0.200, 0.090, 0.080) + oneToFive[3.75] = arrayOf(0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.175, 0.175, 0.175, 0.100, 0.085, 0.085, 0.110, 0.110, 0.185, 0.185, 0.080, 0.080, 0.080, 0.225, 0.225, 0.225, 0.100, 0.090) + oneToFive[4.25] = arrayOf(0.125, 0.125, 0.130, 0.140, 0.140, 0.140, 0.200, 0.200, 0.200, 0.125, 0.090, 0.090, 0.120, 0.120, 0.200, 0.200, 0.100, 0.100, 0.100, 0.250, 0.250, 0.250, 0.125, 0.100) + oneToFive[4.75] = arrayOf(0.125, 0.130, 0.135, 0.150, 0.150, 0.150, 0.200, 0.225, 0.200, 0.125, 0.100, 0.100, 0.125, 0.125, 0.250, 0.200, 0.110, 0.125, 0.125, 0.275, 0.275, 0.275, 0.130, 0.125) + oneToFive[5.25] = arrayOf(0.150, 0.150, 0.150, 0.170, 0.170, 0.170, 0.225, 0.225, 0.225, 0.130, 0.125, 0.125, 0.140, 0.140, 0.250, 0.250, 0.150, 0.150, 0.150, 0.300, 0.300, 0.300, 0.150, 0.150) + oneToFive[6.00] = arrayOf(0.170, 0.170, 0.175, 0.200, 0.200, 0.200, 0.250, 0.250, 0.250, 0.150, 0.125, 0.125, 0.150, 0.150, 0.275, 0.275, 0.170, 0.150, 0.150, 0.350, 0.350, 0.350, 0.175, 0.150) + oneToFive[6.75] = arrayOf(0.200, 0.200, 0.200, 0.225, 0.225, 0.225, 0.275, 0.275, 0.275, 0.200, 0.130, 0.130, 0.175, 0.175, 0.300, 0.300, 0.170, 0.175, 0.175, 0.375, 0.375, 0.375, 0.200, 0.175) + oneToFive[7.50] = arrayOf(0.225, 0.230, 0.235, 0.250, 0.250, 0.250, 0.300, 0.300, 0.300, 0.250, 0.150, 0.150, 0.200, 0.200, 0.325, 0.325, 0.200, 0.200, 0.200, 0.400, 0.450, 0.400, 0.350, 0.200) + + sixToEleven[5.26] = arrayOf(0.18, 0.18, 0.18, 0.20, 0.20, 0.23, 0.25, 0.25, 0.25, 0.18, 0.15, 0.13, 0.15, 0.15, 0.25, 0.25, 0.20, 0.15, 0.18, 0.25, 0.25, 0.25, 0.23, 0.20) + sixToEleven[5.61] = arrayOf(0.18, 0.20, 0.20, 0.23, 0.23, 0.25, 0.28, 0.28, 0.25, 0.20, 0.15, 0.13, 0.15, 0.18, 0.25, 0.25, 0.20, 0.15, 0.18, 0.28, 0.25, 0.25, 0.23, 0.20) + sixToEleven[5.93] = arrayOf(0.20, 0.20, 0.23, 0.25, 0.25, 0.25, 0.30, 0.30, 0.30, 0.25, 0.15, 0.15, 0.18, 0.18, 0.28, 0.28, 0.20, 0.20, 0.20, 0.28, 0.28, 0.28, 0.25, 0.23) + sixToEleven[6.26] = arrayOf(0.20, 0.23, 0.23, 0.25, 0.25, 0.28, 0.33, 0.30, 0.30, 0.25, 0.18, 0.15, 0.18, 0.18, 0.28, 0.28, 0.23, 0.20, 0.20, 0.28, 0.30, 0.28, 0.25, 0.23) + sixToEleven[6.60] = arrayOf(0.23, 0.23, 0.25, 0.25, 0.25, 0.28, 0.33, 0.33, 0.33, 0.28, 0.18, 0.15, 0.18, 0.18, 0.30, 0.28, 0.23, 0.23, 0.20, 0.30, 0.30, 0.30, 0.25, 0.25) + sixToEleven[7.26] = arrayOf(0.23, 0.25, 0.28, 0.28, 0.30, 0.33, 0.38, 0.35, 0.35, 0.30, 0.18, 0.18, 0.18, 0.19, 0.33, 0.30, 0.25, 0.23, 0.23, 0.33, 0.33, 0.33, 0.30, 0.25) + sixToEleven[7.92] = arrayOf(0.25, 0.25, 0.28, 0.30, 0.33, 0.35, 0.38, 0.38, 0.38, 0.35, 0.20, 0.20, 0.23, 0.25, 0.35, 0.33, 0.30, 0.25, 0.25, 0.35, 0.35, 0.35, 0.33, 0.28) + sixToEleven[8.57] = arrayOf(0.28, 0.28, 0.30, 0.30, 0.33, 0.38, 0.40, 0.43, 0.40, 0.38, 0.25, 0.25, 0.25, 0.28, 0.38, 0.38, 0.33, 0.28, 0.28, 0.38, 0.40, 0.38, 0.35, 0.30) + sixToEleven[9.24] = arrayOf(0.30, 0.33, 0.35, 0.38, 0.40, 0.40, 0.43, 0.45, 0.43, 0.40, 0.30, 0.25, 0.28, 0.28, 0.38, 0.40, 0.33, 0.30, 0.30, 0.40, 0.43, 0.40, 0.38, 0.35) + sixToEleven[9.89] = arrayOf(0.35, 0.35, 0.38, 0.40, 0.40, 0.43, 0.45, 0.48, 0.45, 0.40, 0.30, 0.25, 0.28, 0.30, 0.40, 0.43, 0.35, 0.33, 0.33, 0.43, 0.45, 0.43, 0.40, 0.38) + sixToEleven[10.56] = arrayOf(0.38, 0.38, 0.40, 0.43, 0.45, 0.45, 0.48, 0.50, 0.48, 0.40, 0.35, 0.25, 0.30, 0.33, 0.43, 0.45, 0.35, 0.35, 0.35, 0.45, 0.48, 0.45, 0.43, 0.40) + sixToEleven[11.21] = arrayOf(0.40, 0.43, 0.43, 0.45, 0.48, 0.50, 0.53, 0.55, 0.50, 0.40, 0.35, 0.30, 0.33, 0.33, 0.45, 0.48, 0.38, 0.35, 0.37, 0.50, 0.50, 0.48, 0.45, 0.43) + sixToEleven[11.88] = arrayOf(0.43, 0.43, 0.45, 0.45, 0.48, 0.50, 0.55, 0.58, 0.50, 0.40, 0.35, 0.33, 0.33, 0.33, 0.48, 0.50, 0.40, 0.38, 0.38, 0.53, 0.53, 0.50, 0.48, 0.45) + sixToEleven[12.53] = arrayOf(0.45, 0.45, 0.48, 0.50, 0.53, 0.55, 0.60, 0.60, 0.60, 0.45, 0.40, 0.35, 0.35, 0.38, 0.50, 0.53, 0.40, 0.38, 0.38, 0.55, 0.58, 0.55, 0.50, 0.48) + sixToEleven[13.19] = arrayOf(0.48, 0.48, 0.50, 0.55, 0.58, 0.60, 0.65, 0.65, 0.65, 0.50, 0.45, 0.36, 0.38, 0.40, 0.55, 0.55, 0.45, 0.40, 0.40, 0.60, 0.60, 0.58, 0.55, 0.50) + sixToEleven[14.18] = arrayOf(0.53, 0.53, 0.55, 0.60, 0.65, 0.68, 0.70, 0.70, 0.68, 0.60, 0.55, 0.40, 0.40, 0.45, 0.60, 0.60, 0.50, 0.45, 0.45, 0.63, 0.65, 0.63, 0.60, 0.60) + sixToEleven[15.17] = arrayOf(0.55, 0.58, 0.60, 0.65, 0.70, 0.70, 0.75, 0.75, 0.70, 0.65, 0.60, 0.42, 0.42, 0.45, 0.65, 0.65, 0.60, 0.50, 0.50, 0.68, 0.68, 0.65, 0.63, 0.63) + sixToEleven[16.50] = arrayOf(0.60, 0.63, 0.65, 0.70, 0.70, 0.70, 0.80, 0.80, 0.80, 0.70, 0.60, 0.45, 0.45, 0.50, 0.65, 0.70, 0.60, 0.55, 0.55, 0.75, 0.75, 0.70, 0.65, 0.65) + + twelveToSeventeen[10.70] = arrayOf(0.30, 0.30, 0.30, 0.30, 0.40, 0.40, 0.60, 0.60, 0.60, 0.40, 0.35, 0.30, 0.30, 0.35, 0.45, 0.50, 0.40, 0.30, 0.30, 0.40, 0.50, 0.40, 0.40, 0.30) + twelveToSeventeen[11.10] = arrayOf(0.30, 0.30, 0.30, 0.35, 0.40, 0.45, 0.60, 0.60, 0.60, 0.40, 0.40, 0.30, 0.35, 0.40, 0.50, 0.50, 0.30, 0.30, 0.30, 0.50, 0.50, 0.50, 0.30, 0.30) + twelveToSeventeen[11.60] = arrayOf(0.30, 0.30, 0.35, 0.45, 0.45, 0.50, 0.65, 0.65, 0.65, 0.45, 0.40, 0.40, 0.40, 0.40, 0.50, 0.55, 0.55, 0.45, 0.45, 0.50, 0.50, 0.50, 0.40, 0.40) + twelveToSeventeen[13.00] = arrayOf(0.40, 0.40, 0.40, 0.50, 0.55, 0.60, 0.70, 0.70, 0.70, 0.60, 0.50, 0.40, 0.40, 0.40, 0.50, 0.60, 0.60, 0.50, 0.50, 0.60, 0.60, 0.60, 0.40, 0.30) + twelveToSeventeen[15.60] = arrayOf(0.45, 0.50, 0.50, 0.60, 0.65, 0.70, 0.80, 0.80, 0.80, 0.70, 0.60, 0.60, 0.50, 0.50, 0.60, 0.70, 0.70, 0.60, 0.60, 0.60, 0.70, 0.70, 0.50, 0.50) + twelveToSeventeen[17.00] = arrayOf(0.50, 0.55, 0.60, 0.70, 0.75, 0.80, 1.00, 1.00, 1.00, 0.80, 0.70, 0.60, 0.60, 0.60, 0.70, 0.80, 0.80, 0.65, 0.65, 0.65, 0.70, 0.70, 0.60, 0.60) + twelveToSeventeen[18.00] = arrayOf(0.60, 0.65, 0.70, 0.80, 0.85, 0.90, 1.10, 1.10, 1.10, 0.90, 0.80, 0.60, 0.60, 0.60, 0.70, 0.80, 0.80, 0.70, 0.65, 0.70, 0.75, 0.70, 0.60, 0.60) + twelveToSeventeen[20.20] = arrayOf(0.70, 0.75, 0.80, 0.90, 0.95, 1.00, 1.10, 1.10, 1.10, 0.90, 0.80, 0.70, 0.70, 0.70, 0.80, 0.90, 0.90, 0.75, 0.75, 0.75, 0.80, 0.80, 0.70, 0.70) + twelveToSeventeen[21.60] = arrayOf(0.75, 0.80, 0.90, 0.90, 1.00, 1.00, 1.20, 1.20, 1.20, 0.90, 0.80, 0.70, 0.70, 0.70, 0.90, 1.00, 1.00, 0.80, 0.80, 0.80, 0.80, 0.80, 0.70, 0.70) + twelveToSeventeen[23.80] = arrayOf(0.75, 0.80, 0.90, 1.00, 1.10, 1.10, 1.20, 1.20, 1.20, 1.00, 0.90, 0.80, 0.80, 0.80, 0.90, 1.10, 1.10, 0.90, 0.90, 0.90, 1.00, 1.00, 0.80, 0.80) + twelveToSeventeen[26.10] = arrayOf(0.80, 0.80, 0.90, 1.00, 1.20, 1.20, 1.30, 1.30, 1.30, 1.10, 1.00, 0.90, 0.90, 0.90, 1.00, 1.20, 1.10, 0.90, 0.90, 1.00, 1.00, 1.00, 0.90, 0.90) + twelveToSeventeen[28.00] = arrayOf(0.90, 0.90, 1.00, 1.10, 1.10, 1.20, 1.30, 1.30, 1.30, 1.20, 1.00, 1.00, 1.00, 1.00, 1.20, 1.20, 1.20, 1.00, 1.00, 1.10, 1.10, 1.10, 0.90, 0.90) + twelveToSeventeen[30.10] = arrayOf(1.00, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.50, 1.50, 1.30, 1.20, 1.00, 1.00, 1.00, 1.30, 1.40, 1.40, 1.00, 1.00, 1.15, 1.15, 1.10, 1.00, 1.00) + twelveToSeventeen[32.60] = arrayOf(1.10, 1.10, 1.20, 1.20, 1.40, 1.50, 1.50, 1.50, 1.50, 1.30, 1.20, 1.10, 1.10, 1.10, 1.40, 1.50, 1.40, 1.10, 1.10, 1.20, 1.20, 1.20, 1.10, 1.10) + twelveToSeventeen[35.20] = arrayOf(1.20, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.70, 1.50, 1.40, 1.20, 1.10, 1.10, 1.10, 1.40, 1.50, 1.60, 1.40, 1.20, 1.20, 1.30, 1.30, 1.20, 1.20) + twelveToSeventeen[39.00] = arrayOf(1.30, 1.30, 1.40, 1.60, 1.60, 1.60, 1.90, 1.90, 1.90, 1.50, 1.30, 1.20, 1.20, 1.30, 1.50, 1.60, 1.70, 1.80, 1.50, 1.50, 1.60, 1.60, 1.30, 1.30) + twelveToSeventeen[42.80] = arrayOf(1.40, 1.40, 1.50, 1.70, 1.80, 1.80, 2.00, 2.00, 2.00, 1.80, 1.80, 1.50, 1.50, 1.50, 1.60, 1.70, 1.80, 1.90, 1.60, 1.60, 1.70, 1.70, 1.50, 1.50) + twelveToSeventeen[47.30] = arrayOf(1.50, 1.50, 1.70, 1.70, 2.00, 2.00, 2.20, 2.30, 2.20, 2.00, 1.80, 1.60, 1.60, 1.60, 1.80, 2.00, 2.10, 1.90, 1.80, 1.80, 2.00, 2.00, 1.60, 1.60) + } + + private fun closest(map: TreeMap>, key: Double): Array? { + val low = map.floorEntry(key) + val high = map.ceilingEntry(key) + var res: Array? = null + if (low != null && high != null) { + res = if (Math.abs(key - low.key) < Math.abs(key - high.key)) + low.value + else + high.value + } else if (low != null || high != null) { + res = if (low != null) low.value else high.value + } + return res + } + + fun arrayToJson(b: Array): JSONArray { + val basals = JSONArray() + for (i in 0..23) { + val time = String.format(Locale.ENGLISH, "%02d:00", i) + basals.put(JSONObject().put("time", time).put("value", b[i].toString())) + } + return basals + } + + fun singleValueArray(value: Double, sample: Array): JSONArray { + val array = JSONArray() + array.put(JSONObject().put("time", "00:00").put("value", value + sample[0])) + array.put(JSONObject().put("time", "06:00").put("value", value + sample[1])) + array.put(JSONObject().put("time", "09:00").put("value", value + sample[2])) + array.put(JSONObject().put("time", "11:00").put("value", value + sample[3])) + array.put(JSONObject().put("time", "14:00").put("value", value + sample[4])) + array.put(JSONObject().put("time", "16:00").put("value", value + sample[5])) + array.put(JSONObject().put("time", "19:00").put("value", value + sample[6])) + return array + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index d8926fc4c1..b1fb199fc1 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -3,6 +3,7 @@ package info.nightscout.androidaps.db; import android.content.Context; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; + import androidx.annotation.Nullable; import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; @@ -772,8 +773,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TempTarget tempTarget = new TempTarget() .date(trJson.getLong("mills")) .duration(JsonHelper.safeGetInt(trJson, "duration")) - .low(Profile.toMgdl(trJson.getDouble("targetBottom"), units)) - .high(Profile.toMgdl(trJson.getDouble("targetTop"), units)) + .low(Profile.toMgdl(JsonHelper.safeGetDouble(trJson, "targetBottom"), units)) + .high(Profile.toMgdl(JsonHelper.safeGetDouble(trJson, "targetTop"), units)) .reason(JsonHelper.safeGetString(trJson, "reason", "")) ._id(trJson.getString("_id")) .source(Source.NIGHTSCOUT); @@ -1592,14 +1593,23 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { // ---------------- ProfileSwitch handling --------------- - public List getProfileSwitchData(boolean ascending) { + public List getProfileSwitchData(long from, boolean ascending) { try { Dao daoProfileSwitch = getDaoProfileSwitch(); List profileSwitches; QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); queryBuilder.orderBy("date", ascending); queryBuilder.limit(100L); + Where where = queryBuilder.where(); + where.ge("date", from); + queryBuilder.setCountOf(true); PreparedQuery preparedQuery = queryBuilder.prepare(); + long count = daoProfileSwitch.countOf(preparedQuery); + // now do query of count + 1 + queryBuilder = daoProfileSwitch.queryBuilder(); + queryBuilder.orderBy("date", ascending); + queryBuilder.limit(count + 1); + preparedQuery = queryBuilder.prepare(); profileSwitches = daoProfileSwitch.query(preparedQuery); return profileSwitches; } catch (SQLException e) { diff --git a/app/src/main/java/info/nightscout/androidaps/db/TDD.java b/app/src/main/java/info/nightscout/androidaps/db/TDD.java index 93a228316c..be81e36ba3 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TDD.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TDD.java @@ -6,8 +6,11 @@ import com.j256.ormlite.table.DatabaseTable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.utils.DateUtil; /** * Created by mike on 20.09.2017. @@ -56,4 +59,12 @@ public class TDD { ", total=" + total + ']'; } + + public String toText() { + return MainApp.gs(R.string.tddformat, DateUtil.dateStringShort(date), total, bolus, basal); + } + + public String toText(int days) { + return MainApp.gs(R.string.tddformat, String.format("%d ", days) + MainApp.gs(R.string.days), total, bolus, basal); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt index ee267c689e..8ec716a872 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt @@ -1,13 +1,19 @@ package info.nightscout.androidaps.plugins.constraints.versionChecker +import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.ConstraintsInterface +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginDescription +import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.utils.SP import java.util.concurrent.TimeUnit +import kotlin.math.roundToInt /** * Usually we would have a class here. @@ -16,16 +22,23 @@ import java.util.concurrent.TimeUnit * */ object VersionCheckerPlugin : PluginBase(PluginDescription() - .mainType(PluginType.CONSTRAINTS) - .neverVisible(true) - .alwaysEnabled(true) - .showInList(false) - .pluginName(R.string.versionChecker)), ConstraintsInterface { + .mainType(PluginType.CONSTRAINTS) + .neverVisible(true) + .alwaysEnabled(true) + .showInList(false) + .pluginName(R.string.versionChecker)), ConstraintsInterface { + + private val gracePeriod: GracePeriod + get() = if ((BuildConfig.VERSION_NAME.contains("RC", ignoreCase = true))) { + GracePeriod.RC + } else { + GracePeriod.RELEASE + } override fun isClosedLoopAllowed(value: Constraint): Constraint { checkWarning() triggerCheckVersion() - return if (isOldVersion(GRACE_PERIOD_VERY_OLD)) + return if (isOldVersion(gracePeriod.veryOld.daysToMillis())) value.set(false, MainApp.gs(R.string.very_old_version), this) else value @@ -33,41 +46,49 @@ object VersionCheckerPlugin : PluginBase(PluginDescription() private fun checkWarning() { val now = System.currentTimeMillis() - + if (!SP.contains(R.string.key_last_versionchecker_plugin_warning)) { SP.putLong(R.string.key_last_versionchecker_plugin_warning, now) return } - if (isOldVersion(GRACE_PERIOD_WARNING) && shouldWarnAgain(now)) { + if (isOldVersion(gracePeriod.warning.daysToMillis()) && shouldWarnAgain(now)) { // store last notification time SP.putLong(R.string.key_last_versionchecker_plugin_warning, now) //notify - val message = MainApp.gs(R.string.new_version_warning, Math.round((now - SP.getLong(R.string.key_last_time_this_version_detected, now)) / TimeUnit.DAYS.toMillis(1).toDouble())) + val message = MainApp.gs(R.string.new_version_warning, + ((now - SP.getLong(R.string.key_last_time_this_version_detected, now)) / 1L.daysToMillis().toDouble()).roundToInt(), + gracePeriod.old, + gracePeriod.veryOld + ) val notification = Notification(Notification.OLDVERSION, message, Notification.NORMAL) RxBus.send(EventNewNotification(notification)) } } private fun shouldWarnAgain(now: Long) = - now > SP.getLong(R.string.key_last_versionchecker_plugin_warning, 0) + WARN_EVERY + now > SP.getLong(R.string.key_last_versionchecker_plugin_warning, 0) + WARN_EVERY override fun applyMaxIOBConstraints(maxIob: Constraint): Constraint = - if (isOldVersion(GRACE_PERIOD_OLD)) - maxIob.set(0.toDouble(), MainApp.gs(R.string.old_version), this) - else - maxIob + if (isOldVersion(gracePeriod.old.daysToMillis())) + maxIob.set(0.toDouble(), MainApp.gs(R.string.old_version), this) + else + maxIob private fun isOldVersion(gracePeriod: Long): Boolean { val now = System.currentTimeMillis() - return now > SP.getLong(R.string.key_last_time_this_version_detected, 0) + gracePeriod + return now > SP.getLong(R.string.key_last_time_this_version_detected, 0) + gracePeriod } - val WARN_EVERY = TimeUnit.DAYS.toMillis(1) - val GRACE_PERIOD_WARNING = TimeUnit.DAYS.toMillis(30) - val GRACE_PERIOD_OLD = TimeUnit.DAYS.toMillis(60) - val GRACE_PERIOD_VERY_OLD = TimeUnit.DAYS.toMillis(90) + private val WARN_EVERY = TimeUnit.DAYS.toMillis(1) } + +enum class GracePeriod(val warning: Long, val old: Long, val veryOld: Long) { + RELEASE(30, 60, 90), + RC(0, 7, 14) +} + +private fun Long.daysToMillis() = TimeUnit.DAYS.toMillis(this) \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java index 582eedb0e7..49e33c8215 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java @@ -315,11 +315,13 @@ public class NSUpload { JSONObject data = new JSONObject(); data.put("eventType", CareportalEvent.TEMPORARYTARGET); data.put("duration", tempTarget.durationInMinutes); - data.put("reason", tempTarget.reason); - data.put("targetBottom", Profile.fromMgdlToUnits(tempTarget.low, ProfileFunctions.getSystemUnits())); - data.put("targetTop", Profile.fromMgdlToUnits(tempTarget.high, ProfileFunctions.getSystemUnits())); + if (tempTarget.low > 0) { + data.put("reason", tempTarget.reason); + data.put("targetBottom", Profile.fromMgdlToUnits(tempTarget.low, ProfileFunctions.getSystemUnits())); + data.put("targetTop", Profile.fromMgdlToUnits(tempTarget.high, ProfileFunctions.getSystemUnits())); + data.put("units", ProfileFunctions.getSystemUnits()); + } data.put("created_at", DateUtil.toISOString(tempTarget.date)); - data.put("units", ProfileFunctions.getSystemUnits()); data.put("enteredBy", MainApp.gs(R.string.app_name)); uploadCareportalEntryToNS(data); } catch (JSONException e) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java index 004f44858d..b2d18aa4b7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java @@ -103,11 +103,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface @Override protected void onStart() { - initializeTempBasalData(); - initializeTreatmentData(); - initializeExtendedBolusData(); - initializeTempTargetData(); - initializeProfileSwitchData(); + initializeData(range()); super.onStart(); disposable.add(RxBus.INSTANCE .toObservable(EventReloadTreatmentData.class) @@ -115,8 +111,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface .subscribe(event -> { if (L.isEnabled(L.DATATREATMENTS)) log.debug("EventReloadTreatmentData"); - initializeTreatmentData(); - initializeExtendedBolusData(); + initializeTreatmentData(range()); + initializeExtendedBolusData(range()); updateTotalIOBTreatments(); RxBus.INSTANCE.send(event.getNext()); }, @@ -125,13 +121,13 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface disposable.add(RxBus.INSTANCE .toObservable(EventReloadProfileSwitchData.class) .observeOn(Schedulers.io()) - .subscribe(event -> initializeProfileSwitchData(), + .subscribe(event -> initializeProfileSwitchData(range()), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventTempTargetChange.class) .observeOn(Schedulers.io()) - .subscribe(event -> initializeTempTargetData(), + .subscribe(event -> initializeTempTargetData(range()), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE @@ -140,7 +136,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface .subscribe(event -> { if (L.isEnabled(L.DATATREATMENTS)) log.debug("EventReloadTempBasalData"); - initializeTempBasalData(); + initializeTempBasalData(range()); updateTotalIOBTempBasals(); }, FabricPrivacy::logException @@ -157,61 +153,61 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return this.service; } - private void initializeTreatmentData() { + private long range() { + double dia = Constants.defaultDIA; + if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) + dia = ProfileFunctions.getInstance().getProfile().getDia(); + return (long) (60 * 60 * 1000L * (24 + dia)); + } + + public void initializeData(long range) { + initializeTempBasalData(range); + initializeTreatmentData(range); + initializeExtendedBolusData(range); + initializeTempTargetData(range); + initializeProfileSwitchData(range); + } + + private void initializeTreatmentData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeTreatmentData"); - double dia = Constants.defaultDIA; - if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) - dia = ProfileFunctions.getInstance().getProfile().getDia(); - long fromMills = (long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); synchronized (treatments) { treatments.clear(); - treatments.addAll(getService().getTreatmentDataFromTime(fromMills, false)); + treatments.addAll(getService().getTreatmentDataFromTime(DateUtil.now() - range, false)); } } - private void initializeTempBasalData() { + private void initializeTempBasalData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeTempBasalData"); - double dia = Constants.defaultDIA; - if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) - dia = ProfileFunctions.getInstance().getProfile().getDia(); - long fromMills = (long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); - synchronized (tempBasals) { - tempBasals.reset().add(MainApp.getDbHelper().getTemporaryBasalsDataFromTime(fromMills, false)); + tempBasals.reset().add(MainApp.getDbHelper().getTemporaryBasalsDataFromTime(DateUtil.now() - range, false)); } } - private void initializeExtendedBolusData() { + private void initializeExtendedBolusData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeExtendedBolusData"); - double dia = Constants.defaultDIA; - if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) - dia = ProfileFunctions.getInstance().getProfile().getDia(); - long fromMills = (long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); - synchronized (extendedBoluses) { - extendedBoluses.reset().add(MainApp.getDbHelper().getExtendedBolusDataFromTime(fromMills, false)); + extendedBoluses.reset().add(MainApp.getDbHelper().getExtendedBolusDataFromTime(DateUtil.now() - range, false)); } } - private void initializeTempTargetData() { + private void initializeTempTargetData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeTempTargetData"); synchronized (tempTargets) { - long fromMills = System.currentTimeMillis() - 60 * 60 * 1000L * 24; - tempTargets.reset().add(MainApp.getDbHelper().getTemptargetsDataFromTime(fromMills, false)); + tempTargets.reset().add(MainApp.getDbHelper().getTemptargetsDataFromTime(DateUtil.now() - range, false)); } } - private void initializeProfileSwitchData() { + private void initializeProfileSwitchData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeProfileSwitchData"); synchronized (profiles) { - profiles.reset().add(MainApp.getDbHelper().getProfileSwitchData(false)); + profiles.reset().add(MainApp.getDbHelper().getProfileSwitchData(DateUtil.now() - range, false)); } } @@ -261,7 +257,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface if (!pumpInterface.isFakingTempsByExtendedBoluses()) synchronized (extendedBoluses) { - for (Integer pos = 0; pos < extendedBoluses.size(); pos++) { + for (int pos = 0; pos < extendedBoluses.size(); pos++) { ExtendedBolus e = extendedBoluses.get(pos); if (e.date > time) continue; IobTotal calc = e.iobCalc(time); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt index be9adece61..69d89d0063 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt @@ -5,6 +5,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.DialogFragment +import info.nightscout.androidaps.Constants import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.data.Profile @@ -14,16 +15,21 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DateUtil import kotlinx.android.synthetic.main.close.* import kotlinx.android.synthetic.main.profileviewer_fragment.* +import org.json.JSONObject class ProfileViewerDialog : DialogFragment() { private var time: Long = 0 enum class Mode(val i: Int) { RUNNING_PROFILE(1), - PUMP_PROFILE(2) + PUMP_PROFILE(2), + CUSTOM_PROFILE(3) } - private var mode: Mode = Mode.RUNNING_PROFILE; + private var mode: Mode = Mode.RUNNING_PROFILE + private var customProfileJson: String = "" + private var customProfileName: String = "" + private var customProfileUnits: String = Constants.MGDL override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -31,6 +37,9 @@ class ProfileViewerDialog : DialogFragment() { (savedInstanceState ?: arguments)?.let { bundle -> time = bundle.getLong("time", 0) mode = Mode.values()[bundle.getInt("mode", Mode.RUNNING_PROFILE.ordinal)] + customProfileJson = bundle.getString("customProfile", "") + customProfileUnits = bundle.getString("customProfileUnits", Constants.MGDL) + customProfileName = bundle.getString("customProfileName", "") } return inflater.inflate(R.layout.profileviewer_fragment, container, false) @@ -64,6 +73,13 @@ class ProfileViewerDialog : DialogFragment() { profileview_reload.visibility = View.VISIBLE profileview_datelayout.visibility = View.GONE } + Mode.CUSTOM_PROFILE -> { + profile = Profile(JSONObject(customProfileJson), customProfileUnits) + profileName = customProfileName + date = "" + profileview_reload.visibility = View.GONE + profileview_datelayout.visibility = View.GONE + } } profileview_noprofile.visibility = View.VISIBLE @@ -92,6 +108,9 @@ class ProfileViewerDialog : DialogFragment() { super.onSaveInstanceState(bundle) bundle.putLong("time", time) bundle.putInt("mode", mode.ordinal) + bundle.putString("customProfile", customProfileJson) + bundle.putString("customProfileName", customProfileName) + bundle.putString("customProfileUnits", customProfileUnits) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt index a125f7a8c3..e65bd15200 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt @@ -42,7 +42,7 @@ class TreatmentsProfileSwitchFragment : Fragment() { super.onViewCreated(view, savedInstanceState) profileswitch_recyclerview.setHasFixedSize(true) profileswitch_recyclerview.layoutManager = LinearLayoutManager(view.context) - profileswitch_recyclerview.adapter = RecyclerProfileViewAdapter(MainApp.getDbHelper().getProfileSwitchData(false)) + profileswitch_recyclerview.adapter = RecyclerProfileViewAdapter(MainApp.getDbHelper().getProfileSwitchData(DateUtil.now() - T.days(30).msecs(),false)) profileswitch_refreshfromnightscout.setOnClickListener { val builder = AlertDialog.Builder(this.context!!) @@ -77,7 +77,7 @@ class TreatmentsProfileSwitchFragment : Fragment() { } fun updateGUI() = - profileswitch_recyclerview?.swapAdapter(RecyclerProfileViewAdapter(MainApp.getDbHelper().getProfileSwitchData(false)), false) + profileswitch_recyclerview?.swapAdapter(RecyclerProfileViewAdapter(MainApp.getDbHelper().getProfileSwitchData(DateUtil.now() - T.days(30).msecs(),false)), false) inner class RecyclerProfileViewAdapter(var profileSwitchList: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ProfileSwitchViewHolder { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt b/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt new file mode 100644 index 0000000000..deac4a2877 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt @@ -0,0 +1,78 @@ +package info.nightscout.androidaps.utils + +import android.app.Activity +import android.app.Application +import android.os.Bundle +import android.text.Spanned +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.L +import org.slf4j.LoggerFactory + +object ActivityMonitor : Application.ActivityLifecycleCallbacks { + private val log = LoggerFactory.getLogger(L.CORE) + override fun onActivityPaused(activity: Activity?) { + val name = activity?.javaClass?.simpleName ?: return + val resumed = SP.getLong("Monitor_" + name + "_" + "resumed", 0) + if (resumed == 0L) { + log.debug("onActivityPaused: $name resumed == 0") + return + } + val elapsed = DateUtil.now() - resumed + val total = SP.getLong("Monitor_" + name + "_total", 0) + if (total == 0L) { + SP.putLong("Monitor_" + name + "_start", DateUtil.now()) + } + SP.putLong("Monitor_" + name + "_total", total + elapsed) + log.debug("onActivityPaused: $name elapsed=$elapsed total=${total + elapsed}") + } + + override fun onActivityResumed(activity: Activity?) { + val name = activity?.javaClass?.simpleName ?: return + log.debug("onActivityResumed: $name") + SP.putLong("Monitor_" + name + "_" + "resumed", DateUtil.now()) + } + + override fun onActivityStarted(activity: Activity?) { + } + + override fun onActivityDestroyed(activity: Activity?) { + } + + override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { + } + + override fun onActivityStopped(activity: Activity?) { + } + + override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { + } + + fun toText(): String { + val keys: Map = SP.getAll() + var result = "" + for ((key, value) in keys) + if (key.startsWith("Monitor") && key.endsWith("total")) { + val activity = key.split("_")[1].replace("Activity", "") + val duration = DateUtil.niceTimeScalar(value as Long) + val start = SP.getLong(key.replace("total", "start"), 0) + val days = T.msecs(DateUtil.now() - start).days() + result += "$activity: $duration in $days days
" + } + return result + } + + fun stats() :Spanned { + return HtmlHelper.fromHtml("
" + MainApp.gs(R.string.activitymonitor) + ":
" + toText()) + } + + fun reset() { + val keys: Map = SP.getAll() + for ((key, _) in keys) + if (key.startsWith("Monitor") && key.endsWith("total")) { + SP.remove(key) + SP.remove(key.replace("total", "start")) + SP.remove(key.replace("total", "resumed")) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java index 98c4d3a071..2c679a6a28 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java @@ -117,6 +117,14 @@ public class DateUtil { return df.format(mills); } + public static String dateStringShort(long mills) { + String format = "MM/dd"; + if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) { + format = "dd/MM"; + } + return new DateTime(mills).toString(DateTimeFormat.forPattern(format)); + } + public static String timeString(Date date) { String format = "hh:mma"; if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SP.java b/app/src/main/java/info/nightscout/androidaps/utils/SP.java index e45e650ace..191fd9421b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/SP.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/SP.java @@ -3,6 +3,8 @@ package info.nightscout.androidaps.utils; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import java.util.Map; + import info.nightscout.androidaps.MainApp; /** @@ -12,6 +14,10 @@ import info.nightscout.androidaps.MainApp; public class SP { private static SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); + static public Map getAll() { + return sharedPreferences.getAll(); + } + static public void clear() { sharedPreferences.edit().clear().apply(); } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TIR.kt b/app/src/main/java/info/nightscout/androidaps/utils/TIR.kt new file mode 100644 index 0000000000..5f2ea9bbaf --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/TIR.kt @@ -0,0 +1,26 @@ +package info.nightscout.androidaps.utils + +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import kotlin.math.roundToInt + +class TIR(val date: Long, val lowThreshold: Double, val highThreshold: Double) { + internal var below = 0 + internal var inRange = 0 + internal var above = 0 + internal var error = 0 + internal var count = 0 + + fun error() = run { error++ } + fun below() = run { below++; count++ } + fun inRange() = run { inRange++; count++ } + fun above() = run { above++; count++ } + + fun belowPct() = if (count > 0) (below.toDouble() / count * 100.0).roundToInt() else 0 + fun inRangePct() = if (count > 0) (inRange.toDouble() / count * 100.0).roundToInt() else 0 + fun abovePct() = if (count > 0) (above.toDouble() / count * 100.0).roundToInt() else 0 + + fun toText(): String = MainApp.gs(R.string.tirformat, DateUtil.dateStringShort(date), belowPct(), inRangePct(), abovePct()) + + fun toText(days: Int): String = MainApp.gs(R.string.tirformat, "%02d".format(days) + " " + MainApp.gs(R.string.days), belowPct(), inRangePct(), abovePct()) +} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TddCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/TddCalculator.kt new file mode 100644 index 0000000000..b119fe72b9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/TddCalculator.kt @@ -0,0 +1,82 @@ +package info.nightscout.androidaps.utils + +import android.text.Spanned +import android.util.LongSparseArray +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.db.TDD +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import org.slf4j.LoggerFactory + +object TddCalculator : TreatmentsPlugin() { + private val log = LoggerFactory.getLogger(L.DATATREATMENTS) + + fun calculate(days: Long): LongSparseArray { + val range = T.days(days + 1).msecs() + val startTime = MidnightTime.calc(DateUtil.now()) - T.days(days).msecs() + val endTime = MidnightTime.calc(DateUtil.now()) + initializeData(range) + + + val result = LongSparseArray() + for (t in treatmentsFromHistory) { + if (!t.isValid) continue + if (t.date < startTime || t.date > endTime) continue + val midnight = MidnightTime.calc(t.date) + val tdd = result[midnight] ?: TDD(midnight, 0.0, 0.0, 0.0) + tdd.bolus += t.insulin + result.put(midnight, tdd) + } + + for (t in startTime until endTime step T.mins(5).msecs()) { + val midnight = MidnightTime.calc(t) + val tdd = result[midnight] ?: TDD(midnight, 0.0, 0.0, 0.0) + val tbr = getTempBasalFromHistory(t) + val profile = ProfileFunctions.getInstance().getProfile(t) ?: continue + val absoluteRate = tbr?.tempBasalConvertedToAbsolute(t, profile) ?: profile.getBasal(t) + tdd.basal += absoluteRate / 60.0 * 5.0 + result.put(midnight, tdd) + } + for (i in 0 until result.size()) { + val tdd = result.valueAt(i) + tdd.total = tdd.bolus + tdd.basal + } + log.debug(result.toString()) + return result + } + + fun averageTDD(tdds: LongSparseArray): TDD { + val totalTdd = TDD() + for (i in 0 until tdds.size()) { + val tdd = tdds.valueAt(i) + totalTdd.basal += tdd.basal + totalTdd.bolus += tdd.bolus + totalTdd.total += tdd.total + } + totalTdd.basal /= tdds.size().toDouble() + totalTdd.bolus /= tdds.size().toDouble() + totalTdd.total /= tdds.size().toDouble() + return totalTdd + } + + fun stats(): Spanned { + val tdds = calculate(7) + val averageTdd = averageTDD(tdds) + return HtmlHelper.fromHtml( + "" + MainApp.gs(R.string.tdd) + ":
" + + toText(tdds) + + "" + MainApp.gs(R.string.average) + ":
" + + averageTdd.toText(tdds.size()) + ) + } + + fun toText(tdds: LongSparseArray): String { + var t = "" + for (i in 0 until tdds.size()) { + t += "${tdds.valueAt(i).toText()}
" + } + return t + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TirCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/TirCalculator.kt new file mode 100644 index 0000000000..52d303ea39 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/TirCalculator.kt @@ -0,0 +1,83 @@ +package info.nightscout.androidaps.utils + +import android.text.Spanned +import android.util.LongSparseArray +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile + +object TirCalculator { + fun calculate(days: Long, lowMgdl: Double, highMgdl: Double): LongSparseArray { + if (lowMgdl < 39) throw RuntimeException("Low below 39") + if (lowMgdl > highMgdl) throw RuntimeException("Low > High") + val startTime = MidnightTime.calc(DateUtil.now()) - T.days(days).msecs() + val endTime = MidnightTime.calc(DateUtil.now()) + + val bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime(startTime, endTime, true) + val result = LongSparseArray() + for (bg in bgReadings) { + val midnight = MidnightTime.calc(bg.date) + var tir = result[midnight] + if (tir == null) { + tir = TIR(midnight, lowMgdl, highMgdl) + result.append(midnight, tir) + } + if (bg.value < 39) tir.error() + if (bg.value >= 39 && bg.value < lowMgdl) tir.below() + if (bg.value in lowMgdl..highMgdl) tir.inRange() + if (bg.value > highMgdl) tir.above() + } + return result + } + + fun averageTIR(tirs: LongSparseArray): TIR { + val totalTir = + if (tirs.size() > 0) TIR(tirs.valueAt(0).date, tirs.valueAt(0).lowThreshold, tirs.valueAt(0).highThreshold) + else TIR(7, 70.0, 180.0) + for (i in 0 until tirs.size()) { + val tir = tirs.valueAt(i) + totalTir.below += tir.below + totalTir.inRange += tir.inRange + totalTir.above += tir.above + totalTir.error += tir.error + totalTir.count += tir.count + } + return totalTir + } + + fun stats(): Spanned { + val lowTirMgdl = 3.9 * Constants.MMOLL_TO_MGDL + val highTirMgdl = 10.0 * Constants.MMOLL_TO_MGDL + val lowTitMgdl = 3.9 * Constants.MMOLL_TO_MGDL + val highTitMgdl = 7.8 * Constants.MMOLL_TO_MGDL + + val tir7 = calculate(7, lowTirMgdl, highTirMgdl) + val averageTir7 = averageTIR(tir7) + val tir30 = calculate(30, lowTirMgdl, highTirMgdl) + val averageTir30 = averageTIR(tir30) + val tit7 = calculate(7, lowTitMgdl, highTitMgdl) + val averageTit7 = averageTIR(tit7) + val tit30 = calculate(30, lowTitMgdl, highTitMgdl) + val averageTit30 = averageTIR(tit30) + return HtmlHelper.fromHtml( + "
" + MainApp.gs(R.string.tir) + ":
" + + toText(tir7) + + "
" + MainApp.gs(R.string.average) + " (" + Profile.toCurrentUnitsString(lowTirMgdl) + "-" + Profile.toCurrentUnitsString(highTirMgdl) + "):
" + + averageTir7.toText(tir7.size()) + "
" + + averageTir30.toText(tir30.size()) + + "
" + MainApp.gs(R.string.average) + " (" + Profile.toCurrentUnitsString(lowTitMgdl) + "-" + Profile.toCurrentUnitsString(highTitMgdl) + "):
" + + averageTit7.toText(tit7.size()) + "
" + + averageTit30.toText(tit30.size()) + ) + } + + fun toText(tirs: LongSparseArray): String { + var t = "" + for (i in 0 until tirs.size()) { + t += "${tirs.valueAt(i).toText()}
" + } + return t + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/stats_activity.xml b/app/src/main/res/layout/stats_activity.xml new file mode 100644 index 0000000000..83cf37918b --- /dev/null +++ b/app/src/main/res/layout/stats_activity.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + +