From c5ae87d344d688aab9c088f565c5f6a70cd009ff Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 4 Nov 2022 14:34:05 +0100 Subject: [PATCH] migrate out of app module --- app/src/main/AndroidManifest.xml | 10 +- .../nightscout/androidaps/MainActivity.kt | 22 ++- .../info/nightscout/androidaps/MainApp.kt | 7 +- .../activities/ProfileHelperActivity.kt | 16 +- .../androidaps/di/ActivitiesModule.kt | 8 +- .../nightscout/androidaps/di/AppModule.kt | 32 +++- .../DetermineBasalAdapterSMBDynamicISFJS.kt | 2 +- .../androidaps/plugins/source/DexcomPlugin.kt | 2 +- .../RequestDexcomPermissionActivity.kt | 5 +- app/src/main/res/values/strings.xml | 28 --- .../treatments/TreatmentsPluginTest.kt | 79 -------- .../androidaps/interfaces/stats/DexcomTIR.kt | 17 ++ .../interfaces/stats/DexcomTirCalculator.kt | 10 + .../androidaps/interfaces/stats/TIR.kt | 25 +++ .../interfaces/stats/TddCalculator.kt | 16 ++ .../interfaces/stats/TirCalculator.kt | 11 ++ core/src/main/res/values/strings.xml | 3 + .../stats/DexcomTirCalculatorImpl.kt | 15 +- .../implementation/stats/DexcomTirImpl.kt | 21 ++- .../implementation/stats/TddCalculatorImpl.kt | 24 +-- .../implementation/stats/TirCalculatorImpl.kt | 24 +-- .../implementation/stats/TirImpl.kt | 44 +++-- .../src/main/res/values/strings.xml | 14 ++ insight/src/main/res/values/strings.xml | 1 - ui/src/main/AndroidManifest.xml | 8 + .../ui}/activities/StatsActivity.kt | 19 +- .../ui}/activities/SurveyActivity.kt | 29 ++- .../ui}/defaultProfile/DefaultProfile.kt | 17 +- .../ui}/defaultProfile/DefaultProfileDPV.kt | 16 +- .../java/info/nightscout/ui/di/UiModule.kt | 4 + .../nightscout/ui}/utils/ActivityMonitor.kt | 8 +- .../src/main/res/layout/activity_stats.xml | 2 +- .../src/main/res/layout/activity_survey.xml | 4 +- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../main/res/mipmap-hdpi/ic_pumpcontrol.png | Bin .../src/main/res/mipmap-hdpi/ic_yellowowl.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../main/res/mipmap-mdpi/ic_pumpcontrol.png | Bin .../src/main/res/mipmap-mdpi/ic_yellowowl.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xhdpi/ic_pumpcontrol.png | Bin .../main/res/mipmap-xhdpi/ic_yellowowl.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_pumpcontrol.png | Bin .../main/res/mipmap-xxhdpi/ic_yellowowl.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../res/mipmap-xxxhdpi/ic_pumpcontrol.png | Bin .../main/res/mipmap-xxxhdpi/ic_yellowowl.png | Bin ui/src/main/res/values/strings.xml | 17 ++ .../info/nightscout/androidaps/TestBase.kt | 39 ++++ .../androidaps/TestBaseWithProfile.kt | 177 ++++++++++++++++++ .../nightscout/androidaps/TestPumpPlugin.kt | 68 +++++++ .../ui}/defaultProfile/DefaultProfileTest.kt | 4 +- 58 files changed, 594 insertions(+), 254 deletions(-) rename app/src/main/java/info/nightscout/androidaps/{ => plugins/source}/activities/RequestDexcomPermissionActivity.kt (82%) delete mode 100644 app/src/test/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPluginTest.kt create mode 100644 core/src/main/java/info/nightscout/androidaps/interfaces/stats/DexcomTIR.kt create mode 100644 core/src/main/java/info/nightscout/androidaps/interfaces/stats/DexcomTirCalculator.kt create mode 100644 core/src/main/java/info/nightscout/androidaps/interfaces/stats/TIR.kt create mode 100644 core/src/main/java/info/nightscout/androidaps/interfaces/stats/TddCalculator.kt create mode 100644 core/src/main/java/info/nightscout/androidaps/interfaces/stats/TirCalculator.kt rename app/src/main/java/info/nightscout/androidaps/utils/stats/DexcomTirCalculator.kt => implementation/src/main/java/info/nightscout/implementation/stats/DexcomTirCalculatorImpl.kt (80%) rename app/src/main/java/info/nightscout/androidaps/utils/stats/DexcomTIR.kt => implementation/src/main/java/info/nightscout/implementation/stats/DexcomTirImpl.kt (92%) rename app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt => implementation/src/main/java/info/nightscout/implementation/stats/TddCalculatorImpl.kt (90%) rename app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt => implementation/src/main/java/info/nightscout/implementation/stats/TirCalculatorImpl.kt (88%) rename app/src/main/java/info/nightscout/androidaps/utils/stats/TIR.kt => implementation/src/main/java/info/nightscout/implementation/stats/TirImpl.kt (82%) rename {app/src/main/java/info/nightscout/androidaps => ui/src/main/java/info/nightscout/ui}/activities/StatsActivity.kt (85%) rename {app/src/main/java/info/nightscout/androidaps => ui/src/main/java/info/nightscout/ui}/activities/SurveyActivity.kt (82%) rename {app/src/main/java/info/nightscout/androidaps/utils => ui/src/main/java/info/nightscout/ui}/defaultProfile/DefaultProfile.kt (97%) rename {app/src/main/java/info/nightscout/androidaps/utils => ui/src/main/java/info/nightscout/ui}/defaultProfile/DefaultProfileDPV.kt (78%) rename {app/src/main/java/info/nightscout/androidaps => ui/src/main/java/info/nightscout/ui}/utils/ActivityMonitor.kt (96%) rename {app => ui}/src/main/res/layout/activity_stats.xml (99%) rename {app => ui}/src/main/res/layout/activity_survey.xml (98%) rename {app => ui}/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {app => ui}/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename {app => ui}/src/main/res/mipmap-hdpi/ic_pumpcontrol.png (100%) rename {app => ui}/src/main/res/mipmap-hdpi/ic_yellowowl.png (100%) rename {app => ui}/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {app => ui}/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename {app => ui}/src/main/res/mipmap-mdpi/ic_pumpcontrol.png (100%) rename {app => ui}/src/main/res/mipmap-mdpi/ic_yellowowl.png (100%) rename {app => ui}/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {app => ui}/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename {app => ui}/src/main/res/mipmap-xhdpi/ic_pumpcontrol.png (100%) rename {app => ui}/src/main/res/mipmap-xhdpi/ic_yellowowl.png (100%) rename {app => ui}/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {app => ui}/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename {app => ui}/src/main/res/mipmap-xxhdpi/ic_pumpcontrol.png (100%) rename {app => ui}/src/main/res/mipmap-xxhdpi/ic_yellowowl.png (100%) rename {app => ui}/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {app => ui}/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename {app => ui}/src/main/res/mipmap-xxxhdpi/ic_pumpcontrol.png (100%) rename {app => ui}/src/main/res/mipmap-xxxhdpi/ic_yellowowl.png (100%) create mode 100644 ui/src/test/java/info/nightscout/androidaps/TestBase.kt create mode 100644 ui/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt create mode 100644 ui/src/test/java/info/nightscout/androidaps/TestPumpPlugin.kt rename {app/src/test/java/info/nightscout/androidaps/utils => ui/src/test/java/info/nightscout/ui}/defaultProfile/DefaultProfileTest.kt (95%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cd227043a8..df917c1f79 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -90,18 +90,10 @@ android:name=".activities.TreatmentsActivity" android:exported="false" android:theme="@style/AppTheme" /> - - 18) { - ToastUtils.warnToast(this, R.string.invalidage) + ToastUtils.warnToast(this, R.string.invalid_age) return@setOnClickListener } if ((weightUsed[i] < 5 || weightUsed[i] > 150) && tddUsed[i] == 0.0) { - ToastUtils.warnToast(this, R.string.invalidweight) + ToastUtils.warnToast(this, R.string.invalid_weight) return@setOnClickListener } if ((tddUsed[i] < 5 || tddUsed[i] > 150) && weightUsed[i] == 0.0) { - ToastUtils.warnToast(this, R.string.invalidweight) + ToastUtils.warnToast(this, R.string.invalid_weight) return@setOnClickListener } } if (typeSelected[i] == ProfileType.DPV_DEFAULT) { if (ageUsed[i] < 1 || ageUsed[i] > 18) { - ToastUtils.warnToast(this, R.string.invalidage) + ToastUtils.warnToast(this, R.string.invalid_age) return@setOnClickListener } if (tddUsed[i] < 5 || tddUsed[i] > 150) { - ToastUtils.warnToast(this, R.string.invalidweight) + ToastUtils.warnToast(this, R.string.invalid_weight) return@setOnClickListener } if ((pctUsed[i] < 32 || pctUsed[i] > 37)) { diff --git a/app/src/main/java/info/nightscout/androidaps/di/ActivitiesModule.kt b/app/src/main/java/info/nightscout/androidaps/di/ActivitiesModule.kt index aca7453ac2..bb39265155 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/ActivitiesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/ActivitiesModule.kt @@ -3,10 +3,14 @@ package info.nightscout.androidaps.di import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.MainActivity -import info.nightscout.androidaps.activities.* import info.nightscout.androidaps.activities.HistoryBrowseActivity +import info.nightscout.androidaps.activities.PreferencesActivity +import info.nightscout.androidaps.activities.ProfileHelperActivity +import info.nightscout.androidaps.activities.SingleFragmentActivity +import info.nightscout.androidaps.activities.TreatmentsActivity import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity +import info.nightscout.androidaps.plugins.source.activities.RequestDexcomPermissionActivity import info.nightscout.androidaps.setupwizard.SetupWizardActivity @Module @@ -22,8 +26,6 @@ abstract class ActivitiesModule { @ContributesAndroidInjector abstract fun contributesRequestDexcomPermissionActivity(): RequestDexcomPermissionActivity @ContributesAndroidInjector abstract fun contributesSetupWizardActivity(): SetupWizardActivity @ContributesAndroidInjector abstract fun contributesSingleFragmentActivity(): SingleFragmentActivity - @ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity - @ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity @ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/di/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/di/AppModule.kt index affec2109b..58d1463f64 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/AppModule.kt @@ -34,6 +34,9 @@ import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.interfaces.SmsCommunicator import info.nightscout.androidaps.interfaces.TrendCalculator import info.nightscout.androidaps.interfaces.XDripBroadcast +import info.nightscout.androidaps.interfaces.stats.DexcomTirCalculator +import info.nightscout.androidaps.interfaces.stats.TddCalculator +import info.nightscout.androidaps.interfaces.stats.TirCalculator import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin @@ -65,24 +68,31 @@ import info.nightscout.implementation.TrendCalculatorImpl import info.nightscout.implementation.XDripBroadcastImpl import info.nightscout.implementation.constraints.ConstraintsImpl import info.nightscout.implementation.queue.CommandQueueImplementation +import info.nightscout.implementation.stats.DexcomTirCalculatorImpl +import info.nightscout.implementation.stats.TddCalculatorImpl +import info.nightscout.implementation.stats.TirCalculatorImpl import info.nightscout.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.sharedPreferences.SP import javax.inject.Singleton @Suppress("unused") -@Module(includes = [ - AppModule.AppBindings::class -]) +@Module( + includes = [ + AppModule.AppBindings::class + ] +) open class AppModule { @Provides - fun providesPlugins(config: Config, buildHelper: BuildHelper, - @PluginsModule.AllConfigs allConfigs: Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>, - @PluginsModule.PumpDriver pumpDrivers: Lazy>, - @PluginsModule.NotNSClient notNsClient: Lazy>, - @PluginsModule.APS aps: Lazy>, - @PluginsModule.Unfinished unfinished: Lazy>) + fun providesPlugins( + config: Config, buildHelper: BuildHelper, + @PluginsModule.AllConfigs allConfigs: Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>, + @PluginsModule.PumpDriver pumpDrivers: Lazy>, + @PluginsModule.NotNSClient notNsClient: Lazy>, + @PluginsModule.APS aps: Lazy>, + @PluginsModule.Unfinished unfinished: Lazy> + ) : List<@JvmSuppressWildcards PluginBase> { val plugins = allConfigs.toMutableMap() if (config.PUMPDRIVERS) plugins += pumpDrivers.get() @@ -120,6 +130,7 @@ open class AppModule { @Provides @Singleton internal fun provideConstraints(activePlugin: ActivePlugin): Constraints = ConstraintsImpl(activePlugin) + @Module interface AppBindings { @@ -146,6 +157,9 @@ open class AppModule { @Binds fun bindLocalAlertUtilsInterface(localAlertUtils: LocalAlertUtilsImpl): LocalAlertUtils @Binds fun bindActivityNamesInterface(activityNames: ActivityNamesImpl): ActivityNames @Binds fun bindTrendCalculatorInterface(trendCalculator: TrendCalculatorImpl): TrendCalculator + @Binds fun bindTddCalculatorInterface(tddCalculator: TddCalculatorImpl): TddCalculator + @Binds fun bindTirCalculatorInterface(tirCalculator: TirCalculatorImpl): TirCalculator + @Binds fun bindDexcomTirCalculatorInterface(dexcomTirCalculator: DexcomTirCalculatorImpl): DexcomTirCalculator } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt index 6f62428a7e..505ff725b8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt @@ -23,7 +23,7 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.Round -import info.nightscout.androidaps.utils.stats.TddCalculator +import info.nightscout.androidaps.interfaces.stats.TddCalculator import info.nightscout.shared.SafeParse import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt index b48dc49fd4..c170c0a7d6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt @@ -9,7 +9,7 @@ import androidx.work.WorkerParameters import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.activities.RequestDexcomPermissionActivity +import info.nightscout.androidaps.plugins.source.activities.RequestDexcomPermissionActivity import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.TherapyEvent diff --git a/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/activities/RequestDexcomPermissionActivity.kt similarity index 82% rename from app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/source/activities/RequestDexcomPermissionActivity.kt index fd01500652..8196dd8675 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/activities/RequestDexcomPermissionActivity.kt @@ -1,11 +1,10 @@ -package info.nightscout.androidaps.activities +package info.nightscout.androidaps.plugins.source.activities import android.os.Bundle +import info.nightscout.androidaps.activities.DialogAppCompatActivity import info.nightscout.androidaps.plugins.source.DexcomPlugin -import javax.inject.Inject class RequestDexcomPermissionActivity : DialogAppCompatActivity() { - @Inject lateinit var dexcomPlugin: DexcomPlugin private val requestCode = "AndroidAPS <3".map { it.code }.sum() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 14422b30e3..5f9e52ef75 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -253,7 +253,6 @@ Shorten tab titles Always use short average delta instead of simple delta Useful when data from unfiltered sources like xDrip+ gets noisy. - Profile Default value: 3 This is a key OpenAPS safety cap. What this does is limit your basals to be 3x (in this people) your biggest basal rate. You likely will not need to change this, but you should be aware that’s what is discussed about “3x max daily; 4x current” for safety caps. Default value: 4 This is the other half of the key OpenAPS safety caps, and the other half of “3x max daily; 4x current” of the safety caps. This means your basal, regardless of max basal set on your pump, cannot be any higher than this number times the current level of your basal. This is to prevent people from getting into dangerous territory by setting excessively high max basals before understanding how the algorithm works. Again, the default is 4x; most people will never need to adjust this and are instead more likely to need to adjust other settings if they feel like they are “running into” this safety cap. Default value: 1.2\nThis is a multiplier cap for autosens (and soon autotune) to set a 20%% max limit on how high the autosens ratio can be, which in turn determines how high autosens can adjust basals, how low it can adjust ISF, and how low it can set the BG target. @@ -589,7 +588,6 @@ SMB not allowed in open loop mode Food IobCobCalculator - reset openapsmb_max_iob Maximum total IOB OpenAPS can\'t go over [U] This value is called Max IOB in OpenAPS context\nOpenAPS will not add more insulin if current IOB is greater than this value @@ -802,25 +800,7 @@ Profile name contains dots.\nThis is not supported by NS.\nProfile is not uploaded to NS. Lower value of in range area (display only) Higher value of in range area (display only) - Age - Weight - ID: - Submit - Most common profile: - Note: Only data visible on this screen will be anonymously uploaded. ID is assigned to this installation of AAPS. You can submit data again if your main profile get changed but let it running at least for a week to make result visible in time in range. Your help is appreciated. - Invalid age entry - Invalid weight entry Invalid % entry - Average - TIR - Day TIR - Night TIR - Detailed 14 days - SD: %1$s - HbA1c: - Activity monitor - Do you want to reset activity stats? - Statistics Random BG Generate random BG data (Demo mode only) BG @@ -927,7 +907,6 @@ ns_receive_cgm Receive/backfill CGM data Accept CGM data from NS - Calculation in progress Missing profile name Error in IC values Error in basal values @@ -1023,13 +1002,6 @@ Show loop %1$d selected Sort - Very low - Low - High - Very high - Below - In range - Above Show loop records Hide loop records Loop status diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPluginTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPluginTest.kt deleted file mode 100644 index 3f0583fbc4..0000000000 --- a/app/src/test/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPluginTest.kt +++ /dev/null @@ -1,79 +0,0 @@ -package info.nightscout.androidaps.plugins.treatments - -import dagger.android.AndroidInjector -import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.TestBaseWithProfile -import info.nightscout.androidaps.database.AppRepository -import info.nightscout.shared.sharedPreferences.SP -import org.junit.Test -import org.mockito.Mock - -@Suppress("SpellCheckingInspection") -class TreatmentsPluginTest : TestBaseWithProfile() { - - @Mock lateinit var sp: SP - @Mock lateinit var repository: AppRepository - - val injector = HasAndroidInjector { - AndroidInjector { - } - } - - @Test fun dummy() {} -/* - private lateinit var insulinOrefRapidActingPlugin: InsulinOrefRapidActingPlugin - private lateinit var sot: TreatmentsPlugin - - @Before - fun prepare() { - insulinOrefRapidActingPlugin = InsulinOrefRapidActingPlugin(profileInjector, rh, profileFunction, rxBus, aapsLogger) - - `when`(profileFunction.getProfile(ArgumentMatchers.anyLong())).thenReturn(validProfile) - `when`(activePluginProvider.activeInsulin).thenReturn(insulinOrefRapidActingPlugin) - - sot = TreatmentsPlugin(profileInjector, aapsLogger, rxBus, aapsSchedulers, rh, context, sp, profileFunction, activePluginProvider, nsUpload, fabricPrivacy, dateUtil, databaseHelper, repository) - sot.service = treatmentService - } - - @Test - fun `zero TBR should produce zero absolute insulin`() { - val now = dateUtil._now() - val tbrs: MutableList = ArrayList() - tbrs.add(TemporaryBasal(injector).date(now - T.hours(30).msecs()).duration(10000).percent(0)) - - `when`(databaseHelper.getTemporaryBasalsDataFromTime(ArgumentMatchers.anyLong(), ArgumentMatchers.anyBoolean())).thenReturn(tbrs) - sot.initializeData(T.hours(30).msecs()) - val iob = sot.getAbsoluteIOBTempBasals(now) - Assert.assertEquals(0.0, iob.iob, 0.0) - } - - @Test - fun `90pct TBR and should produce less absolute insulin`() { - val now = dateUtil._now() - val tbrs: MutableList = ArrayList() - `when`(databaseHelper.getTemporaryBasalsDataFromTime(ArgumentMatchers.anyLong(), ArgumentMatchers.anyBoolean())).thenReturn(tbrs) - sot.initializeData(T.hours(30).msecs()) - val iob100pct = sot.getAbsoluteIOBTempBasals(now) - - tbrs.add(TemporaryBasal(injector).date(now - T.hours(30).msecs()).duration(10000).percent(90)) - sot.initializeData(T.hours(30).msecs()) - val iob90pct = sot.getAbsoluteIOBTempBasals(now) - Assert.assertTrue(iob100pct.basaliob > iob90pct.basaliob) - } - - @Test - fun `110pct TBR and should produce 10pct more absolute insulin`() { - val now = dateUtil._now() - val tbrs: MutableList = ArrayList() - `when`(databaseHelper.getTemporaryBasalsDataFromTime(ArgumentMatchers.anyLong(), ArgumentMatchers.anyBoolean())).thenReturn(tbrs) - sot.initializeData(T.hours(30).msecs()) - val iob100pct = sot.getAbsoluteIOBTempBasals(now) - - tbrs.add(TemporaryBasal(injector).date(now - T.hours(30).msecs()).duration(10000).percent(110)) - sot.initializeData(T.hours(30).msecs()) - val iob110pct = sot.getAbsoluteIOBTempBasals(now) - Assert.assertEquals(1.1, iob110pct.basaliob / iob100pct.basaliob, 0.0001) - } - - */ -} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/stats/DexcomTIR.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/DexcomTIR.kt new file mode 100644 index 0000000000..925b728ca2 --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/DexcomTIR.kt @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.interfaces.stats + +import android.content.Context +import android.widget.TableRow +import android.widget.TextView +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.ResourceHelper + +interface DexcomTIR { + + fun calculateSD(): Double + fun toHbA1cView(context: Context, rh: ResourceHelper): TextView + fun toSDView(context: Context, rh: ResourceHelper, profileFunction: ProfileFunction): TextView + fun toRangeHeaderView(context: Context, rh: ResourceHelper, profileFunction: ProfileFunction): TextView + fun toTableRowHeader(context: Context, rh: ResourceHelper): TableRow + fun toTableRow(context: Context, rh: ResourceHelper): TableRow +} diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/stats/DexcomTirCalculator.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/DexcomTirCalculator.kt new file mode 100644 index 0000000000..bf203cb48a --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/DexcomTirCalculator.kt @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.interfaces.stats + +import android.content.Context +import android.widget.TableLayout + +interface DexcomTirCalculator { + + fun calculate(): DexcomTIR + fun stats(context: Context): TableLayout +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/stats/TIR.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/TIR.kt new file mode 100644 index 0000000000..ba7b6a5a13 --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/TIR.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.interfaces.stats + +import android.content.Context +import android.widget.TableRow +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.utils.DateUtil + +interface TIR { + + val date: Long + val lowThreshold: Double + val highThreshold: Double + var below: Int + var inRange: Int + var above: Int + var error: Int + var count: Int + fun error() + fun below() + fun inRange() + fun above() + + fun toTableRow(context: Context, rh: ResourceHelper, dateUtil: DateUtil): TableRow + fun toTableRow(context: Context, rh: ResourceHelper, days: Int): TableRow +} diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/stats/TddCalculator.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/TddCalculator.kt new file mode 100644 index 0000000000..f8da1e0c74 --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/TddCalculator.kt @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.interfaces.stats + +import android.content.Context +import android.util.LongSparseArray +import android.widget.TableLayout +import info.nightscout.androidaps.database.entities.TotalDailyDose + +interface TddCalculator { + + fun calculate(days: Long): LongSparseArray + fun calculateToday(): TotalDailyDose + fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose + fun calculate(startTime: Long, endTime: Long): TotalDailyDose + fun averageTDD(tdds: LongSparseArray): TotalDailyDose? + fun stats(context: Context): TableLayout +} diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/stats/TirCalculator.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/TirCalculator.kt new file mode 100644 index 0000000000..3a3d1cc6e3 --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/stats/TirCalculator.kt @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.interfaces.stats + +import android.content.Context +import android.util.LongSparseArray +import android.widget.TableLayout + +interface TirCalculator { + + fun calculate(days: Long, lowMgdl: Double, highMgdl: Double): LongSparseArray + fun stats(context: Context): TableLayout +} \ No newline at end of file diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index e3dffa9a06..f6e472ba88 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -225,6 +225,9 @@ high in range low + Average + TIR + TDD Total Limiting max basal rate to %1$.2f U/h because of %2$s diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/DexcomTirCalculator.kt b/implementation/src/main/java/info/nightscout/implementation/stats/DexcomTirCalculatorImpl.kt similarity index 80% rename from app/src/main/java/info/nightscout/androidaps/utils/stats/DexcomTirCalculator.kt rename to implementation/src/main/java/info/nightscout/implementation/stats/DexcomTirCalculatorImpl.kt index 64508f1762..1fd2c58bc1 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/DexcomTirCalculator.kt +++ b/implementation/src/main/java/info/nightscout/implementation/stats/DexcomTirCalculatorImpl.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.utils.stats +package info.nightscout.implementation.stats import android.annotation.SuppressLint import android.content.Context @@ -7,6 +7,8 @@ import android.widget.TableLayout import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.interfaces.stats.DexcomTIR +import info.nightscout.androidaps.interfaces.stats.DexcomTirCalculator import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.MidnightTime import info.nightscout.androidaps.utils.T @@ -14,26 +16,27 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class DexcomTirCalculator @Inject constructor( +class DexcomTirCalculatorImpl @Inject constructor( private val rh: ResourceHelper, private val profileFunction: ProfileFunction, private val dateUtil: DateUtil, private val repository: AppRepository -) { +) : DexcomTirCalculator { + val days = 14L - fun calculate(): DexcomTIR { + override fun calculate(): DexcomTIR { val startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs()) val endTime = MidnightTime.calc(dateUtil.now()) val bgReadings = repository.compatGetBgReadingsDataFromTime(startTime, endTime, true).blockingGet() - val result = DexcomTIR() + val result = DexcomTirImpl() for (bg in bgReadings) result.add(bg.timestamp, bg.value) return result } @SuppressLint("SetTextI18n") - fun stats(context: Context): TableLayout = + override fun stats(context: Context): TableLayout = TableLayout(context).also { layout -> val tir = calculate() layout.layoutParams = TableLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/DexcomTIR.kt b/implementation/src/main/java/info/nightscout/implementation/stats/DexcomTirImpl.kt similarity index 92% rename from app/src/main/java/info/nightscout/androidaps/utils/stats/DexcomTIR.kt rename to implementation/src/main/java/info/nightscout/implementation/stats/DexcomTirImpl.kt index c0f7acf1cf..653745cb33 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/DexcomTIR.kt +++ b/implementation/src/main/java/info/nightscout/implementation/stats/DexcomTirImpl.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.utils.stats +package info.nightscout.implementation.stats import android.annotation.SuppressLint import android.content.Context @@ -7,16 +7,17 @@ import android.view.Gravity import android.widget.TableRow import android.widget.TextView import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.interfaces.stats.DexcomTIR +import info.nightscout.implementation.R import java.util.* import kotlin.math.pow import kotlin.math.roundToInt import kotlin.math.sqrt -class DexcomTIR { +class DexcomTirImpl : DexcomTIR { private var veryLow = 0 private var low = 0 @@ -26,7 +27,7 @@ class DexcomTIR { private var error = 0 private var count = 0 - var sum = 0.0 + private var sum = 0.0 val values = mutableListOf() private val veryLowTirMgdl = Constants.STATS_RANGE_VERY_LOW_MMOL * Constants.MMOLL_TO_MGDL @@ -65,14 +66,14 @@ class DexcomTIR { private fun veryHighPct() = if (count > 0) veryHigh.toDouble() / count * 100.0 else 0.0 private fun mean() = sum / count - fun calculateSD(): Double { + override fun calculateSD(): Double { if (count == 0) return 0.0 var standardDeviation = 0.0 for (num in values) standardDeviation += (num - mean()).pow(2.0) return sqrt(standardDeviation / count) } - fun toHbA1cView(context: Context, rh: ResourceHelper): TextView = + override fun toHbA1cView(context: Context, rh: ResourceHelper): TextView = TextView(context).apply { text = if (count == 0) "" @@ -86,7 +87,7 @@ class DexcomTIR { } @SuppressLint("SetTextI18n") - fun toSDView(context: Context, rh: ResourceHelper, profileFunction: ProfileFunction): TextView = + override fun toSDView(context: Context, rh: ResourceHelper, profileFunction: ProfileFunction): TextView = TextView(context).apply { val sd = calculateSD() text = "\n" + rh.gs(R.string.std_deviation, Profile.toUnitsString(sd, sd * Constants.MGDL_TO_MMOLL, profileFunction.getUnits())) @@ -94,7 +95,7 @@ class DexcomTIR { gravity = Gravity.CENTER_HORIZONTAL } - fun toRangeHeaderView(context: Context, rh: ResourceHelper, profileFunction: ProfileFunction): TextView = + override fun toRangeHeaderView(context: Context, rh: ResourceHelper, profileFunction: ProfileFunction): TextView = TextView(context).apply { text = StringBuilder() .append(rh.gs(R.string.detailed_14_days)) @@ -129,7 +130,7 @@ class DexcomTIR { setTextAppearance(android.R.style.TextAppearance_Material_Medium) } - fun toTableRowHeader(context: Context, rh: ResourceHelper): TableRow = + override fun toTableRowHeader(context: Context, rh: ResourceHelper): TableRow = TableRow(context).also { header -> val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT) header.layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT) @@ -142,7 +143,7 @@ class DexcomTIR { } @SuppressLint("SetTextI18n") - fun toTableRow(context: Context, rh: ResourceHelper): TableRow = + override fun toTableRow(context: Context, rh: ResourceHelper): TableRow = TableRow(context).also { row -> val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT, 1f) row.layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt b/implementation/src/main/java/info/nightscout/implementation/stats/TddCalculatorImpl.kt similarity index 90% rename from app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt rename to implementation/src/main/java/info/nightscout/implementation/stats/TddCalculatorImpl.kt index 009671e58a..0c0c621a75 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt +++ b/implementation/src/main/java/info/nightscout/implementation/stats/TddCalculatorImpl.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.utils.stats +package info.nightscout.implementation.stats import android.content.Context import android.graphics.Typeface @@ -8,27 +8,29 @@ import android.view.ViewGroup import android.widget.TableLayout import android.widget.TableRow import android.widget.TextView -import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.TotalDailyDose -import info.nightscout.androidaps.extensions.convertedToAbsolute import info.nightscout.androidaps.extensions.toTableRow import info.nightscout.androidaps.extensions.toTableRowHeader import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.interfaces.stats.TddCalculator import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.MidnightTime import info.nightscout.androidaps.utils.T +import info.nightscout.implementation.R import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import javax.inject.Inject +import javax.inject.Singleton -class TddCalculator @Inject constructor( +@Singleton +class TddCalculatorImpl @Inject constructor( private val aapsLogger: AAPSLogger, private val rh: ResourceHelper, private val activePlugin: ActivePlugin, @@ -36,9 +38,9 @@ class TddCalculator @Inject constructor( private val dateUtil: DateUtil, private val iobCobCalculator: IobCobCalculator, private val repository: AppRepository -) { +) : TddCalculator { - fun calculate(days: Long): LongSparseArray { + override fun calculate(days: Long): LongSparseArray { var startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs()) val endTime = MidnightTime.calc(dateUtil.now()) val stepSize = T.hours(24).msecs() @@ -69,19 +71,19 @@ class TddCalculator @Inject constructor( return result } - fun calculateToday(): TotalDailyDose { + override fun calculateToday(): TotalDailyDose { val startTime = MidnightTime.calc(dateUtil.now()) val endTime = dateUtil.now() return calculate(startTime, endTime) } - fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose { + override fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose { val startTime = dateUtil.now() + T.hours(hour = startHours).msecs() val endTime = dateUtil.now() + T.hours(hour = endHours).msecs() return calculate(startTime, endTime) } - fun calculate(startTime: Long, endTime: Long): TotalDailyDose { + override fun calculate(startTime: Long, endTime: Long): TotalDailyDose { val startTimeAligned = startTime - startTime % (5 * 60 * 1000) val endTimeAligned = endTime - endTime % (5 * 60 * 1000) val tdd = TotalDailyDose(timestamp = startTimeAligned) @@ -112,7 +114,7 @@ class TddCalculator @Inject constructor( return tdd } - fun averageTDD(tdds: LongSparseArray): TotalDailyDose? { + override fun averageTDD(tdds: LongSparseArray): TotalDailyDose? { val totalTdd = TotalDailyDose(timestamp = dateUtil.now()) if (tdds.size() == 0) return null for (i in 0 until tdds.size()) { @@ -129,7 +131,7 @@ class TddCalculator @Inject constructor( return totalTdd } - fun stats(context: Context): TableLayout { + override fun stats(context: Context): TableLayout { val tdds = calculate(7) val averageTdd = averageTDD(tdds) val todayTdd = calculateToday() diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt b/implementation/src/main/java/info/nightscout/implementation/stats/TirCalculatorImpl.kt similarity index 88% rename from app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt rename to implementation/src/main/java/info/nightscout/implementation/stats/TirCalculatorImpl.kt index 628c131788..0442497f4f 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt +++ b/implementation/src/main/java/info/nightscout/implementation/stats/TirCalculatorImpl.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.utils.stats +package info.nightscout.implementation.stats import android.annotation.SuppressLint import android.content.Context @@ -9,26 +9,28 @@ import android.view.ViewGroup import android.widget.TableLayout import android.widget.TextView import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.interfaces.stats.TIR +import info.nightscout.androidaps.interfaces.stats.TirCalculator import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.MidnightTime import info.nightscout.androidaps.utils.T -import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.implementation.R import javax.inject.Inject import javax.inject.Singleton @Singleton -class TirCalculator @Inject constructor( +class TirCalculatorImpl @Inject constructor( private val rh: ResourceHelper, private val profileFunction: ProfileFunction, private val dateUtil: DateUtil, private val repository: AppRepository -) { +) : TirCalculator { - fun calculate(days: Long, lowMgdl: Double, highMgdl: Double): LongSparseArray { + override 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()) @@ -40,7 +42,7 @@ class TirCalculator @Inject constructor( val midnight = MidnightTime.calc(bg.timestamp) var tir = result[midnight] if (tir == null) { - tir = TIR(midnight, lowMgdl, highMgdl) + tir = TirImpl(midnight, lowMgdl, highMgdl) result.append(midnight, tir) } if (bg.value < 39) tir.error() @@ -53,9 +55,9 @@ class TirCalculator @Inject constructor( private fun averageTIR(tirs: LongSparseArray): TIR { val totalTir = if (tirs.size() > 0) { - TIR(tirs.valueAt(0).date, tirs.valueAt(0).lowThreshold, tirs.valueAt(0).highThreshold) + TirImpl(tirs.valueAt(0).date, tirs.valueAt(0).lowThreshold, tirs.valueAt(0).highThreshold) } else { - TIR(7, 70.0, 180.0) + TirImpl(7, 70.0, 180.0) } for (i in 0 until tirs.size()) { val tir = tirs.valueAt(i) @@ -69,7 +71,7 @@ class TirCalculator @Inject constructor( } @SuppressLint("SetTextI18n") - fun stats(context: Context): TableLayout = + override fun stats(context: Context): TableLayout = TableLayout(context).also { layout -> val lowTirMgdl = Constants.STATS_RANGE_LOW_MMOL * Constants.MMOLL_TO_MGDL val highTirMgdl = Constants.STATS_RANGE_HIGH_MMOL * Constants.MMOLL_TO_MGDL @@ -92,7 +94,7 @@ class TirCalculator @Inject constructor( gravity = Gravity.CENTER_HORIZONTAL setTextAppearance(android.R.style.TextAppearance_Material_Medium) }) - layout.addView(TIR.toTableRowHeader(context, rh)) + layout.addView(TirImpl.toTableRowHeader(context, rh)) for (i in 0 until tir7.size()) layout.addView(tir7.valueAt(i).toTableRow(context, rh, dateUtil)) layout.addView( TextView(context).apply { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/TIR.kt b/implementation/src/main/java/info/nightscout/implementation/stats/TirImpl.kt similarity index 82% rename from app/src/main/java/info/nightscout/androidaps/utils/stats/TIR.kt rename to implementation/src/main/java/info/nightscout/implementation/stats/TirImpl.kt index 90ebef0c0e..7934d806ae 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/TIR.kt +++ b/implementation/src/main/java/info/nightscout/implementation/stats/TirImpl.kt @@ -1,26 +1,38 @@ -package info.nightscout.androidaps.utils.stats +package info.nightscout.implementation.stats import android.annotation.SuppressLint import android.content.Context import android.view.Gravity import android.widget.TableRow import android.widget.TextView -import info.nightscout.androidaps.R -import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.interfaces.stats.TIR +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.implementation.R -class TIR(val date: Long, val lowThreshold: Double, val highThreshold: Double) { +class TirImpl(override val date: Long, override val lowThreshold: Double, override val highThreshold: Double) : TIR { - internal var below = 0 - internal var inRange = 0 - internal var above = 0 - internal var error = 0 - internal var count = 0 + override var below = 0 + override var inRange = 0 + override var above = 0 + override var error = 0 + override var count = 0 - fun error() = run { error++ } - fun below() = run { below++; count++ } - fun inRange() = run { inRange++; count++ } - fun above() = run { above++; count++ } + override fun error() { + error++ + } + + override fun below() { + below++; count++ + } + + override fun inRange() { + inRange++; count++ + } + + override fun above() { + above++; count++ + } private fun belowPct() = if (count > 0) below.toDouble() / count * 100.0 else 0.0 private fun inRangePct() = if (count > 0) 100 - belowPct() - abovePct() else 0.0 @@ -39,8 +51,8 @@ class TIR(val date: Long, val lowThreshold: Double, val highThreshold: Double) { header.addView(TextView(context).apply { gravity = Gravity.CENTER_HORIZONTAL; layoutParams = lp.apply { column = 3; weight = 1f }; text = rh.gs(R.string.above) }) } } - - fun toTableRow(context: Context, rh: ResourceHelper, dateUtil: DateUtil): TableRow = + + override fun toTableRow(context: Context, rh: ResourceHelper, dateUtil: DateUtil): TableRow = TableRow(context).also { row -> val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT, 1f) row.layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT) @@ -52,7 +64,7 @@ class TIR(val date: Long, val lowThreshold: Double, val highThreshold: Double) { } @SuppressLint("SetTextI18n") - fun toTableRow(context: Context, rh: ResourceHelper, days: Int): TableRow = + override fun toTableRow(context: Context, rh: ResourceHelper, days: Int): TableRow = TableRow(context).also { row -> val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT, 1f) row.layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT) diff --git a/implementation/src/main/res/values/strings.xml b/implementation/src/main/res/values/strings.xml index 62774daa74..d000ee1998 100644 --- a/implementation/src/main/res/values/strings.xml +++ b/implementation/src/main/res/values/strings.xml @@ -23,4 +23,18 @@ Error asking for permissions This device does not appear to support battery optimization whitelisting - you may experience performance issues. + + Very low + Low + High + Very high + Below + In range + Above + HbA1c: + SD: %1$s + Detailed 14 days + Day TIR + Night TIR + diff --git a/insight/src/main/res/values/strings.xml b/insight/src/main/res/values/strings.xml index a6bfaf9b02..b8bef8b2e9 100644 --- a/insight/src/main/res/values/strings.xml +++ b/insight/src/main/res/values/strings.xml @@ -17,7 +17,6 @@ Not inserted TDD Bolus TDD Basal - TDD Total %1$d%% for %2$d / %3$d min Multiwave bolus %1$.2f / %2$.2f U for %3$d min diff --git a/ui/src/main/AndroidManifest.xml b/ui/src/main/AndroidManifest.xml index 29f4af7a96..1baaae3f21 100644 --- a/ui/src/main/AndroidManifest.xml +++ b/ui/src/main/AndroidManifest.xml @@ -35,6 +35,14 @@ android:name=".activities.ErrorHelperActivity" android:exported="false" android:theme="@style/Theme.MaterialComponents.Translucent" /> + + diff --git a/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt b/ui/src/main/java/info/nightscout/ui/activities/StatsActivity.kt similarity index 85% rename from app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt rename to ui/src/main/java/info/nightscout/ui/activities/StatsActivity.kt index 748acc6699..0be7333f5d 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt +++ b/ui/src/main/java/info/nightscout/ui/activities/StatsActivity.kt @@ -1,20 +1,21 @@ -package info.nightscout.androidaps.activities +package info.nightscout.ui.activities import android.annotation.SuppressLint import android.os.Bundle import android.widget.TextView -import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources -import info.nightscout.androidaps.databinding.ActivityStatsBinding +import info.nightscout.androidaps.interfaces.stats.DexcomTirCalculator +import info.nightscout.androidaps.interfaces.stats.TddCalculator +import info.nightscout.androidaps.interfaces.stats.TirCalculator import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.utils.ActivityMonitor import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.rx.AapsSchedulers -import info.nightscout.androidaps.utils.stats.DexcomTirCalculator -import info.nightscout.androidaps.utils.stats.TddCalculator -import info.nightscout.androidaps.utils.stats.TirCalculator +import info.nightscout.ui.R +import info.nightscout.ui.databinding.ActivityStatsBinding +import info.nightscout.ui.utils.ActivityMonitor import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign @@ -41,7 +42,7 @@ class StatsActivity : NoSplashAppCompatActivity() { binding.tdds.addView(TextView(this).apply { text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress) }) binding.tir.addView(TextView(this).apply { text = getString(R.string.tir) + ": " + rh.gs(R.string.calculation_in_progress) }) - binding.activity.addView(TextView(this).apply { text = getString(R.string.activitymonitor) + ": " + rh.gs(R.string.calculation_in_progress) }) + binding.activity.addView(TextView(this).apply { text = getString(R.string.activity_monitor) + ": " + rh.gs(R.string.calculation_in_progress) }) disposable += Single.fromCallable { tddCalculator.stats(this) } .subscribeOn(aapsSchedulers.io) @@ -74,7 +75,7 @@ class StatsActivity : NoSplashAppCompatActivity() { binding.ok.setOnClickListener { finish() } binding.reset.setOnClickListener { - OKDialog.showConfirmation(this, rh.gs(R.string.doyouwantresetstats)) { + OKDialog.showConfirmation(this, rh.gs(R.string.do_you_want_reset_stats)) { uel.log(Action.STAT_RESET, Sources.Stats) activityMonitor.reset() recreate() diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt b/ui/src/main/java/info/nightscout/ui/activities/SurveyActivity.kt similarity index 82% rename from app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt rename to ui/src/main/java/info/nightscout/ui/activities/SurveyActivity.kt index 1d108a7d17..a24a56d4b4 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt +++ b/ui/src/main/java/info/nightscout/ui/activities/SurveyActivity.kt @@ -1,32 +1,29 @@ -package info.nightscout.androidaps.activities +package info.nightscout.ui.activities import android.os.Bundle import android.widget.ArrayAdapter import com.google.firebase.auth.FirebaseAuth import com.google.firebase.database.FirebaseDatabase -import info.nightscout.androidaps.R -import info.nightscout.androidaps.databinding.ActivitySurveyBinding +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.utils.ActivityMonitor +import info.nightscout.androidaps.interfaces.stats.TddCalculator import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.InstanceId import info.nightscout.androidaps.utils.ToastUtils -import info.nightscout.androidaps.utils.defaultProfile.DefaultProfile -import info.nightscout.androidaps.utils.stats.TddCalculator -import info.nightscout.androidaps.utils.stats.TirCalculator import info.nightscout.shared.SafeParse import info.nightscout.shared.logging.LTag +import info.nightscout.ui.R +import info.nightscout.ui.databinding.ActivitySurveyBinding +import info.nightscout.ui.defaultProfile.DefaultProfile import javax.inject.Inject class SurveyActivity : NoSplashAppCompatActivity() { @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var tddCalculator: TddCalculator - @Inject lateinit var tirCalculator: TirCalculator @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var activityMonitor: ActivityMonitor @Inject lateinit var defaultProfile: DefaultProfile @Inject lateinit var dateUtil: DateUtil @@ -43,24 +40,20 @@ class SurveyActivity : NoSplashAppCompatActivity() { val profileList = profileStore?.getProfileList() ?: return binding.spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList) - //binding.tdds.text = tddCalculator.stats() - //binding.tir.text = tirCalculator.stats() - //binding.activity.text = activityMonitor.stats() - binding.profile.setOnClickListener { val age = SafeParse.stringToDouble(binding.age.text.toString()) val weight = SafeParse.stringToDouble(binding.weight.text.toString()) val tdd = SafeParse.stringToDouble(binding.tdd.text.toString()) if (age < 1 || age > 120) { - ToastUtils.warnToast(this, R.string.invalidage) + ToastUtils.warnToast(this, R.string.invalid_age) return@setOnClickListener } if ((weight < 5 || weight > 150) && tdd == 0.0) { - ToastUtils.warnToast(this, R.string.invalidweight) + ToastUtils.warnToast(this, R.string.invalid_weight) return@setOnClickListener } if ((tdd < 5 || tdd > 150) && weight == 0.0) { - ToastUtils.warnToast(this, R.string.invalidweight) + ToastUtils.warnToast(this, R.string.invalid_weight) return@setOnClickListener } profileFunction.getProfile()?.let { runningProfile -> @@ -84,11 +77,11 @@ class SurveyActivity : NoSplashAppCompatActivity() { r.age = SafeParse.stringToInt(binding.age.text.toString()) r.weight = SafeParse.stringToInt(binding.weight.text.toString()) if (r.age < 1 || r.age > 120) { - ToastUtils.warnToast(this, R.string.invalidage) + ToastUtils.warnToast(this, R.string.invalid_age) return@setOnClickListener } if (r.weight < 5 || r.weight > 150) { - ToastUtils.warnToast(this, R.string.invalidweight) + ToastUtils.warnToast(this, R.string.invalid_weight) return@setOnClickListener } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/defaultProfile/DefaultProfile.kt b/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfile.kt similarity index 97% rename from app/src/main/java/info/nightscout/androidaps/utils/defaultProfile/DefaultProfile.kt rename to ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfile.kt index c69b454bc8..d78c22a190 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/defaultProfile/DefaultProfile.kt +++ b/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfile.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.utils.defaultProfile +package info.nightscout.ui.defaultProfile import info.nightscout.androidaps.data.PureProfile import info.nightscout.androidaps.extensions.pureProfileFromJson @@ -13,31 +13,32 @@ import javax.inject.Inject import javax.inject.Singleton import kotlin.math.abs +@Suppress("LocalVariableName") @Singleton class DefaultProfile @Inject constructor(val dateUtil: DateUtil) { - var oneToFive: TreeMap> = TreeMap() - var sixToEleven: TreeMap> = TreeMap() - var twelveToSeventeen: TreeMap> = TreeMap() - var eighteenToTwentyFour: TreeMap> = TreeMap() + private var oneToFive: TreeMap> = TreeMap() + private var sixToEleven: TreeMap> = TreeMap() + private var twelveToSeventeen: TreeMap> = TreeMap() + @Suppress("unused") var eighteenToTwentyFour: TreeMap> = TreeMap() fun profile(age: Double, tdd: Double, weight: Double, units: GlucoseUnit): PureProfile? { val profile = JSONObject() - if (age >= 1 && age < 6) { + if (age in 1.0..5.0) { 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", singleValueArrayFromMmolToUnits(isf, arrayOf(0.0, -2.0, -0.0, -0.0, -2.0, 0.0, -2.0),units)) - } else if (age >= 6 && age < 12) { + } else if (age in 6.0..11.0) { 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", singleValueArrayFromMmolToUnits(isf, arrayOf(0.0, -1.0, -0.0, -0.0, -1.0, 0.0, -1.0),units)) - } else if (age >= 12 && age <= 18) { + } else if (age in 12.0..18.0) { 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) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/defaultProfile/DefaultProfileDPV.kt b/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfileDPV.kt similarity index 78% rename from app/src/main/java/info/nightscout/androidaps/utils/defaultProfile/DefaultProfileDPV.kt rename to ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfileDPV.kt index d339cda45e..72b50cb342 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/defaultProfile/DefaultProfileDPV.kt +++ b/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfileDPV.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.utils.defaultProfile +package info.nightscout.ui.defaultProfile import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.PureProfile @@ -15,22 +15,22 @@ import javax.inject.Singleton @Singleton class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector, val dateUtil: DateUtil) { - var oneToFive = arrayOf(3.97, 3.61, 3.46, 3.70, 3.76, 3.87, 4.18, 4.01, 3.76, 3.54, 3.15, 2.80, 2.86, 3.21, 3.61, 3.97, 4.43, 4.96, 5.10, 5.50, 5.81, 6.14, 5.52, 5.10) - var sixToEleven = arrayOf(4.20, 4.27, 4.41, 4.62, 4.92, 5.09, 5.01, 4.47, 3.89, 3.33, 3.10, 2.91, 2.97, 3.08, 3.36, 3.93, 4.52, 4.76, 4.69, 4.63, 4.63, 4.47, 4.47, 4.31) - var twelveToEighteen = arrayOf(3.47, 3.80, 4.31, 4.95, 5.59, 6.11, 5.89, 5.11, 4.31, 3.78, 3.55, 3.39, 3.35, 3.39, 3.64, 3.97, 4.53, 4.59, 4.50, 4.00, 3.69, 3.39, 3.35, 3.35) + private var oneToFive = arrayOf(3.97, 3.61, 3.46, 3.70, 3.76, 3.87, 4.18, 4.01, 3.76, 3.54, 3.15, 2.80, 2.86, 3.21, 3.61, 3.97, 4.43, 4.96, 5.10, 5.50, 5.81, 6.14, 5.52, 5.10) + private var sixToEleven = arrayOf(4.20, 4.27, 4.41, 4.62, 4.92, 5.09, 5.01, 4.47, 3.89, 3.33, 3.10, 2.91, 2.97, 3.08, 3.36, 3.93, 4.52, 4.76, 4.69, 4.63, 4.63, 4.47, 4.47, 4.31) + private var twelveToEighteen = arrayOf(3.47, 3.80, 4.31, 4.95, 5.59, 6.11, 5.89, 5.11, 4.31, 3.78, 3.55, 3.39, 3.35, 3.39, 3.64, 3.97, 4.53, 4.59, 4.50, 4.00, 3.69, 3.39, 3.35, 3.35) fun profile(age: Double, tdd: Double, basalSumPct: Double, units: GlucoseUnit): PureProfile? { val basalSum = tdd * basalSumPct val profile = JSONObject() - if (age >= 1 && age < 6) { + if (age in 1.0..5.0) { profile.put("basal", arrayToJson(oneToFive, basalSum)) profile.put("carbratio", singleValueArray(0.0)) profile.put("sens", singleValueArrayFromMmolToUnits(0.0, units)) - } else if (age >= 6 && age < 12) { + } else if (age in 6.0..11.0) { profile.put("basal", arrayToJson(sixToEleven, basalSum)) profile.put("carbratio", singleValueArray(0.0)) profile.put("sens", singleValueArrayFromMmolToUnits(0.0, units)) - } else if (age >= 12 && age <= 18) { + } else if (age in 12.0..18.0) { profile.put("basal", arrayToJson(twelveToEighteen, basalSum)) profile.put("carbratio", singleValueArray(0.0)) profile.put("sens", singleValueArrayFromMmolToUnits(0.0, units)) @@ -56,12 +56,14 @@ class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector, va return basals } + @Suppress("SameParameterValue") private fun singleValueArray(value: Double): JSONArray { val array = JSONArray() array.put(JSONObject().put("time", "00:00").put("value", value).put("timeAsSeconds", 0 * 3600)) return array } + @Suppress("SameParameterValue") private fun singleValueArrayFromMmolToUnits(value: Double, units: GlucoseUnit): JSONArray { val array = JSONArray() array.put(JSONObject().put("time", "00:00").put("value", Profile.fromMmolToUnits(value, units)).put("timeAsSeconds", 0 * 3600)) diff --git a/ui/src/main/java/info/nightscout/ui/di/UiModule.kt b/ui/src/main/java/info/nightscout/ui/di/UiModule.kt index 2afc10c402..4be8806672 100644 --- a/ui/src/main/java/info/nightscout/ui/di/UiModule.kt +++ b/ui/src/main/java/info/nightscout/ui/di/UiModule.kt @@ -2,8 +2,10 @@ package info.nightscout.ui.di import dagger.Module import dagger.android.ContributesAndroidInjector +import info.nightscout.ui.activities.SurveyActivity import info.nightscout.ui.activities.BolusProgressHelperActivity import info.nightscout.ui.activities.ErrorHelperActivity +import info.nightscout.ui.activities.StatsActivity import info.nightscout.ui.activities.TDDStatsActivity import info.nightscout.ui.dialogs.CalibrationDialog import info.nightscout.ui.dialogs.CarbsDialog @@ -18,5 +20,7 @@ abstract class UiModule { @ContributesAndroidInjector abstract fun contributesTDDStatsActivity(): TDDStatsActivity @ContributesAndroidInjector abstract fun contributeBolusProgressHelperActivity(): BolusProgressHelperActivity @ContributesAndroidInjector abstract fun contributeErrorHelperActivity(): ErrorHelperActivity + @ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity + @ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt b/ui/src/main/java/info/nightscout/ui/utils/ActivityMonitor.kt similarity index 96% rename from app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt rename to ui/src/main/java/info/nightscout/ui/utils/ActivityMonitor.kt index 1b099697f5..88565f49a1 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt +++ b/ui/src/main/java/info/nightscout/ui/utils/ActivityMonitor.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.utils +package info.nightscout.ui.utils import android.app.Activity import android.app.Application @@ -10,12 +10,14 @@ import android.view.ViewGroup import android.widget.TableLayout import android.widget.TableRow import android.widget.TextView -import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T import info.nightscout.shared.SafeParse import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.ui.R import javax.inject.Inject import javax.inject.Singleton @@ -69,7 +71,7 @@ class ActivityMonitor @Inject constructor( layout.layoutParams = TableLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) layout.addView( TextView(context).apply { - text = rh.gs(R.string.activitymonitor) + text = rh.gs(R.string.activity_monitor) setTypeface(typeface, Typeface.BOLD) gravity = Gravity.CENTER_HORIZONTAL setTextAppearance(android.R.style.TextAppearance_Material_Medium) diff --git a/app/src/main/res/layout/activity_stats.xml b/ui/src/main/res/layout/activity_stats.xml similarity index 99% rename from app/src/main/res/layout/activity_stats.xml rename to ui/src/main/res/layout/activity_stats.xml index 107e375125..705a571bf9 100644 --- a/app/src/main/res/layout/activity_stats.xml +++ b/ui/src/main/res/layout/activity_stats.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".activities.StatsActivity"> + tools:context="info.nightscout.ui.activities.StatsActivity"> Configure opacity AAPS widget + + Activity monitor + Do you want to reset activity stats? + Statistics + Calculation in progress + Invalid age entry + Invalid weight entry + reset + ID: + Submit + Profile + Age + Weight + Most common profile: + Note: Only data visible on this screen will be anonymously uploaded. ID is assigned to this installation of AAPS. You can submit data again if your main profile get changed but let it running at least for a week to make result visible in time in range. Your help is appreciated. + + diff --git a/ui/src/test/java/info/nightscout/androidaps/TestBase.kt b/ui/src/test/java/info/nightscout/androidaps/TestBase.kt new file mode 100644 index 0000000000..3d78d1b4f6 --- /dev/null +++ b/ui/src/test/java/info/nightscout/androidaps/TestBase.kt @@ -0,0 +1,39 @@ +package info.nightscout.androidaps + +import info.nightscout.shared.logging.AAPSLoggerTest +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.rx.TestAapsSchedulers +import org.junit.Before +import org.junit.Rule +import org.mockito.Mockito +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import java.util.* + +open class TestBase { + + val aapsLogger = AAPSLoggerTest() + val aapsSchedulers: AapsSchedulers = TestAapsSchedulers() + + // Add a JUnit rule that will setup the @Mock annotated vars and log. + // Another possibility would be to add `MockitoAnnotations.initMocks(this) to the setup method. + @get:Rule + val mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Before + fun setupLocale() { + Locale.setDefault(Locale.ENGLISH) + System.setProperty("disableFirebase", "true") + } + + // Workaround for Kotlin nullability. + // https://medium.com/@elye.project/befriending-kotlin-and-mockito-1c2e7b0ef791 + // https://stackoverflow.com/questions/30305217/is-it-possible-to-use-mockito-in-kotlin + fun anyObject(): T { + Mockito.any() + return uninitialized() + } + + @Suppress("Unchecked_Cast") + fun uninitialized(): T = null as T +} \ No newline at end of file diff --git a/ui/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/ui/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt new file mode 100644 index 0000000000..0eb177fb7c --- /dev/null +++ b/ui/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -0,0 +1,177 @@ +package info.nightscout.androidaps + +import android.content.Context +import dagger.android.AndroidInjector +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.database.embedments.InsulinConfiguration +import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch +import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.ProfileStore +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.interfaces.ResourceHelper +import org.json.JSONObject +import org.junit.Before +import org.mockito.ArgumentMatchers.anyDouble +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.`when` +import org.mockito.invocation.InvocationOnMock + +@Suppress("SpellCheckingInspection") +open class TestBaseWithProfile : TestBase() { + + @Mock lateinit var activePluginProvider: ActivePlugin + @Mock lateinit var rh: ResourceHelper + @Mock lateinit var iobCobCalculator: IobCobCalculator + @Mock lateinit var fabricPrivacy: FabricPrivacy + @Mock lateinit var profileFunction: ProfileFunction + @Mock lateinit var config: Config + @Mock lateinit var context: Context + + lateinit var dateUtil: DateUtil + val rxBus = RxBus(aapsSchedulers, aapsLogger) + + val profileInjector = HasAndroidInjector { AndroidInjector { } } + + private lateinit var validProfileJSON: String + lateinit var validProfile: ProfileSealed.Pure + lateinit var effectiveProfileSwitch: EffectiveProfileSwitch + + @Suppress("PropertyName") val TESTPROFILENAME = "someProfile" + + @Before + fun prepareMock() { + validProfileJSON = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"}," + + "{\"time\":\"2:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}]," + + "\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + dateUtil = Mockito.spy(DateUtil(context)) + `when`(dateUtil.now()).thenReturn(1656358822000) + validProfile = ProfileSealed.Pure(pureProfileFromJson(JSONObject(validProfileJSON), dateUtil)!!) + effectiveProfileSwitch = EffectiveProfileSwitch( + timestamp = dateUtil.now(), + basalBlocks = validProfile.basalBlocks, + isfBlocks = validProfile.isfBlocks, + icBlocks = validProfile.icBlocks, + targetBlocks = validProfile.targetBlocks, + glucoseUnit = EffectiveProfileSwitch.GlucoseUnit.MMOL, + originalProfileName = "", + originalCustomizedName = "", + originalTimeshift = 0, + originalPercentage = 100, + originalDuration = 0, + originalEnd = 0, + insulinConfiguration = InsulinConfiguration("", 0, 0) + ) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + String.format(rh.gs(string), arg1) + }.`when`(rh).gs(anyInt(), anyInt()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + String.format(rh.gs(string), arg1) + }.`when`(rh).gs(anyInt(), anyDouble()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + String.format(rh.gs(string), arg1) + }.`when`(rh).gs(anyInt(), anyString()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + String.format(rh.gs(string), arg1, arg2) + }.`when`(rh).gs(anyInt(), anyString(), anyString()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + String.format(rh.gs(string), arg1, arg2) + }.`when`(rh).gs(anyInt(), anyString(), anyInt()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + String.format(rh.gs(string), arg1, arg2) + }.`when`(rh).gs(anyInt(), anyDouble(), anyString()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + String.format(rh.gs(string), arg1, arg2) + }.`when`(rh).gs(anyInt(), anyDouble(), anyInt()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + String.format(rh.gs(string), arg1, arg2) + }.`when`(rh).gs(anyInt(), anyInt(), anyInt()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + String.format(rh.gs(string), arg1, arg2) + }.`when`(rh).gs(anyInt(), anyInt(), anyString()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + val arg3 = invocation.getArgument(3) + String.format(rh.gs(string), arg1, arg2, arg3) + }.`when`(rh).gs(anyInt(), anyInt(), anyInt(), anyString()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + val arg3 = invocation.getArgument(3) + String.format(rh.gs(string), arg1, arg2, arg3) + }.`when`(rh).gs(anyInt(), anyInt(), anyString(), anyString()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + val arg3 = invocation.getArgument(3) + String.format(rh.gs(string), arg1, arg2, arg3) + }.`when`(rh).gs(anyInt(), anyDouble(), anyInt(), anyString()) + + Mockito.doAnswer { invocation: InvocationOnMock -> + val string = invocation.getArgument(0) + val arg1 = invocation.getArgument(1) + val arg2 = invocation.getArgument(2) + val arg3 = invocation.getArgument(3) + String.format(rh.gs(string), arg1, arg2, arg3) + }.`when`(rh).gs(anyInt(), anyString(), anyInt(), anyString()) + + } + + fun getValidProfileStore(): ProfileStore { + val json = JSONObject() + val store = JSONObject() + store.put(TESTPROFILENAME, JSONObject(validProfileJSON)) + json.put("defaultProfile", TESTPROFILENAME) + json.put("store", store) + return ProfileStore(profileInjector, json, dateUtil) + } +} diff --git a/ui/src/test/java/info/nightscout/androidaps/TestPumpPlugin.kt b/ui/src/test/java/info/nightscout/androidaps/TestPumpPlugin.kt new file mode 100644 index 0000000000..c533893b3f --- /dev/null +++ b/ui/src/test/java/info/nightscout/androidaps/TestPumpPlugin.kt @@ -0,0 +1,68 @@ +package info.nightscout.androidaps + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.interfaces.PumpDescription +import info.nightscout.androidaps.interfaces.Pump +import info.nightscout.androidaps.interfaces.PumpSync +import info.nightscout.androidaps.plugins.common.ManufacturerType +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.utils.TimeChangeType +import org.json.JSONObject + +@Suppress("MemberVisibilityCanBePrivate") +class TestPumpPlugin(val injector: HasAndroidInjector) : Pump { + + var connected = false + var isProfileSet = true + + override fun isConnected() = connected + override fun isConnecting() = false + override fun isHandshakeInProgress() = false + val lastData = 0L + + val baseBasal = 0.0 + override val pumpDescription = PumpDescription() + + override fun isInitialized(): Boolean = true + override fun isSuspended(): Boolean = false + override fun isBusy(): Boolean = false + override fun connect(reason: String) { + connected = true + } + + override fun disconnect(reason: String) { + connected = false + } + + override fun stopConnecting() { + connected = false + } + + override fun waitForDisconnectionInSeconds(): Int = 0 + override fun getPumpStatus(reason: String) {} + override fun setNewBasalProfile(profile: Profile): PumpEnactResult = PumpEnactResult(injector) + override fun isThisProfileSet(profile: Profile): Boolean = isProfileSet + override fun lastDataTime(): Long = lastData + override val baseBasalRate: Double = baseBasal + override val reservoirLevel: Double = 0.0 + override val batteryLevel: Int = 0 + override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult = PumpEnactResult(injector).success(true) + override fun stopBolusDelivering() {} + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult = PumpEnactResult(injector).success(true) + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult = PumpEnactResult(injector).success(true) + override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult = PumpEnactResult(injector).success(true) + override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult = PumpEnactResult(injector).success(true) + override fun cancelExtendedBolus(): PumpEnactResult = PumpEnactResult(injector).success(true) + override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject = JSONObject() + override fun manufacturer(): ManufacturerType = ManufacturerType.AAPS + override fun model(): PumpType = PumpType.GENERIC_AAPS + override fun serialNumber(): String = "1" + override fun shortStatus(veryShort: Boolean): String = "" + override val isFakingTempsByExtendedBoluses: Boolean = false + override fun loadTDDs(): PumpEnactResult = PumpEnactResult(injector).success(true) + override fun canHandleDST(): Boolean = true + override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) {} +} \ No newline at end of file diff --git a/app/src/test/java/info/nightscout/androidaps/utils/defaultProfile/DefaultProfileTest.kt b/ui/src/test/java/info/nightscout/ui/defaultProfile/DefaultProfileTest.kt similarity index 95% rename from app/src/test/java/info/nightscout/androidaps/utils/defaultProfile/DefaultProfileTest.kt rename to ui/src/test/java/info/nightscout/ui/defaultProfile/DefaultProfileTest.kt index 8bfbd998c6..d24fcd4cbc 100644 --- a/app/src/test/java/info/nightscout/androidaps/utils/defaultProfile/DefaultProfileTest.kt +++ b/ui/src/test/java/info/nightscout/ui/defaultProfile/DefaultProfileTest.kt @@ -1,8 +1,8 @@ -package info.nightscout.androidaps.utils.defaultProfile +package info.nightscout.ui.defaultProfile +import info.nightscout.androidaps.TestBaseWithProfile import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.interfaces.GlucoseUnit -import info.nightscout.androidaps.TestBaseWithProfile import org.junit.Assert.assertEquals import org.junit.Test