diff --git a/app/build.gradle b/app/build.gradle index ffa92cddd2..65236b15cf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -105,7 +105,7 @@ android { defaultConfig { multiDexEnabled true versionCode 1500 - version "3.0.0.2-dev-n" + version "3.0.0.2-dev-o" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' diff --git a/app/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js b/app/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js index 2944f6077f..24b8c29718 100644 --- a/app/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js @@ -218,7 +218,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ //********************************************************************************* console.error("---------------------------------------------------------"); - console.error( " Dynamic ISF version Beta 1.6.4 "); + console.error( " Dynamic ISF version Beta 1.6.5 "); console.error("---------------------------------------------------------"); @@ -824,14 +824,21 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ console.log("EventualBG is" +eventualBG+" ;"); + minIOBPredBG = Math.max(39,minIOBPredBG); + minCOBPredBG = Math.max(39,minCOBPredBG); + minUAMPredBG = Math.max(39,minUAMPredBG); + minPredBG = round(minIOBPredBG); + + var fSensBG = Math.min(minPredBG,bg); + if (bg > target_bg && glucose_status.delta < 3 && glucose_status.delta > -3 && glucose_status.short_avgdelta > -3 && glucose_status.short_avgdelta < 3 && eventualBG > target_bg && eventualBG < bg ) { - var future_sens = ( 1800 / (Math.log((((eventualBG * 0.5) + (bg * 0.5))/ins_val)+1)*TDD)); + var future_sens = ( 1800 / (Math.log((((fSensBG * 0.5) + (bg * 0.5))/ins_val)+1)*TDD)); //var future_sens_old = ( 277700 / (TDD * ((bg * 0.5) + (eventualBG * 0.5 )))); console.log("Future state sensitivity is " +future_sens+" based on eventual and current bg due to flat glucose level above target"); rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;"; } - else if( glucose_status.delta > 0 && eventualBG > target_bg ) { + else if( glucose_status.delta > 0 && eventualBG > target_bg || eventualBG > bg ) { var future_sens = ( 1800 / (Math.log((bg/ins_val)+1)*TDD)); //var future_sens_old = ( 277700 / (TDD * bg)); console.log("Future state sensitivity is " +future_sens+" using current bg due to small delta or variation"); @@ -839,7 +846,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } else { - var future_sens = ( 1800 / (Math.log((eventualBG/ins_val)+1)*TDD)); + var future_sens = ( 1800 / (Math.log((fSensBG/ins_val)+1)*TDD)); //var future_sens_old = ( 277700 / (TDD * eventualBG)); console.log("Future state sensitivity is " +future_sens+" based on eventual bg due to -ve delta"); rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;"; @@ -848,10 +855,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ - minIOBPredBG = Math.max(39,minIOBPredBG); - minCOBPredBG = Math.max(39,minCOBPredBG); - minUAMPredBG = Math.max(39,minUAMPredBG); - minPredBG = round(minIOBPredBG); + var fractionCarbsLeft = meal_data.mealCOB/meal_data.carbs; // if we have COB and UAM is enabled, average both @@ -1314,4 +1318,4 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ }; -module.exports = determine_basal; \ No newline at end of file +module.exports = determine_basal; diff --git a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt index c0f577d427..8da9192724 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt @@ -49,6 +49,7 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck.ProtectionType.* import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.plugins.aps.openAPSSMBDynamicISF.OpenAPSSMBDynamicISFPlugin import info.nightscout.shared.SafeParse import info.nightscout.shared.sharedPreferences.SP import javax.inject.Inject @@ -79,6 +80,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang @Inject lateinit var nsClientPlugin: NSClientPlugin @Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin @Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin + @Inject lateinit var openAPSSMBDynamicISFPlugin: OpenAPSSMBDynamicISFPlugin @Inject lateinit var safetyPlugin: SafetyPlugin @Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin @Inject lateinit var sensitivityOref1Plugin: SensitivityOref1Plugin @@ -171,6 +173,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang addPreferencesFromResourceIfEnabled(loopPlugin, rootKey, config.APS) addPreferencesFromResourceIfEnabled(openAPSAMAPlugin, rootKey, config.APS) addPreferencesFromResourceIfEnabled(openAPSSMBPlugin, rootKey, config.APS) + addPreferencesFromResourceIfEnabled(openAPSSMBDynamicISFPlugin, rootKey, config.APS) addPreferencesFromResourceIfEnabled(sensitivityAAPSPlugin, rootKey) addPreferencesFromResourceIfEnabled(sensitivityWeightedAveragePlugin, rootKey) addPreferencesFromResourceIfEnabled(sensitivityOref1Plugin, rootKey) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.kt index 60f0f5006a..7725617e88 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.kt @@ -2,9 +2,9 @@ package info.nightscout.androidaps.activities import android.content.Context import android.os.Bundle -import android.text.Editable -import android.text.TextWatcher +import android.view.Menu import android.view.MenuItem +import androidx.appcompat.widget.SearchView import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceScreen import info.nightscout.androidaps.R @@ -15,6 +15,7 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa private var preferenceId = 0 private var myPreferenceFragment: MyPreferenceFragment? = null + private var searchView: SearchView? = null private lateinit var binding: ActivityPreferencesBinding @@ -24,15 +25,6 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa binding = ActivityPreferencesBinding.inflate(layoutInflater) setContentView(binding.root) - binding.prefFilter.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - filterPreferences() - } - - override fun afterTextChanged(s: Editable) {} - }) - title = rh.gs(R.string.nav_preferences) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true) @@ -40,12 +32,29 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa preferenceId = intent.getIntExtra("id", -1) myPreferenceFragment?.arguments = Bundle().also { it.putInt("id", preferenceId) - it.putString("filter", binding.prefFilter.text.toString()) } if (savedInstanceState == null) supportFragmentManager.beginTransaction().replace(R.id.frame_layout, myPreferenceFragment!!).commit() } + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_preferences, menu) + val searchItem = menu.findItem(R.id.menu_search) + searchView = searchItem.actionView as SearchView + searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + + override fun onQueryTextChange(newText: String): Boolean { + myPreferenceFragment?.setFilter(newText) + return false + } + + override fun onQueryTextSubmit(query: String): Boolean { + return false + } + }) + return super.onCreateOptionsMenu(menu) + } + override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen): Boolean { val fragment = MyPreferenceFragment() fragment.arguments = Bundle().also { @@ -60,10 +69,6 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa super.attachBaseContext(LocaleHelper.wrap(newBase)) } - private fun filterPreferences() { - myPreferenceFragment?.setFilter(binding.prefFilter.text.toString()) - } - override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { android.R.id.home -> { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt index 81563f0944..dcba11d059 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt @@ -1,13 +1,13 @@ package info.nightscout.androidaps.activities import android.annotation.SuppressLint -import android.content.res.ColorStateList import android.os.Bundle import android.text.Editable import android.text.TextWatcher -import android.view.Menu -import android.widget.PopupMenu +import android.widget.ArrayAdapter import android.widget.TextView +import com.google.android.material.tabs.TabLayout +import com.google.common.collect.Lists import info.nightscout.androidaps.R import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.data.PureProfile @@ -18,7 +18,6 @@ import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged import info.nightscout.androidaps.utils.DateUtil @@ -72,61 +71,49 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { binding = ActivityProfilehelperBinding.inflate(layoutInflater) setContentView(binding.root) - binding.menu1.setOnClickListener { - switchTab(0, typeSelected[0]) - } - binding.menu2.setOnClickListener { - switchTab(1, typeSelected[1]) - } + binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + switchTab(tab.position, typeSelected[tab.position]) + } - binding.profileType.setOnClickListener { - PopupMenu(this, binding.profileType).apply { - menuInflater.inflate(R.menu.menu_profilehelper, menu) - setOnMenuItemClickListener { item -> - binding.profileType.setText(item.title) - when (item.itemId) { - R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT) - R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT) - R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT) - R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE) - R.id.menu_profileswitch -> switchTab(tabSelected, ProfileType.PROFILE_SWITCH) - } - true - } - show() + override fun onTabUnselected(tab: TabLayout.Tab) {} + override fun onTabReselected(tab: TabLayout.Tab) {} + }) + + val profileTypeList = Lists.newArrayList( + rh.gs(R.string.motoldefaultprofile), + rh.gs(R.string.dpvdefaultprofile), + rh.gs(R.string.currentprofile), + rh.gs(R.string.availableprofile), + rh.gs(R.string.careportal_profileswitch) + ) + binding.profileType.setAdapter(ArrayAdapter(this, R.layout.spinner_centered, profileTypeList)) + + binding.profileType.setOnItemClickListener { _, _, _, _ -> + when (binding.profileType.text.toString()) { + rh.gs(R.string.motoldefaultprofile) -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT) + rh.gs(R.string.dpvdefaultprofile) -> switchTab(tabSelected, ProfileType.DPV_DEFAULT) + rh.gs(R.string.currentprofile) -> switchTab(tabSelected, ProfileType.CURRENT) + rh.gs(R.string.availableprofile) -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE) + rh.gs(R.string.careportal_profileswitch) -> switchTab(tabSelected, ProfileType.PROFILE_SWITCH) } } // Active profile profileList = activePlugin.activeProfileSource.profile?.getProfileList() ?: ArrayList() - binding.availableProfileList.setOnClickListener { - PopupMenu(this, binding.availableProfileList).apply { - var order = 0 - for (name in profileList) menu.add(Menu.NONE, order, order++, name) - setOnMenuItemClickListener { item -> - binding.availableProfileList.setText(item.title) - profileUsed[tabSelected] = item.itemId - true - } - show() - } + binding.availableProfileList.setAdapter(ArrayAdapter(this, R.layout.spinner_centered, profileList)) + binding.availableProfileList.setOnItemClickListener { _, _, index, _ -> + profileUsed[tabSelected] = index } // Profile switch profileSwitch = repository.getEffectiveProfileSwitchDataFromTime(dateUtil.now() - T.months(2).msecs(), true).blockingGet() - binding.profileswitchList.setOnClickListener { - PopupMenu(this, binding.profileswitchList).apply { - var order = 0 - for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.originalCustomizedName) - setOnMenuItemClickListener { item -> - binding.profileswitchList.setText(item.title) - profileSwitchUsed[tabSelected] = item.itemId - true - } - show() - } + val profileswitchListNames = profileSwitch.map { it.originalCustomizedName } + binding.profileswitchList.setAdapter(ArrayAdapter(this, R.layout.spinner_centered, profileswitchListNames)) + binding.profileswitchList.setOnItemClickListener { _, _, index, _ -> + profileSwitchUsed[tabSelected] = index } // Default profile @@ -250,7 +237,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): PureProfile? = - try { // profile must not exist + try { // Profile must not exist when (typeSelected[tab]) { ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits()) @@ -279,15 +266,13 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } private fun switchTab(tab: Int, newContent: ProfileType, storeOld: Boolean = true) { - setBackgroundColorOnSelected(tab) // Store values for selected tab. listBox values are stored on selection change if (storeOld) storeValues() tabSelected = tab typeSelected[tabSelected] = newContent - binding.profileTypeTitle.defaultHintTextColor = ColorStateList.valueOf(rh.gac( this, if (tab == 0) R.attr.helperProfileColor else R.attr.examinedProfileColor)) - // show new content + // Show new content binding.profileType.setText( when (typeSelected[tabSelected]) { ProfileType.MOTOL_DEFAULT -> rh.gs(R.string.motoldefaultprofile) @@ -295,28 +280,27 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { ProfileType.CURRENT -> rh.gs(R.string.currentprofile) ProfileType.AVAILABLE_PROFILE -> rh.gs(R.string.availableprofile) ProfileType.PROFILE_SWITCH -> rh.gs(R.string.careportal_profileswitch) - } + }, + false ) binding.defaultProfile.visibility = (newContent == ProfileType.MOTOL_DEFAULT || newContent == ProfileType.DPV_DEFAULT).toVisibility() binding.currentProfile.visibility = (newContent == ProfileType.CURRENT).toVisibility() binding.availableProfile.visibility = (newContent == ProfileType.AVAILABLE_PROFILE).toVisibility() binding.profileSwitch.visibility = (newContent == ProfileType.PROFILE_SWITCH).toVisibility() - // restore selected values + // Restore selected values binding.age.value = ageUsed[tabSelected] binding.weight.value = weightUsed[tabSelected] binding.tdd.value = tddUsed[tabSelected] binding.basalPctFromTdd.value = pctUsed[tabSelected] binding.basalPctFromTddRow.visibility = (newContent == ProfileType.DPV_DEFAULT).toVisibility() - if (profileList.isNotEmpty()) - binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString()) - if (profileSwitch.isNotEmpty()) - binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].originalCustomizedName) + if (profileList.isNotEmpty()) { + binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString(), false) + } + if (profileSwitch.isNotEmpty()) { + binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].originalCustomizedName, false) + } } - private fun setBackgroundColorOnSelected(tab: Int) { - binding.menu1.setBackgroundColor(rh.gac(this, if (tab == 1) R.attr.defaultBackground else R.attr.helperProfileColor)) - binding.menu2.setBackgroundColor(rh.gac(this, if (tab == 0) R.attr.defaultBackground else R.attr.examinedProfileColor)) - } } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt index 7c73776689..ff3d0c778a 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt @@ -56,9 +56,9 @@ class CarbsDialog : DialogFragmentWithDate() { companion object { - private const val FAV1_DEFAULT = 5 - private const val FAV2_DEFAULT = 10 - private const val FAV3_DEFAULT = 20 + const val FAV1_DEFAULT = 5 + const val FAV2_DEFAULT = 10 + const val FAV3_DEFAULT = 20 } private var queryingProtection = false diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt index 9efb110a4f..7c45fe99ee 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt @@ -58,9 +58,9 @@ class InsulinDialog : DialogFragmentWithDate() { companion object { - private const val PLUS1_DEFAULT = 0.5 - private const val PLUS2_DEFAULT = 1.0 - private const val PLUS3_DEFAULT = 2.0 + const val PLUS1_DEFAULT = 0.5 + const val PLUS2_DEFAULT = 1.0 + const val PLUS3_DEFAULT = 2.0 } private var queryingProtection = false diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt index 8af683bcdb..9c8fe4ba91 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt @@ -345,6 +345,7 @@ class WizardDialog : DaggerDialogFragment() { val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() runOnUiThread { + _binding ?: return@runOnUiThread if (carbsPassedIntoWizard != 0.0) { binding.carbsInput.value = carbsPassedIntoWizard } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective2.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective2.kt index b32fab8e96..4c79036f9b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective2.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective2.kt @@ -178,6 +178,21 @@ class Objective2(injector: HasAndroidInjector) : Objective(injector, "exam", R.s .option(Option(R.string.iob_negiob, true)) .option(Option(R.string.iob_posiob, true)) ) + tasks.add(ExamTask(this, R.string.cob_label, R.string.cob_question, "cob1") + .option(Option(R.string.cob_longer, true)) + .option(Option(R.string.cob_shorter, false)) + .option(Option(R.string.cob_no_effect, false)) + ) + tasks.add(ExamTask(this, R.string.cob_label, R.string.cob2_question, "cob2") + .option(Option(R.string.cob2_longer, false)) + .option(Option(R.string.cob2_shorter, true)) + .option(Option(R.string.cob2_no_effect, false)) + ) + tasks.add(ExamTask(this, R.string.cob_label, R.string.cob3_question, "cob3") + .option(Option(R.string.cob3_longer, false)) + .option(Option(R.string.cob3_shorter, false)) + .option(Option(R.string.cob3_no_effect, true)) + ) tasks.add(ExamTask(this, R.string.breadgrams_label, R.string.blank, "breadgrams") .option(Option(R.string.breadgrams_grams, true)) .option(Option(R.string.breadgrams_exchange, false)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneFragment.kt index 4895dc48c8..b96dd4bdeb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneFragment.kt @@ -113,7 +113,6 @@ class AutotuneFragment : DaggerFragment() { ?: defaultValue, 1.0, 30.0, 1.0, DecimalFormat("0"), false, null, textWatcher ) binding.autotuneRun.setOnClickListener { - autotunePlugin.calculationRunning = true autotunePlugin.lastNbDays = daysBack.toString() log("Run Autotune $profileName, $daysBack days") Thread { @@ -146,7 +145,7 @@ class AutotuneFragment : DaggerFragment() { autotunePlugin.tunedProfile?.let { tunedProfile -> showConfirmation(requireContext(), rh.gs(R.string.autotune_copy_localprofile_button), - rh.gs(R.string.autotune_copy_local_profile_message) + "\n" + localName + " " + dateUtil.dateAndTimeString(autotunePlugin.lastRun), + rh.gs(R.string.autotune_copy_local_profile_message) + "\n" + localName, Runnable { localProfilePlugin.addProfile(localProfilePlugin.copyFrom(tunedProfile.getProfile(circadian), localName)) rxBus.send(EventLocalProfileChanged()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneIob.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneIob.kt index bac89d9f9e..a634ee876c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneIob.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneIob.kt @@ -39,7 +39,7 @@ open class AutotuneIob @Inject constructor( private val autotuneFS: AutotuneFS ) { - private val nsTreatments = ArrayList() + private var nsTreatments = ArrayList() private var dia: Double = Constants.defaultDIA var boluses: ArrayList = ArrayList() var meals = ArrayList() @@ -59,14 +59,28 @@ open class AutotuneIob @Inject constructor( initializeTreatmentData(from - range(), to) initializeTempBasalData(from - range(), to, tunedProfile) initializeExtendedBolusData(from - range(), to, tunedProfile) - tempBasals.sortWith { o1: TemporaryBasal, o2: TemporaryBasal -> (o2.timestamp - o1.timestamp).toInt() } - // Without Neutral TBR, Autotune Web will ignore iob for periods without TBR running - addNeutralTempBasal(from - range(), to, tunedProfile) - nsTreatments.sortWith { o1: NsTreatment, o2: NsTreatment -> (o2.date - o1.date).toInt() } - this.boluses.sortWith { o1: Bolus, o2: Bolus -> (o2.timestamp - o1.timestamp).toInt() } + sortTempBasal() + addNeutralTempBasal(from - range(), to, tunedProfile) // Without Neutral TBR, Autotune Web will ignore iob for periods without TBR running + sortNsTreatments() + sortBoluses() aapsLogger.debug(LTag.AUTOTUNE, "Nb Treatments: " + nsTreatments.size + " Nb meals: " + meals.size) } + @Synchronized + private fun sortTempBasal() { + tempBasals = ArrayList(tempBasals.toList().sortedWith { o1: TemporaryBasal, o2: TemporaryBasal -> (o2.timestamp - o1.timestamp).toInt() }) + } + + @Synchronized + private fun sortNsTreatments() { + nsTreatments = ArrayList(nsTreatments.toList().sortedWith { o1: NsTreatment, o2: NsTreatment -> (o2.date - o1.date).toInt() }) + } + + @Synchronized + private fun sortBoluses() { + boluses = ArrayList(boluses.toList().sortedWith { o1: Bolus, o2: Bolus -> (o2.timestamp - o1.timestamp).toInt() }) + } + private fun initializeBgreadings(from: Long, to: Long) { glucose = repository.compatGetBgReadingsDataFromTime(from, to, false).blockingGet() } @@ -146,6 +160,7 @@ open class AutotuneIob @Inject constructor( // addNeutralTempBasal will add a fake neutral TBR (100%) to have correct basal rate in exported file for periods without TBR running // to be able to compare results between oref0 algo and aaps + @Synchronized private fun addNeutralTempBasal(from: Long, to: Long, tunedProfile: ATProfile) { var previousStart = to for (i in tempBasals.indices) { @@ -180,6 +195,7 @@ open class AutotuneIob @Inject constructor( // toSplittedTimestampTB will split all TBR across hours in different TBR with correct absolute value to be sure to have correct basal rate // even if profile rate is not the same + @Synchronized private fun toSplittedTimestampTB(tb: TemporaryBasal, tunedProfile: ATProfile) { var splittedTimestamp = tb.timestamp val cutInMilliSec = T.mins(60).msecs() //30 min to compare with oref0, 60 min to improve accuracy @@ -295,7 +311,7 @@ open class AutotuneIob @Inject constructor( return result } - + @Synchronized fun glucoseToJSON(): String { val glucoseJson = JSONArray() for (bgreading in glucose) @@ -303,6 +319,7 @@ open class AutotuneIob @Inject constructor( return glucoseJson.toString(2) } + @Synchronized fun bolusesToJSON(): String { val bolusesJson = JSONArray() for (bolus in boluses) @@ -310,6 +327,7 @@ open class AutotuneIob @Inject constructor( return bolusesJson.toString(2) } + @Synchronized fun nsHistoryToJSON(): String { val json = JSONArray() for (t in nsTreatments) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotunePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotunePlugin.kt index 46367ec5b4..869ee8001f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotunePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotunePlugin.kt @@ -23,6 +23,7 @@ import info.nightscout.androidaps.utils.MidnightTime import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.interfaces.BuildHelper import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP import org.json.JSONException import org.json.JSONObject @@ -65,7 +66,7 @@ class AutotunePlugin @Inject constructor( ), Autotune { @Volatile override var lastRunSuccess: Boolean = false @Volatile var result: String = "" - @Volatile var calculationRunning: Boolean = false + @Volatile override var calculationRunning: Boolean = false @Volatile var lastRun: Long = 0 @Volatile var selectedProfile = "" @Volatile var lastNbDays: String = "" @@ -77,7 +78,13 @@ class AutotunePlugin @Inject constructor( val days = InputWeekDay() val autotuneStartHour: Int = 4 - override fun aapsAutotune(daysBack: Int, autoSwitch: Boolean, profileToTune: String, weekDays: BooleanArray?): String { + override fun aapsAutotune(daysBack: Int, autoSwitch: Boolean, profileToTune: String, weekDays: BooleanArray?) { + lastRunSuccess = false + if (calculationRunning) { + aapsLogger.debug(LTag.AUTOMATION, "Autotune run detected, Autotune Run Cancelled") + return + } + calculationRunning = true weekDays?.let { for (i in weekDays.indices) days.weekdays[i] = weekDays[i] @@ -93,18 +100,25 @@ class AutotunePlugin @Inject constructor( log(sb.toString()) tunedProfile = null updateButtonVisibility = View.GONE - lastRunSuccess = false var logResult = "" result = "" if (profileFunction.getProfile() == null) { result = rh.gs(R.string.profileswitch_ismissing) - return result + rxBus.send(EventAutotuneUpdateGui()) + calculationRunning = false + return } val detailedLog = sp.getBoolean(R.string.key_autotune_additional_log, false) calculationRunning = true lastNbDays = "" + daysBack lastRun = dateUtil.now() - val profileStore = activePlugin.activeProfileSource.profile ?: return rh.gs(R.string.profileswitch_ismissing) + val profileStore = activePlugin.activeProfileSource.profile + if (profileStore == null) { + result = rh.gs(R.string.profileswitch_ismissing) + rxBus.send(EventAutotuneUpdateGui()) + calculationRunning = false + return + } selectedProfile = if (profileToTune.isEmpty()) profileFunction.getProfileName() else profileToTune profileFunction.getProfile()?.let { currentProfile -> profile = profileStore.getSpecificProfile(profileToTune)?.let { ProfileSealed.Pure(it) } ?: currentProfile @@ -153,7 +167,7 @@ class AutotunePlugin @Inject constructor( preppedGlucose?.let { preppedGlucose -> //preppedGlucose and tunedProfile should never be null here autotuneFS.exportPreppedGlucose(preppedGlucose) tunedProfile = autotuneCore.tuneAllTheThings(preppedGlucose, it, pumpProfile).also { tunedProfile -> - autotuneFS.exportTunedProfile(tunedProfile!!) //<=> newprofile.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine + autotuneFS.exportTunedProfile(tunedProfile) //<=> newprofile.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine if (currentCalcDay < calcDays) { log("Partial result for day ${i + 1}".trimIndent()) result = rh.gs(R.string.autotune_partial_result, currentCalcDay, calcDays) @@ -172,11 +186,11 @@ class AutotunePlugin @Inject constructor( if (tunedProfile == null) { result = rh.gs(R.string.autotune_error) log("TunedProfile is null on day ${i + 1}") - calculationRunning = false - rxBus.send(EventAutotuneUpdateGui()) autotuneFS.exportResult(result) autotuneFS.exportLogAndZip(lastRun) - return result + rxBus.send(EventAutotuneUpdateGui()) + calculationRunning = false + return } } } @@ -222,13 +236,16 @@ class AutotunePlugin @Inject constructor( } tunedProfile?.let { - lastRunSuccess = true saveLastRun() + lastRunSuccess = true rxBus.send(EventAutotuneUpdateGui()) calculationRunning = false - return result + return } - return rh.gs(R.string.autotune_error) + result = rh.gs(R.string.autotune_error) + rxBus.send(EventAutotuneUpdateGui()) + calculationRunning = false + return } private fun showResults(tunedProfile: ATProfile?, pumpProfile: ATProfile): String { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt index 63429ac077..64623dbf12 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt @@ -12,6 +12,8 @@ import info.nightscout.androidaps.database.entities.* import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction +import info.nightscout.androidaps.dialogs.CarbsDialog +import info.nightscout.androidaps.dialogs.InsulinDialog import info.nightscout.androidaps.events.EventMobileToWear import info.nightscout.androidaps.extensions.convertedToAbsolute import info.nightscout.androidaps.extensions.toStringShort @@ -269,6 +271,13 @@ class DataHandlerMobile @Inject constructor( aapsLogger.debug(LTag.WEAR, "SnoozeAlert received $it from ${it.sourceNodeId}") alarmSoundServiceHelper.stopService(context, "Muted from wear") }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.WearException::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "WearException received $it from ${it.sourceNodeId}") + fabricPrivacy.logWearException(it) + }, fabricPrivacy::logException) } private fun handleTddStatus() { @@ -694,7 +703,11 @@ class DataHandlerMobile @Inject constructor( unitsMgdl = profileFunction.getUnits() == GlucoseUnit.MGDL, bolusPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100), maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48), - maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0) + maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0), + insulinButtonIncrement1 = sp.getDouble(R.string.key_insulin_button_increment_1, InsulinDialog.PLUS1_DEFAULT), + insulinButtonIncrement2 = sp.getDouble(R.string.key_insulin_button_increment_2, InsulinDialog.PLUS2_DEFAULT), + carbsButtonIncrement1 = sp.getInt(R.string.key_carbs_button_increment_1, CarbsDialog.FAV1_DEFAULT), + carbsButtonIncrement2 = sp.getInt(R.string.key_carbs_button_increment_2, CarbsDialog.FAV2_DEFAULT) ) ) ) @@ -1185,4 +1198,4 @@ class DataHandlerMobile @Inject constructor( @Synchronized private fun sendError(errorMessage: String) { rxBus.send(EventMobileToWear(EventData.ConfirmAction(rh.gs(R.string.error), errorMessage, returnCommand = EventData.Error(dateUtil.now())))) // ignore return path } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt index ed1ac23bdb..4292307c59 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt @@ -8,18 +8,13 @@ import dagger.android.AndroidInjection import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.events.EventMobileToWear -import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.androidaps.interfaces.Config -import info.nightscout.androidaps.interfaces.IobCobCalculator -import info.nightscout.androidaps.interfaces.Loop -import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.plugins.general.wear.WearPlugin import info.nightscout.androidaps.plugins.general.wear.events.EventWearUpdateGui import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.utils.DefaultValueHelper -import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.wizard.QuickWizard @@ -116,7 +111,7 @@ class DataLayerListenerServiceMobile : WearableListenerService() { if (wearPlugin.isEnabled()) { when (messageEvent.path) { rxPath -> { - aapsLogger.debug(LTag.WEAR, "onMessageReceived: ${String(messageEvent.data)}") + aapsLogger.debug(LTag.WEAR, "onMessageReceived rxPath: ${String(messageEvent.data)}") val command = EventData.deserialize(String(messageEvent.data)) rxBus.send(command.also { it.sourceNodeId = messageEvent.sourceNodeId }) } @@ -134,7 +129,7 @@ class DataLayerListenerServiceMobile : WearableListenerService() { aapsLogger.debug(LTag.WEAR, "Nodes: ${capabilityInfo.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}") val bestNode = pickBestNodeId(capabilityInfo.nodes) transcriptionNodeId = bestNode?.id - wearPlugin.connectedDevice = bestNode?.displayName ?: "---" + wearPlugin.connectedDevice = bestNode?.displayName ?: rh.gs(R.string.no_watch_connected) rxBus.send(EventWearUpdateGui()) aapsLogger.debug(LTag.WEAR, "Selected node: ${bestNode?.displayName} $transcriptionNodeId") rxBus.send(EventMobileToWear(EventData.ActionPing(System.currentTimeMillis()))) @@ -203,4 +198,4 @@ class DataLayerListenerServiceMobile : WearableListenerService() { const val WEAR_CAPABILITY = "androidaps_wear" } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt index 7e0e29fc8f..a68d6ee25a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt @@ -23,6 +23,7 @@ import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged @@ -52,6 +53,7 @@ class LocalProfileFragment : DaggerFragment() { @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var localProfilePlugin: LocalProfilePlugin + @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var hardLimits: HardLimits @Inject lateinit var protectionCheck: ProtectionCheck @Inject lateinit var dateUtil: DateUtil @@ -117,6 +119,11 @@ class LocalProfileFragment : DaggerFragment() { }) binding.diaLabel.labelFor = binding.dia.editTextId binding.unlock.setOnClickListener { queryProtection() } + + val profiles = localProfilePlugin.profile?.getProfileList() ?: ArrayList() + val activeProfile = profileFunction.getProfileName() + val profileIndex = profiles.indexOf(activeProfile) + localProfilePlugin.currentProfileIndex = if (profileIndex >= 0) profileIndex else 0 } fun build() { @@ -387,13 +394,14 @@ class LocalProfileFragment : DaggerFragment() { private fun processVisibility(position: Int) { binding.diaPlaceholder.visibility = (position == 0).toVisibility() - binding.ic.visibility = (position == 1).toVisibility() - binding.isf.visibility = (position == 2).toVisibility() - binding.basal.visibility = (position == 3).toVisibility() - binding.target.visibility = (position == 4).toVisibility() + binding.ic.visibility = (position == 1).toVisibility() + binding.isf.visibility = (position == 2).toVisibility() + binding.basal.visibility = (position == 3).toVisibility() + binding.target.visibility = (position == 4).toVisibility() } private fun updateProtectedUi() { + _binding ?: return val isLocked = protectionCheck.isLocked(ProtectionCheck.Protection.PREFERENCES) binding.mainLayout.visibility = isLocked.not().toVisibility() binding.unlock.visibility = isLocked.toVisibility() diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt index e07e492e9a..f9b7ba509a 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt @@ -1,6 +1,5 @@ package info.nightscout.androidaps.utils.stats -import android.annotation.SuppressLint import android.content.Context import android.graphics.Typeface import android.util.LongSparseArray @@ -99,9 +98,19 @@ class TddCalculator @Inject constructor( return result } + fun calculateToday(): TotalDailyDose { + var startTime = MidnightTime.calc(dateUtil.now()) + val endTime = dateUtil.now() + return calculate(startTime, endTime) + } + 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 { val tdd = TotalDailyDose(timestamp = startTime) repository.getBolusesDataFromTimeToTime(startTime, endTime, true).blockingGet() .filter { it.type != Bolus.Type.PRIMING } @@ -147,10 +156,10 @@ class TddCalculator @Inject constructor( return totalTdd } - @SuppressLint("SetTextI18n") fun stats(context: Context): TableLayout { val tdds = calculate(7) val averageTdd = averageTDD(tdds) + val todayTdd = calculateToday() val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT) return TableLayout(context).also { layout -> layout.layoutParams = TableLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) @@ -172,6 +181,13 @@ class TddCalculator @Inject constructor( }) layout.addView(averageTdd.toTableRow(context, rh, tdds.size(), includeCarbs = true)) } + layout.addView(TextView(context).apply { + text = rh.gs(R.string.today) + setTypeface(typeface, Typeface.BOLD) + gravity = Gravity.CENTER_HORIZONTAL + setTextAppearance(android.R.style.TextAppearance_Material_Medium) + }) + layout.addView(todayTdd.toTableRow(context, rh, dateUtil, includeCarbs = true)) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/ui/TimeListEdit.java b/app/src/main/java/info/nightscout/androidaps/utils/ui/TimeListEdit.java index 437bb3cbd2..e1645c907f 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/ui/TimeListEdit.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/ui/TimeListEdit.java @@ -182,7 +182,7 @@ public class TimeListEdit { numberPickers1[position].setTextWatcher(new TextWatcher() { @Override public void afterTextChanged(Editable s) { - Double value1 = SafeParse.INSTANCE.stringToDouble(numberPickers1[position].getText()); + Double value1 = SafeParse.INSTANCE.stringToDouble(numberPickers1[position].getText(), 0.0); Double value2 = value2(position); if (data2 != null && value1 > value2) { value2 = value1; @@ -209,7 +209,7 @@ public class TimeListEdit { @Override public void afterTextChanged(Editable s) { Double value1 = value1(position); - Double value2 = SafeParse.INSTANCE.stringToDouble(numberPickers2[position].getText()); + Double value2 = SafeParse.INSTANCE.stringToDouble(numberPickers2[position].getText(), 0.0); if (data2 != null && value2 < value1) { value1 = value2; numberPickers1[position].setValue(value1); diff --git a/app/src/main/res/drawable/ic_sort.xml b/app/src/main/res/drawable/ic_sort.xml index 4b1317ac20..8ff707c8ca 100644 --- a/app/src/main/res/drawable/ic_sort.xml +++ b/app/src/main/res/drawable/ic_sort.xml @@ -2,7 +2,6 @@ android:width="24dp" android:height="24dp" android:autoMirrored="true" - android:tint="#FFFFFF" android:viewportWidth="24" android:viewportHeight="24"> - - - - - - - + android:layout_marginBottom="10dp"> - - - + + android:hint="@string/profiletype" + android:paddingHorizontal="5dp"> - + android:inputType="none" /> @@ -206,21 +192,18 @@ android:orientation="vertical"> + android:hint="@string/selected_profile" + android:paddingHorizontal="5dp"> - + android:inputType="none" /> @@ -233,21 +216,19 @@ android:orientation="vertical"> + android:hint="@string/careportal_profileswitch" + android:paddingHorizontal="5dp"> - + + android:inputType="none" /> diff --git a/app/src/main/res/layout/overview_graphs_layout.xml b/app/src/main/res/layout/overview_graphs_layout.xml index 70bcfa678e..d1e35bb237 100644 --- a/app/src/main/res/layout/overview_graphs_layout.xml +++ b/app/src/main/res/layout/overview_graphs_layout.xml @@ -22,6 +22,7 @@ android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentEnd="true" + android:layout_marginEnd="3dp" android:contentDescription="@string/chart_menu" android:paddingTop="5dp" app:srcCompat="@drawable/ic_arrow_drop_down_white_24dp" /> diff --git a/app/src/main/res/layout/wear_fragment.xml b/app/src/main/res/layout/wear_fragment.xml index 2bcbb389ac..5c6dd20d9a 100644 --- a/app/src/main/res/layout/wear_fragment.xml +++ b/app/src/main/res/layout/wear_fragment.xml @@ -13,7 +13,7 @@ android:gravity="center_vertical|center_horizontal" android:paddingTop="10dp" android:paddingBottom="10dp" - android:text="---" + android:text="@string/no_watch_connected" android:textAppearance="@style/TextAppearance.AppCompat.Medium" tools:ignore="HardcodedText" /> diff --git a/app/src/main/res/menu/menu_preferences.xml b/app/src/main/res/menu/menu_preferences.xml new file mode 100644 index 0000000000..cb7da0d8fc --- /dev/null +++ b/app/src/main/res/menu/menu_preferences.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/menu/menu_profilehelper.xml b/app/src/main/res/menu/menu_profilehelper.xml deleted file mode 100644 index ca6e6cb9fc..0000000000 --- a/app/src/main/res/menu/menu_profilehelper.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-ca-rES/exam.xml b/app/src/main/res/values-ca-rES/exam.xml index e954253896..1d7fce44c7 100644 --- a/app/src/main/res/values-ca-rES/exam.xml +++ b/app/src/main/res/values-ca-rES/exam.xml @@ -134,7 +134,6 @@ Lliurar un bolus amb part de la insulina que falta utilitzant una jeringa/ploma o el menú d\'encebat. No fer res i permetre que AndroidAPS corregeixi els possibles valors alts de glucèmia que es produeixin. Insulina \"a bord\" (IOB) - Marqueu totes les respostes correctes. El valor d\'IOB es veu afectat per les basals temporals lliurades. Una basal temporal alta no serà lliurada si la glucèmia està per sota de l\'objectiu. Un valor d\'IOB positiu durant un període de temps important suggereix resistència a la insulina o àpats no anunciats. diff --git a/app/src/main/res/values-cs-rCZ/exam.xml b/app/src/main/res/values-cs-rCZ/exam.xml index 674f46bec9..095314b067 100644 --- a/app/src/main/res/values-cs-rCZ/exam.xml +++ b/app/src/main/res/values-cs-rCZ/exam.xml @@ -136,8 +136,20 @@ Porovnat hodnoty v AndroidAPS s hodnotami v historii pumpy (pokud to pumpa podporuje). Pošlete bolus na vypočtený „chybějící“ inzulín buď injekční stříkačkou nebo perem nebo pomocí plnění na pumpě. Neudělejte nic a dovolte AndroidAPS opravit jakoukoliv výslednou vysokou hladinu glukózy v krvi. + Zbývající sacharidy (COB) + Jak změna hodnoty citlivosti ovlivňuje výpočet COB? + Zvýšení hodnoty citlivosti prodlouží dobu absorpce sacharidů + Zvýšení hodnoty citlivosti zkrátí dobu absorpce sacharidů + Zvýšení citlivosti neovlivní vypočtenou absorpci sacharidů + Jak změna inzulíno-sacharidového poměru ovlivňuje výpočet COB? + Zvýšení hodnoty inzulíno-sacharidového poměru prodlouží dobu absorpce sacharidů + Zvýšení hodnoty inzulíno-sacharidového poměru zkrátí dobu absorpce sacharidů + Zvýšení inzulíno-sacharidového poměru neovlivní vypočtenou absorpci sacharidů + Jak procentuální změna profilu ovlivňuje výpočet COB? + Nastavením profilu na 150 % dojde k prodloužení doby absorpce sacharidů + Nastavením profilu na 150 % dojde ke zkrácení doby absorpce sacharidů + Nastavení profilu na 150% neovlivní vypočtenou absorpci sacharidů Aktivní inzulín (IOB) - Zatrhněte všechny správné odpovědi. Hodnota IOB je ovlivněna vydanými dočasnými bazály. Vysoký dočasný bazál nebude spuštěn, pokud je Vaše glykémie nižší než cíl. Negativní IOB po značnou dobu bez cvičení naznačuje, že váš profil je příliš \"silný\" a ve vašem profilu je potřeba méně inzulínu. diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index c577d77db7..5a9ff0129f 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -1037,10 +1037,12 @@ pouze na hodinkách pouze na telefonu úchyt pro přetažení + Hledat GlucoRx Aidex Aidex Přijímat hodnoty glykémie ze senzoru GlucoRx Aidex. Zablokováno možností nabíjení Zablokováno možností připojení + (Žádné hodinky nejsou připojeny) diff --git a/app/src/main/res/values-da-rDK/exam.xml b/app/src/main/res/values-da-rDK/exam.xml index 31dc1e91d2..2a4276dfe3 100644 --- a/app/src/main/res/values-da-rDK/exam.xml +++ b/app/src/main/res/values-da-rDK/exam.xml @@ -136,7 +136,6 @@ Bolus en del af din beregnede \"glemte\" insulin med enten sprøjter / pen eller brug af en kanylefyldning. Gør ingenting og tillad AndroidAPS at rette op på højt blodsukker. Insulin om bord (IOB) - Tjek alle de korrekte svar. IOB-værdi påvirkes af midlertidige basaler. Høj midlertidlig basal vil ikke blive givet, når dit blodsukker er under målet. Negative IOB i en længere periode uden motion tyder på, at din profil er for stærk, og at der er behov for mindre insulin i dine indstillinger. diff --git a/app/src/main/res/values-de-rDE/exam.xml b/app/src/main/res/values-de-rDE/exam.xml index 2037d190da..6b25674a29 100644 --- a/app/src/main/res/values-de-rDE/exam.xml +++ b/app/src/main/res/values-de-rDE/exam.xml @@ -134,7 +134,6 @@ Gib einen Teil-Bolus des \'nicht erhaltenen\' Insulins mit Spritze / Pen oder durch Befüllen der Kanüle. Unternimm nichts und lass AndroidAPS die entstehenden hohen Glukosewerte korrigieren. Aktives Insulin (IOB) - Kreuze alle richtigen Antworten an. IOB wird von den abgegebenen temporären Basalraten beeinflusst. Eine hohe temporäre Basalrate wird nicht abgegeben, wenn der BZ unterhalb des Zielwerts liegt. Ein negativer IOB über einen längeren Zeitraum bei fehlender Bewegung deutet darauf hin, dass Dein Profil zu stark ist und weniger Insulin in Deinen Einstellungen benötigt wird. diff --git a/app/src/main/res/values-es-rES/exam.xml b/app/src/main/res/values-es-rES/exam.xml index d4ca2b6d4a..a8cc55b822 100644 --- a/app/src/main/res/values-es-rES/exam.xml +++ b/app/src/main/res/values-es-rES/exam.xml @@ -136,8 +136,20 @@ Comparar valores en AndroidAPS con el historial de la bomba (si la bomba lo soporta). Bolo una proporción de la insulina calculada “perdida” por jeringa/pluma o usando menú de llenado. No haga nada y permite que AndroidAPS corrija cualquier nivel alto de glucosa en la sangre resultante. + Carbohidratos activos (COB) + ¿Cómo afecta el cambio del valor ISF al cálculo de COB? + Incrementar el ISF hará que los carbohidratos se absorban más lentamente + Incrementar el ISF hará que los carbohidratos se absorban más rápidamente + Incrementar el ISF no afectará la absorción calculada de carbohidratos + ¿Cómo afecta cambiar los valores de IC a los cálculos de COB? + Incrementar el IC hará que los carbohidratos se absorban más lentamente + Incrementar el IC hará que los carbohidratos se absorban más rápidamente + Incrementar el IC no afectará la absorción calculada de carbohidratos + ¿Cómo afecta el cambio de porcentaje del perfil al cálculo de los COB? + Establecer el perfil a 150% hará que los carbohidratos se absorban más lentamente + Establecer el perfil a 150% hará que los carbohidratos se absorban más rápidamente + Establecer el perfil a 150% no afectará la absorción calculada de carbohidratos Insulina a bordo (IOB) - Marca todas las respuestas correctas. El valor del IOB se ve afectado por las bases temporales ejecutadas. No se dará alta tasa basal temporal cuando su nivel de azúcar en sangre esté por debajo del objetivo. El IOB negativo durante un período sustancial en ausencia de ejercicio sugiere que tu perfil es demasiado fuerte y que se necesita menos insulina en tus ajustes. diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index d80010a14a..d8e5d59e56 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -1037,10 +1037,12 @@ Sólo en reloj Sólo en teléfono Arrastrar y soltar + Buscar GlucoRx Aidex Aidex Recibir los valores de glucosa de GlucoRx Aidex CGMS Bloqueado por opciones de carga Bloqueado por opciones de conectividad + (Ningún reloj conectado) diff --git a/app/src/main/res/values-fr-rFR/exam.xml b/app/src/main/res/values-fr-rFR/exam.xml index bab13d862d..3fec7d38da 100644 --- a/app/src/main/res/values-fr-rFR/exam.xml +++ b/app/src/main/res/values-fr-rFR/exam.xml @@ -136,8 +136,20 @@ Comparer les valeurs dans l\'historique d\'AndroidAPS et de la pompe (si la pompe le permet). Faites un bolus du montant de votre insuline calculée « manquante » soit par seringue / stylo ou en utilisant un amorçage. Ne faites rien et laissez AndroidAPS corriger tout taux élevé de glycémie qui en résulte. + Glucides Actifs (GA) + Comment le changement de la SI impacte le calcul des GA ? + Augmenter la SI rendra l\'absorption des glucides plus longue + L\'augmentation de la SI réduira le temps d\'absorption des glucides + L\'augmentation de la SI n\'affectera pas les calculs de l\'absorption des glucides + Comment le changement de G/I impacte le calcul des GA ? + L\'augmentation de G/I rendra l\'absorption des glucides plus longue + L\'augmentation de G/I réduira le temps d\'absorption des glucides + L\'augmentation de G/I n\'impactera pas l\'absorption des glucides calculés + Comment le changement du pourcentage de profil impacte le calcul des GA ? + Définir le profil à 150% rendra l\'absorption des glucides plus longue + Définir le profil à 150% réduira le temps d\'absorption des glucides + Définir le profil à 150% n\'impactera pas le calcul de l\'absorption des glucides Insuline Active (IA) - Sélectionnez toutes les bonnes réponses. La valeur de l\'IA est impactée par les débits de basal temporaires émis. Une basale temp haute ne sera pas donnée quand votre glycémie est en dessous de la cible. Une IA négative pendant une période significative en l\'absence d\'exercice indique que votre profil est trop fort et qu\'il faut moins d\'insuline dans vos paramètres. diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index a278365845..5988390a0d 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -434,6 +434,8 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S Activer les transmissions locales OpenAPS SMB SI dynamique + Facteur d\'ajustement Si dynamique % + Facteur d\'ajustement pour Si dynamique. Définissez plus de 100 % pour des corrections plus agressives et moins de 100 % pour des corrections moins agressives. Activer RNS Activer SMB Utiliser les Super Micro Bolus au lieu des débits de base temporaires pour une action rapide @@ -968,6 +970,7 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S Gly maxi hors limite! Cible temporaire:\nMin: %1$s\nMax : %2$s\nDurée : %3$s Cible temporaire:\nCible: %1$s\nDurée: %2$s + Cible temp:\nRaison: %1$s\nCible : %2$s\nDurée : %3$s Assistant: %1$s\nInsuline : %2$.2fU\nGlucides : %3$dg Assistant Calc :\nInsuline : %1$.2fU\nGlucides : %2$dg Afficher l\'entrée sur l\'appareil : @@ -1034,10 +1037,12 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S seulement sur la montre uniquement sur le téléphone glissez déposez ici + Chercher GlucoRx Aidex Aidex Recevoir les valeurs de glycémie du MGC GlucoRx Aidex. Bloqué par les options de recharge Bloqué par les options de connectivité + (Pas de montre connectée) diff --git a/app/src/main/res/values-it-rIT/exam.xml b/app/src/main/res/values-it-rIT/exam.xml index c91b576f44..1841752c18 100644 --- a/app/src/main/res/values-it-rIT/exam.xml +++ b/app/src/main/res/values-it-rIT/exam.xml @@ -137,7 +137,6 @@ Erogare un bolo con parte della tua insulina calcolata \"mancante\" usando una siringa/penna o la funzione caricamento. Non fare nulla e permettere ad AndroidAPS di correggere eventuali glicemie alte. Insulina attiva (IOB) - Seleziona tutte le risposte corrette. Il valore IOB è influenzato dalle basali temporanee emesse. Non verrà emessa una basale temporanea alta quando la tua glicemia è al di sotto del target. IOB negativo per un periodo considerevole in assenza di esercizio fisico suggerisce che il tuo profilo è troppo incisivo e che è necessaria meno insulina nelle tue impostazioni. diff --git a/app/src/main/res/values-iw-rIL/exam.xml b/app/src/main/res/values-iw-rIL/exam.xml index e4f2d5dd9e..3e03a6e498 100644 --- a/app/src/main/res/values-iw-rIL/exam.xml +++ b/app/src/main/res/values-iw-rIL/exam.xml @@ -136,8 +136,20 @@ השוו את הרשומות שב-AndroidAPS עם היסטוריית המשאבה (אם המשאבה תומכת בכך). מזריקים בולוס על חלק מהאינסולין המחושב \"שחסר\" עם מזרק\\עט או באמצעות תיחול (Prime). לא לעשות דבר ולאפשר ל-AndroidAPS לתקן כל רמת סוכר גבוהה. + פחמ\' פעילות + איך שינויים ביחס התיקון משפיעים על חישוב הפחמימות הפעילות? + הגדלת ערך יחס התיקון תגרום להארכת משך ספיגת הפחמימות + הגדלת ערך יחס התיקון תגרום לקיצור משך ספיגת הפחמימות + הגדלת ערך יחס התיקון לא תשפיע על חישוב ספיגת הפחמימות + איך שינויים ביחס הפחמימות משפיעים על חישוב הפחמימות הפעילות? + הגדלת ערך יחס הפחמימות תגרום להארכת משך ספיגת הפחמימות + הגדלת ערך יחס הפחמימות תגרום לקיצור משך ספיגת הפחמימות + הגדלת ערך יחס הפחמימות לא תשפיע על חישוב ספיגת הפחמימות + איך שינויים באחוזי הפרופיל משפיעים על חישוב הפחמימות הפעילות? + הגדרת 150% פרופיל תגרום להארכת משך ספיגת הפחמימות + הגדרת 150% פרופיל תגרום לקיצור משך ספיגת הפחמימות + הגדרת 150% פרופיל לא תשפיע על חישוב משך ספיגת הפחמימות אינסולין פעיל בגוף (IOB) - סמנו את כל התשובות הנכונות. ערך האינסולין הפעיל (IOB) מושפע ממינונים בזאליים זמניים שסופקו. מינון בזאלי גבוה לא יינתן כאשר רמת הסוכר נמוכה מערך המטרה. אינסולין פעיל שלילי לאורך זמן בו לא נעשית פעילות מצביע על כך שהפרופיל חזק מדי, נדרש פחות אינסולין ויש להגדיר זאת בפרופיל. diff --git a/app/src/main/res/values-iw-rIL/strings.xml b/app/src/main/res/values-iw-rIL/strings.xml index 0be19f2f23..a51a33dfee 100644 --- a/app/src/main/res/values-iw-rIL/strings.xml +++ b/app/src/main/res/values-iw-rIL/strings.xml @@ -1033,10 +1033,12 @@ על השעון בלבד על הטלפון בלבד ידית גרירה + חיפוש GlucoRx Aidex Aidex קבלת נתוני סוכר מחיישני GlucoRx Aidex. חסום ע\"י הגדרות טעינה חסום ע\"י הגדרות חיבור + (השעון לא מחובר) diff --git a/app/src/main/res/values-ko-rKR/exam.xml b/app/src/main/res/values-ko-rKR/exam.xml index 8aaf183241..89ae40d8dc 100644 --- a/app/src/main/res/values-ko-rKR/exam.xml +++ b/app/src/main/res/values-ko-rKR/exam.xml @@ -134,7 +134,6 @@ \"주입되지 않은\" 인슐린양을 계산하여 시린지/펜 또는 prime 기능을 사용하여 인슐린을 주입합니다. 특별한 조치를 취하지 않고, AndroidAPS가 결과적으로 높은 혈당 정도를 조정할 수 있도록 합니다. 활성 인슐린 (IOB) - 올바른 답변을 모두 체크하세요. IOB 값은 실행된 임시 basal의 영향을 받습니다. 혈당이 목표보다 낮을 경우 높은 임시 basal은 실행되지 않습니다. 운동 없이도 음수의 IOB (마이너스 IOB) 구간이 많을 경우, 프로파일이 너무 강한 것이며 인슐린 양을 적게 설정해야 합니다. diff --git a/app/src/main/res/values-lt-rLT/exam.xml b/app/src/main/res/values-lt-rLT/exam.xml index 9e5bb2cb09..dc3d8612f4 100644 --- a/app/src/main/res/values-lt-rLT/exam.xml +++ b/app/src/main/res/values-lt-rLT/exam.xml @@ -134,7 +134,6 @@ Susileisti trūkstamą insulino kiekį švirkštu ar naudojantis infuzinės sistemos užpildymo funkcija. Nieko nedaryti ir leisti AndroidAPS koreguoti padidėjusį gliukozės kiekį kraujyje. Aktyvus insulinas organizme (AIO) - Pasirinkite visus teisingus atsakymus. AIO reikšmei įtakos turi nustatyta laikina valandinė bazė. Padidinta laikina bazė nebus nustatyta, jei kraujo glikemija yra žemiau nustatyto tikslo. Jei ilgą laiką stebima neigiama AIO reikšmė ir nėra fizinio aktyvumo, tai rodo, kad jūsų nustatymai yra per „agresyvūs“ ir kad jums reikia mažiau insulino nei nustatyta. diff --git a/app/src/main/res/values-nl-rNL/exam.xml b/app/src/main/res/values-nl-rNL/exam.xml index 5a64cd78c6..e871ace199 100644 --- a/app/src/main/res/values-nl-rNL/exam.xml +++ b/app/src/main/res/values-nl-rNL/exam.xml @@ -137,7 +137,6 @@ Bolus een deel van je berekende \'gemiste\' insuline door een injectiespuit/pen of door de uitvulfunctie te gebruiken. Doe niets en laat AndroidAPS de resulterende hoge bloedglucosespiegel corrigeren. Insuline aan boord (IOB) - Selecteer alle juiste antwoorden. De IOB-waarde wordt beïnvloed door eerdere tijdelijke basaalstanden. Een hoge tijdelijke basaal zal niet worden ingesteld wanneer je bloedsuiker onder het streefdoel ligt. Negatieve IOB voor een aanzienlijke periode vrij van inspanning, suggereert dat je profiel te hoog is ingesteld, dus dat er minder insuline nodig is. diff --git a/app/src/main/res/values-no-rNO/exam.xml b/app/src/main/res/values-no-rNO/exam.xml index 087dc7d9be..d2de2e7b0f 100644 --- a/app/src/main/res/values-no-rNO/exam.xml +++ b/app/src/main/res/values-no-rNO/exam.xml @@ -136,8 +136,20 @@ Sammenlign verdiene i AndroidAPS og pumpehistorikken (hvis pumpen støtter dette). Gi en bolus med en del av det insulinet du mangler enten med penn eller ved å bruke prime funksjonen. Ikke gjør noe og la AndroidAPS korrigere eventuelle høye blodsukkerverdier. + Karbohydrater ombord (COB) + Hvordan vil endring av ISF-verdi påvirke COB-beregning? + Økning av ISF gjør at karbohydrater absorberes over lengre tid + Økning av ISF gjør at karbohydrater absorberes over kortere tid + Økning av ISF påvirker ikke beregningen av absorbering av karbohydrater + Hvordan vil endring av IC-verdi påvirke COB-beregning? + Økning av IC gjør at karbohydrater absorberes over lengre tid + Økning av IC gjør at karbohydrater absorberes over kortere tid + Økning av IC påvirker ikke beregningen av absorbering av karbohydrater + Hvordan vil endring av profil-prosent påvirke COB-beregning? + Ved å sette profilen til 150% vil karbohydrater absorberes over lengre tid + Ved å sette profilen til 150% vil karbohydrater absorberes over kortere tid + Ved å sette profilen til 150% påvirkes ikke beregningen av absorbering av karbohydrater Aktivt insulin (IOB) - Marker alle riktige svar. IOB verdi påvirkes av midlertidige temp basaler. Høy temp basal vil ikke bli gitt når ditt blodsukker er under målverdi. Negativ IOB i en lengre periode selv om du ikke trener tyder på at profilen din gir for mye insulin og kanskje bør reduseres i innstillingene. diff --git a/app/src/main/res/values-no-rNO/strings.xml b/app/src/main/res/values-no-rNO/strings.xml index 5967430250..21dbe2cb6a 100644 --- a/app/src/main/res/values-no-rNO/strings.xml +++ b/app/src/main/res/values-no-rNO/strings.xml @@ -1037,10 +1037,12 @@ kun på klokke kun på telefon dra og slipp håndtering + Søk GlucoRx Aidex Aidex Motta BS verdier fra GlucoRx Aidex CGM. Blokkert på grunn av ladealternativer Blokkert på grunn av tilkoblingsalternativer + (Ingen klokke tilkoblet) diff --git a/app/src/main/res/values-pl-rPL/exam.xml b/app/src/main/res/values-pl-rPL/exam.xml index dfa9014d5e..b9ce8332d5 100644 --- a/app/src/main/res/values-pl-rPL/exam.xml +++ b/app/src/main/res/values-pl-rPL/exam.xml @@ -134,7 +134,6 @@ Podaj obliczoną dawkę „pominiętej” insuliny za pomocą strzykawki / wstrzykiwacza lub za pomocą pompy funkcji wypełnienia. Nie rób nic i pozwól AndroidAPS na skorygowanie każdego uzyskanego wysokiego stężenia glukozy we krwi. Aktywna insulina (IOB) - Sprawdź/Zaznacz wszystkie prawidłowe odpowiedzi. Na wartość IOB mają wpływ wykonane tymczasowe dawki bazowe. Wysoka tymczasowa dawka bazowa nie zostanie podana, gdy twóje poziom cukru we krwi jest poniżej poziomu docelowego. Negatywne IOB utrzymujące się przez znaczny okres czasu bez wysiłku fizycznego sugeruje, że Twój profil jest zbyt silny i mniej insuliny jest wymagane w Twoich ustawieniach. diff --git a/app/src/main/res/values-pt-rBR/exam.xml b/app/src/main/res/values-pt-rBR/exam.xml index 0a6e10f3a3..c2bf8ae20a 100644 --- a/app/src/main/res/values-pt-rBR/exam.xml +++ b/app/src/main/res/values-pt-rBR/exam.xml @@ -136,7 +136,6 @@ Dê uma porção do bolus calculado não administrado por meio de seringa/caneta ou usando o menu ações -> purgar/preencher. Não faça nada e permita que o AndroidAPS corrija qualquer nível de glicose elevado resultante. Insulina ativa (IA) - Marque todas as respostas corretas. O valor de IA é afetado por basais temporárias efetuadas. Um basal alto temporário não será efetuado quando sua glicose estiver abaixo do alvo. Insulina Ativa (IA) negativa por um período substancial na ausência de exercícios, sugere que seu perfil é muito agressivo e menos insulina deve ser considerada nas suas configurações. diff --git a/app/src/main/res/values-pt-rPT/exam.xml b/app/src/main/res/values-pt-rPT/exam.xml index 3b66353316..5d3cd2fca4 100644 --- a/app/src/main/res/values-pt-rPT/exam.xml +++ b/app/src/main/res/values-pt-rPT/exam.xml @@ -134,7 +134,6 @@ Bólus uma proporção de insulina calculada \'não administrada\' ou com seringa/caneta ou purgando. Faça nada e permita que o AndroidAPS corrija qualquer nível de glicose elevado que daí resulte. Insulina ativa (IA ) - Marque todas as respostas corretas. O valor do IA é afectado por basais temporárias emitidas. Basal temporária alta não será dada quando a sua glicose estiver abaixo do alvo. IA negativa por um período substancial na ausência de exercício, sugere que o seu perfil é muito alto e é necessária menos insulina nas suas configurações. diff --git a/app/src/main/res/values-ro-rRO/exam.xml b/app/src/main/res/values-ro-rRO/exam.xml index e71e92bee4..4b5f2acaf1 100644 --- a/app/src/main/res/values-ro-rRO/exam.xml +++ b/app/src/main/res/values-ro-rRO/exam.xml @@ -134,7 +134,6 @@ Bolusati o cantitate parțiala de insulina din insulina calculata ca fiind lipsa utilizând seringa/penul sau amorsarea. Nu faceți nimic și permiteți AndroidAPS să corecteze valorile mari ale glicemiei. Insulină activă (IOB) - Bifați toate răspunsurile corecte. Valoarea IOB este afectată de bazalele temporare emise. Rata bazala ridicata temporara nu va fi livrata atunci când glicemia este sub nivelul țintă. Un IOB negativ pentru o perioadă substanțială în absența exercițiilor fizice sugerează că profilul tău este prea puternic și este nevoie de mai puţină insulină în setările inițiale. diff --git a/app/src/main/res/values-ru-rRU/exam.xml b/app/src/main/res/values-ru-rRU/exam.xml index d5edb09ce2..58e9d84eaf 100644 --- a/app/src/main/res/values-ru-rRU/exam.xml +++ b/app/src/main/res/values-ru-rRU/exam.xml @@ -136,8 +136,20 @@ Сравните значения в истории AndroidAPS и помпы (если помпа это поддерживает). Рассчитать и ввести «пропущенный» вами инсулин шприцем/ручкой или через кнопку заполнения канюли. Ничего не делать и позволить AndroidAPS исправить возможный высокий уровень ГК. + Активные углеводы COB - углеводы в процессе компенсации + Как изменение значения ISF влияет на расчет COB? + Увеличение фактора чувствительности ISF потребует больше времени на усвоение углеводов + Увеличение фактора чувствительности ISF потребует меньше времени на усвоение углеводов + Увеличение фактора чувствительности ISF не повлияет на расчетное время усвоения углеводов + Как изменение значения IC влияет на расчет COB? + Увеличение IC увеличит время усвоения углеводов + Увеличение IC уменьшит время усвоения углеводов + Увеличение IC не повлияет на расчетное время усвоения углеводов + Как изменение процента профиля влияет на расчет активных углеводов COB? + Установка профиля на 150% потребует больше времени на усвоение углеводов + Установка профиля на 150% потребует меньше времени на усвоение углеводов + Установка профиля на 150% не повлияет на расчетное время усвоения углеводов Активный инсулин (IOB) - Отметьте все правильные ответы. На величину IOB влияют устанавливаемые ВБС. Высокая ВБС не будет применяться, если уровень сахара в крови ниже целевого. Отрицательный IOB в течение длительного периода при отсутствии физнагрузки указывает на то, что ваш профиль завышен и требуется снизить кол-во инсулина в настройках. diff --git a/app/src/main/res/values-ru-rRU/objectives.xml b/app/src/main/res/values-ru-rRU/objectives.xml index 65f4dd716f..499fa398bf 100644 --- a/app/src/main/res/values-ru-rRU/objectives.xml +++ b/app/src/main/res/values-ru-rRU/objectives.xml @@ -6,7 +6,7 @@ Цель %1$d не начата Цель %1$d не завершена Настройка визуализации и мониторинга, анализ базала и коэффициентов - Убедитесь, что BG доступен в Nightscut, и данные инсулина помпы загружаются + Убедитесь, что значения ГК и данные по инсулину помпы передаются в Nightscout Старт незамкнутого цикла Начинайте работу в режиме незамкнутого цикла и ручной подстройки величины временного базала. Установите и применяйте временные цели и временные цели по умолчанию (напр. углеводы при нагрузке или купировании гипо) Глубже понимаем незакольцованную систему Open Loop, включая ее рекомендации по временным базалам diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index 9baf342a88..fa3d099a67 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -6,6 +6,7 @@ Безопасность терапии Макс разрешенный болюс [U] ед. Макс разрешенные углеводы (г) + Уровень ГК, ниже которого происходит остановка подачи инсулина Настройки %1$s Настройки Синхронизировать назначения с NS @@ -432,6 +433,8 @@ Активировать локальную передачу Супер микро болюс OpenAPS Динамический ISF + Коэффициент регулировки динамического диапазона чувствительности ISF % + Коэффициент корректировки динамического диапазона чувствительности ISF. Установите более 100% для агрессивных доз коррекции и менее 100% для менее агрессивной коррекции. Включить непредвиденный прием пищи UAM Включить супер микро болюс SMB Для ускорения действия используйте супер микро болюсы SMB вместо временного базала @@ -590,6 +593,7 @@ Эта величина называется Max IOB в OpenAPS context\nOpenAPS не будет добавлять инсулин если текущий активный инсулин ее превысит Максимальное время усваимости пищи [h] Время, за которое любая еда считается усвоившейся. Оставшиеся углеводы не будут учтены. + Значение уровня ГК, на котором происходит приостановка подачи инсулина. Значения по умолчанию являются стандартными для целевого диапазона. Пользователь может установить значения между 60 мг/дл (3.3 ммоль/л) и 100 мг/дл (5.5 ммоль/л) Параметры, заданные ниже 65/3.6 приводят к использованию значений по умолчанию Показать поле примечаний в диалогах назначений Далее Назад @@ -965,6 +969,7 @@ Макс ГК вне диапазона! ВремЦель:\nМин: %1$s\nМакс.: %2$s\nДлительность: %3$s ВремЦель:\nЦель: %1$s\nДлительность: %2$s + ВремЦель:\nПричина: %1$s\nЦель.: %2$s\nДлительность: %3$s Мастер: %1$s\nИнсулин: %2$.2fЕд\nУгл: %3$dg Мастер:\nИнсулин: %1$.2fЕд\nУгл: %2$dg Показать запись на устройстве: @@ -1031,10 +1036,12 @@ только на часах только на телефоне якорь перетягивания + Поиск Aidex GlucoRx Aidex Получить значения ГК от GlucoRx Aidex Заблокировано опциями зарядки Заблокировано настройками подключения + (Часы не подключены) diff --git a/app/src/main/res/values-sk-rSK/exam.xml b/app/src/main/res/values-sk-rSK/exam.xml index 54aaa5002d..1aefaf97da 100644 --- a/app/src/main/res/values-sk-rSK/exam.xml +++ b/app/src/main/res/values-sk-rSK/exam.xml @@ -136,8 +136,20 @@ Porovnaj hodnoty v AndroidAPS z históriou v tvojej pumpe (ak pumpa má túto funkciu). Pošlite bolus na vypočítaný „chýbajúci“ inzulín buď injekčnou striekačkou, alebo perom, alebo pomocou plnenia na pumpe. Nerobte nič a dovoľte AndroidAPS upraviť akúkoľvek výslednú vysokú hladinu glukózy v krvi. + Aktívne sacharidy (COB) + Ako ovplyvňuje zmena hodnoty citlivosti výpočet COB? + Zvýšenie hodnoty citlivosti predĺži dobu absorbcie sacharidov + Zvýšenie hodnoty citlivosti skráti dobu absorbcie sacharidov + Zvýšenie citlivosti neovplyvní vypočítanú absorbciu sacharidov + Ako ovplyvňuje zmena inzulíno-sacharidového pomeru výpočet COB? + Zvýšenie hodnoty inzulíno-sacharidového pomeru predĺži dobu absorbcie sacharidov + Zvýšenie hodnoty inzulíno-sacharidového pomeru skráti dobu absorbcie sacharidov + Zvýšenie inzulíno-sacharidového pomeru neovplyvní vypočítanú absorbciu sacharidov + Ako ovplyvňuje percentuálna zmena profilu výpočet COB? + Nastavením profilu na 150 % dôjde k predĺženiu doby absorbcie sacharidov + Nastavením profilu na 150 % dôjde ku skráteniu doby absorbcie sacharidov + Nastavenie profilu na 150% neovplyvní vypočítanú absorbciu sacharidov Aktívny inzulín (IOB) - Zaškrtnite všetky správne odpovede. Hodnota IOB je ovplyvnená vydanými dočasnými bazálmi. Vysoký dočasný bazál nebude spustený, pokiaľ je Vaša glykémia nižšia, ako cieľ. Negatívne IOB po značnú dobu bez cvičenia naznačuje, že váš profil je príliš \"silný\" a vo vašom profile je potrebného menej inzulínu. diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 5b7a784c4d..c77651b5d5 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -1037,10 +1037,12 @@ iba na hodinkách iba na telefóne úchop potiahni a pusť + Hľadať GlucoRx Aidex Aidex Prijímať hodnoty glykémie zo senzora GlucoRx Aidex. Zablokované možnosti nabíjania Zablokované možnosti pripojenia + (Žiadne hodinky nie sú pripojené) diff --git a/app/src/main/res/values-sv-rSE/exam.xml b/app/src/main/res/values-sv-rSE/exam.xml index 9657e08fdd..cf588d6136 100644 --- a/app/src/main/res/values-sv-rSE/exam.xml +++ b/app/src/main/res/values-sv-rSE/exam.xml @@ -134,7 +134,6 @@ Ge en bolus med en del av det insulin du saknar antingen med penna eller genom att använda förfyllningsfunktionen. Gör ingenting och låt AndroidAPS korrigera eventuella höga blodglukosnivåer. Aktivt Insulin (IOB) - Markera ett eller flera alternativ. IOB-värdet påverkas av temp basaler. Hög temp basal kommer inte att ges när ditt BG-värde är under målet. Negativ IOB under en längre period även om du inte motionerat, tyder på att din profil ger för mycket insulin. diff --git a/app/src/main/res/values-tr-rTR/exam.xml b/app/src/main/res/values-tr-rTR/exam.xml index 979bfb5b2e..68b9eb13ca 100644 --- a/app/src/main/res/values-tr-rTR/exam.xml +++ b/app/src/main/res/values-tr-rTR/exam.xml @@ -136,16 +136,28 @@ AndroidAPS ve pompa geçmişindeki değerleri karşılaştırın (pompa bunu destekliyorsa). Şırınga/kalem veya başlatma kullanarak hesaplanan \"eksik\" insülininizi bolus olarak karşılayın. Hiçbir şey yapmayın ve AndroidAPS\'in ortaya çıkabilecek yüksek kan şekeri seviyesini düzeltmesine müsade edin. - Aktif İnsülin (IOB) - Tüm doğru cevapları kontrol edin. - IOB değeri, verilen geçici bazallardan etkilenir. + Aktif Karbonhidrat (AKRB) + İDF değerinin değiştirilmesi AKRB (Aktif karbonhidrat) hesaplamasını nasıl etkiler? + Artan İDF, karbonhidratların daha uzun süre emilmesini sağlayacaktır + Artan İDF, karbonhidratların daha kısa sürede emilmesini sağlayacaktır + Artan İDF, hesaplanan karbonhidrat emilimini etkilemez + Değişen Kİ değeri AKRB hesaplamasını nasıl etkiler? + Artan Kİ oranı, karbonhidratların daha uzun sürede emilmesini sağlar + Artan Kİ, karbonhidratların daha kısa sürede emilmesini sağlayacaktır + Artan Kİ oranı, hesaplanan karbonhidrat emilimini etkilemez + Değişen profil yüzdesi AKRB hesaplamasını nasıl etkiler? + Profili %150\'ye ayarlamak karbonhidratların daha uzun sürede emilmesini sağlar + Profili %150\'ye ayarlamak karbonhidratların daha kısa sürede emilmesini sağlar + Profili %150\'ye ayarlamak hesaplanan karbonhidrat emilimini etkilemez + Aktif İnsülin (AİNS) + AİNS değeri, verilen geçici bazallardan etkilenir. Kan şekeriniz hedefin altına düştüğünde yüksek geçici bazal verilmeyecektir. - Egzersiz yapmadığınız halde uzun bir dönem için negatif IOB, profilinizin çok güçlü olduğunu ve ayarlarınızda daha az insüline ihtiyaç duyulduğunu gösterir. + Egzersiz yapmadığınız halde uzun bir dönem için negatif AİNS, profilinizin çok güçlü olduğunu ve ayarlarınızda daha az insüline ihtiyaç duyulduğunu gösterir. Önemli bir süre için pozitif IOB, insülin direncini veya habersiz öğünleri gösterir. Karbonhidrat girişi ve boluslar Tüketilen karbonhidratları tahmin etmek ve kaydetmek için sadece gram kullanılmalıdır. Tüketilen karbonhidratlar uygun bir değişim sistemi kullanılarak kaydedilebilir (örn. DAFNE \"CHO\" değişimleri veya Avrupa \"Ekmek Birimleri\"). - AndroidAPS, karbonhidrat \"çözünmesini\" tahmin etmek ve COB\'ı hesaplamak için dinamik bir model kullanır. + AndroidAPS, karbonhidrat \"çözünmesini\" tahmin etmek ve AKRB\'ı hesaplamak için dinamik bir model kullanır. Kan şekeri seviyeleri kabul edilebilir değerlerin dışındaysa (çok düşük veya çok yüksek), karbonhidrat veya insülin düzeltmeleri için öneriler sağlamak için bolus hesaplayıcı kullanılabilir. https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/FAQ.html#insulin-to-carb-ratio-ic-g-u yayma-karbonhidratlar @@ -158,11 +170,11 @@ Uzaktan İzleme AndroidAPS\'i (örneğin çocuğunuz için) uzaktan nasıl izleyebilirsiniz? NS Client uygulaması, Nightscout uygulaması ve Nightscout web sayfasının tümü, AndroidAPS\'i uzaktan takip etmenize olanak tanır. - Diğer uygulamalar (ör. Dexcom takip, xDrip) bazı parametreleri (ör. kan şekeri/sensör değerleri) uzaktan takip etmenize izin verir, ancak farklı algoritma kullanımı, hatalı IOB veya COB değerlerine sebep olabilir. + Diğer uygulamalar (ör. Dexcom takip, xDrip) bazı parametreleri (ör. kan şekeri/sensör değerleri) uzaktan takip etmenize izin verir, ancak farklı algoritma kullanımı, hatalı AİNS veya AKRB değerlerine sebep olabilir. AndroidAPS\'yi uzaktan takip etmek için her iki cihazın da internet erişimine sahip olması gerekir (ör. Wi-Fi veya mobil/hücresel ağ verileri aracılığıyla). Uzak takipçi olarak kullanılan NS İstemcisi, AndroidAPS\'i hem izleyecek hem de tam kontrol sağlayacaktır. https://androidaps.readthedocs.io/en/latest/EN/Children/Children.html - İnsülin Duyarlılık Faktörü (ISF) + İnsülin Duyarlılık Faktörü (İDF) ISF değerlerini yükseltmek, belirli bir karbonhidrat miktarını karşılamak için daha fazla insülin verilmesine yol açacaktır. ISF değerinin düşürülmesi, hedeflenen kan şekerinin üzerinde bir düzeltme için daha fazla insülin verilmesine yol açar. Kan şekeri seviyeleri hedefin altında olduğunda ISF\'yi yükseltmenin veya düşürmenin insülin iletimi üzerinde hiçbir etkisi yoktur. @@ -175,7 +187,7 @@ Karbonhidrat insülin Oranı (I:C oranı) Daha yüksek I:C oranları, belirli bir miktarda karbonhidrat için daha az insülin verilmesine yol açar. Daha düşük I:C oranları, belirli bir miktarda karbonhidrat için daha az insülin verilmesine yol açar. - 0 COB\'niz varsa, IC oranını değiştirmek, belirli bir KŞ değerini düzeltmek için farklı insülin miktarına sebep olur. + 0 AKRB varsa, Kİ oranını değiştirmek, belirli bir KŞ değerini düzeltmek için farklı insülin miktarına sebep olur. Ekmek (değişim) birimini 10g veya 12g olarak sayarsanız IC farklı olacaktır. IC anlamı şudur: 1Ü insülin kaç ekmek birimi (değişim) kapsar. https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/FAQ.html#carbohydrate-to-insulin-ratio-cr-g-u diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index 7e728fcaef..12b3ce5b18 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -6,6 +6,7 @@ Tedavi güvenliği Maks izin verilen bolus [U] Maks izin verilen karbonhidrat [g] + KŞ seviyesi altında düşük glikoz duraklatma meydana gelir %1$s Tercihler Tercihler NS\'tan tedavileri güncelle @@ -44,27 +45,27 @@ SMS komutlarını kullanarak uzaktan AndroidAPS kontrolü. İnsülin: Karbonhidrat: - IOB: - IOB: - Toplam IOB: - Toplam IOB etkinliği: + AİNS: + AİNS: + Toplam AİNS: + Toplam AİNS etkinliği: Süre: Oran: İns: - IOB: - Toplam IOB: + AİNS: + Toplam AİNS: GH Karbonhidrat Düzeltme - Bolus IOB + Bolus AİNS Şimdi Çalıştır SANAL POMPA Son Çalıştırma Girdi Parametreleri Glikoz durumu Mevcut Geçici Bazal - IOB Verisi + AİNS Verisi Profil Yemek Verisi Sonuç @@ -115,7 +116,7 @@ Yeni öneri mevcut Karbonhidrat Önerisi Nightscout\'un desteklenmeyen sürümü - Bazal IOB + Bazal AİNS Bolus kısıtlaması uygulandı Karbonhidrat kısıtlaması uygulandı Diğer @@ -139,8 +140,8 @@ İçe aktarma ayarları Maks Ü/s geçici Bazal ayarlanabilir Bu değer OpenAPS\'te \"maksimum bazal\" olarak adlandırılır - OpenAPS\'nin vermesine izin verilen maksimum bazal IOB (Aktif insülin) [U] - Bu değere OpenAPS bağlamında Maks IOB denir.\nAPS\'de bir kerede verilebilen maksimum insülin [U] ü. + OpenAPS\'in gönderebileceği maksimum bazal AİNS (Aktif insülin) [U] + Bu değere OpenAPS bağlamında Maks AİNS denir.\nAPS\'de bir kerede verilebilen maksimum insülin [U] ü. Dışa aktarılan tercihleri şifrelemek için kullanılacak ana parola sorulacaktır. İçe aktarılan tercihlerin şifresini çözmek için gerekli olan ana parola sorulacaktır. Aktarım iptal edildi! Tercihler dışa aktarılmadı! @@ -316,8 +317,8 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d Günlüğü temizle NSCLIENT yazma izni yok. Yanlış API Anahtarı? Wear ayarları - Ayrıntılı IOB göster - Saat arayüzü üzerinde IOB\'yi bolus ve bazal olarak ayırın + Ayrıntılı AİNS göster + Saat arayüzü üzerinde AİNS\'i bolus ve bazal olarak ayırın başarısız - lütfen telefonu kontrol edin n/a Hasta tipi @@ -356,7 +357,7 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d Döngü duraklatıldı Döngü devam ettirildi 15 dk eğilim - COB + AKRB Süperbolus NS\'a uygulama başlangıcını kaydet Ayarları uygulamak için uygulamadan çıkılıyor. @@ -439,7 +440,7 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d SMB (Super Micro Bolus) etkinleştir Daha hızlı bir etki için geçici bazal yerine Super Micro Bolus kullanın (Uam) Bildirilmemiş öğünlerin tespiti - IOB Eğrisi Tepe Zamanı + AİNS Eğrisi Tepe Zamanı Tepe zamanı [min] Tepe Serbest tepe Oref @@ -495,28 +496,28 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d Nightscout Client KŞ NS KŞ KŞ Hesaplaması - Bolus IOB (Aktif İnsülin) hesaplaması - Bazal IOB (Aktif İnsülin) hesaplaması + Bolus AİNS hesaplaması + Bazal AİNS hesaplaması Trend hesapla Süperbolus hesapla Sadece pozitif Sadece negatif - COB (Aktif Karbonhidrat) hesaplaması + AKRB (Aktif Karbonhidrat) hesaplaması Geçici hedef hesaplaması Yüzde Hesaplaması Döngü etkin APS Seçildi NSClient\'in yazma izni var Kapalı mod etkin - Maksimal IOB doğru şekilde ayarlandı + Maksimal AİNS doğru şekilde ayarlandı Seçili kaynaktan KŞ kullanılabilir Yayma bolus iletim hatası SMB\'yi her zaman etkinleştir SMB\'yi her zaman bağımsız olarak boluslarla etkinleştirin. G5 gibi verilerin yalnızca filtrelenmesiyle yalnızca KŞ kaynağıyla mümkündür Yemeklerden sonra SMB\'yi etkinleştir - Karbonhidrat alındıktan sonra 6 saat süreyle SMB\'yi (Super Micro Bolus) etkinleştirin.0 COB (Aktif Karbonhidrat) ile de çalışır, Şayet KŞ kaynağıyla kullanılacaksa verilerin iyice filtrelenmesiyle mümkündür.örn. Dexcom G5 gibi - SMB\'yi COB ile etkinleştir - COB (Aktif Karbonhidrat) aktif olduğunda SMB\'yi (Super Micro Bolus) etkinleştirin. + 0 AKRB (Aktif Karbonhidrat) olsa dahi, karbonhidrat alındıktan sonra 6 saat süreyle SMB\'yi (Süper Mikro Bolus) etkinleştirir. Şayet KŞ kaynağıyla kullanılacaksa verilerin iyice filtrelenmesiyle (Dexcom G5-G6 gibi) mümkündür. + SMB\'yi AKRB ile etkinleştir + AKRB (Aktif Karbonhidrat) aktif olduğunda SMB\'yi (Süper Mikro Bolus) etkinleştirin. Geçici hedeflerle SMB\'yi etkinleştir Geçici hedef aktif olduğunda SMB\'yi etkinleştir (yakında yeme, egzersiz) Yüksek geçici hedeflerle SMB\'yi etkinleştir @@ -579,7 +580,7 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d Bazal doğru ayarlanmış %2$s\'den dolayı yayma bolus %1$.1f Ü ile sınırlandırılıyor %2$s\'den dolayı karbonhidratlar %1$d g ile sınırlandırılıyor - %2$s\'den dolayı IOB (Aktif İnsülin)%1$.1f Ü ile sınırlandırılıyor + %2$s\'den dolayı AİNS %1$.1f Ü ile sınırlandırılıyor tercihlerde maksimum değer sert sınır Durumu okumak başarısız oldu @@ -589,8 +590,8 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d Açık Döngü modunda SMB\'ye (Super Micro Bolus) izin verilmiyor Yiyecek sıfırla - OpenAPS tarafından aşılmayacak, maksimum toplam IOB(Aktif İnsülin)[U] - Bu değer OpenAPS bağlamında Max IOB (Aktif İnsülin) olarak adlandırılır\nOpenAPS, mevcut IOB bu değerden daha büyükse daha fazla insülin eklemez + OpenAPS tarafından aşılamayacak maksimum toplam AİNS (Aktif İnsülin)[U] + Bu değer OpenAPS bağlamında Max AİNS (Aktif İnsülin) olarak adlandırılır\nOpenAPS, mevcut AİNS bu değerden daha büyükse daha fazla insülin vermez Besinin maksimum emilim süresi [h] Herhangi bir öğünün emildiği kabul edilen zaman. Kalan karbonhidratlar kesilecek. Altına düştüğünde insülinin askıya alındığı KŞ değeri. Varsayılan değer, standart hedef modeli kullanır. Kullanıcı değeri 60mg/dl (3,3mmol/l) ile 100mg/dl(5.5mmol/l) arasında ayarlayabilir. 65/3,6\'nın altındaki değerler varsayılan modelin kullanılmasına neden olur @@ -632,7 +633,7 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d Durum oku Kurulum sihirbazını atla AndroidAPS\'ın bazal değişiklikleri önerme/yapmasını sağlamak için aşağıdaki düğmeye basın - Duyarlılık eklentisi, duyarlılık tespiti ve COB hesaplaması için kullanılır. Daha fazla bilgi için ziyaret edin: + Duyarlılık eklentisi, duyarlılık tespiti ve AKRB hesaplaması için kullanılır. Daha fazla bilgi için ziyaret edin: https://androidaps.readthedocs.io/en/latest/Configuration/Sensitivity-detection-and-COB.html NSClient, Nightscout\'a bağlantıyı sağlar. Şimdi bu kısmı atlayabilirsiniz, ancak ayarlamadan görevleri geçemezsiniz. Lütfen unutmayın: yeni insülin profilleri en az 5 saat DIA gerektirir. Yeni profildeki DIA 5–6 saat, eski insülin profillerinde DIA 3saat\'e eşittir. @@ -736,9 +737,9 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d BYODA BYODA \'Kendi Dexcom Uygulamanızı Oluşturun\' uygulamasından KŞ değerlerini alın. - COB vs IOB + AKRB vs AİNS Bolus kısıtlaması uygulandı: %1$.2f Ü ile %2$.2f Ü - !!!!! Yavaş karbonhidrat emilimi tespit edildi: zamanın %2$d%% si. Hesaplamanızı iki kez kontrol edin. Aktif Karbonhidrat (COB) olduğundan fazla tahmin edilebilir, bu nedenle daha fazla insülin verilebilir !!!!!]]> + !!!!! Yavaş karbonhidrat emilimi tespit edildi: zamanın %2$d%% si. Hesaplamanızı iki kez kontrol edin. Aktif Karbonhidrat (AKRB) olduğundan fazla tahmin edilebilir, bu nedenle daha fazla insülin verilebilir !!!!!]]> Bolus sihirbazı sonucunun bu kadarını ilet [%] Bolus sihirbazı hesaplamayı gerçekleştirir ancak hesaplanan insülinin yalnızca bu kısmı iletilir. SMB algoritması ile kullanışlıdır. Ertele @@ -746,7 +747,7 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d Geçersiz mesaj %1$s ISF: %2$.1f %1$.0fg IC: %2$.1f - %1$.1fg IC: %2$.1f + %1$.1fg Kİ: %2$.1f %1$d%% dk. Profil adı: @@ -897,7 +898,7 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d GBO ve EB (Yayma Bolus) alın Başka bir örnek üzerinden girilen GBO ve EB (Yayma Bolusu) kabul edin İnsülinleri alın - NS veya NSClient aracılığıyla girilen insülini kabul edin (iletilmez, yalnızca aktif insüline göre (IOB) hesaplanır) + NS veya NSClient aracılığıyla girilen insülini kabul edin (iletilmez, yalnızca AİNS\'e göre hesaplanır) Karbonhidratları alın NS veya NSClient aracılığıyla girilen karbonhidratları kabul edin Terapi etkinlikleri alın @@ -977,13 +978,13 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d Seçili hızlı asistan artık mevcut değil, lütfen kutucuğu yenileyin Hesaplamayı temel alacak yeni bir KŞ yok! Etkin profil ayarlanmadı! - Aktif karbonhidrat bilinmiyor! KŞ okuması eksik veya son uygulama yeniden başlatılsın mı? + Aktif karbonhidrat (AKRB) bilinmiyor! KŞ okuması eksik veya son uygulama yeniden başlatılsın mı? Karbonhidrat kısıtlaması ihlali! Hesap (KİO: %1$.1f, İDF: %2$.1f) Karb: %1$.2fÜ - COB: %1$.0fg %2$.2fÜ + AKRB: %1$.0fg %2$.2fÜ KŞ: %1$.2fÜ - Aktif İnsülin: %1$.2fÜ + AİNS: %1$.2fÜ Süperbolus: %1$.2fÜ 15\' trend: %1$.2fÜ Yüzde: %1$.2fÜ x %2$d%% ≈ %3$.2fÜ @@ -1037,10 +1038,12 @@ Aktif Karbonhidratın ne kadar hızlı sindirildiğine ve KŞ\'nin beklenenden d sadece saatte sadece telefonda sürükle ve bırak kolu + Ara GlucoRx Aidex Aidex GlucoRx Aidex CGMS\'den KŞ değerleri alır. Şarj seçenekleri tarafından engellendi Bağlantı seçenekleri tarafından engellendi + (Saat Bağlı Değil) diff --git a/app/src/main/res/values-zh-rCN/exam.xml b/app/src/main/res/values-zh-rCN/exam.xml index ae7b8c0ad4..bb37fdc4ef 100644 --- a/app/src/main/res/values-zh-rCN/exam.xml +++ b/app/src/main/res/values-zh-rCN/exam.xml @@ -137,7 +137,6 @@ 通过注射器或胰岛素笔输注你计算出的“遗漏”胰岛素的一部分。 不做任何事情,允许AndroidAPS纠正高血糖导致的任何结果。 活性胰岛素(IOB) - 选择所有正确的答案。 活性胰岛素IOB数值受发布的临时基础率影响。 当您的血糖低于目标值时,不会被给予高临时基础率。 在没有运动的情况下,活性胰岛素IOB长时间负数表明你的个人配置太强,你需要在配置中减少的胰岛素用量。 diff --git a/app/src/main/res/values/exam.xml b/app/src/main/res/values/exam.xml index d61962065a..ff2a4f9d5d 100644 --- a/app/src/main/res/values/exam.xml +++ b/app/src/main/res/values/exam.xml @@ -136,8 +136,20 @@ Compare values in AndroidAPS and pump history (if pump supports this). Bolus a proportion of your calculated ‘missed’ insulin by either syringe/pen or using a prime. Do nothing and allow AndroidAPS to correct any resulting high blood glucose level. + Carbs on Board (COB) + How changing ISF value affects COB calculation? + Increasing ISF will take absorbing carbs a longer time + Increasing ISF will take absorbing carbs a shorter time + Increasing ISF will not affect calculated carbs absorption + How changing IC value affects COB calculation? + Increasing IC will take absorbing carbs a longer time + Increasing IC will take absorbing carbs a shorter time + Increasing IC will not affect calculated carbs absorption + How changing profile percentage affects COB calculation? + Setting profile to 150% will take absorbing carbs a longer time + Setting profile to 150% will take absorbing carbs a shorter time + Setting profile to 150% will not affect calculated carbs absorption Insulin on Board (IOB) - Check all the correct answers. IOB value is affected by issued temporary basals. High temp basal will not be given when your blood sugar is below target. Negative IOB for a substantial period in the absence of exercise suggests your profile is too strong and less insulin is needed in your settings. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d33d543dd9..7bc428a968 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1242,13 +1242,15 @@ only on watch only on phone drag and drop handle + Search - + GlucoRx Aidex Aidex Receive BG values from GlucoRx Aidex CGMS. Blocked by charging options Blocked by connectivity options + (No Watch Connected) diff --git a/app/src/main/res/xml/pref_autotune.xml b/app/src/main/res/xml/pref_autotune.xml index fc5f6dbc72..aef72365a0 100644 --- a/app/src/main/res/xml/pref_autotune.xml +++ b/app/src/main/res/xml/pref_autotune.xml @@ -4,7 +4,7 @@ + app:initialExpandedChildrenCount="0"> Spustit automatizace Přidat pravidlo Odstranit/řadit + Zastavit zpracovávání diff --git a/automation/src/main/res/values-es-rES/strings.xml b/automation/src/main/res/values-es-rES/strings.xml index f07949b232..3522537a95 100644 --- a/automation/src/main/res/values-es-rES/strings.xml +++ b/automation/src/main/res/values-es-rES/strings.xml @@ -123,4 +123,5 @@ Ejecutar automatizaciones Añadir regla Eliminar/ordenar + Parar procesamiento diff --git a/automation/src/main/res/values-fr-rFR/strings.xml b/automation/src/main/res/values-fr-rFR/strings.xml index 397e534b59..bc8aec0c4c 100644 --- a/automation/src/main/res/values-fr-rFR/strings.xml +++ b/automation/src/main/res/values-fr-rFR/strings.xml @@ -123,4 +123,5 @@ Exécuter les automatisations Ajouter une règle Supprimer/trier + Arrêter le traitement diff --git a/automation/src/main/res/values-no-rNO/strings.xml b/automation/src/main/res/values-no-rNO/strings.xml index ffa6fc6483..92fd9ecf6d 100644 --- a/automation/src/main/res/values-no-rNO/strings.xml +++ b/automation/src/main/res/values-no-rNO/strings.xml @@ -123,4 +123,5 @@ Kjør automatisering Legg til regel Fjern/sorter + Stopp prosessering diff --git a/automation/src/main/res/values-ru-rRU/strings.xml b/automation/src/main/res/values-ru-rRU/strings.xml index 181d25930f..d91dd62b17 100644 --- a/automation/src/main/res/values-ru-rRU/strings.xml +++ b/automation/src/main/res/values-ru-rRU/strings.xml @@ -123,4 +123,5 @@ Запустить автоматизацию Добавить правило Удаление/сортировка + Остановить обработку diff --git a/automation/src/main/res/values-tr-rTR/strings.xml b/automation/src/main/res/values-tr-rTR/strings.xml index 2873b09421..adafbd5e22 100644 --- a/automation/src/main/res/values-tr-rTR/strings.xml +++ b/automation/src/main/res/values-tr-rTR/strings.xml @@ -93,8 +93,8 @@ Hedef [%1$s]: Son bolus önce Son bolus zamanı %1$s %2$s dakika önce - COB - AK (COB) %1$s %2$.0f + AKRB + AKRB %1$s %2$.0f IOB [U]: Mesafe [m]: Yinelenen zaman @@ -123,4 +123,5 @@ Otomasyonları çalıştır Kural ekle Kaldır/sırala + İşlemeyi durdur diff --git a/automation/src/main/res/values/strings.xml b/automation/src/main/res/values/strings.xml index 30ca8cf431..3e7d2daf50 100644 --- a/automation/src/main/res/values/strings.xml +++ b/automation/src/main/res/values/strings.xml @@ -126,5 +126,6 @@ Run automations Add rule Remove/sort + Stop processing \ No newline at end of file diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt index 874077109b..68066c6e7b 100644 --- a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt @@ -5,13 +5,14 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.interfaces.ConfigBuilder import info.nightscout.androidaps.interfaces.Loop +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.automation.actions.Action import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopEnable +import info.nightscout.androidaps.plugins.general.automation.actions.ActionStopProcessing import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnectorTest import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy -import info.nightscout.androidaps.interfaces.ResourceHelper import org.json.JSONObject import org.junit.Assert import org.junit.Test @@ -70,4 +71,14 @@ class AutomationEventTest : TestBase() { Assert.assertFalse(event.actions === clone.actions) // not the same object reference Assert.assertEquals(clone.toJSON(), clone.toJSON()) } + + @Test + fun hasStopProcessing() { + val event = AutomationEvent(injector) + event.title = "Test" + event.trigger = TriggerDummy(injector).instantiate(JSONObject(TriggerConnectorTest.oneItem)) as TriggerConnector + Assert.assertFalse(event.hasStopProcessing()) + event.addAction(ActionStopProcessing(injector)) + Assert.assertTrue(event.hasStopProcessing()) + } } diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopProcessingTest.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopProcessingTest.kt new file mode 100644 index 0000000000..c16de7bdac --- /dev/null +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopProcessingTest.kt @@ -0,0 +1,44 @@ +package info.nightscout.androidaps.plugins.general.automation.actions + +import info.nightscout.androidaps.automation.R +import info.nightscout.androidaps.queue.Callback +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.`when` + +class ActionStopProcessingTest : ActionsTestBase() { + + lateinit var sut: ActionStopProcessing + + @Before + fun setup() { + + `when`(rh.gs(R.string.stop_processing)).thenReturn("Stop processing") + sut = ActionStopProcessing(injector) + } + + @Test + fun friendlyNameTest() { + Assert.assertEquals(R.string.stop_processing, sut.friendlyName()) + } + + @Test + fun shortDescriptionTest() { + Assert.assertEquals("Stop processing", sut.shortDescription()) + } + + @Test + fun iconTest() { + Assert.assertEquals(R.drawable.ic_stop_24dp, sut.icon()) + } + + @Test + fun doActionTest() { + sut.doAction(object : Callback() { + override fun run() { + Assert.assertTrue(result.success) + } + }) + } +} \ No newline at end of file diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt index 783fcbb2e6..0461810d0d 100644 --- a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt @@ -144,6 +144,9 @@ ActionsTestBase : TestBaseWithProfile() { it.profileFunction = profileFunction it.uel = uel } + if (it is ActionStopProcessing) { + it.rh = rh + } if (it is PumpEnactResult) { it.rh = rh } diff --git a/build.gradle b/build.gradle index f4ac0a131f..8feda7ccf3 100644 --- a/build.gradle +++ b/build.gradle @@ -2,15 +2,15 @@ buildscript { ext { - kotlin_version = '1.7.0' + kotlin_version = '1.7.10' core_version = '1.8.0' rxjava_version = '3.1.5' rxandroid_version = '3.0.0' rxkotlin_version = '3.0.1' room_version = '2.4.2' - lifecycle_version = '2.4.1' + lifecycle_version = '2.5.0' dagger_version = '2.42' - coroutines_version = '1.6.3' + coroutines_version = '1.6.4' activity_version = '1.4.0' fragmentktx_version = '1.4.1' ormLite_version = '4.46' @@ -66,7 +66,7 @@ plugins { id "io.gitlab.arturbosch.detekt" version "1.20.0" id "org.jlleitschuh.gradle.ktlint" version "10.3.0" id 'org.barfuin.gradle.jacocolog' version '2.0.0' - id 'org.jetbrains.kotlin.android' version '1.7.0' apply false + id 'org.jetbrains.kotlin.android' version '1.7.10' apply false } allprojects { diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/Autotune.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/Autotune.kt index fbce082882..310be83e1b 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/Autotune.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/Autotune.kt @@ -2,8 +2,9 @@ package info.nightscout.androidaps.interfaces interface Autotune { - fun aapsAutotune(daysBack: Int, autoSwitch: Boolean, profileToTune: String = "", days: BooleanArray? = null): String + fun aapsAutotune(daysBack: Int, autoSwitch: Boolean, profileToTune: String = "", days: BooleanArray? = null) fun atLog(message: String) var lastRunSuccess: Boolean + var calculationRunning: Boolean } \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt b/core/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt index e4ed8f77fa..46a8813120 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt @@ -10,6 +10,10 @@ import info.nightscout.androidaps.core.R import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.ObjectInputStream import javax.inject.Inject import javax.inject.Singleton @@ -79,7 +83,7 @@ class FabricPrivacy @Inject constructor( // Crashlytics log message fun logMessage(message: String) { - aapsLogger.info(LTag.CORE,"Crashlytics log message: $message") + aapsLogger.info(LTag.CORE, "Crashlytics log message: $message") FirebaseCrashlytics.getInstance().log(message) } @@ -92,4 +96,31 @@ class FabricPrivacy @Inject constructor( fun fabricEnabled(): Boolean { return sp.getBoolean(R.string.key_enable_fabric, true) } -} \ No newline at end of file + + fun logWearException(wearException: EventData.WearException) { + aapsLogger.debug(LTag.WEAR, "logWearException") + FirebaseCrashlytics.getInstance().apply { + setCustomKey("wear_exception", true) + setCustomKey("wear_board", wearException.board) + setCustomKey("wear_fingerprint", wearException.fingerprint) + setCustomKey("wear_sdk", wearException.sdk) + setCustomKey("wear_model", wearException.model) + setCustomKey("wear_manufacturer", wearException.manufacturer) + setCustomKey("wear_product", wearException.product) + } + logException(byteArrayToThrowable(wearException.exception)) + } + + private fun byteArrayToThrowable(wearExceptionData: ByteArray): Throwable { + val bis = ByteArrayInputStream(wearExceptionData) + try { + val ois = ObjectInputStream(bis) + return ois.readObject() as Throwable + } catch (e: IOException) { + e.printStackTrace() + } catch (e: ClassNotFoundException) { + e.printStackTrace() + } + return IllegalArgumentException("Wear Exception could not be de-serialized") + } +} diff --git a/core/src/main/java/info/nightscout/androidaps/utils/userEntry/UserEntryPresentationHelper.kt b/core/src/main/java/info/nightscout/androidaps/utils/userEntry/UserEntryPresentationHelper.kt index 0bc1c3d8ca..faae359445 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/userEntry/UserEntryPresentationHelper.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/userEntry/UserEntryPresentationHelper.kt @@ -91,9 +91,9 @@ class UserEntryPresentationHelper @Inject constructor( Sources.Insight -> R.drawable.ic_insight_128 Sources.Combo -> R.drawable.ic_combo_128 Sources.Medtronic -> R.drawable.ic_veo_128 - Sources.Omnipod -> R.drawable.ic_pod_128 - Sources.OmnipodEros -> R.drawable.ic_pod_128 - Sources.OmnipodDash -> R.drawable.ic_pod_128 + Sources.Omnipod -> R.drawable.ic_patch_pump_outline + Sources.OmnipodEros -> R.drawable.ic_patch_pump_outline + Sources.OmnipodDash -> R.drawable.ic_patch_pump_outline Sources.MDI -> R.drawable.ic_ict Sources.VirtualPump -> R.drawable.ic_virtual_pump Sources.SMS -> R.drawable.ic_sms diff --git a/core/src/main/res/drawable/ic_close.xml b/core/src/main/res/drawable/ic_close.xml index 49ac6f1398..b6edb1d328 100644 --- a/core/src/main/res/drawable/ic_close.xml +++ b/core/src/main/res/drawable/ic_close.xml @@ -1,10 +1,9 @@ diff --git a/core/src/main/res/drawable/ic_generic_cgm.xml b/core/src/main/res/drawable/ic_generic_cgm.xml index 2758d5d8b8..2838fce45f 100644 --- a/core/src/main/res/drawable/ic_generic_cgm.xml +++ b/core/src/main/res/drawable/ic_generic_cgm.xml @@ -1,45 +1,21 @@ - - - - - - - - - - - + android:fillColor="?attr/colorControlNormal"/> diff --git a/core/src/main/res/drawable/ic_glimp.xml b/core/src/main/res/drawable/ic_glimp.xml index ed10736e93..d58294f27e 100644 --- a/core/src/main/res/drawable/ic_glimp.xml +++ b/core/src/main/res/drawable/ic_glimp.xml @@ -3,85 +3,86 @@ android:height="64dp" android:viewportWidth="64" android:viewportHeight="64"> - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/core/src/main/res/drawable/ic_insulin.xml b/core/src/main/res/drawable/ic_insulin.xml index a273562b2d..a4f653fe07 100644 --- a/core/src/main/res/drawable/ic_insulin.xml +++ b/core/src/main/res/drawable/ic_insulin.xml @@ -3,28 +3,16 @@ android:height="64dp" android:viewportWidth="64" android:viewportHeight="64"> - - - - - - - - + + + + diff --git a/core/src/main/res/drawable/ic_nightscout_profile.xml b/core/src/main/res/drawable/ic_nightscout_profile.xml index 7fb7c6f4a5..9b9b07d3f9 100644 --- a/core/src/main/res/drawable/ic_nightscout_profile.xml +++ b/core/src/main/res/drawable/ic_nightscout_profile.xml @@ -1,10 +1,23 @@ - - - + + - - - + android:strokeWidth="3.09" + android:strokeColor="?attr/colorControlNormal" /> + + + diff --git a/core/src/main/res/drawable/ic_nightscout_syncs.xml b/core/src/main/res/drawable/ic_nightscout_syncs.xml index d90bc99dd3..381de5de2c 100644 --- a/core/src/main/res/drawable/ic_nightscout_syncs.xml +++ b/core/src/main/res/drawable/ic_nightscout_syncs.xml @@ -1,11 +1,27 @@ - - - - - + - - + android:strokeWidth="3.09" + android:strokeColor="?attr/colorControlNormal" /> + + + + + diff --git a/core/src/main/res/drawable/ic_nsclient_bg.xml b/core/src/main/res/drawable/ic_nsclient_bg.xml index 00e95c65cb..fff0a558f6 100644 --- a/core/src/main/res/drawable/ic_nsclient_bg.xml +++ b/core/src/main/res/drawable/ic_nsclient_bg.xml @@ -3,15 +3,20 @@ android:height="64dp" android:viewportWidth="64" android:viewportHeight="64"> - - - + + + + + diff --git a/core/src/main/res/drawable/ic_patch_pump_outline.xml b/core/src/main/res/drawable/ic_patch_pump_outline.xml new file mode 100644 index 0000000000..6a86ec1503 --- /dev/null +++ b/core/src/main/res/drawable/ic_patch_pump_outline.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/core/src/main/res/drawable/ic_refresh.xml b/core/src/main/res/drawable/ic_refresh.xml index 8229a9a64c..ceda60bb9a 100644 --- a/core/src/main/res/drawable/ic_refresh.xml +++ b/core/src/main/res/drawable/ic_refresh.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/core/src/main/res/drawable/ic_sensor.xml b/core/src/main/res/drawable/ic_sensor.xml index 39caee867c..4ce0d49c60 100644 --- a/core/src/main/res/drawable/ic_sensor.xml +++ b/core/src/main/res/drawable/ic_sensor.xml @@ -1,5 +1,12 @@ - - - + + + diff --git a/core/src/main/res/drawable/ic_sort.xml b/core/src/main/res/drawable/ic_sort.xml index 4b1317ac20..8ff707c8ca 100644 --- a/core/src/main/res/drawable/ic_sort.xml +++ b/core/src/main/res/drawable/ic_sort.xml @@ -2,7 +2,6 @@ android:width="24dp" android:height="24dp" android:autoMirrored="true" - android:tint="#FFFFFF" android:viewportWidth="24" android:viewportHeight="24"> + android:fillColor="?attr/colorControlNormal"/> diff --git a/core/src/main/res/values-cs-rCZ/strings.xml b/core/src/main/res/values-cs-rCZ/strings.xml index 8f03f86ea7..b0570bbe77 100644 --- a/core/src/main/res/values-cs-rCZ/strings.xml +++ b/core/src/main/res/values-cs-rCZ/strings.xml @@ -488,6 +488,7 @@ Autotune spuštěno bez přepnutí profilu Autotune spuštěno, a profil automaticky přepnut Chyba při posledním spuštění Autotune + Byla zjištěna jiná běžící úloha Autotune, spuštění zrušeno %1$d den %1$d dnů diff --git a/core/src/main/res/values-es-rES/strings.xml b/core/src/main/res/values-es-rES/strings.xml index 6c427b6661..24f6b404b5 100644 --- a/core/src/main/res/values-es-rES/strings.xml +++ b/core/src/main/res/values-es-rES/strings.xml @@ -488,6 +488,7 @@ Autotune ejecutado sin cambio de perfil Autotune ejecutado y perfil cambiado automáticamente Error durante la última ejecución de Autotune + Se ha detectado otra ejecución de Autotune, ejecución cancelada %1$d día %1$d días diff --git a/core/src/main/res/values-fr-rFR/strings.xml b/core/src/main/res/values-fr-rFR/strings.xml index 2170468b20..cfd4038982 100644 --- a/core/src/main/res/values-fr-rFR/strings.xml +++ b/core/src/main/res/values-fr-rFR/strings.xml @@ -488,6 +488,7 @@ Autotune exécuté sans changement de profil Autotune exécuté et le profil automatiquement activé Erreur lors de la dernière exécution d\'Autotune + Une autre exécution d\'Autotune est détectée, l\'exécution est annulée %1$d jour %1$d jours diff --git a/core/src/main/res/values-iw-rIL/strings.xml b/core/src/main/res/values-iw-rIL/strings.xml index 700387ef45..20cb735c6e 100644 --- a/core/src/main/res/values-iw-rIL/strings.xml +++ b/core/src/main/res/values-iw-rIL/strings.xml @@ -488,6 +488,7 @@ כוונון אוטומטי מופעל ללא החלפת פרופיל כוונון אוטומטי הופעל והפרופיל הוחלף אוטומטית שגיאה במהלך הכיוונון האוטומטי האחרון + נמצאה הפעלה נוספת של הכוונון האוטומטי ברקע, ההפעלה מבוטלת %1$d יום %1$d ימים diff --git a/core/src/main/res/values-no-rNO/strings.xml b/core/src/main/res/values-no-rNO/strings.xml index d8b19182c7..b25407ac6a 100644 --- a/core/src/main/res/values-no-rNO/strings.xml +++ b/core/src/main/res/values-no-rNO/strings.xml @@ -488,6 +488,7 @@ Autotune utført uten profilbytte Autotune utført og profil automatisk skiftet ut Feil oppdaget under siste Autotune kjøring + Autotune kjører allerede. Kjøring avbrutt %1$d dag %1$d dager diff --git a/core/src/main/res/values-ru-rRU/strings.xml b/core/src/main/res/values-ru-rRU/strings.xml index 6cd340ae38..473b44ee80 100644 --- a/core/src/main/res/values-ru-rRU/strings.xml +++ b/core/src/main/res/values-ru-rRU/strings.xml @@ -488,6 +488,7 @@ Autotune выполнен без переключения профиля Autotune выполнен и профиль переключён автоматически Ошибка во время последнего выполнения Autotune + Обнаружен другой запуск Autotune, выполнение отменено %1$d день %1$d дня diff --git a/core/src/main/res/values-sk-rSK/strings.xml b/core/src/main/res/values-sk-rSK/strings.xml index 485f80b80d..1de2cd04aa 100644 --- a/core/src/main/res/values-sk-rSK/strings.xml +++ b/core/src/main/res/values-sk-rSK/strings.xml @@ -488,6 +488,7 @@ Autotune spustený bez prepnutia profilu Autotune spustený a profil automaticky prepnutý Chyba pri poslednom spustení Autotune + Bola zistená iná spustená úloha Autotune, spustenie zrušené %1$d deň %1$d dní diff --git a/core/src/main/res/values-tr-rTR/strings.xml b/core/src/main/res/values-tr-rTR/strings.xml index 07bb349882..4ecfd908e0 100644 --- a/core/src/main/res/values-tr-rTR/strings.xml +++ b/core/src/main/res/values-tr-rTR/strings.xml @@ -103,7 +103,7 @@ yok Glikoz IOB - COB + AKRB İsim: Zaman WiFi SSID @@ -488,6 +488,7 @@ OtoAyar profil değiştirme olmadan çalıştırıldı OtoAyar çalıştırıldı ve profil otomatik olarak değiştirildi Son OtoAyar çalışması sırasında hata oluştu + Başka bir OtoAyar çalıştırması tespit edildi, çalıştırma iptal edildi %1$d gün %1$d gün diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index fbb3a8a91f..66aa5bb148 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -601,6 +601,7 @@ Autotune runned without profile switch Autotune runned and profile automatically switched Error during last Autotune run + Another run of Autotune is detected, run cancelled %1$d day diff --git a/diaconn/src/main/res/values-iw-rIL/strings.xml b/diaconn/src/main/res/values-iw-rIL/strings.xml index 793e713ae8..d07e682614 100644 --- a/diaconn/src/main/res/values-iw-rIL/strings.xml +++ b/diaconn/src/main/res/values-iw-rIL/strings.xml @@ -135,4 +135,6 @@ בעת השהייה עקב סוכר נמוך, ההזרקה מוגבלת מצב השהייה עקב סוכר נמוך מופעל, פקודת ON נדחתה. מצב השהייה עקב סוכר נמוך כבוי, פקודת OFF נדחתה. + שלח יומני משאבה לענן Diaconn. + סנכרון Diaconn Cloud diff --git a/icons/action_add.svg b/icons/action_add.svg new file mode 100644 index 0000000000..929dd4f7d9 --- /dev/null +++ b/icons/action_add.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/icons/action_minus.svg b/icons/action_minus.svg new file mode 100644 index 0000000000..c7ef549809 --- /dev/null +++ b/icons/action_minus.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/icons/cancel.svg b/icons/cancel.svg new file mode 100644 index 0000000000..070444ebd3 --- /dev/null +++ b/icons/cancel.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/icons/confirm.svg b/icons/confirm.svg new file mode 100644 index 0000000000..0e93b2634e --- /dev/null +++ b/icons/confirm.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/icons/icon_snooze.svg b/icons/icon_snooze.svg new file mode 100644 index 0000000000..c2190ac542 --- /dev/null +++ b/icons/icon_snooze.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + diff --git a/icons/loop.svg b/icons/loop.svg new file mode 100644 index 0000000000..b9e96b325d --- /dev/null +++ b/icons/loop.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/icons/setting_off.svg b/icons/setting_off.svg new file mode 100644 index 0000000000..ae79852ac7 --- /dev/null +++ b/icons/setting_off.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/icons/setting_on.svg b/icons/setting_on.svg new file mode 100644 index 0000000000..72d9d52c8b --- /dev/null +++ b/icons/setting_on.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt index e3e0642a70..84ac9a6138 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt @@ -2,7 +2,6 @@ package info.nightscout.androidaps.plugins.pump.medtronic import android.annotation.SuppressLint import android.content.Intent -import android.graphics.Color import android.os.Bundle import android.os.Handler import android.os.HandlerThread @@ -16,13 +15,11 @@ import info.nightscout.androidaps.events.EventTempBasalChange import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueue import info.nightscout.androidaps.interfaces.PumpSync -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState import info.nightscout.androidaps.plugins.pump.common.events.EventRefreshButtonState import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusActivity @@ -42,8 +39,9 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.WarnColors import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject @@ -59,7 +57,6 @@ class MedtronicFragment : DaggerFragment() { @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var medtronicPumpPlugin: MedtronicPumpPlugin @Inject lateinit var warnColors: WarnColors - @Inject lateinit var rileyLinkUtil: RileyLinkUtil @Inject lateinit var medtronicUtil: MedtronicUtil @Inject lateinit var medtronicPumpStatus: MedtronicPumpStatus @Inject lateinit var rileyLinkServiceData: RileyLinkServiceData @@ -92,7 +89,7 @@ class MedtronicFragment : DaggerFragment() { binding.rlStatus.text = rh.gs(RileyLinkServiceState.NotStarted.resourceId) - binding.pumpStatusIcon.setTextColor(rh.gac(context,R.attr.defaultTextColor)) + binding.pumpStatusIcon.setTextColor(rh.gac(context, R.attr.defaultTextColor)) @SuppressLint("SetTextI18n") binding.pumpStatusIcon.text = "{fa-bed}" @@ -139,9 +136,9 @@ class MedtronicFragment : DaggerFragment() { .toObservable(EventRileyLinkDeviceStatusChange::class.java) .observeOn(aapsSchedulers.main) .subscribe({ - aapsLogger.debug(LTag.PUMP, "onStatusEvent(EventRileyLinkDeviceStatusChange): $it") - setDeviceStatus() - }, fabricPrivacy::logException) + aapsLogger.debug(LTag.PUMP, "onStatusEvent(EventRileyLinkDeviceStatusChange): $it") + setDeviceStatus() + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventMedtronicPumpValuesChanged::class.java) .observeOn(aapsSchedulers.main) @@ -158,10 +155,10 @@ class MedtronicFragment : DaggerFragment() { .toObservable(EventMedtronicPumpConfigurationChanged::class.java) .observeOn(aapsSchedulers.main) .subscribe({ - aapsLogger.debug(LTag.PUMP, "EventMedtronicPumpConfigurationChanged triggered") - medtronicPumpPlugin.rileyLinkService?.verifyConfiguration() - updateGUI() - }, fabricPrivacy::logException) + aapsLogger.debug(LTag.PUMP, "EventMedtronicPumpConfigurationChanged triggered") + medtronicPumpPlugin.rileyLinkService?.verifyConfiguration() + updateGUI() + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventPumpStatusChanged::class.java) .observeOn(aapsSchedulers.main) @@ -191,7 +188,7 @@ class MedtronicFragment : DaggerFragment() { @Synchronized private fun setDeviceStatus() { val resourceId = rileyLinkServiceData.rileyLinkServiceState.resourceId - val rileyLinkError = medtronicPumpPlugin.rileyLinkService?.error + val rileyLinkError =rileyLinkServiceData.rileyLinkError binding.rlStatus.text = when { rileyLinkServiceData.rileyLinkServiceState == RileyLinkServiceState.NotStarted -> rh.gs(resourceId) @@ -200,7 +197,7 @@ class MedtronicFragment : DaggerFragment() { rileyLinkServiceData.rileyLinkServiceState.isError && rileyLinkError != null -> "{fa-bluetooth-b} " + rh.gs(rileyLinkError.getResourceId(RileyLinkTargetDevice.MedtronicPump)) else -> "{fa-bluetooth-b} " + rh.gs(resourceId) } - binding.rlStatus.setTextColor(rh.gac( context, if (rileyLinkError != null) R.attr.warningColor else R.attr.defaultTextColor)) + binding.rlStatus.setTextColor(rh.gac(context, if (rileyLinkError != null) R.attr.warningColor else R.attr.defaultTextColor)) binding.errors.text = rileyLinkServiceData.rileyLinkError?.let { @@ -253,8 +250,10 @@ class MedtronicFragment : DaggerFragment() { private fun displayNotConfiguredDialog() { context?.let { - OKDialog.show(it, rh.gs(R.string.medtronic_warning), - rh.gs(R.string.medtronic_error_operation_not_possible_no_configuration), null) + OKDialog.show( + it, rh.gs(R.string.medtronic_warning), + rh.gs(R.string.medtronic_error_operation_not_possible_no_configuration), null + ) } } @@ -272,7 +271,7 @@ class MedtronicFragment : DaggerFragment() { val min = (System.currentTimeMillis() - medtronicPumpStatus.lastConnection) / 1000 / 60 if (medtronicPumpStatus.lastConnection + 60 * 1000 > System.currentTimeMillis()) { binding.lastConnection.setText(R.string.medtronic_pump_connected_now) - binding.lastConnection.setTextColor(rh.gac(context,R.attr.defaultTextColor)) + binding.lastConnection.setTextColor(rh.gac(context, R.attr.defaultTextColor)) } else if (medtronicPumpStatus.lastConnection + 30 * 60 * 1000 < System.currentTimeMillis()) { if (min < 60) { @@ -288,10 +287,10 @@ class MedtronicFragment : DaggerFragment() { binding.lastConnection.text = (rh.gq(R.plurals.duration_days, d, d) + " " + rh.gs(R.string.ago)) } - binding.lastConnection.setTextColor(rh.gac(context,R.attr.warningColor)) + binding.lastConnection.setTextColor(rh.gac(context, R.attr.warningColor)) } else { binding.lastConnection.text = minAgo - binding.lastConnection.setTextColor(rh.gac(context,R.attr.defaultTextColor)) + binding.lastConnection.setTextColor(rh.gac(context, R.attr.defaultTextColor)) } } @@ -329,7 +328,8 @@ class MedtronicFragment : DaggerFragment() { if (medtronicPumpStatus.batteryType == BatteryType.None || medtronicPumpStatus.batteryVoltage == null) { binding.pumpStateBattery.text = "{fa-battery-" + medtronicPumpStatus.batteryRemaining / 25 + "} " } else { - binding.pumpStateBattery.text = "{fa-battery-" + medtronicPumpStatus.batteryRemaining / 25 + "} " + medtronicPumpStatus.batteryRemaining + "%" + String.format(" (%.2f V)", medtronicPumpStatus.batteryVoltage) + binding.pumpStateBattery.text = + "{fa-battery-" + medtronicPumpStatus.batteryRemaining / 25 + "} " + medtronicPumpStatus.batteryRemaining + "%" + String.format(" (%.2f V)", medtronicPumpStatus.batteryVoltage) } warnColors.setColorInverse(binding.pumpStateBattery, medtronicPumpStatus.batteryRemaining.toDouble(), 25.0, 10.0) @@ -340,19 +340,15 @@ class MedtronicFragment : DaggerFragment() { medtronicPumpPlugin.rileyLinkService?.verifyConfiguration() binding.errors.text = medtronicPumpStatus.errorInfo - val showRileyLinkBatteryLevel: Boolean = rileyLinkServiceData.showBatteryLevel - - if (showRileyLinkBatteryLevel) { + if (rileyLinkServiceData.showBatteryLevel) { binding.rlBatteryView.visibility = View.VISIBLE binding.rlBatteryLabel.visibility = View.VISIBLE binding.rlBatteryState.visibility = View.VISIBLE binding.rlBatteryLayout.visibility = View.VISIBLE binding.rlBatterySemicolon.visibility = View.VISIBLE - if (rileyLinkServiceData.batteryLevel == null) { - binding.rlBatteryState.text = " ?" - } else { - binding.rlBatteryState.text = "{fa-battery-" + rileyLinkServiceData.batteryLevel / 25 + "} " + rileyLinkServiceData.batteryLevel + "%" - } + binding.rlBatteryState.text = + if (rileyLinkServiceData.batteryLevel == null) " ?" + else "{fa-battery-${rileyLinkServiceData.batteryLevel!! / 25}} ${rileyLinkServiceData.batteryLevel}%" } else { binding.rlBatteryView.visibility = View.GONE binding.rlBatteryLabel.visibility = View.GONE diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt index 2fbebf5bf7..dd43c9bf3f 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt @@ -20,6 +20,7 @@ import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract +import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.common.events.EventRefreshButtonState @@ -33,6 +34,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask import info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryTBR import info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncEntriesCreator +import info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil import info.nightscout.androidaps.plugins.pump.common.utils.ProfileUtil import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry @@ -54,7 +56,6 @@ import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil.Comp import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.TimeChangeType -import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag @@ -90,7 +91,7 @@ class MedtronicPumpPlugin @Inject constructor( dateUtil: DateUtil, aapsSchedulers: AapsSchedulers, pumpSync: PumpSync, - pumpSyncStorage: info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage + pumpSyncStorage: PumpSyncStorage ) : PumpPluginAbstract( PluginDescription() // .mainType(PluginType.PUMP) // @@ -194,16 +195,18 @@ class MedtronicPumpPlugin @Inject constructor( } } - override fun onStartCustomActions() { + override fun hasService(): Boolean { + return true + } + + override fun onStartScheduledPumpActions() { // check status every minute (if any status needs refresh we send readStatus command) Thread { do { SystemClock.sleep(60000) if (this.isInitialized) { - val statusRefresh = workWithStatusRefresh( - StatusRefreshAction.GetData, null, null - )!! + val statusRefresh = synchronized(statusRefreshMap) { HashMap(statusRefreshMap) } if (doWeHaveAnyStatusNeededRefreshing(statusRefresh)) { if (!commandQueue.statusInQueue()) { commandQueue.readStatus(rh.gs(R.string.scheduled_status_refresh), null) @@ -215,27 +218,13 @@ class MedtronicPumpPlugin @Inject constructor( }.start() } - override val serviceClass: Class<*> - get() = RileyLinkMedtronicService::class.java - - override val pumpStatusData: info.nightscout.androidaps.plugins.pump.common.data.PumpStatus - get() = medtronicPumpStatus - - override fun deviceID(): String { - return "Medtronic" - } - - override val isFakingTempsByExtendedBoluses: Boolean - get() = false - - override fun canHandleDST(): Boolean { - return false - } - - // Pump Plugin + override val serviceClass: Class<*> = RileyLinkMedtronicService::class.java + override val pumpStatusData: PumpStatus get() = medtronicPumpStatus + override fun deviceID(): String = "Medtronic" + override val isFakingTempsByExtendedBoluses: Boolean = false + override fun canHandleDST(): Boolean = false private var isServiceSet: Boolean = false - - override val rileyLinkService: RileyLinkMedtronicService? = rileyLinkMedtronicService + override val rileyLinkService: RileyLinkMedtronicService? get() = rileyLinkMedtronicService override val pumpInfo: RileyLinkPumpInfo get() = RileyLinkPumpInfo( @@ -244,7 +233,7 @@ class MedtronicPumpPlugin @Inject constructor( medtronicPumpStatus.serialNumber ) - override val lastConnectionTimeMillis: Long = medtronicPumpStatus.lastConnection + override val lastConnectionTimeMillis: Long get() = medtronicPumpStatus.lastConnection override fun setLastCommunicationToNow() { medtronicPumpStatus.setLastCommunicationToNow() @@ -319,12 +308,8 @@ class MedtronicPumpPlugin @Inject constructor( private val isPumpNotReachable: Boolean get() { val rileyLinkServiceState = rileyLinkServiceData.rileyLinkServiceState - if (rileyLinkServiceState == null) { - aapsLogger.debug(LTag.PUMP, "RileyLink unreachable. RileyLinkServiceState is null.") - return false - } - if (rileyLinkServiceState != RileyLinkServiceState.PumpConnectorReady // - && rileyLinkServiceState != RileyLinkServiceState.RileyLinkReady // + if (rileyLinkServiceState != RileyLinkServiceState.PumpConnectorReady + && rileyLinkServiceState != RileyLinkServiceState.RileyLinkReady && rileyLinkServiceState != RileyLinkServiceState.TuneUpDevice ) { aapsLogger.debug(LTag.PUMP, "RileyLink unreachable.") @@ -334,10 +319,7 @@ class MedtronicPumpPlugin @Inject constructor( } private fun refreshAnyStatusThatNeedsToBeRefreshed() { - val statusRefresh = workWithStatusRefresh( - StatusRefreshAction.GetData, null, - null - )!! + val statusRefresh = synchronized(statusRefreshMap) { HashMap(statusRefreshMap) } if (!doWeHaveAnyStatusNeededRefreshing(statusRefresh)) { return } @@ -677,19 +659,9 @@ class MedtronicPumpPlugin @Inject constructor( @Suppress("SameParameterValue") private fun setNotReachable(isBolus: Boolean, success: Boolean): PumpEnactResult { setRefreshButtonEnabled(true) - if (isBolus) { - bolusDeliveryType = BolusDeliveryType.Idle - } - return if (success) { - PumpEnactResult(injector) // - .success(true) // - .enacted(false) - } else { - PumpEnactResult(injector) // - .success(false) // - .enacted(false) // - .comment(R.string.medtronic_pump_status_pump_unreachable) - } + if (isBolus) bolusDeliveryType = BolusDeliveryType.Idle + return if (success) PumpEnactResult(injector).success(true).enacted(false) + else PumpEnactResult(injector).success(false).enacted(false).comment(R.string.medtronic_pump_status_pump_unreachable) } override fun stopBolusDelivering() { @@ -776,7 +748,7 @@ class MedtronicPumpPlugin @Inject constructor( PumpEnactResult(injector).success(false).enacted(false) // .comment(R.string.medtronic_cmd_tbr_could_not_be_delivered) } else { - medtronicPumpStatus.tempBasalStart = Date() + medtronicPumpStatus.tempBasalStart = System.currentTimeMillis() medtronicPumpStatus.tempBasalAmount = absoluteRate medtronicPumpStatus.tempBasalLength = durationInMinutes @@ -993,42 +965,15 @@ class MedtronicPumpPlugin @Inject constructor( MedtronicStatusRefreshType.RemainingInsulin -> { val remaining = medtronicPumpStatus.reservoirRemainingUnits val min: Int = if (remaining > 50) 4 * 60 else if (remaining > 20) 60 else 15 - workWithStatusRefresh(StatusRefreshAction.Add, refreshType, getTimeInFutureFromMinutes(min)) + synchronized(statusRefreshMap) { statusRefreshMap[refreshType] = getTimeInFutureFromMinutes(min) } } MedtronicStatusRefreshType.PumpTime, MedtronicStatusRefreshType.Configuration, MedtronicStatusRefreshType.BatteryStatus, MedtronicStatusRefreshType.PumpHistory -> { - workWithStatusRefresh( - StatusRefreshAction.Add, refreshType, - getTimeInFutureFromMinutes(refreshType.refreshTime + additionalTimeInMinutes) - ) + synchronized(statusRefreshMap) { statusRefreshMap[refreshType] = getTimeInFutureFromMinutes(refreshType.refreshTime + additionalTimeInMinutes) } } } } - private enum class StatusRefreshAction { - Add, // - GetData - } - - @Synchronized - private fun workWithStatusRefresh( - action: StatusRefreshAction, // - statusRefreshType: MedtronicStatusRefreshType?, // - time: Long? - ): Map? { - return when (action) { - StatusRefreshAction.Add -> { - statusRefreshMap[statusRefreshType!!] = time!! - null - } - - StatusRefreshAction.GetData -> { - HashMap(statusRefreshMap) - } - - } - } - private fun getTimeInFutureFromMinutes(minutes: Int): Long { return System.currentTimeMillis() + getTimeInMs(minutes) } diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.kt index 752d7e187b..2151ac018a 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.kt +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.kt @@ -29,6 +29,7 @@ import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpSta import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage import info.nightscout.shared.sharedPreferences.SP import org.apache.commons.lang3.StringUtils import org.joda.time.LocalDateTime @@ -57,7 +58,7 @@ class MedtronicHistoryData @Inject constructor( val medtronicPumpHistoryDecoder: MedtronicPumpHistoryDecoder, val medtronicPumpStatus: MedtronicPumpStatus, val pumpSync: PumpSync, - val pumpSyncStorage: info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage + val pumpSyncStorage: PumpSyncStorage ) { val allHistory: MutableList = mutableListOf() diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.kt index 504994800f..82238a9e3f 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.kt +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.kt @@ -122,7 +122,7 @@ class MedtronicPumpStatus @Inject constructor(private val rh: ResourceHelper, get() { if (tempBasalStart == null) return null if (tempBasalEnd == null) { - val startTime = tempBasalStart!!.time + val startTime = tempBasalStart!! tempBasalEnd = startTime + tempBasalLength!! * 60 * 1000 } if (System.currentTimeMillis() > tempBasalEnd!!) { diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt index e5157aadec..4154965d3b 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt @@ -177,7 +177,7 @@ class RileyLinkMedtronicService : RileyLinkService() { medtronicPumpStatus.pumpFrequency = pumpFrequency val isFrequencyUS = pumpFrequency == frequencies[0] val newTargetFrequency = if (isFrequencyUS) // - RileyLinkTargetFrequency.Medtronic_US else RileyLinkTargetFrequency.Medtronic_WorldWide + RileyLinkTargetFrequency.MedtronicUS else RileyLinkTargetFrequency.MedtronicWorldWide if (rileyLinkServiceData.rileyLinkTargetFrequency != newTargetFrequency) { rileyLinkServiceData.rileyLinkTargetFrequency = newTargetFrequency } diff --git a/omnipod-common/src/main/res/layout/omnipod_common_overview_buttons.xml b/omnipod-common/src/main/res/layout/omnipod_common_overview_buttons.xml index 50c2861bf3..14844d8efc 100644 --- a/omnipod-common/src/main/res/layout/omnipod_common_overview_buttons.xml +++ b/omnipod-common/src/main/res/layout/omnipod_common_overview_buttons.xml @@ -1,6 +1,9 @@ + android:layout_height="wrap_content" + android:orientation="horizontal" + tools:parentTag="android.widget.LinearLayout"> - \ No newline at end of file + diff --git a/omnipod-common/src/main/res/layout/omnipod_common_overview_pod_info.xml b/omnipod-common/src/main/res/layout/omnipod_common_overview_pod_info.xml index ff9e401287..9813bcd36b 100644 --- a/omnipod-common/src/main/res/layout/omnipod_common_overview_pod_info.xml +++ b/omnipod-common/src/main/res/layout/omnipod_common_overview_pod_info.xml @@ -2,24 +2,26 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:orientation="vertical" + tools:parentTag="android.widget.LinearLayout"> + android:focusable="true" + android:orientation="horizontal"> @@ -49,17 +51,17 @@ android:id="@+id/omnipod_common_overview_lot_number_layout" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" - android:focusable="true"> + android:focusable="true" + android:orientation="horizontal"> @@ -88,17 +90,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -127,17 +129,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -166,17 +168,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -206,17 +208,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -246,17 +248,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -302,17 +304,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -350,17 +352,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -398,17 +400,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -446,17 +448,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -494,17 +496,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -542,17 +544,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -590,17 +592,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -638,17 +640,17 @@ + android:focusable="true" + android:orientation="horizontal"> @@ -691,4 +693,5 @@ android:rotationX="180" android:rotationY="180" app:drawableTopCompat="@drawable/ic_pod" /> - \ No newline at end of file + + diff --git a/omnipod-common/src/main/res/layout/omnipod_common_pod_deactivation_wizard_activity.xml b/omnipod-common/src/main/res/layout/omnipod_common_pod_deactivation_wizard_activity.xml index f6143346a7..334bb28408 100644 --- a/omnipod-common/src/main/res/layout/omnipod_common_pod_deactivation_wizard_activity.xml +++ b/omnipod-common/src/main/res/layout/omnipod_common_pod_deactivation_wizard_activity.xml @@ -10,6 +10,6 @@ android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" - app:navGraph="@navigation/omnipod_common_pod_deactivation_wizard_navigation_graph" /> - \ No newline at end of file + + diff --git a/omnipod-dash/src/main/res/layout/omnipod_dash_overview_bluetooth_status.xml b/omnipod-dash/src/main/res/layout/omnipod_dash_overview_bluetooth_status.xml index c4d2b73130..a117c1ce23 100644 --- a/omnipod-dash/src/main/res/layout/omnipod_dash_overview_bluetooth_status.xml +++ b/omnipod-dash/src/main/res/layout/omnipod_dash_overview_bluetooth_status.xml @@ -1,20 +1,22 @@ + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + tools:parentTag="android.widget.LinearLayout"> + android:focusable="true" + android:orientation="horizontal"> + + android:focusable="true" + android:orientation="horizontal"> @@ -82,18 +85,18 @@ android:id="@+id/connectionQuality" android:layout_width="match_parent" android:layout_height="wrap_content" + android:focusable="true" android:orientation="horizontal" - android:visibility="gone" - android:focusable="true"> + android:visibility="gone"> + + android:visibility="gone"> + null"); + throw new IllegalStateException("Can not init a new pod state: State is " + + "null or discarded?"); } podState = new PodState(address); storePodState(); } /** - * @return true if we have a Pod state (which at least contains an address), indicating it is legal to call getters on PodStateManager + * @return true if we have a Pod state (which at least contains an valid address), indicating + * it is legal to call getters on PodStateManager */ public final boolean hasPodState() { - return podState != null; + + return this.podState != null + && this.podState.getAddress() != 0x0; // 0x0=discarded } /** diff --git a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/rileylink/service/RileyLinkOmnipodService.java b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/rileylink/service/RileyLinkOmnipodService.java index 8520db88d3..c6a7d37063 100644 --- a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/rileylink/service/RileyLinkOmnipodService.java +++ b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/rileylink/service/RileyLinkOmnipodService.java @@ -97,10 +97,8 @@ public class RileyLinkOmnipodService extends RileyLinkService { /* private functions */ - // PumpInterface - REMOVE - public boolean isInitialized() { - return rileyLinkServiceData.rileyLinkServiceState.isReady(); + return rileyLinkServiceData.getRileyLinkServiceState().isReady(); } @Override diff --git a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/ui/OmnipodErosOverviewFragment.kt b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/ui/OmnipodErosOverviewFragment.kt index 429301d62c..e26bb0dfb0 100644 --- a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/ui/OmnipodErosOverviewFragment.kt +++ b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/ui/OmnipodErosOverviewFragment.kt @@ -287,9 +287,9 @@ class OmnipodErosOverviewFragment : DaggerFragment() { podInfoBinding.timeOnPod.setTextColor( rh.gac(context, if (podStateManager.timeDeviatesMoreThan(OmnipodConstants.TIME_DEVIATION_THRESHOLD)) { - R.attr.defaultTextColor - } else { R.attr.warningColor + } else { + R.attr.defaultTextColor }) ) val expiresAt = podStateManager.expiresAt diff --git a/omnipod-eros/src/main/res/layout/omnipod_eros_overview_riley_link_status.xml b/omnipod-eros/src/main/res/layout/omnipod_eros_overview_riley_link_status.xml index ebf63b10a4..8034efcc7a 100644 --- a/omnipod-eros/src/main/res/layout/omnipod_eros_overview_riley_link_status.xml +++ b/omnipod-eros/src/main/res/layout/omnipod_eros_overview_riley_link_status.xml @@ -1,20 +1,22 @@ + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + tools:parentTag="android.widget.LinearLayout"> + android:focusable="true" + android:orientation="horizontal"> diff --git a/omnipod-eros/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/manager/AapsErosPodStateManagerTest.kt b/omnipod-eros/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/manager/AapsErosPodStateManagerTest.kt index 7d0748726d..8c15bc9fa8 100644 --- a/omnipod-eros/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/manager/AapsErosPodStateManagerTest.kt +++ b/omnipod-eros/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/manager/AapsErosPodStateManagerTest.kt @@ -27,7 +27,7 @@ class AapsErosPodStateManagerTest : TestBase() { val now = DateTime(2020, 1, 1, 1, 2, 3, timeZone) DateTimeUtils.setCurrentMillisFixed(now.millis) val podStateManager = AapsErosPodStateManager(aapsLogger, sp, rxBus) - podStateManager.initState(0x0) + podStateManager.initState(0x01) podStateManager.setInitializationParameters( 0, 0, FirmwareVersion(1, 1, 1), FirmwareVersion(2, 2, 2), timeZone, PodProgressStatus.ABOVE_FIFTY_UNITS @@ -46,7 +46,7 @@ class AapsErosPodStateManagerTest : TestBase() { val now = DateTime(2020, 1, 1, 1, 2, 3, timeZone) DateTimeUtils.setCurrentMillisFixed(now.millis) val podStateManager = AapsErosPodStateManager(aapsLogger, sp, rxBus) - podStateManager.initState(0x0) + podStateManager.initState(0x01) podStateManager.setInitializationParameters( 0, 0, FirmwareVersion(1, 1, 1), FirmwareVersion(2, 2, 2), timeZone, PodProgressStatus.ABOVE_FIFTY_UNITS @@ -70,7 +70,7 @@ class AapsErosPodStateManagerTest : TestBase() { val now = DateTime(2020, 1, 1, 1, 2, 3, timeZone) DateTimeUtils.setCurrentMillisFixed(now.millis) val podStateManager = AapsErosPodStateManager(aapsLogger, sp, rxBus) - podStateManager.initState(0x0) + podStateManager.initState(0x01) podStateManager.setInitializationParameters( 0, 0, FirmwareVersion(1, 1, 1), FirmwareVersion(2, 2, 2), timeZone, PodProgressStatus.ABOVE_FIFTY_UNITS diff --git a/pump-common/src/main/AndroidManifest.xml b/pump-common/src/main/AndroidManifest.xml index b935efb489..84d3bd3279 100644 --- a/pump-common/src/main/AndroidManifest.xml +++ b/pump-common/src/main/AndroidManifest.xml @@ -1,6 +1,18 @@ - + + + + + + + + + diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt index 00ee367a8c..a302ffa3f5 100644 --- a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.text.format.DateFormat +import com.google.gson.GsonBuilder import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.PumpEnactResult @@ -26,6 +27,7 @@ import info.nightscout.androidaps.utils.DecimalFormatter.to0Decimal import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag @@ -39,9 +41,9 @@ import org.json.JSONObject */ // When using this class, make sure that your first step is to create mConnection (see MedtronicPumpPlugin) abstract class PumpPluginAbstract protected constructor( - pluginDescription: PluginDescription?, + pluginDescription: PluginDescription, pumpType: PumpType, - injector: HasAndroidInjector?, + injector: HasAndroidInjector, rh: ResourceHelper, aapsLogger: AAPSLogger, commandQueue: CommandQueue, @@ -53,8 +55,8 @@ abstract class PumpPluginAbstract protected constructor( var dateUtil: DateUtil, var aapsSchedulers: AapsSchedulers, var pumpSync: PumpSync, - var pumpSyncStorage: info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage -) : PumpPluginBase(pluginDescription!!, injector!!, aapsLogger, rh, commandQueue), Pump, Constraints, info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncEntriesCreator { + var pumpSyncStorage: PumpSyncStorage +) : PumpPluginBase(pluginDescription, injector, aapsLogger, rh, commandQueue), Pump, Constraints, info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncEntriesCreator { private val disposable = CompositeDisposable() @@ -68,30 +70,41 @@ abstract class PumpPluginAbstract protected constructor( protected var displayConnectionMessages = false var pumpType: PumpType = PumpType.GENERIC_AAPS + get() = field set(value) { field = value pumpDescription.fillFor(value) } + protected var gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() + abstract fun initPumpStatusData() + open fun hasService(): Boolean { + return true + } + override fun onStart() { super.onStart() initPumpStatusData() - val intent = Intent(context, serviceClass) - context.bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE) + if (hasService()) { + val intent = Intent(context, serviceClass) + context.bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE) + disposable.add(rxBus + .toObservable(EventAppExit::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ _ -> context.unbindService(serviceConnection!!) }) { throwable: Throwable? -> fabricPrivacy.logException(throwable!!) } + ) + } serviceRunning = true - disposable.add(rxBus - .toObservable(EventAppExit::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ _ -> context.unbindService(serviceConnection!!) }) { throwable: Throwable? -> fabricPrivacy.logException(throwable!!) } - ) - onStartCustomActions() + onStartScheduledPumpActions() } override fun onStop() { - aapsLogger.debug(LTag.PUMP, deviceID() + " onStop()") - context.unbindService(serviceConnection!!) + aapsLogger.debug(LTag.PUMP, model().model + " onStop()") + if (hasService()) { + context.unbindService(serviceConnection!!) + } serviceRunning = false disposable.clear() super.onStop() @@ -100,7 +113,7 @@ abstract class PumpPluginAbstract protected constructor( /** * If we need to run any custom actions in onStart (triggering events, etc) */ - abstract fun onStartCustomActions() + abstract fun onStartScheduledPumpActions() /** * Service class (same one you did serviceConnection for) @@ -231,7 +244,7 @@ abstract class PumpPluginAbstract protected constructor( val extended = JSONObject() try { battery.put("percent", pumpStatusData.batteryRemaining) - status.put("status", pumpStatusData.pumpStatusType.status) + status.put("status", pumpStatusData.pumpRunningState.status) extended.put("Version", version) try { extended.put("ActiveProfile", profileName) @@ -265,50 +278,23 @@ abstract class PumpPluginAbstract protected constructor( override fun shortStatus(veryShort: Boolean): String { var ret = "" - if (pumpStatusData.lastConnection == 0L) { - ret += "LastConn: never\n" + ret += if (pumpStatusData.lastConnection == 0L) { + "LastConn: never\n" } else { - val agoMsec = System.currentTimeMillis() - pumpStatusData.lastConnection - val agoMin = (agoMsec / 60.0 / 1000.0).toInt() - ret += "LastConn: $agoMin min ago\n" + val agoMin = ((System.currentTimeMillis() - pumpStatusData.lastConnection) / 60.0 / 1000.0).toInt() + "LastConn: $agoMin min ago\n" } - if (pumpStatusData.lastBolusTime != null && pumpStatusData.lastBolusTime!!.time != 0L) { - ret += """ - LastBolus: ${to2Decimal(pumpStatusData.lastBolusAmount!!)}U @${DateFormat.format("HH:mm", pumpStatusData.lastBolusTime)} - - """.trimIndent() + pumpStatusData.lastBolusTime?.let { + if (it.time != 0L) { + ret += "LastBolus: ${to2Decimal(pumpStatusData.lastBolusAmount!!)}U @${DateFormat.format("HH:mm", it)}\n" + } } - val activeTemp = pumpSync.expectedPumpState().temporaryBasal - if (activeTemp != null) { - ret += """ - Temp: ${activeTemp.toStringFull(dateUtil)} - - """.trimIndent() - } - val activeExtendedBolus = pumpSync.expectedPumpState().extendedBolus - if (activeExtendedBolus != null) { - ret += """ - Extended: ${activeExtendedBolus.toStringFull(dateUtil)} - - """.trimIndent() - } - // if (!veryShort) { - // ret += "TDD: " + DecimalFormatter.to0Decimal(pumpStatus.dailyTotalUnits) + " / " - // + pumpStatus.maxDailyTotalUnits + " U\n"; - // } - ret += """ - IOB: ${pumpStatusData.iob}U - - """.trimIndent() - ret += """ - Reserv: ${to0Decimal(pumpStatusData.reservoirRemainingUnits)}U - - """.trimIndent() - ret += """ - Batt: ${pumpStatusData.batteryRemaining} - - """.trimIndent() + pumpSync.expectedPumpState().temporaryBasal?.let { ret += "Temp: ${it.toStringFull(dateUtil)}\n" } + pumpSync.expectedPumpState().extendedBolus?.let { ret += "Extended: ${it.toStringFull(dateUtil)}\n" } + ret += "IOB: ${pumpStatusData.iob}U\n" + ret += "Reserv: ${to0Decimal(pumpStatusData.reservoirRemainingUnits)}U\n" + ret += "Batt: ${pumpStatusData.batteryRemaining}\n" return ret } @@ -324,6 +310,7 @@ abstract class PumpPluginAbstract protected constructor( // bolus needed, ask pump to deliver it deliverBolus(detailedBolusInfo) } else { + detailedBolusInfo.timestamp = System.currentTimeMillis() // no bolus required, carb only treatment pumpSyncStorage.addCarbs(PumpDbEntryCarbs(detailedBolusInfo, this)) @@ -360,4 +347,4 @@ abstract class PumpPluginAbstract protected constructor( pumpDescription.fillFor(pumpType) this.pumpType = pumpType } -} \ No newline at end of file +} diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/ble/BondStateReceiver.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/ble/BondStateReceiver.kt new file mode 100644 index 0000000000..37df0f9e0c --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/ble/BondStateReceiver.kt @@ -0,0 +1,77 @@ +package info.nightscout.androidaps.plugins.pump.common.ble + +import android.bluetooth.BluetoothDevice +import android.content.Context +import android.content.Intent +import androidx.annotation.StringRes +import com.google.gson.Gson +import dagger.android.DaggerBroadcastReceiver +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.pump.common.events.EventPumpConnectionParametersChanged +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import info.nightscout.shared.sharedPreferences.SP +import javax.inject.Inject + +class BondStateReceiver( + @StringRes var deviceAddress: Int, + @StringRes var bondedFlag: Int, + var targetDevice: String, + var targetState: Int +) : DaggerBroadcastReceiver() { + + @Inject lateinit var sp: SP + @Inject lateinit var context: Context + @Inject lateinit var rh: ResourceHelper + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var rxBus: RxBus + + var TAG = LTag.PUMPBTCOMM + var gson = Gson() + var applicationContext: Context? = null + + override fun onReceive(context: Context, intent: Intent) { + super.onReceive(context, intent) + val action = intent.action + val device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) + aapsLogger.info(TAG, "in onReceive: INTENT" + gson.toJson(intent)) + if (device == null) { + aapsLogger.error(TAG, "onReceive. Device is null. Exiting.") + return + } else { + if (device.address != targetDevice) { + aapsLogger.error(TAG, "onReceive. Device is not the same as targetDevice. Exiting.") + return + } + } + + // Check if action is valid + if (action == null) return + + // Take action depending on new bond state + if (action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) { + val bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR) + val previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1) + aapsLogger.info(TAG, "in onReceive: bondState=$bondState, previousBondState=$previousBondState") + if (bondState == targetState) { + aapsLogger.info(TAG, "onReceive: found targeted state: $targetState") + val currentDeviceSettings = sp.getString(deviceAddress, "") + if (currentDeviceSettings.equals(targetDevice)) { + if (targetState == 12) { + sp.putBoolean(bondedFlag, true) + rxBus.send(EventPumpConnectionParametersChanged()) + } else if (targetState == 10) { + sp.putBoolean(bondedFlag, false) + rxBus.send(EventPumpConnectionParametersChanged()) + } + context.unregisterReceiver(this) + } else { + aapsLogger.error(TAG, "onReceive: Device stored in SP is not the same as target device, process interrupted") + } + } else { + aapsLogger.info(TAG, "onReceive: currentBondState=$bondState, targetBondState=$targetState") + } + } + } +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.kt index 9ecd30bca7..9c4ea272d9 100644 --- a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.kt +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.kt @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.pump.common.data -import info.nightscout.androidaps.plugins.pump.common.defs.PumpStatusType +import info.nightscout.androidaps.plugins.pump.common.defs.PumpRunningState import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import java.util.* @@ -33,12 +33,13 @@ abstract class PumpStatus(var pumpType: PumpType) { var dailyTotalUnits: Double? = null var maxDailyTotalUnits: String? = null var units: String? = null // Constants.MGDL or Constants.MMOL - var pumpStatusType = PumpStatusType.Running + var pumpRunningState = PumpRunningState.Running var basalsByHour: DoubleArray? = null - var tempBasalStart: Date? = null + var tempBasalStart: Long? = null var tempBasalAmount: Double? = 0.0 var tempBasalLength: Int? = 0 var tempBasalEnd: Long? = null + var pumpTime: PumpTimeDifferenceDto? = null abstract fun initSettings() diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpTimeDifferenceDto.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpTimeDifferenceDto.kt new file mode 100755 index 0000000000..561967c714 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpTimeDifferenceDto.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.pump.common.data + +import org.joda.time.DateTime +import org.joda.time.Seconds + +/** + * Created by andy on 28/05/2021. + */ +class PumpTimeDifferenceDto constructor(var localDeviceTime: DateTime, + var pumpTime: DateTime) { + + var timeDifference = 0 + + fun calculateDifference() { + val secondsBetween = Seconds.secondsBetween(localDeviceTime, pumpTime) + timeDifference = secondsBetween.seconds + + // val diff = localDeviceTime - pumpTime + // timeDifference = (diff / 1000.0).toInt() + } + + init { + calculateDifference() + } +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/BasalProfileStatus.java b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/BasalProfileStatus.java new file mode 100755 index 0000000000..0d56ee4ef8 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/BasalProfileStatus.java @@ -0,0 +1,14 @@ +package info.nightscout.androidaps.plugins.pump.common.defs; + +/** + * Created by andy on 1/20/19. + */ + +public enum BasalProfileStatus { + + NotInitialized, // + ProfileOK, // + ProfileChanged, // + ; + +} diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDriverState.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDriverState.kt index f72163ce92..6f6124005d 100644 --- a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDriverState.kt +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDriverState.kt @@ -1,14 +1,26 @@ package info.nightscout.androidaps.plugins.pump.common.defs -enum class PumpDriverState { +import info.nightscout.androidaps.plugins.pump.common.R - NotInitialized, - Connecting, - Connected, - Initialized, - Ready, Busy, - Suspended; +// TODO there are 3 classes now, that do similar things, sort of, need to define exact rules: PumpDeviceState, PumpDriverState, PumpStatusState + +// TODO split this enum into 2 +enum class PumpDriverState(var resourceId: Int) { + + NotInitialized(R.string.pump_status_not_initialized), // this state should be set only when driver is created + Connecting(R.string.connecting), // + Connected(R.string.connected), // + Initialized(R.string.pump_status_initialized), // this is weird state that probably won't be used, since its more driver centric that communication centric + EncryptCommunication(R.string.pump_status_encrypt), // + Ready(R.string.pump_status_ready), + Busy(R.string.pump_status_busy), // + Suspended(R.string.pump_status_suspended), // + Sleeping(R.string.pump_status_sleeping), + ExecutingCommand(R.string.pump_status_executing_command), + Disconnecting(R.string.disconnecting), + Disconnected(R.string.disconnected), + ErrorCommunicatingWithPump(R.string.pump_status_error_comm); fun isConnected(): Boolean = this == Connected || this == Initialized || this == Busy || this == Suspended fun isInitialized(): Boolean = this == Initialized || this == Busy || this == Suspended -} \ No newline at end of file +} diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt index 4a98471dc4..189353809a 100644 --- a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt @@ -2,7 +2,7 @@ package info.nightscout.androidaps.plugins.pump.common.defs import info.nightscout.androidaps.plugins.pump.common.R import info.nightscout.androidaps.interfaces.ResourceHelper -import java.util.* +import kotlin.streams.toList /** * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes @@ -11,9 +11,10 @@ import java.util.* * * Author: Andy {andy.rozman@gmail.com} */ -enum class PumpHistoryEntryGroup(val resourceId: Int) { +enum class PumpHistoryEntryGroup(val resourceId: Int, val pumpTypeGroupConfig: PumpTypeGroupConfig = PumpTypeGroupConfig.All) { All(R.string.history_group_all), + Base(R.string.history_group_base), Bolus(R.string.history_group_bolus), Basal(R.string.history_group_basal), Prime(R.string.history_group_prime), @@ -22,7 +23,14 @@ enum class PumpHistoryEntryGroup(val resourceId: Int) { Glucose(R.string.history_group_glucose), Notification(R.string.history_group_notification), Statistic(R.string.history_group_statistic), - Unknown(R.string.history_group_unknown); + Other(R.string.history_group_other), + Unknown(R.string.history_group_unknown), + + // Ypso + EventsOnly(R.string.history_group_events), + EventsNoStat(R.string.history_group_events_no_stat) + + ; var translated: String? = null private set @@ -33,9 +41,10 @@ enum class PumpHistoryEntryGroup(val resourceId: Int) { companion object { - private var translatedList: MutableList? = null + @JvmStatic private var translatedList: MutableList? = null - private fun doTranslation(rh: ResourceHelper) { + fun doTranslation(rh: ResourceHelper) { + if (translatedList != null) return translatedList = ArrayList() for (pumpHistoryEntryGroup in values()) { pumpHistoryEntryGroup.translated = rh.gs(pumpHistoryEntryGroup.resourceId) @@ -43,9 +52,27 @@ enum class PumpHistoryEntryGroup(val resourceId: Int) { } } + // FIXME this is just for Java compatibility reasons (can be removed when all drivers using it are in Kotlin - OmnipodEros still in java) fun getTranslatedList(rh: ResourceHelper): List { + return getTranslatedList(rh, PumpTypeGroupConfig.All) + } + + fun getTranslatedList(rh: ResourceHelper, pumpTypeGroupConfig: PumpTypeGroupConfig = PumpTypeGroupConfig.All): List { if (translatedList == null) doTranslation(rh) - return translatedList!! + + val outList: List + + if (pumpTypeGroupConfig == PumpTypeGroupConfig.All) { + outList = translatedList!!.stream() + .filter { pre -> pre.pumpTypeGroupConfig == PumpTypeGroupConfig.All } + .toList(); + } else { + outList = translatedList!!.stream() + .filter { pre -> (pre.pumpTypeGroupConfig == PumpTypeGroupConfig.All || pre.pumpTypeGroupConfig == pumpTypeGroupConfig) } + .toList(); + } + + return outList } } diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpRunningState.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpRunningState.kt new file mode 100755 index 0000000000..f9dcabbf98 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpRunningState.kt @@ -0,0 +1,9 @@ +package info.nightscout.androidaps.plugins.pump.common.defs + +// TODO there are 3 classes now, that do similar things, sort of, need to define exact rules: PumpDeviceState, PumpDriverState, PumpStatusState + +enum class PumpRunningState(val status: String) { + + Running("normal"), + Suspended("suspended"); +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpTypeGroupConfig.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpTypeGroupConfig.kt new file mode 100644 index 0000000000..cc553c732f --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpTypeGroupConfig.kt @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.plugins.pump.common.defs + +enum class PumpTypeGroupConfig { + All, + Medtronic, + OmnipodEros, + YpsoPump +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpUpdateFragmentType.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpUpdateFragmentType.kt new file mode 100755 index 0000000000..0558597127 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpUpdateFragmentType.kt @@ -0,0 +1,49 @@ +package info.nightscout.androidaps.plugins.pump.common.defs + +import java.util.* + +enum class PumpUpdateFragmentType { + None, + PumpStatus, + DriverStatus, + Queue, + Bolus, + TBR, + ProfileChange, + TBRCount, + BolusCount, + TreatmentValues(Arrays.asList(Bolus, TBR, TBRCount, BolusCount, ProfileChange)), // Last Bolus, TBR, Profile Change, TBR Count, Bolus Count + Full, + Configuration, // Firmware, Errors + Battery, + Reservoir, + OtherValues(Arrays.asList(Battery, Reservoir)), // Battery, Reservoir + Custom_1, + Custom_2, + Custom_3, + Custom_4, + Custom_5, + Custom_6, + Custom_7, + Custom_8 + ; + + final var children: List? = null + + constructor() { + } + + constructor(children: List) { + this.children = children; + } + + fun isOptionIncluded(type: PumpUpdateFragmentType): Boolean { + if (this == type) + return true + else if (this.children != null && this.children!!.contains(type)) + return true; + + return false; + } + +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/PumpDriverConfiguration.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/PumpDriverConfiguration.kt new file mode 100644 index 0000000000..c5f28a4645 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/PumpDriverConfiguration.kt @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.plugins.pump.common.driver + +import info.nightscout.androidaps.plugins.pump.common.driver.history.PumpHistoryDataProvider + +interface PumpDriverConfiguration { + + fun getPumpBLESelector(): PumpBLESelector + + fun getPumpHistoryDataProvider(): PumpHistoryDataProvider + +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/PumpDriverConfigurationCapable.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/PumpDriverConfigurationCapable.kt new file mode 100644 index 0000000000..3c8e66fde3 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/PumpDriverConfigurationCapable.kt @@ -0,0 +1,7 @@ +package info.nightscout.androidaps.plugins.pump.common.driver + +interface PumpDriverConfigurationCapable { + + fun getPumpDriverConfiguration(): PumpDriverConfiguration + +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/ble/PumpBLESelector.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/ble/PumpBLESelector.kt new file mode 100644 index 0000000000..530c127a4e --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/ble/PumpBLESelector.kt @@ -0,0 +1,105 @@ +package info.nightscout.androidaps.plugins.pump.common.driver + +import android.bluetooth.BluetoothDevice +import android.bluetooth.le.ScanFilter +import android.bluetooth.le.ScanSettings +import android.content.Context + +interface PumpBLESelector { + + /** + * Called on resume + */ + fun onResume() + + /** + * Called on destory + */ + fun onDestroy() + + /** + * This method is called when device is being removed (it can be empty if you don't need to do any special action, but if you + * have to unbound (for example), then this is method where to call it. For unbounding removeBond is available + */ + fun removeDevice(device: BluetoothDevice) + + /** + * Cleanup method after device was removed + */ + fun cleanupAfterDeviceRemoved() + + /** + * operations when scan failed + */ + fun onScanFailed(context: Context, errorCode: Int) + + /** + * operations when scan starts + */ + fun onStartLeDeviceScan(context: Context) + + /** + * operations when scan stops + */ + fun onStopLeDeviceScan(context: Context) + + /** + * operations when scan was stopped manualy (press on button) + */ + fun onManualStopLeDeviceScan(context: Context) + + /** + * operations when on non manual stop of scan (on timeout) + */ + fun onNonManualStopLeDeviceScan(context: Context) + + /** + * get Scan Filters + */ + fun getScanFilters(): List? + + /** + * get Scan Settings + */ + fun getScanSettings(): ScanSettings? + + /** + * filter device on search (for cases where we can't do it with Scan Filters + */ + fun filterDevice(device: BluetoothDevice): BluetoothDevice? + + /** + * operations when device selected + */ + fun onDeviceSelected(bluetoothDevice: BluetoothDevice, bleAddress: String, deviceName: String) + + /** + * If pump has no name, this name will be used + */ + fun getUnknownPumpName(): String + + /** + * get Address of Currently selected pump, empty string if none + */ + fun currentlySelectedPumpAddress(): String + + /** + * get Name of Currently selected pump, getUnknownPumpName() string if none + */ + fun currentlySelectedPumpName(): String + + /** + * Get Translation Text + */ + fun getText(key: PumpBLESelectorText): String + +} + +enum class PumpBLESelectorText { + SCAN_TITLE, + SELECTED_PUMP_TITLE, + REMOVE_TITLE, + REMOVE_TEXT, + NO_SELECTED_PUMP, + PUMP_CONFIGURATION +} diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/ble/PumpBLESelectorAbstract.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/ble/PumpBLESelectorAbstract.kt new file mode 100644 index 0000000000..872645fa30 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/ble/PumpBLESelectorAbstract.kt @@ -0,0 +1,108 @@ +package info.nightscout.androidaps.plugins.pump.common.driver.ble + +import android.bluetooth.BluetoothDevice +import android.bluetooth.le.ScanFilter +import android.bluetooth.le.ScanSettings +import android.content.Context +import android.widget.Toast +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.pump.common.R +import info.nightscout.androidaps.plugins.pump.common.driver.PumpBLESelector +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import info.nightscout.shared.sharedPreferences.SP + +abstract class PumpBLESelectorAbstract constructor( + var resourceHelper: ResourceHelper, + var aapsLogger: AAPSLogger, + var sp: SP, + var rxBus: RxBus, + var context: Context +) : PumpBLESelector { + + protected val TAG = LTag.PUMPBTCOMM + + override fun getScanSettings(): ScanSettings? { + return ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build() + } + + override fun getScanFilters(): MutableList? { + return null + } + + override fun filterDevice(device: BluetoothDevice): BluetoothDevice? { + return device + } + + override fun onResume() { + } + + override fun onDestroy() { + } + + override fun removeDevice(device: BluetoothDevice) { + } + + override fun cleanupAfterDeviceRemoved() { + } + + override fun onManualStopLeDeviceScan(context: Context) { + } + + override fun onNonManualStopLeDeviceScan(context: Context) { + } + + //fun onDeviceSelected(bluetoothDevice: BluetoothDevice, bleAddress: String, deviceName: String) + + override fun onScanFailed(context: Context, errorCode: Int) { + Toast.makeText( + context, resourceHelper.gs(R.string.ble_config_scan_error, errorCode), + Toast.LENGTH_LONG + ).show() + } + + override fun onStartLeDeviceScan(context: Context) { + Toast.makeText(context, R.string.ble_config_scan_scanning, Toast.LENGTH_SHORT).show() + } + + override fun onStopLeDeviceScan(context: Context) { + Toast.makeText(context, R.string.ble_config_scan_finished, Toast.LENGTH_SHORT).show() + } + + protected fun removeBond(bluetoothDevice: BluetoothDevice): Boolean { + return try { + val method = bluetoothDevice.javaClass.getMethod("removeBond") + val resultObject = method.invoke(bluetoothDevice) + if (resultObject == null) { + aapsLogger.error(TAG, "ERROR: result object is null") + false + } else { + val result = resultObject as Boolean + if (result) { + aapsLogger.info(TAG, "Successfully removed bond") + } else { + aapsLogger.warn(TAG, "Bond was not removed") + } + result + } + } catch (e: Exception) { + aapsLogger.error(TAG, "ERROR: could not remove bond") + e.printStackTrace() + false + } + } + + protected fun getBondingStatusDescription(state: Int): String { + return if (state == 10) { + "BOND_NONE" + } else if (state == 11) { + "BOND_BONDING" + } else if (state == 12) { + "BOND_BONDED" + } else { + "UNKNOWN BOND STATUS ($state)" + } + } + +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpDataConverter.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpDataConverter.kt new file mode 100644 index 0000000000..0c8c8d1b25 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpDataConverter.kt @@ -0,0 +1,4 @@ +package info.nightscout.androidaps.plugins.pump.common.driver.history + +interface PumpDataConverter { +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpHistoryDataProvider.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpHistoryDataProvider.kt new file mode 100644 index 0000000000..6f2389338e --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpHistoryDataProvider.kt @@ -0,0 +1,75 @@ +package info.nightscout.androidaps.plugins.pump.common.driver.history + +import androidx.annotation.StringRes +import info.nightscout.androidaps.plugins.pump.common.R +import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup + +interface PumpHistoryDataProvider { + + /** + * Get Data, specified with PumpHistoryPeriod + */ + fun getData(period: PumpHistoryPeriod): List + + /** + * Get Initial Period + */ + fun getInitialPeriod(): PumpHistoryPeriod + + /** + * Get InitialData + */ + fun getInitialData(): List + + /** + * Get Allowed Pump History Groups (for specific pump) + */ + fun getAllowedPumpHistoryGroups(): List + + /** + * Get Spinner Width in pixels (same as specifying 150dp) + */ + fun getSpinnerWidthInPixels(): Int + + /** + * Get Translation Text + */ + fun getText(key: PumpHistoryText): String + + /** + * For filtering of items + */ + fun isItemInSelection(itemGroup: PumpHistoryEntryGroup, targetGroup: PumpHistoryEntryGroup): Boolean + +} + +enum class PumpHistoryPeriod constructor( + @StringRes var stringId: Int, + var isHours: Boolean = false +) { + + TODAY(R.string.time_today), + LAST_HOUR(R.string.time_last_hour, true), + LAST_3_HOURS(R.string.time_last_3_hours, true), + LAST_6_HOURS(R.string.time_last_6_hours, true), + LAST_12_HOURS(R.string.time_last_12_hours, true), + LAST_2_DAYS(R.string.time_last_2_days), + LAST_4_DAYS(R.string.time_last_4_days), + LAST_WEEK(R.string.time_last_week), + LAST_MONTH(R.string.time_last_month), + ALL(R.string.history_group_all) + +} + +enum class PumpHistoryText { + + PUMP_HISTORY, + + // OLD ONES + SCAN_TITLE, + SELECTED_PUMP_TITLE, + REMOVE_TITLE, + REMOVE_TEXT, + NO_SELECTED_PUMP, + PUMP_CONFIGURATION +} diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpHistoryDataProviderAbstract.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpHistoryDataProviderAbstract.kt new file mode 100644 index 0000000000..53dbbc0aba --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpHistoryDataProviderAbstract.kt @@ -0,0 +1,46 @@ +package info.nightscout.androidaps.plugins.pump.common.driver.history + +import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup +import java.util.* + +abstract class PumpHistoryDataProviderAbstract : PumpHistoryDataProvider { + + override fun getInitialData(): List { + return getData(getInitialPeriod()); + } + + override fun getSpinnerWidthInPixels(): Int { + return 150 + } + + protected fun getStartingTimeForData(period: PumpHistoryPeriod): Long { + val gregorianCalendar = GregorianCalendar() + + if (!period.isHours) { + gregorianCalendar.set(Calendar.HOUR_OF_DAY, 0) + gregorianCalendar.set(Calendar.MINUTE, 0) + gregorianCalendar.set(Calendar.SECOND, 0) + gregorianCalendar.set(Calendar.MILLISECOND, 0) + } + + when (period) { + PumpHistoryPeriod.TODAY -> return gregorianCalendar.timeInMillis + PumpHistoryPeriod.ALL -> return 0L + PumpHistoryPeriod.LAST_2_DAYS -> gregorianCalendar.add(Calendar.DAY_OF_MONTH, -1) + PumpHistoryPeriod.LAST_4_DAYS -> gregorianCalendar.add(Calendar.DAY_OF_MONTH, -3) + PumpHistoryPeriod.LAST_WEEK -> gregorianCalendar.add(Calendar.WEEK_OF_YEAR, -1) + PumpHistoryPeriod.LAST_MONTH -> gregorianCalendar.add(Calendar.MONTH, -1) + PumpHistoryPeriod.LAST_HOUR -> gregorianCalendar.add(Calendar.HOUR_OF_DAY, -1) + PumpHistoryPeriod.LAST_3_HOURS -> gregorianCalendar.add(Calendar.HOUR_OF_DAY, -3) + PumpHistoryPeriod.LAST_6_HOURS -> gregorianCalendar.add(Calendar.HOUR_OF_DAY, -6) + PumpHistoryPeriod.LAST_12_HOURS -> gregorianCalendar.add(Calendar.HOUR_OF_DAY, -12) + } + + return gregorianCalendar.timeInMillis + } + + override fun isItemInSelection(itemGroup: PumpHistoryEntryGroup, targetGroup: PumpHistoryEntryGroup): Boolean { + return itemGroup === targetGroup + } + +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpHistoryEntry.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpHistoryEntry.kt new file mode 100644 index 0000000000..02e4e91b4c --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/driver/history/PumpHistoryEntry.kt @@ -0,0 +1,18 @@ +package info.nightscout.androidaps.plugins.pump.common.driver.history + +import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup +import info.nightscout.androidaps.interfaces.ResourceHelper + +interface PumpHistoryEntry { + + fun prepareEntryData(resourceHelper: ResourceHelper, pumpDataConverter: PumpDataConverter) + + fun getEntryDateTime(): String + + fun getEntryType(): String + + fun getEntryValue(): String + + fun getEntryTypeGroup(): PumpHistoryEntryGroup + +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventBondChanged.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventBondChanged.kt new file mode 100644 index 0000000000..8e2c8b978c --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventBondChanged.kt @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.plugins.pump.common.events + +import info.nightscout.androidaps.events.Event + +class EventBondChanged( + var connectionAddress: String, + var bondStatus: Boolean +) : Event() diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventPumpChanged.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventPumpChanged.kt new file mode 100755 index 0000000000..36b1107882 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventPumpChanged.kt @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.plugins.pump.common.events + +import info.nightscout.androidaps.events.Event + +class EventPumpChanged(var serialNumber: String, + var connectionAddress: String, + var parameters: MutableMap? = null) : Event() { +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventPumpConnectionParametersChanged.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventPumpConnectionParametersChanged.kt new file mode 100644 index 0000000000..d6c5b1563a --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventPumpConnectionParametersChanged.kt @@ -0,0 +1,6 @@ +package info.nightscout.androidaps.plugins.pump.common.events + +import info.nightscout.androidaps.events.Event + +class EventPumpConnectionParametersChanged : Event() { +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventPumpFragmentValuesChanged.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventPumpFragmentValuesChanged.kt new file mode 100755 index 0000000000..fcb55dcd11 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventPumpFragmentValuesChanged.kt @@ -0,0 +1,14 @@ +package info.nightscout.androidaps.plugins.pump.common.events + +import info.nightscout.androidaps.events.Event +import info.nightscout.androidaps.plugins.pump.common.defs.PumpUpdateFragmentType + +class EventPumpFragmentValuesChanged : Event { + + var updateType: PumpUpdateFragmentType = PumpUpdateFragmentType.None + + constructor(updateType: PumpUpdateFragmentType) { + this.updateType = updateType + } + +} \ No newline at end of file diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventRefreshButtonState.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventRefreshButtonState.kt old mode 100644 new mode 100755 similarity index 100% rename from rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventRefreshButtonState.kt rename to pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/events/EventRefreshButtonState.kt diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/ui/PumpBLEConfigActivity.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/ui/PumpBLEConfigActivity.kt new file mode 100755 index 0000000000..6333af2cc2 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/ui/PumpBLEConfigActivity.kt @@ -0,0 +1,338 @@ +package info.nightscout.androidaps.plugins.pump.common.ui + +import android.annotation.SuppressLint +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothManager +import android.bluetooth.le.BluetoothLeScanner +import android.bluetooth.le.ScanCallback +import android.bluetooth.le.ScanFilter +import android.bluetooth.le.ScanResult +import android.bluetooth.le.ScanSettings +import android.content.Context +import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.AdapterView.OnItemClickListener +import android.widget.BaseAdapter +import android.widget.TextView +import dagger.android.support.DaggerAppCompatActivity +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.pump.common.R +import info.nightscout.androidaps.plugins.pump.common.ble.BlePreCheck +import info.nightscout.androidaps.plugins.pump.common.databinding.PumpBleConfigActivityBinding +import info.nightscout.androidaps.plugins.pump.common.driver.PumpBLESelector +import info.nightscout.androidaps.plugins.pump.common.driver.PumpBLESelectorText +import info.nightscout.androidaps.plugins.pump.common.driver.PumpDriverConfigurationCapable +import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import info.nightscout.shared.sharedPreferences.SP +import org.apache.commons.lang3.StringUtils +import javax.inject.Inject + +@SuppressLint("MissingPermission") +class PumpBLEConfigActivity : DaggerAppCompatActivity() { + + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var sp: SP + @Inject lateinit var blePreCheck: BlePreCheck + @Inject lateinit var context: Context + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var rxBus: RxBus + + private lateinit var binding: PumpBleConfigActivityBinding + private lateinit var bleSelector: PumpBLESelector + + private var settings: ScanSettings? = null + private var filters: List? = null + private var bleScanner: BluetoothLeScanner? = null + private var deviceListAdapter = LeDeviceListAdapter() + private val handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + private val bluetoothAdapter: BluetoothAdapter? get() = (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?)?.adapter + var scanning = false + private val devicesMap: MutableMap = HashMap() + + private val stopScanAfterTimeoutRunnable = Runnable { + if (scanning) { + stopLeDeviceScan(false) + } + } + + @SuppressLint("MissingPermission") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = PumpBleConfigActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + + if (!blePreCheck.prerequisitesCheck(this)) { + aapsLogger.error(TAG, "prerequisitesCheck failed.") + finish() + return + } + + // Configuration + val activePump = activePlugin.activePump + + if (activePump is PumpDriverConfigurationCapable) { + bleSelector = activePump.getPumpDriverConfiguration().getPumpBLESelector() + } else { + throw RuntimeException("PumpBLEConfigActivity can be used only with PumpDriverConfigurationCapable pump driver.") + } + + binding.pumpBleConfigCurrentlySelectedText.text = bleSelector.getText(PumpBLESelectorText.SELECTED_PUMP_TITLE) + binding.pumpBleConfigScanTitle.text = bleSelector.getText(PumpBLESelectorText.SCAN_TITLE) + + title = bleSelector.getText(PumpBLESelectorText.PUMP_CONFIGURATION) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + + binding.pumpBleConfigScanDeviceList.adapter = deviceListAdapter + binding.pumpBleConfigScanDeviceList.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, view: View, _: Int, _: Long -> + // stop scanning if still active + if (scanning) { + stopLeDeviceScan(true) + } + val bleAddress = (view.findViewById(R.id.pump_ble_config_scan_item_device_address) as TextView).text.toString() + val deviceName = (view.findViewById(R.id.pump_ble_config_scan_item_device_name) as TextView).text.toString() + + if (devicesMap.containsKey(bleAddress)) { + aapsLogger.debug(TAG, "Device FOUND in deviceMap: $bleAddress") + val bluetoothDevice = devicesMap[bleAddress] + bleSelector.onDeviceSelected(bluetoothDevice!!, bleAddress, deviceName) + } else { + aapsLogger.debug(TAG, "Device NOT found in deviceMap: $bleAddress") + } + + finish() + } + binding.pumpBleConfigScanStart.setOnClickListener { startLeDeviceScan() } + binding.pumpBleConfigButtonScanStop.setOnClickListener { + if (scanning) { + stopLeDeviceScan(true) + } + } + + binding.pumpBleConfigButtonRemove.setOnClickListener { + OKDialog.showConfirmation( + this@PumpBLEConfigActivity, + bleSelector.getText(PumpBLESelectorText.REMOVE_TITLE), + bleSelector.getText(PumpBLESelectorText.REMOVE_TEXT), + Runnable { + val deviceAddress: String = binding.pumpBleConfigCurrentlySelectedPumpAddress.text.toString() + aapsLogger.debug(TAG, "Removing device as selected: $deviceAddress") + if (devicesMap.containsKey(deviceAddress)) { + val bluetoothDevice = devicesMap[deviceAddress] + aapsLogger.debug(TAG, "Device can be detected near, so trying to remove bond if possible.") + bleSelector.removeDevice(bluetoothDevice!!) + } else { + val remoteDevice = bluetoothAdapter!!.getRemoteDevice(deviceAddress) + if (remoteDevice != null) { + bleSelector.removeDevice(remoteDevice) + } + } + bleSelector.cleanupAfterDeviceRemoved() + updateCurrentlySelectedBTDevice() + }) + } + } + + private fun updateCurrentlySelectedBTDevice() { + val address = bleSelector.currentlySelectedPumpAddress() + if (StringUtils.isEmpty(address)) { + binding.pumpBleConfigCurrentlySelectedPumpName.text = bleSelector.getText(PumpBLESelectorText.NO_SELECTED_PUMP) + binding.pumpBleConfigCurrentlySelectedPumpAddress.visibility = View.GONE + binding.pumpBleConfigButtonRemove.visibility = View.GONE + } else { + binding.pumpBleConfigCurrentlySelectedPumpAddress.visibility = View.VISIBLE + binding.pumpBleConfigButtonRemove.visibility = View.VISIBLE + binding.pumpBleConfigCurrentlySelectedPumpName.text = bleSelector.currentlySelectedPumpName() + binding.pumpBleConfigCurrentlySelectedPumpAddress.text = address + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { + android.R.id.home -> { + finish() + true + } + + else -> false + } + + override fun onResume() { + super.onResume() + bleSelector.onResume() + prepareForScanning() + updateCurrentlySelectedBTDevice() + } + + override fun onDestroy() { + super.onDestroy() + if (scanning) { + stopLeDeviceScan(false) + } + bleSelector.onDestroy() + } + + private fun prepareForScanning() { + bleScanner = bluetoothAdapter!!.bluetoothLeScanner + settings = bleSelector.getScanSettings() + filters = bleSelector.getScanFilters() + } + + private val bleScanCallback: ScanCallback = object : ScanCallback() { + + override fun onScanResult(callbackType: Int, scanRecord: ScanResult) { + aapsLogger.debug(TAG, scanRecord.toString()) + runOnUiThread { if (addDevice(scanRecord)) deviceListAdapter.notifyDataSetChanged() } + } + + override fun onBatchScanResults(results: List) { + runOnUiThread { + var added = false + for (result in results) { + aapsLogger.debug(TAG, "SCAN: " + result.advertisingSid + " name=" + result.device.address) + if (addDevice(result)) added = true + } + if (added) + deviceListAdapter.notifyDataSetChanged() + } + } + + private fun addDevice(result: ScanResult): Boolean { + var device = result.device + + device = bleSelector.filterDevice(device) + + if (device == null) { + return false + } + + deviceListAdapter.addDevice(result) + if (!devicesMap.containsKey(device.address)) { + devicesMap[device.address] = device + } + return true + } + + override fun onScanFailed(errorCode: Int) { + aapsLogger.error(TAG, "Scan Failed - Error Code: $errorCode") + bleSelector.onScanFailed(this@PumpBLEConfigActivity, errorCode) + } + } + + private fun startLeDeviceScan() { + if (bleScanner == null) { + aapsLogger.error(LTag.PUMPBTCOMM, "startLeDeviceScan failed: bleScanner is null") + return + } + deviceListAdapter.clear() + deviceListAdapter.notifyDataSetChanged() + handler.postDelayed(stopScanAfterTimeoutRunnable, SCAN_PERIOD_MILLIS) + runOnUiThread { + binding.pumpBleConfigScanStart.isEnabled = false + binding.pumpBleConfigButtonScanStop.visibility = View.VISIBLE + } + scanning = true + bleScanner!!.startScan(filters, settings, bleScanCallback) + aapsLogger.debug(LTag.PUMPBTCOMM, "startLeDeviceScan: Scanning Start") + bleSelector.onStartLeDeviceScan(this@PumpBLEConfigActivity) + } + + private fun stopLeDeviceScan(manualStop: Boolean) { + if (scanning) { + scanning = false + bleScanner!!.stopScan(bleScanCallback) + aapsLogger.debug(LTag.PUMPBTCOMM, "stopLeDeviceScan: Scanning Stop") + bleSelector.onStopLeDeviceScan(this@PumpBLEConfigActivity) + handler.removeCallbacks(stopScanAfterTimeoutRunnable) + } + if (manualStop) { + bleSelector.onManualStopLeDeviceScan(this@PumpBLEConfigActivity) + } else { + bleSelector.onNonManualStopLeDeviceScan(this@PumpBLEConfigActivity) + } + + runOnUiThread { + binding.pumpBleConfigScanStart.isEnabled = true + binding.pumpBleConfigButtonScanStop.visibility = View.GONE + } + } + + private inner class LeDeviceListAdapter : BaseAdapter() { + + private var devicesList: ArrayList = arrayListOf() + private var devicesMap: MutableMap = mutableMapOf() + + fun addDevice(result: ScanResult) { + if (!devicesList.contains(result.device)) { + devicesList.add(result.device) + } + devicesMap[result.device] = result.rssi + notifyDataSetChanged() + } + + fun clear() { + devicesList.clear() + devicesMap.clear() + notifyDataSetChanged() + } + + override fun getCount(): Int { + val c = devicesList.size + aapsLogger.info(TAG, "D: count=$c") + return c + } + + override fun getItem(i: Int): Any = devicesList[i] + override fun getItemId(i: Int): Long = i.toLong() + + override fun getView(i: Int, convertView: View?, viewGroup: ViewGroup?): View { + var v = convertView + val holder: ViewHolder + if (v == null) { + v = View.inflate(applicationContext, R.layout.pump_ble_config_scan_item, null) + holder = ViewHolder() + holder.deviceAddress = v.findViewById(R.id.pump_ble_config_scan_item_device_address) + holder.deviceName = v.findViewById(R.id.pump_ble_config_scan_item_device_name) + v.tag = holder + } else { + // reuse view if already exists + holder = v.tag as ViewHolder + } + + val device = devicesList[i] + var deviceName = device.name + if (StringUtils.isBlank(deviceName)) { + deviceName = bleSelector.getUnknownPumpName() + } + deviceName += " [" + devicesMap[device] + "]" + val currentlySelectedAddress = bleSelector.currentlySelectedPumpAddress() // TODO + if (currentlySelectedAddress == device.address) { + deviceName += " (" + resources.getString(R.string.ble_config_scan_selected) + ")" + } + holder.deviceName!!.text = deviceName + holder.deviceAddress!!.text = device.address + return v!! + } + } + + internal class ViewHolder { + var deviceName: TextView? = null + var deviceAddress: TextView? = null + } + + companion object { + private val TAG = LTag.PUMPBTCOMM + private const val SCAN_PERIOD_MILLIS: Long = 15000 + } +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/ui/PumpHistoryActivity.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/ui/PumpHistoryActivity.kt new file mode 100755 index 0000000000..a29cf41094 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/ui/PumpHistoryActivity.kt @@ -0,0 +1,217 @@ +package info.nightscout.androidaps.plugins.pump.common.ui + +import android.content.Context +import android.os.Bundle +import android.os.SystemClock +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import dagger.android.support.DaggerAppCompatActivity +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.plugins.pump.common.R +import info.nightscout.androidaps.plugins.pump.common.databinding.PumpHistoryActivityBinding +import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup +import info.nightscout.androidaps.plugins.pump.common.driver.PumpDriverConfigurationCapable +import info.nightscout.androidaps.plugins.pump.common.driver.history.PumpHistoryDataProvider +import info.nightscout.androidaps.plugins.pump.common.driver.history.PumpHistoryEntry +import info.nightscout.androidaps.plugins.pump.common.driver.history.PumpHistoryText +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import javax.inject.Inject + +class PumpHistoryActivity : DaggerAppCompatActivity() { + + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var context: Context + + var filteredHistoryList: MutableList = mutableListOf() + var typeListFull: List? = null + var fullList: MutableList = mutableListOf() + + private lateinit var historyDataProvider: PumpHistoryDataProvider + private lateinit var binding: PumpHistoryActivityBinding + + var manualChange = false + + lateinit var recyclerViewAdapter: RecyclerViewAdapter + + private fun prepareData() { + + val allData = historyDataProvider.getInitialData() + + aapsLogger.info(LTag.PUMP, "Loaded ${allData.size} items from database. [initialSize=${historyDataProvider.getInitialPeriod()}]") + + this.fullList.addAll(allData) + } + + private fun filterHistory(group: PumpHistoryEntryGroup) { + filteredHistoryList.clear() + + if (group === PumpHistoryEntryGroup.All) { + filteredHistoryList.addAll(fullList) + } else { + for (pumpHistoryEntry in fullList) { + if (historyDataProvider.isItemInSelection(pumpHistoryEntry.getEntryTypeGroup(), group)) { + filteredHistoryList.add(pumpHistoryEntry) + } + } + } + + aapsLogger.info(LTag.PUMP, "Filtered list ${filteredHistoryList.size} items (group ${group}), from full list (${fullList.size}).") + + recyclerViewAdapter.setHistoryListInternal(filteredHistoryList) + recyclerViewAdapter.notifyDataSetChanged() + + } + + override fun onResume() { + super.onResume() + //filterHistory(selectedGroup) + //setHistoryTypeSpinner() + //aapsLogger.info(LTag.PUMP, "onResume") + //binding.pumpHistoryRoot.requestLayout() + } + + private fun setHistoryTypeSpinner() { + manualChange = true + for (i in typeListFull!!.indices) { + if (typeListFull!![i].entryGroup === selectedGroup) { + binding.pumpHistoryType.setSelection(i) + break + } + } + SystemClock.sleep(200) + manualChange = false + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = PumpHistoryActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + + // Configuration + val activePump = activePlugin.activePump + + if (activePump is PumpDriverConfigurationCapable) { + historyDataProvider = activePump.getPumpDriverConfiguration().getPumpHistoryDataProvider() + } else { + throw RuntimeException("PumpHistoryActivity can be used only with PumpDriverConfigurationCapable pump driver.") + } + + prepareData() + + binding.pumpHistoryRecyclerView.setHasFixedSize(true) + binding.pumpHistoryRecyclerView.layoutManager = LinearLayoutManager(this) + recyclerViewAdapter = RecyclerViewAdapter(filteredHistoryList) + binding.pumpHistoryRecyclerView.adapter = recyclerViewAdapter + binding.pumpHistoryStatus.visibility = View.GONE + typeListFull = getTypeList(historyDataProvider.getAllowedPumpHistoryGroups()) + val spinnerAdapter = ArrayAdapter(this, R.layout.spinner_centered, typeListFull!!) + + binding.pumpHistoryText.text = historyDataProvider.getText(PumpHistoryText.PUMP_HISTORY) + + binding.pumpHistoryType.adapter = spinnerAdapter + binding.pumpHistoryType.getLayoutParams().width = fromDpToSize(historyDataProvider.getSpinnerWidthInPixels()) + binding.pumpHistoryType.requestLayout(); + binding.pumpHistoryType.setOnItemSelectedListener(object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { + if (manualChange) return + val selected = binding.pumpHistoryType.getSelectedItem() as TypeList + showingType = selected + selectedGroup = selected.entryGroup + filterHistory(selectedGroup) + val selectedText = parent!!.getChildAt(0) as TextView + selectedText.textSize = 15.0f // FIXME hack for selected item, also concerns pump_type marginTop + + binding.pumpHistoryTop.requestLayout() + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + if (manualChange) return + filterHistory(PumpHistoryEntryGroup.All) + } + }) + binding.pumpHistoryTypeText.requestLayout() + } + + private fun getTypeList(list: List?): List { + val typeList = ArrayList() + for (pumpHistoryEntryGroup in list!!) { + typeList.add(TypeList(pumpHistoryEntryGroup)) + } + return typeList + } + + fun fromDpToSize(dpSize: Int): Int { + val scale = context.resources.displayMetrics.density + val pixelsFl = ((dpSize * scale) + 0.5f) + return pixelsFl.toInt() + } + + class TypeList internal constructor(var entryGroup: PumpHistoryEntryGroup) { + + var name: String + override fun toString(): String { + return name + } + + init { + name = entryGroup.translated!! + } + } + + class RecyclerViewAdapter internal constructor( + var historyList: List + ) : RecyclerView.Adapter() { + + fun setHistoryListInternal(historyList: List) { + this.historyList = historyList + } + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): HistoryViewHolder { + val v = LayoutInflater.from(viewGroup.context).inflate( + R.layout.pump_history_item, // + viewGroup, false + ) + return HistoryViewHolder(v) + } + + override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) { + val record = historyList[position] + holder.timeView.text = record.getEntryDateTime() + holder.typeView.text = record.getEntryType() + holder.valueView.text = record.getEntryValue() + } + + override fun getItemCount(): Int { + return historyList.size + } + + class HistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + var timeView: TextView + var typeView: TextView + var valueView: TextView + + init { + timeView = itemView.findViewById(R.id.pump_history_time) + typeView = itemView.findViewById(R.id.pump_history_source) + valueView = itemView.findViewById(R.id.pump_history_description) + } + } + + } + + companion object { + var showingType: TypeList? = null + var selectedGroup = PumpHistoryEntryGroup.All + } +} \ No newline at end of file diff --git a/pump-common/src/main/res/layout/pump_ble_config_activity.xml b/pump-common/src/main/res/layout/pump_ble_config_activity.xml new file mode 100755 index 0000000000..b72fbd7e3f --- /dev/null +++ b/pump-common/src/main/res/layout/pump_ble_config_activity.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + +