diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt index ff3e361a9b..86c90e1657 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt @@ -8,9 +8,9 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.data.defaultProfile.DefaultProfile import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.utils.ActivityMonitor import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.InstanceId @@ -62,16 +62,19 @@ class SurveyActivity : NoSplashAppCompatActivity() { ToastUtils.showToastInUiThread(this, R.string.invalidweight) return@setOnClickListener } - val profile = defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) - 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") + profileFunction.getProfile()?.let { runningProfile -> + val profile = defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) + ProfileViewerDialog().also { pvd -> + pvd.arguments = Bundle().also { + it.putLong("time", DateUtil.now()) + it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal) + it.putString("customProfile", runningProfile.data.toString()) + it.putString("customProfile2", profile.data.toString()) + it.putString("customProfileUnits", profile.units) + it.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight") + } + }.show(supportFragmentManager, "ProfileViewDialog") + } } survey_submit.setOnClickListener { diff --git a/core/src/main/java/info/nightscout/androidaps/data/Profile.java b/core/src/main/java/info/nightscout/androidaps/data/Profile.java index 7f583663bb..9fe8c7846e 100644 --- a/core/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/core/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -370,7 +370,7 @@ public class Profile { return lastValue; } - protected String format_HH_MM(Integer timeAsSeconds) { + public static String format_HH_MM(Integer timeAsSeconds) { String time; int hour = timeAsSeconds / 60 / 60; int minutes = (timeAsSeconds - hour * 60 * 60) / 60; diff --git a/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt b/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt index 56e48bd894..75708b76ae 100644 --- a/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt +++ b/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt @@ -1,6 +1,7 @@ package info.nightscout.androidaps.dialogs import android.os.Bundle +import android.text.Spanned import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -14,10 +15,12 @@ import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.DatabaseHelperInterface import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import kotlinx.android.synthetic.main.close.* import kotlinx.android.synthetic.main.dialog_profileviewer.* import org.json.JSONObject +import java.text.DecimalFormat import javax.inject.Inject class ProfileViewerDialog : DaggerDialogFragment() { @@ -32,11 +35,13 @@ class ProfileViewerDialog : DaggerDialogFragment() { enum class Mode(val i: Int) { RUNNING_PROFILE(1), CUSTOM_PROFILE(2), - DB_PROFILE(3) + DB_PROFILE(3), + PROFILE_COMPARE(4) } private var mode: Mode = Mode.RUNNING_PROFILE private var customProfileJson: String = "" + private var customProfileJson2: String = "" private var customProfileName: String = "" private var customProfileUnits: String = Constants.MGDL @@ -49,6 +54,8 @@ class ProfileViewerDialog : DaggerDialogFragment() { customProfileJson = bundle.getString("customProfile", "") customProfileUnits = bundle.getString("customProfileUnits", Constants.MGDL) customProfileName = bundle.getString("customProfileName", "") + if (mode == Mode.PROFILE_COMPARE) + customProfileJson2 = bundle.getString("customProfile2", "") } dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) @@ -65,11 +72,13 @@ class ProfileViewerDialog : DaggerDialogFragment() { close.setOnClickListener { dismiss() } val profile: Profile? + val profile2: Profile? val profileName: String? val date: String? when (mode) { Mode.RUNNING_PROFILE -> { profile = activePlugin.activeTreatments.getProfileSwitchFromHistory(time)?.profileObject + profile2 = null profileName = activePlugin.activeTreatments.getProfileSwitchFromHistory(time)?.customizedName date = dateUtil.dateAndTimeString(activePlugin.activeTreatments.getProfileSwitchFromHistory(time)?.date ?: 0) @@ -78,6 +87,15 @@ class ProfileViewerDialog : DaggerDialogFragment() { Mode.CUSTOM_PROFILE -> { profile = Profile(injector, JSONObject(customProfileJson), customProfileUnits) + profile2 = null + profileName = customProfileName + date = "" + profileview_datelayout.visibility = View.GONE + } + + Mode.PROFILE_COMPARE -> { + profile = Profile(injector, JSONObject(customProfileJson), customProfileUnits) + profile2 = Profile(injector, JSONObject(customProfileJson2), customProfileUnits) profileName = customProfileName date = "" profileview_datelayout.visibility = View.GONE @@ -86,6 +104,7 @@ class ProfileViewerDialog : DaggerDialogFragment() { Mode.DB_PROFILE -> { val profileList = databaseHelper.getProfileSwitchData(time, true) profile = if (profileList.isNotEmpty()) profileList[0].profileObject else null + profile2 = null profileName = if (profileList.isNotEmpty()) profileList[0].customizedName else null date = if (profileList.isNotEmpty()) dateUtil.dateAndTimeString(profileList[0].date) else null profileview_datelayout.visibility = View.VISIBLE @@ -93,20 +112,38 @@ class ProfileViewerDialog : DaggerDialogFragment() { } profileview_noprofile.visibility = View.VISIBLE - profile?.let { - profileview_units.text = it.units - profileview_dia.text = resourceHelper.gs(R.string.format_hours, it.dia) - profileview_activeprofile.text = profileName - profileview_date.text = date - profileview_ic.text = it.icList - profileview_isf.text = it.isfList - profileview_basal.text = it.basalList - profileview_target.text = it.targetList - basal_graph.show(it) + if (mode == Mode.PROFILE_COMPARE) + profile?.let { profile1 -> + profile2?.let { profile2 -> + profileview_units.text = profile1.units + profileview_dia.text = HtmlHelper.fromHtml(formatColors("", profile1.dia, profile1.dia, DecimalFormat("0.00"), resourceHelper.gs(R.string.shorthour))) + profileview_activeprofile.text = profileName + profileview_date.text = date + profileview_ic.text = ics(profile1, profile2) + profileview_isf.text = isfs(profile1, profile2) + profileview_basal.text = basals(profile1, profile2) + profileview_target.text = HtmlHelper.fromHtml(formatColors("", profile1.targetList + "
", profile2.targetList, "")) + basal_graph.show(profile1, profile2) + } - profileview_noprofile.visibility = View.GONE - profileview_invalidprofile.visibility = if (it.isValid("ProfileViewDialog")) View.GONE else View.VISIBLE - } + profileview_noprofile.visibility = View.GONE + profileview_invalidprofile.visibility = if (profile1.isValid("ProfileViewDialog")) View.GONE else View.VISIBLE + } + else + profile?.let { + profileview_units.text = it.units + profileview_dia.text = resourceHelper.gs(R.string.format_hours, it.dia) + profileview_activeprofile.text = profileName + profileview_date.text = date + profileview_ic.text = it.icList + profileview_isf.text = it.isfList + profileview_basal.text = it.basalList + profileview_target.text = it.targetList + basal_graph.show(it) + + profileview_noprofile.visibility = View.GONE + profileview_invalidprofile.visibility = if (it.isValid("ProfileViewDialog")) View.GONE else View.VISIBLE + } } override fun onStart() { @@ -121,6 +158,79 @@ class ProfileViewerDialog : DaggerDialogFragment() { bundle.putString("customProfile", customProfileJson) bundle.putString("customProfileName", customProfileName) bundle.putString("customProfileUnits", customProfileUnits) + if (mode == Mode.PROFILE_COMPARE) + bundle.putString("customProfile2", customProfileJson2) } + private fun formatColors(label: String, value1: Double, value2: Double, format: DecimalFormat, units: String): String { + return formatColors(label, format.format(value1), format.format(value2), units) + } + + private fun formatColors(label: String, text1: String, text2: String, units: String): String { + var s = "$label" + s += " " + s += "$text1" + s += " " + s += "$text2" + s += " " + s += "$units" + return s + } + + private fun basals(profile1: Profile, profile2: Profile): Spanned { + var prev1 = 0.0 + var prev2 = 0.0 + val s = StringBuilder() + for (hour in 0..23) { + val val1 = profile1.getBasalTimeFromMidnight(hour * 60 * 60) + val val2 = profile2.getBasalTimeFromMidnight(hour * 60 * 60) + if (val1 != prev1 || val2 != prev2) { + s.append(formatColors(Profile.format_HH_MM(hour * 60 * 60), val1, val2, DecimalFormat("0.00"), resourceHelper.gs(R.string.profile_ins_units_per_hour))) + s.append("
") + } + prev1 = val1 + prev2 = val2 + } + s.append(formatColors( + " ∑ ", + profile1.baseBasalSum(), + profile2.baseBasalSum(), + DecimalFormat("0.00"), + resourceHelper.gs(R.string.insulin_unit_shortname))) + return HtmlHelper.fromHtml(s.toString()) + } + + private fun ics(profile1: Profile, profile2: Profile): Spanned { + var prev1 = 0.0 + var prev2 = 0.0 + val s = StringBuilder() + for (hour in 0..23) { + val val1 = profile1.getIcTimeFromMidnight(hour * 60 * 60) + val val2 = profile2.getIcTimeFromMidnight(hour * 60 * 60) + if (val1 != prev1 || val2 != prev2) { + s.append(formatColors(Profile.format_HH_MM(hour * 60 * 60), val1, val2, DecimalFormat("0.0"), resourceHelper.gs(R.string.profile_carbs_per_unit))) + s.append("
") + } + prev1 = val1 + prev2 = val2 + } + return HtmlHelper.fromHtml(s.toString()) + } + + private fun isfs(profile1: Profile, profile2: Profile): Spanned { + var prev1 = 0.0 + var prev2 = 0.0 + val s = StringBuilder() + for (hour in 0..23) { + val val1 = Profile.fromMgdlToUnits(profile1.getIsfMgdlTimeFromMidnight(hour * 60 * 60), profile1.units) + val val2 = Profile.fromMgdlToUnits(profile2.getIsfMgdlTimeFromMidnight(hour * 60 * 60), profile1.units) + if (val1 != prev1 || val2 != prev2) { + s.append(formatColors(Profile.format_HH_MM(hour * 60 * 60), val1, val2, DecimalFormat("0.0"), resourceHelper.gs(R.string.profile_carbs_per_unit))) + s.append("
") + } + prev1 = val1 + prev2 = val2 + } + return HtmlHelper.fromHtml(s.toString()) + } } diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileGraph.kt b/core/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileGraph.kt index a48f5e4118..8dd922ca60 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileGraph.kt +++ b/core/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileGraph.kt @@ -1,13 +1,16 @@ package info.nightscout.androidaps.plugins.treatments.fragments import android.content.Context +import android.graphics.Color import android.util.AttributeSet import com.jjoe64.graphview.GraphView import com.jjoe64.graphview.series.DataPoint import com.jjoe64.graphview.series.LineGraphSeries +import info.nightscout.androidaps.core.R import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.utils.Round import java.util.* +import kotlin.math.max class ProfileGraph : GraphView { @@ -21,7 +24,7 @@ class ProfileGraph : GraphView { basalArray.add(DataPoint(hour.toDouble(), profile.getBasalTimeFromMidnight(hour * 60 * 60))) basalArray.add(DataPoint((hour + 1).toDouble(), profile.getBasalTimeFromMidnight(hour * 60 * 60))) } - val basalDataPoints: Array = Array(basalArray.size){ i-> basalArray[i]} + val basalDataPoints: Array = Array(basalArray.size) { i -> basalArray[i] } val basalSeries: LineGraphSeries = LineGraphSeries(basalDataPoints) addSeries(basalSeries) basalSeries.thickness = 8 @@ -35,4 +38,40 @@ class ProfileGraph : GraphView { gridLabelRenderer.numHorizontalLabels = 13 gridLabelRenderer.verticalLabelsColor = basalSeries.color } + + fun show(profile1: Profile, profile2: Profile) { + removeAllSeries() + + // profile 1 + val basalArray1: MutableList = ArrayList() + for (hour in 0..23) { + basalArray1.add(DataPoint(hour.toDouble(), profile1.getBasalTimeFromMidnight(hour * 60 * 60))) + basalArray1.add(DataPoint((hour + 1).toDouble(), profile1.getBasalTimeFromMidnight(hour * 60 * 60))) + } + val basalSeries1: LineGraphSeries = LineGraphSeries(Array(basalArray1.size) { i -> basalArray1[i] }) + addSeries(basalSeries1) + basalSeries1.thickness = 8 + basalSeries1.isDrawBackground = true + + // profile 2 + val basalArray2: MutableList = ArrayList() + for (hour in 0..23) { + basalArray2.add(DataPoint(hour.toDouble(), profile2.getBasalTimeFromMidnight(hour * 60 * 60))) + basalArray2.add(DataPoint((hour + 1).toDouble(), profile2.getBasalTimeFromMidnight(hour * 60 * 60))) + } + val basalSeries2: LineGraphSeries = LineGraphSeries(Array(basalArray2.size) { i -> basalArray2[i] }) + addSeries(basalSeries2) + basalSeries2.thickness = 8 + basalSeries2.isDrawBackground = false + basalSeries2.color = context.getColor(R.color.examinedProfile) + basalSeries2.backgroundColor = context.getColor(R.color.examinedProfile) + + viewport.isXAxisBoundsManual = true + viewport.setMinX(0.0) + viewport.setMaxX(24.0) + viewport.isYAxisBoundsManual = true + viewport.setMinY(0.0) + viewport.setMaxY(Round.ceilTo(max(profile1.maxDailyBasal, profile2.maxDailyBasal) * 1.1, 0.5)) + gridLabelRenderer.numHorizontalLabels = 13 + } } \ No newline at end of file diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml index 27e0e33010..d6def91fd7 100644 --- a/core/src/main/res/values/colors.xml +++ b/core/src/main/res/values/colors.xml @@ -23,6 +23,7 @@ #FF000000 #FFFF5555 #FF000000 + #FFFF5555 #C803A9F4