Autotune: resolve NPE

This commit is contained in:
Milos Kozak 2022-06-28 11:25:14 +02:00
parent 37aebc1575
commit 951b33ded7
3 changed files with 83 additions and 48 deletions

View file

@ -350,7 +350,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
} }
} }
if (pref is EditTextPreference) { if (pref is EditTextPreference) {
if (pref.getKey().contains("password") || pref.getKey().contains("pin") || pref.getKey().contains("secret")) { if (pref.getKey().contains("password") || pref.getKey().contains("pin") || pref.getKey().contains("secret") || pref.getKey().contains("token")) {
pref.setSummary("******") pref.setSummary("******")
} else if (pref.text != null) { } else if (pref.text != null) {
pref.dialogMessage = pref.dialogMessage pref.dialogMessage = pref.dialogMessage

View file

@ -18,37 +18,37 @@ import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.databinding.AutotuneFragmentBinding import info.nightscout.androidaps.databinding.AutotuneFragmentBinding
import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.extensions.runOnUiThread
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
import info.nightscout.androidaps.plugins.general.autotune.events.EventAutotuneUpdateGui import info.nightscout.androidaps.plugins.general.autotune.events.EventAutotuneUpdateGui
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.extensions.runOnUiThread
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.MidnightTime import info.nightscout.androidaps.utils.MidnightTime
import info.nightscout.androidaps.utils.Round import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.SafeParse import info.nightscout.shared.SafeParse
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import org.json.JSONObject import org.json.JSONObject
//import org.slf4j.LoggerFactory
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject import javax.inject.Inject
class AutotuneFragment : DaggerFragment() { class AutotuneFragment : DaggerFragment() {
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var autotunePlugin: AutotunePlugin @Inject lateinit var autotunePlugin: AutotunePlugin
@Inject lateinit var autotuneFS: AutotuneFS @Inject lateinit var autotuneFS: AutotuneFS
@ -61,13 +61,16 @@ class AutotuneFragment : DaggerFragment() {
@Inject lateinit var rh: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var rxBus: RxBus @Inject lateinit var rxBus: RxBus
@Inject lateinit var injector: HasAndroidInjector @Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsSchedulers: AapsSchedulers
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
//private val log = LoggerFactory.getLogger(AutotunePlugin::class.java) //private val log = LoggerFactory.getLogger(AutotunePlugin::class.java)
private var _binding: AutotuneFragmentBinding? = null private var _binding: AutotuneFragmentBinding? = null
private lateinit var profileStore: ProfileStore private lateinit var profileStore: ProfileStore
private var profileName = "" private var profileName = ""
private lateinit var profile: ATProfile private var profile: ATProfile? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
@ -88,12 +91,13 @@ class AutotuneFragment : DaggerFragment() {
profileStore = activePlugin.activeProfileSource.profile ?: ProfileStore(injector, JSONObject(), dateUtil) profileStore = activePlugin.activeProfileSource.profile ?: ProfileStore(injector, JSONObject(), dateUtil)
profileName = if (binding.profileList.text.toString() == rh.gs(R.string.active)) "" else binding.profileList.text.toString() profileName = if (binding.profileList.text.toString() == rh.gs(R.string.active)) "" else binding.profileList.text.toString()
profileFunction.getProfile()?.let { currentProfile -> profileFunction.getProfile()?.let { currentProfile ->
profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?:currentProfile, LocalInsulin(""), injector) profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?: currentProfile, LocalInsulin(""), injector)
} }
binding.tuneDays.setParams( binding.tuneDays.setParams(
savedInstanceState?.getDouble("tunedays") savedInstanceState?.getDouble("tunedays")
?: defaultValue, 1.0, 30.0, 1.0, DecimalFormat("0"), false, null, textWatcher) ?: defaultValue, 1.0, 30.0, 1.0, DecimalFormat("0"), false, null, textWatcher
)
binding.autotuneRun.setOnClickListener { binding.autotuneRun.setOnClickListener {
val daysBack = SafeParse.stringToInt(binding.tuneDays.text) val daysBack = SafeParse.stringToInt(binding.tuneDays.text)
autotunePlugin.calculationRunning = true autotunePlugin.calculationRunning = true
@ -105,11 +109,10 @@ class AutotuneFragment : DaggerFragment() {
updateGui() updateGui()
} }
binding.profileList.onItemClickListener = AdapterView.OnItemClickListener { _, _, _, _ -> binding.profileList.onItemClickListener = AdapterView.OnItemClickListener { _, _, _, _ ->
if (!autotunePlugin.calculationRunning) if (!autotunePlugin.calculationRunning) {
{
profileName = if (binding.profileList.text.toString() == rh.gs(R.string.active)) "" else binding.profileList.text.toString() profileName = if (binding.profileList.text.toString() == rh.gs(R.string.active)) "" else binding.profileList.text.toString()
profileFunction.getProfile()?.let { currentProfile -> profileFunction.getProfile()?.let { currentProfile ->
profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?:currentProfile, LocalInsulin(""), injector) profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?: currentProfile, LocalInsulin(""), injector)
} }
autotunePlugin.selectedProfile = profileName autotunePlugin.selectedProfile = profileName
resetParam() resetParam()
@ -204,13 +207,13 @@ class AutotuneFragment : DaggerFragment() {
binding.autotuneCompare.setOnClickListener { binding.autotuneCompare.setOnClickListener {
val pumpProfile = autotunePlugin.pumpProfile val pumpProfile = autotunePlugin.pumpProfile
val circadian = sp.getBoolean(R.string.key_autotune_circadian_ic_isf, false) val circadian = sp.getBoolean(R.string.key_autotune_circadian_ic_isf, false)
val tunedprofile = if (circadian) autotunePlugin.tunedProfile?.circadianProfile else autotunePlugin.tunedProfile?.profile val tunedProfile = if (circadian) autotunePlugin.tunedProfile?.circadianProfile else autotunePlugin.tunedProfile?.profile
ProfileViewerDialog().also { pvd -> ProfileViewerDialog().also { pvd ->
pvd.arguments = Bundle().also { pvd.arguments = Bundle().also {
it.putLong("time", dateUtil.now()) it.putLong("time", dateUtil.now())
it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal) it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal)
it.putString("customProfile", pumpProfile.profile.toPureNsJson(dateUtil).toString()) it.putString("customProfile", pumpProfile.profile.toPureNsJson(dateUtil).toString())
it.putString("customProfile2", tunedprofile?.toPureNsJson(dateUtil).toString()) it.putString("customProfile2", tunedProfile?.toPureNsJson(dateUtil).toString())
it.putString("customProfileUnits", profileFunction.getUnits().asText) it.putString("customProfileUnits", profileFunction.getUnits().asText)
it.putString("customProfileName", pumpProfile.profilename + "\n" + rh.gs(R.string.autotune_tunedprofile_name)) it.putString("customProfileName", pumpProfile.profilename + "\n" + rh.gs(R.string.autotune_tunedprofile_name))
} }
@ -270,10 +273,8 @@ class AutotuneFragment : DaggerFragment() {
super.onResume() super.onResume()
disposable += rxBus disposable += rxBus
.toObservable(EventAutotuneUpdateGui::class.java) .toObservable(EventAutotuneUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({ updateGui() }, fabricPrivacy::logException)
updateGui()
}, { fabricPrivacy.logException(it) })
checkNewDay() checkNewDay()
updateGui() updateGui()
} }
@ -291,7 +292,7 @@ class AutotuneFragment : DaggerFragment() {
profileStore = activePlugin.activeProfileSource.profile ?: ProfileStore(injector, JSONObject(), dateUtil) profileStore = activePlugin.activeProfileSource.profile ?: ProfileStore(injector, JSONObject(), dateUtil)
profileName = if (binding.profileList.text.toString() == rh.gs(R.string.active)) "" else binding.profileList.text.toString() profileName = if (binding.profileList.text.toString() == rh.gs(R.string.active)) "" else binding.profileList.text.toString()
profileFunction.getProfile()?.let { currentProfile -> profileFunction.getProfile()?.let { currentProfile ->
profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?:currentProfile, LocalInsulin(""), injector) profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?: currentProfile, LocalInsulin(""), injector)
} }
val profileList: ArrayList<CharSequence> = profileStore.getProfileList() val profileList: ArrayList<CharSequence> = profileStore.getProfileList()
profileList.add(0, rh.gs(R.string.active)) profileList.add(0, rh.gs(R.string.active))
@ -315,6 +316,7 @@ class AutotuneFragment : DaggerFragment() {
autotunePlugin.calculationRunning -> { autotunePlugin.calculationRunning -> {
binding.tuneWarning.text = rh.gs(R.string.autotune_warning_during_run) binding.tuneWarning.text = rh.gs(R.string.autotune_warning_during_run)
} }
autotunePlugin.lastRunSuccess -> { autotunePlugin.lastRunSuccess -> {
binding.autotuneCopylocal.visibility = View.VISIBLE binding.autotuneCopylocal.visibility = View.VISIBLE
binding.autotuneUpdateProfile.visibility = autotunePlugin.updateButtonVisibility binding.autotuneUpdateProfile.visibility = autotunePlugin.updateButtonVisibility
@ -323,9 +325,9 @@ class AutotuneFragment : DaggerFragment() {
binding.tuneWarning.text = rh.gs(R.string.autotune_warning_after_run) binding.tuneWarning.text = rh.gs(R.string.autotune_warning_after_run)
binding.autotuneCompare.visibility = View.VISIBLE binding.autotuneCompare.visibility = View.VISIBLE
} }
else -> { else -> {
if (profile.isValid) binding.autotuneRun.visibility = (profile?.isValid == true).toVisibility()
binding.autotuneRun.visibility = View.VISIBLE
binding.autotuneCheckInputProfile.visibility = View.VISIBLE binding.autotuneCheckInputProfile.visibility = View.VISIBLE
} }
} }
@ -335,10 +337,9 @@ class AutotuneFragment : DaggerFragment() {
private fun checkNewDay() { private fun checkNewDay() {
val runToday = autotunePlugin.lastRun > MidnightTime.calc(dateUtil.now() - autotunePlugin.autotuneStartHour * 3600 * 1000L) + autotunePlugin.autotuneStartHour * 3600 * 1000L val runToday = autotunePlugin.lastRun > MidnightTime.calc(dateUtil.now() - autotunePlugin.autotuneStartHour * 3600 * 1000L) + autotunePlugin.autotuneStartHour * 3600 * 1000L
if (runToday && autotunePlugin.result != "") if (runToday && autotunePlugin.result != "") {
{
binding.tuneWarning.text = rh.gs(R.string.autotune_warning_after_run) binding.tuneWarning.text = rh.gs(R.string.autotune_warning_after_run)
} else if (!runToday || autotunePlugin.result.isEmpty()) { //if new day reinit result, default days, warning and button's visibility } else if (!runToday || autotunePlugin.result.isEmpty()) { //if new day re-init result, default days, warning and button's visibility
resetParam(!runToday) resetParam(!runToday)
} }
} }
@ -351,7 +352,7 @@ class AutotuneFragment : DaggerFragment() {
return warning return warning
} }
profileFunction.getProfile()?.let { currentProfile -> profileFunction.getProfile()?.let { currentProfile ->
profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?:currentProfile, LocalInsulin(""), injector) profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?: currentProfile, LocalInsulin(""), injector).also { profile ->
if (!profile.isValid) return rh.gs(R.string.autotune_profile_invalid) if (!profile.isValid) return rh.gs(R.string.autotune_profile_invalid)
if (profile.icSize > 1) { if (profile.icSize > 1) {
warning += nl + rh.gs(R.string.autotune_ic_warning, profile.icSize, profile.ic) warning += nl + rh.gs(R.string.autotune_ic_warning, profile.icSize, profile.ic)
@ -361,6 +362,7 @@ class AutotuneFragment : DaggerFragment() {
warning += nl + rh.gs(R.string.autotune_isf_warning, profile.isfSize, Profile.fromMgdlToUnits(profile.isf, profileFunction.getUnits()), profileFunction.getUnits().asText) warning += nl + rh.gs(R.string.autotune_isf_warning, profile.isfSize, Profile.fromMgdlToUnits(profile.isf, profileFunction.getUnits()), profileFunction.getUnits().asText)
} }
} }
}
return warning return warning
} }
@ -376,7 +378,10 @@ class AutotuneFragment : DaggerFragment() {
} }
private val textWatcher = object : TextWatcher { private val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) { updateGui() } override fun afterTextChanged(s: Editable) {
updateGui()
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (binding.tuneDays.text.isNotEmpty()) { if (binding.tuneDays.text.isNotEmpty()) {
@ -387,7 +392,9 @@ class AutotuneFragment : DaggerFragment() {
autotunePlugin.lastNbDays = binding.tuneDays.text autotunePlugin.lastNbDays = binding.tuneDays.text
resetParam(false) resetParam(false)
} }
} catch (e:Exception) { } } catch (e: Exception) {
fabricPrivacy.logException(e)
}
} }
} }
} }
@ -469,7 +476,7 @@ class AutotuneFragment : DaggerFragment() {
} }
} }
private fun toTableRowHeader(basal:Boolean = false): TableRow = private fun toTableRowHeader(basal: Boolean = false): TableRow =
TableRow(context).also { header -> TableRow(context).also { header ->
val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT).apply { weight = 1f } val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT).apply { weight = 1f }
header.layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT).apply { gravity = Gravity.CENTER_HORIZONTAL } header.layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT).apply { gravity = Gravity.CENTER_HORIZONTAL }
@ -500,7 +507,7 @@ class AutotuneFragment : DaggerFragment() {
}) })
} }
private fun toTableRowValue(hour: String, inputValue: Double, tunedValue: Double, format:String = "%.3f", missing: String = ""): TableRow = private fun toTableRowValue(hour: String, inputValue: Double, tunedValue: Double, format: String = "%.3f", missing: String = ""): TableRow =
TableRow(context).also { row -> TableRow(context).also { row ->
val percentValue = Round.roundTo(tunedValue / inputValue * 100 - 100, 1.0).toInt().toString() + "%" val percentValue = Round.roundTo(tunedValue / inputValue * 100 - 100, 1.0).toInt().toString() + "%"
val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT).apply { weight = 1f } val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT).apply { weight = 1f }

View file

@ -17,9 +17,15 @@ import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientU
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.sdk.NSAndroidClient
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class NSClientFragment : DaggerFragment() { class NSClientFragment : DaggerFragment() {
@ -32,6 +38,7 @@ class NSClientFragment : DaggerFragment() {
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var dataSyncSelector: DataSyncSelector @Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var aapsLogger: AAPSLogger
companion object { companion object {
@ -39,6 +46,7 @@ class NSClientFragment : DaggerFragment() {
const val ID_MENU_RESTART = 7 const val ID_MENU_RESTART = 7
const val ID_MENU_SEND_NOW = 8 const val ID_MENU_SEND_NOW = 8
const val ID_MENU_FULL_SYNC = 9 const val ID_MENU_FULL_SYNC = 9
const val ID_MENU_STATUS = 10
} }
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
@ -80,6 +88,7 @@ class NSClientFragment : DaggerFragment() {
menu.add(Menu.FIRST, ID_MENU_RESTART, 0, rh.gs(R.string.restart)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_RESTART, 0, rh.gs(R.string.restart)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(Menu.FIRST, ID_MENU_SEND_NOW, 0, rh.gs(R.string.deliver_now)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_SEND_NOW, 0, rh.gs(R.string.deliver_now)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(Menu.FIRST, ID_MENU_FULL_SYNC, 0, rh.gs(R.string.full_sync)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_FULL_SYNC, 0, rh.gs(R.string.full_sync)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(Menu.FIRST, ID_MENU_STATUS, 0, "TEST STATUS").setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.setGroupDividerEnabled(true) menu.setGroupDividerEnabled(true)
} }
} }
@ -111,6 +120,25 @@ class NSClientFragment : DaggerFragment() {
true true
} }
ID_MENU_STATUS -> {
context?.let { context ->
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
scope.launch {
val client = NSAndroidClient(
baseUrl = sp.getString(R.string.key_nsclientinternal_url, "").lowercase().replace("https://", ""),
accessToken = sp.getString(R.string.key_nsclient_token, ""),
context = context,
logging = true
)
val status = client.getStatus()
aapsLogger.debug(status.toString())
val svgs = client.getSgvs()
aapsLogger.debug(svgs.toString())
}
}
true
}
else -> false else -> false
} }