migrate out of app module

This commit is contained in:
Milos Kozak 2022-11-04 14:34:05 +01:00
parent 732f2b4b40
commit c5ae87d344
58 changed files with 594 additions and 254 deletions

View file

@ -90,18 +90,10 @@
android:name=".activities.TreatmentsActivity"
android:exported="false"
android:theme="@style/AppTheme" />
<activity
android:name=".activities.SurveyActivity"
android:exported="false"
android:theme="@style/AppTheme" />
<activity
android:name=".activities.ProfileHelperActivity"
android:exported="false"
android:theme="@style/AppTheme" />
<activity
android:name=".activities.StatsActivity"
android:exported="false"
android:theme="@style/AppTheme" />
<!-- Receive new BG readings from other local apps -->
<receiver
@ -212,7 +204,7 @@
android:name=".plugins.general.maintenance.activities.LogSettingActivity"
android:exported="false" />
<activity
android:name=".activities.RequestDexcomPermissionActivity"
android:name=".plugins.source.activities.RequestDexcomPermissionActivity"
android:exported="false" />
<uses-library

View file

@ -30,7 +30,12 @@ import com.google.android.material.tabs.TabLayoutMediator
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.joanzapata.iconify.Iconify
import com.joanzapata.iconify.fonts.FontAwesomeModule
import info.nightscout.androidaps.activities.*
import info.nightscout.androidaps.activities.HistoryBrowseActivity
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
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.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.ActivityMainBinding
@ -38,9 +43,17 @@ import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventInitializationChanged
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.AndroidPermission
import info.nightscout.androidaps.interfaces.BuildHelper
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.Constraints
import info.nightscout.androidaps.interfaces.IconsProvider
import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.SmsCommunicator
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
@ -56,9 +69,10 @@ import info.nightscout.androidaps.utils.tabs.TabPageAdapter
import info.nightscout.androidaps.utils.ui.UIRunnable
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.ui.activities.StatsActivity
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import java.util.*
import java.util.Locale
import javax.inject.Inject
import kotlin.system.exitProcess

View file

@ -41,13 +41,13 @@ import info.nightscout.androidaps.receivers.KeepAliveWorker
import info.nightscout.androidaps.receivers.NetworkChangeReceiver
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver
import info.nightscout.androidaps.services.AlarmSoundServiceHelper
import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.ProcessLifecycleListener
import info.nightscout.androidaps.utils.locale.LocaleHelper
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.ui.utils.ActivityMonitor
import info.nightscout.ui.widget.Widget
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.exceptions.UndeliverableException
@ -55,7 +55,6 @@ import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.plugins.RxJavaPlugins
import rxdogtag2.RxDogTag
import java.io.IOException
import java.net.SocketException
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Provider
@ -76,7 +75,7 @@ class MainApp : DaggerApplication() {
@Inject lateinit var compatDBHelper: CompatDBHelper
@Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize
@Suppress("unused") @Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var alarmSoundServiceHelper: AlarmSoundServiceHelper
@Inject lateinit var notificationStore: NotificationStore
@ -166,7 +165,7 @@ class MainApp : DaggerApplication() {
if (e is UndeliverableException) {
e = e.cause!!
}
if (e is IOException || e is SocketException) {
if (e is IOException) {
// fine, irrelevant network problem or API that throws on cancellation
return@setErrorHandler
}

View file

@ -25,10 +25,10 @@ import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.defaultProfile.DefaultProfile
import info.nightscout.androidaps.utils.defaultProfile.DefaultProfileDPV
import info.nightscout.ui.defaultProfile.DefaultProfile
import info.nightscout.ui.defaultProfile.DefaultProfileDPV
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.stats.TddCalculator
import info.nightscout.androidaps.interfaces.stats.TddCalculator
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -183,25 +183,25 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
for (i in 0..1) {
if (typeSelected[i] == ProfileType.MOTOL_DEFAULT) {
if (ageUsed[i] < 1 || ageUsed[i] > 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)) {

View file

@ -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
}

View file

@ -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<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.NotNSClient notNsClient: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.APS aps: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.Unfinished unfinished: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>)
fun providesPlugins(
config: Config, buildHelper: BuildHelper,
@PluginsModule.AllConfigs allConfigs: Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>,
@PluginsModule.PumpDriver pumpDrivers: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.NotNSClient notNsClient: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.APS aps: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.Unfinished unfinished: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>
)
: 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
}
}

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -253,7 +253,6 @@
<string name="short_tabtitles">Shorten tab titles</string>
<string name="always_use_shortavg">Always use short average delta instead of simple delta</string>
<string name="always_use_shortavg_summary">Useful when data from unfiltered sources like xDrip+ gets noisy.</string>
<string name="profile">Profile</string>
<string name="openapsama_max_daily_safety_multiplier_summary">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 thats what is discussed about “3x max daily; 4x current” for safety caps.</string>
<string name="openapsama_current_basal_safety_multiplier_summary">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.</string>
<string name="openapsama_autosens_max_summary">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.</string>
@ -589,7 +588,6 @@
<string name="smbnotallowedinopenloopmode">SMB not allowed in open loop mode</string>
<string name="food_short">Food</string>
<string name="iobcobcalculator" translatable="false">IobCobCalculator</string>
<string name="reset">reset</string>
<string name="key_openapssmb_max_iob" translatable="false">openapsmb_max_iob</string>
<string name="openapssmb_maxiob_title">Maximum total IOB OpenAPS can\'t go over [U]</string>
<string name="openapssmb_maxiob_summary">This value is called Max IOB in OpenAPS context\nOpenAPS will not add more insulin if current IOB is greater than this value</string>
@ -802,25 +800,7 @@
<string name="profilenamecontainsdot">Profile name contains dots.\nThis is not supported by NS.\nProfile is not uploaded to NS.</string>
<string name="low_mark_comment">Lower value of in range area (display only)</string>
<string name="high_mark_comment">Higher value of in range area (display only)</string>
<string name="age">Age</string>
<string name="weight_label">Weight</string>
<string name="id">ID:</string>
<string name="submit">Submit</string>
<string name="mostcommonprofile">Most common profile:</string>
<string name="survey_comment">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.</string>
<string name="invalidage">Invalid age entry</string>
<string name="invalidweight">Invalid weight entry</string>
<string name="invalidpct">Invalid % entry</string>
<string name="average">Average</string>
<string name="tir">TIR</string>
<string name="day_tir">Day TIR</string>
<string name="night_tir">Night TIR</string>
<string name="detailed_14_days">Detailed 14 days</string>
<string name="std_deviation">SD: %1$s</string>
<string name="hba1c">HbA1c: </string>
<string name="activitymonitor">Activity monitor</string>
<string name="doyouwantresetstats">Do you want to reset activity stats?</string>
<string name="statistics">Statistics</string>
<string name="randombg">Random BG</string>
<string name="description_source_randombg">Generate random BG data (Demo mode only)</string>
<string name="randombg_short">BG</string>
@ -927,7 +907,6 @@
<string name="key_ns_receive_cgm" translatable="false">ns_receive_cgm</string>
<string name="ns_receive_cgm">Receive/backfill CGM data</string>
<string name="ns_receive_cgm_summary">Accept CGM data from NS</string>
<string name="calculation_in_progress">Calculation in progress</string>
<string name="missing_profile_name">Missing profile name</string>
<string name="error_in_ic_values">Error in IC values</string>
<string name="error_in_basal_values">Error in basal values</string>
@ -1023,13 +1002,6 @@
<string name="show_loop">Show loop</string>
<string name="count_selected">%1$d selected</string>
<string name="sort_label">Sort</string>
<string name="veryLow" comment="below 3.1">Very low</string>
<string name="low" comment="3.1-3.9">Low</string>
<string name="high" comment="10.0-13.9">High</string>
<string name="veryHigh" comment="above 13.9">Very high</string>
<string name="below" comment="below &quot;in range&quot;">Below</string>
<string name="in_range">In range</string>
<string name="above" comment="above &quot;in range&quot;">Above</string>
<string name="show_loop_records">Show loop records</string>
<string name="show_hide_records">Hide loop records</string>
<string name="loop_status">Loop status</string>

View file

@ -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<TemporaryBasal> = 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<TemporaryBasal> = 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<TemporaryBasal> = 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)
}
*/
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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<TotalDailyDose>
fun calculateToday(): TotalDailyDose
fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose
fun calculate(startTime: Long, endTime: Long): TotalDailyDose
fun averageTDD(tdds: LongSparseArray<TotalDailyDose>): TotalDailyDose?
fun stats(context: Context): TableLayout
}

View file

@ -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<TIR>
fun stats(context: Context): TableLayout
}

View file

@ -225,6 +225,9 @@
<string name="a11y_high">high</string>
<string name="a11y_inrange">in range</string>
<string name="a11y_low">low</string>
<string name="average">Average</string>
<string name="tir">TIR</string>
<string name="tdd_total">TDD Total</string>
<!-- Constraints-->
<string name="limitingbasalratio">Limiting max basal rate to %1$.2f U/h because of %2$s</string>

View file

@ -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)

View file

@ -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<Double>()
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)

View file

@ -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<TotalDailyDose> {
override fun calculate(days: Long): LongSparseArray<TotalDailyDose> {
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>): TotalDailyDose? {
override fun averageTDD(tdds: LongSparseArray<TotalDailyDose>): 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()

View file

@ -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<TIR> {
override fun calculate(days: Long, lowMgdl: Double, highMgdl: Double): LongSparseArray<TIR> {
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>): 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 {

View file

@ -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)

View file

@ -23,4 +23,18 @@
<string name="error_asking_for_permissions">Error asking for permissions</string>
<string name="alert_dialog_permission_battery_optimization_failed">This device does not appear to support battery optimization whitelisting - you may experience performance issues.</string>
<!-- Stats -->
<string name="veryLow" comment="below 3.1">Very low</string>
<string name="low" comment="3.1-3.9">Low</string>
<string name="high" comment="10.0-13.9">High</string>
<string name="veryHigh" comment="above 13.9">Very high</string>
<string name="below" comment="below &quot;in range&quot;">Below</string>
<string name="in_range">In range</string>
<string name="above" comment="above &quot;in range&quot;">Above</string>
<string name="hba1c">HbA1c: </string>
<string name="std_deviation">SD: %1$s</string>
<string name="detailed_14_days">Detailed 14 days</string>
<string name="day_tir">Day TIR</string>
<string name="night_tir">Night TIR</string>
</resources>

View file

@ -17,7 +17,6 @@
<string name="not_inserted">Not inserted</string>
<string name="tdd_bolus">TDD Bolus</string>
<string name="tdd_basal">TDD Basal</string>
<string name="tdd_total">TDD Total</string>
<string name="tbr_formatter">%1$d%% for %2$d / %3$d min</string>
<string name="multiwave_bolus">Multiwave bolus</string>
<string name="eb_formatter">%1$.2f / %2$.2f U for %3$d min</string>

View file

@ -35,6 +35,14 @@
android:name=".activities.ErrorHelperActivity"
android:exported="false"
android:theme="@style/Theme.MaterialComponents.Translucent" />
<activity
android:name=".activities.SurveyActivity"
android:exported="false"
android:theme="@style/AppTheme" />
<activity
android:name=".activities.StatsActivity"
android:exported="false"
android:theme="@style/AppTheme" />
</application>

View file

@ -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()

View file

@ -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
}

View file

@ -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<Double, Array<Double>> = TreeMap()
var sixToEleven: TreeMap<Double, Array<Double>> = TreeMap()
var twelveToSeventeen: TreeMap<Double, Array<Double>> = TreeMap()
var eighteenToTwentyFour: TreeMap<Double, Array<Double>> = TreeMap()
private var oneToFive: TreeMap<Double, Array<Double>> = TreeMap()
private var sixToEleven: TreeMap<Double, Array<Double>> = TreeMap()
private var twelveToSeventeen: TreeMap<Double, Array<Double>> = TreeMap()
@Suppress("unused") var eighteenToTwentyFour: TreeMap<Double, Array<Double>> = 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)

View file

@ -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))

View file

@ -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
}

View file

@ -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)

View file

@ -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">
<LinearLayout
android:layout_width="match_parent"

View file

@ -68,7 +68,7 @@
android:layout_height="match_parent">
<TextView
android:id="@+id/weigth_label"
android:id="@+id/weight_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
@ -124,7 +124,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:text="@string/mostcommonprofile"
android:text="@string/most_common_profile"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<Spinner

View file

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View file

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View file

Before

Width:  |  Height:  |  Size: 7 KiB

After

Width:  |  Height:  |  Size: 7 KiB

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View file

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View file

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -19,4 +19,21 @@
<string name="configure">Configure opacity</string>
<string name="widget_description">AAPS widget</string>
<!-- Activities-->
<string name="activity_monitor">Activity monitor</string>
<string name="do_you_want_reset_stats">Do you want to reset activity stats?</string>
<string name="statistics">Statistics</string>
<string name="calculation_in_progress">Calculation in progress</string>
<string name="invalid_age">Invalid age entry</string>
<string name="invalid_weight">Invalid weight entry</string>
<string name="reset">reset</string>
<string name="id">ID:</string>
<string name="submit">Submit</string>
<string name="profile">Profile</string>
<string name="age">Age</string>
<string name="weight_label">Weight</string>
<string name="most_common_profile">Most common profile:</string>
<string name="survey_comment">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.</string>
</resources>

View file

@ -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 <T> anyObject(): T {
Mockito.any<T>()
return uninitialized()
}
@Suppress("Unchecked_Cast")
fun <T> uninitialized(): T = null as T
}

View file

@ -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<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
String.format(rh.gs(string), arg1)
}.`when`(rh).gs(anyInt(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
String.format(rh.gs(string), arg1)
}.`when`(rh).gs(anyInt(), anyDouble())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
String.format(rh.gs(string), arg1)
}.`when`(rh).gs(anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
val arg2 = invocation.getArgument<String?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyString(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
val arg2 = invocation.getArgument<Int?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyString(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
val arg2 = invocation.getArgument<String?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyDouble(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
val arg2 = invocation.getArgument<Int?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyDouble(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<Int?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyInt(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<String?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<Int?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyInt(), anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<String?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyInt(), anyString(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
val arg2 = invocation.getArgument<Int?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyDouble(), anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
val arg2 = invocation.getArgument<Int?>(2)
val arg3 = invocation.getArgument<String?>(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)
}
}

View file

@ -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) {}
}

View file

@ -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