diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index a9c3d0e4d9..c76f23799b 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -3,9 +3,12 @@
+
-
-
+
+
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt
index 04095b64a8..18c902f6a4 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt
@@ -44,10 +44,11 @@ class StatusLightHandler @Inject constructor(
handleAge(careportal_pb_age, TherapyEvent.Type.PUMP_BATTERY_CHANGE, R.string.key_statuslights_bage_warning, 216.0, R.string.key_statuslights_bage_critical, 240.0)
}
if (!config.NSCLIENT) {
+ val insulinUnit = rh.gs(R.string.insulin_unit_shortname)
if (pump.model() == PumpType.OMNIPOD_EROS || pump.model() == PumpType.OMNIPOD_DASH) {
- handleOmnipodReservoirLevel(careportal_reservoir_level, R.string.key_statuslights_res_critical, 10.0, R.string.key_statuslights_res_warning, 80.0, pump.reservoirLevel, "U")
+ handleOmnipodReservoirLevel(careportal_reservoir_level, R.string.key_statuslights_res_critical, 10.0, R.string.key_statuslights_res_warning, 80.0, pump.reservoirLevel, insulinUnit)
} else {
- handleLevel(careportal_reservoir_level, R.string.key_statuslights_res_critical, 10.0, R.string.key_statuslights_res_warning, 80.0, pump.reservoirLevel, "U")
+ handleLevel(careportal_reservoir_level, R.string.key_statuslights_res_critical, 10.0, R.string.key_statuslights_res_warning, 80.0, pump.reservoirLevel, insulinUnit)
}
if (bgSource.sensorBatteryLevel != -1)
handleLevel(careportal_sensor_battery_level, R.string.key_statuslights_sbat_critical, 5.0, R.string.key_statuslights_sbat_warning, 20.0, bgSource.sensorBatteryLevel.toDouble(), "%")
diff --git a/core/android_module_dependencies.gradle b/core/android_module_dependencies.gradle
index d163350b9b..0329267ab3 100644
--- a/core/android_module_dependencies.gradle
+++ b/core/android_module_dependencies.gradle
@@ -22,6 +22,18 @@ dependencies {
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
+
+ // This is necessary to prevent Gradle build errors like:
+ //
+ // Duplicate class androidx.lifecycle.ViewModelLazy found in modules jetified-lifecycle-viewmodel-ktx-2.3.1-runtime (androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1) and lifecycle-viewmodel-2.5.0-runtime (androidx.lifecycle:lifecycle-viewmodel:2.5.0)
+ //
+ // By explicitly adding these dependencies, the jetifier
+ // is forced to use the correct lifecycle version instead
+ // of automatically picking 2.3.1.
+ //
+ // See: https://stackoverflow.com/a/69832319/560774
+ implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
}
allOpen {
diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/Pump.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/Pump.kt
index f736a314d4..7a29c198c7 100644
--- a/core/src/main/java/info/nightscout/androidaps/interfaces/Pump.kt
+++ b/core/src/main/java/info/nightscout/androidaps/interfaces/Pump.kt
@@ -96,12 +96,15 @@ interface Pump {
fun isThisProfileSet(profile: Profile): Boolean
/**
- * @return timestamp of last connection to the pump
+ * @return timestamp of last connection to the pump in milliseconds
*/
fun lastDataTime(): Long
/**
* Currently running base basal rate [U/h]
+ *
+ * This _must not_ be affected by current pump states
+ * (TBRs, pump suspended/running etc.)
*/
val baseBasalRate: Double
diff --git a/shared/src/main/java/info/nightscout/shared/logging/AAPSLogger.kt b/shared/src/main/java/info/nightscout/shared/logging/AAPSLogger.kt
index 10a956013a..2eaf448b45 100644
--- a/shared/src/main/java/info/nightscout/shared/logging/AAPSLogger.kt
+++ b/shared/src/main/java/info/nightscout/shared/logging/AAPSLogger.kt
@@ -5,7 +5,6 @@ package info.nightscout.shared.logging
*/
interface AAPSLogger {
-
fun debug(message: String)
fun debug(enable: Boolean, tag: LTag, message: String)
fun debug(tag: LTag, message: String)
@@ -21,4 +20,13 @@ interface AAPSLogger {
fun error(message: String)
fun error(message: String, throwable: Throwable)
fun error(format: String, vararg arguments: Any?)
+
+ // These are variants of the calls above that allow for explicitly
+ // specifying the exact logging location. They are primarily meant
+ // as a way to integrate other logging infrastructures into AndroidAPS,
+ // and typically aren't practical to use directly for logging in code.
+ fun debug(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String)
+ fun info(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String)
+ fun warn(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String)
+ fun error(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String)
}
\ No newline at end of file
diff --git a/shared/src/main/java/info/nightscout/shared/logging/AAPSLoggerProduction.kt b/shared/src/main/java/info/nightscout/shared/logging/AAPSLoggerProduction.kt
index 56cca06ed2..be0c6a54df 100644
--- a/shared/src/main/java/info/nightscout/shared/logging/AAPSLoggerProduction.kt
+++ b/shared/src/main/java/info/nightscout/shared/logging/AAPSLoggerProduction.kt
@@ -74,9 +74,29 @@ class AAPSLoggerProduction constructor(val l: L) : AAPSLogger {
override fun error(tag: LTag, format: String, vararg arguments: Any?) {
LoggerFactory.getLogger(tag.tag).error(stackLogMarker() + format, arguments)
}
+
+ override fun debug(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String) {
+ LoggerFactory.getLogger(tag.tag).debug(logLocationPrefix(className, methodName, lineNumber) + message)
+ }
+
+ override fun info(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String) {
+ LoggerFactory.getLogger(tag.tag).info(logLocationPrefix(className, methodName, lineNumber) + message)
+ }
+
+ override fun warn(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String) {
+ LoggerFactory.getLogger(tag.tag).warn(logLocationPrefix(className, methodName, lineNumber) + message)
+ }
+
+ override fun error(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String) {
+ LoggerFactory.getLogger(tag.tag).error(logLocationPrefix(className, methodName, lineNumber) + message)
+ }
}
-fun StackTraceElement.toLogString(): String = "[${this.className.substringAfterLast(".")}.${this.methodName}():${this.lineNumber}]: "
+private fun logLocationPrefix(className: String, methodName: String, lineNumber: Int) =
+ "[$className.$methodName():$lineNumber]: "
+
+fun StackTraceElement.toLogString(): String =
+ logLocationPrefix(this.className.substringAfterLast("."), this.methodName, this.lineNumber)
/* Needs to be inline. Don't remove even if IDE suggests it. */
@Suppress("NOTHING_TO_INLINE")
diff --git a/shared/src/main/java/info/nightscout/shared/logging/AAPSLoggerTest.kt b/shared/src/main/java/info/nightscout/shared/logging/AAPSLoggerTest.kt
index e2ea149545..d7ff2ded5a 100644
--- a/shared/src/main/java/info/nightscout/shared/logging/AAPSLoggerTest.kt
+++ b/shared/src/main/java/info/nightscout/shared/logging/AAPSLoggerTest.kt
@@ -65,4 +65,20 @@ class AAPSLoggerTest : AAPSLogger {
override fun error(tag: LTag, format: String, vararg arguments: Any?) {
println("ERROR: : " + tag.tag + " " + String.format(format, arguments))
}
+
+ override fun debug(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String) {
+ println("DEBUG: : ${tag.tag} $className.$methodName():$lineNumber $message")
+ }
+
+ override fun info(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String) {
+ println("INFO: : ${tag.tag} $className.$methodName():$lineNumber $message")
+ }
+
+ override fun warn(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String) {
+ println("WARN: : ${tag.tag} $className.$methodName():$lineNumber $message")
+ }
+
+ override fun error(className: String, methodName: String, lineNumber: Int, tag: LTag, message: String) {
+ println("ERROR: : ${tag.tag} $className.$methodName():$lineNumber $message")
+ }
}
\ No newline at end of file
diff --git a/shared/src/main/java/info/nightscout/shared/sharedPreferences/Delegates.kt b/shared/src/main/java/info/nightscout/shared/sharedPreferences/Delegates.kt
new file mode 100644
index 0000000000..a7f80f7128
--- /dev/null
+++ b/shared/src/main/java/info/nightscout/shared/sharedPreferences/Delegates.kt
@@ -0,0 +1,83 @@
+package info.nightscout.shared.sharedPreferences
+
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+/*
+
+These classes allow to combine SP values with Kotlin delegates like this:
+
+ private var myValue: String
+ by SPDelegateString(sp, "myValueKey", "default-value")
+
+Then, accessing myValue works by simply using the Kotlin setters & getters:
+
+ val value = myValue // reading from sp
+ myValue = "newvalue" // writing to sp
+
+*/
+
+class SPDelegateBoolean(
+ private val sp: SP,
+ private val key: String,
+ private val defaultValue: Boolean = false,
+ private val commit: Boolean = false
+) : ReadWriteProperty {
+ override fun getValue(thisRef: Any, property: KProperty<*>) =
+ sp.getBoolean(key, defaultValue)
+
+ override fun setValue(thisRef: Any, property: KProperty<*>, value: Boolean) =
+ sp.edit(commit = commit) { putBoolean(key, value) }
+}
+
+class SPDelegateDouble(
+ private val sp: SP,
+ private val key: String,
+ private val defaultValue: Double = 0.0,
+ private val commit: Boolean = false
+) : ReadWriteProperty {
+ override fun getValue(thisRef: Any, property: KProperty<*>) =
+ sp.getDouble(key, defaultValue)
+
+ override fun setValue(thisRef: Any, property: KProperty<*>, value: Double) =
+ sp.edit(commit = commit) { putDouble(key, value) }
+}
+
+class SPDelegateLong(
+ private val sp: SP,
+ private val key: String,
+ private val defaultValue: Long = 0,
+ private val commit: Boolean = false
+) : ReadWriteProperty {
+ override fun getValue(thisRef: Any, property: KProperty<*>) =
+ sp.getLong(key, defaultValue)
+
+ override fun setValue(thisRef: Any, property: KProperty<*>, value: Long) =
+ sp.edit(commit = commit) { putLong(key, value) }
+}
+
+class SPDelegateInt(
+ private val sp: SP,
+ private val key: String,
+ private val defaultValue: Int = 0,
+ private val commit: Boolean = false
+) : ReadWriteProperty {
+ override fun getValue(thisRef: Any, property: KProperty<*>) =
+ sp.getInt(key, defaultValue)
+
+ override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) =
+ sp.edit(commit = commit) { putInt(key, value) }
+}
+
+class SPDelegateString(
+ private val sp: SP,
+ private val key: String,
+ private val defaultValue: String = "",
+ private val commit: Boolean = false
+) : ReadWriteProperty {
+ override fun getValue(thisRef: Any, property: KProperty<*>) =
+ sp.getString(key, defaultValue)
+
+ override fun setValue(thisRef: Any, property: KProperty<*>, value: String) =
+ sp.edit(commit = commit) { putString(key, value) }
+}
diff --git a/shared/src/main/java/info/nightscout/shared/sharedPreferences/SP.kt b/shared/src/main/java/info/nightscout/shared/sharedPreferences/SP.kt
index ca4b5b5ae7..5c3a36d101 100644
--- a/shared/src/main/java/info/nightscout/shared/sharedPreferences/SP.kt
+++ b/shared/src/main/java/info/nightscout/shared/sharedPreferences/SP.kt
@@ -7,6 +7,45 @@ import androidx.annotation.StringRes
*/
interface SP {
+ // Using a helper Editor interface to distinguish its
+ // methods from SP's. The latter always run apply().
+ // The whole point of the edit() function below is to
+ // _avoid_ unnecessary apply() / commit() calls, so
+ // we cannot use SP's put* methods in edit().
+ interface Editor {
+ fun clear()
+
+ fun remove(@StringRes resourceID: Int)
+ fun remove(key: String)
+
+ fun putBoolean(key: String, value: Boolean)
+ fun putBoolean(@StringRes resourceID: Int, value: Boolean)
+ fun putDouble(key: String, value: Double)
+ fun putDouble(@StringRes resourceID: Int, value: Double)
+ fun putLong(key: String, value: Long)
+ fun putLong(@StringRes resourceID: Int, value: Long)
+ fun putInt(key: String, value: Int)
+ fun putInt(@StringRes resourceID: Int, value: Int)
+ fun putString(key: String, value: String)
+ fun putString(@StringRes resourceID: Int, value: String)
+ }
+
+ /**
+ * Allows for editing shared preferences in a scoped manner.
+ *
+ * This works just the same way as the androidx.core.content.edit
+ * extension does. An [Editor] instance is created and used as
+ * the receiver of [block]. When the block is done, either
+ * the shared preferences commit or apply functions are called,
+ * depending on the value of [commit].
+ *
+ * Example:
+ *
+ * sp.edit(commit = false) {
+ * putString("my-key", "abc123")
+ * }
+ */
+ fun edit(commit: Boolean = false, block: Editor.() -> Unit)
fun getAll(): Map
fun clear()
diff --git a/shared/src/main/java/info/nightscout/shared/sharedPreferences/SPImplementation.kt b/shared/src/main/java/info/nightscout/shared/sharedPreferences/SPImplementation.kt
index 5bed8fbb04..71ebc4808c 100644
--- a/shared/src/main/java/info/nightscout/shared/sharedPreferences/SPImplementation.kt
+++ b/shared/src/main/java/info/nightscout/shared/sharedPreferences/SPImplementation.kt
@@ -1,7 +1,9 @@
package info.nightscout.shared.sharedPreferences
+import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
+import androidx.annotation.StringRes
import info.nightscout.shared.SafeParse
import javax.inject.Inject
import javax.inject.Singleton
@@ -12,6 +14,62 @@ class SPImplementation @Inject constructor(
private val context: Context
) : SP {
+ @SuppressLint("ApplySharedPref")
+ override fun edit(commit: Boolean, block: SP.Editor.() -> Unit) {
+ val spEdit = sharedPreferences.edit()
+
+ val edit = object : SP.Editor {
+ override fun clear() {
+ spEdit.clear()
+ }
+
+ override fun remove(@StringRes resourceID: Int) {
+ spEdit.remove(context.getString(resourceID))
+ }
+ override fun remove(key: String) {
+ spEdit.remove(key)
+ }
+
+ override fun putBoolean(key: String, value: Boolean) {
+ spEdit.putBoolean(key, value)
+ }
+ override fun putBoolean(@StringRes resourceID: Int, value: Boolean) {
+ spEdit.putBoolean(context.getString(resourceID), value)
+ }
+ override fun putDouble(key: String, value: Double) {
+ spEdit.putString(key, value.toString())
+ }
+ override fun putDouble(@StringRes resourceID: Int, value: Double) {
+ spEdit.putString(context.getString(resourceID), value.toString())
+ }
+ override fun putLong(key: String, value: Long) {
+ spEdit.putLong(key, value)
+ }
+ override fun putLong(@StringRes resourceID: Int, value: Long) {
+ spEdit.putLong(context.getString(resourceID), value)
+ }
+ override fun putInt(key: String, value: Int) {
+ spEdit.putInt(key, value)
+ }
+ override fun putInt(@StringRes resourceID: Int, value: Int) {
+ spEdit.putInt(context.getString(resourceID), value)
+ }
+ override fun putString(key: String, value: String) {
+ spEdit.putString(key, value)
+ }
+ override fun putString(@StringRes resourceID: Int, value: String) {
+ spEdit.putString(context.getString(resourceID), value)
+ }
+ }
+
+ block(edit)
+
+ if (commit)
+ spEdit.commit()
+ else
+ spEdit.apply()
+ }
+
override fun getAll(): Map = sharedPreferences.all
override fun clear() = sharedPreferences.edit().clear().apply()