ProfilePlugin -> plugins module

This commit is contained in:
Milos Kozak 2022-11-04 19:13:37 +01:00
parent 683f87cd15
commit efcae9f073
60 changed files with 432 additions and 374 deletions

View file

@ -18,8 +18,8 @@ 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.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
@ -41,7 +41,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultProfile: DefaultProfile
@Inject lateinit var defaultProfileDPV: DefaultProfileDPV
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var profilePlugin: ProfilePlugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var repository: AppRepository
@ -135,8 +135,8 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
else defaultProfileDPV.profile(age, tdd, pct / 100.0, profileFunction.getUnits())
profile?.let {
OKDialog.showConfirmation(this, rh.gs(R.string.careportal_profileswitch), rh.gs(R.string.copytolocalprofile), Runnable {
localProfilePlugin.addProfile(
localProfilePlugin.copyFrom(
profilePlugin.addProfile(
profilePlugin.copyFrom(
it, "DefaultProfile " +
dateUtil.dateAndTimeAndSecondsString(dateUtil.now())
.replace(".", "/")

View file

@ -30,8 +30,8 @@ import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.ActionModeHelper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
@ -55,7 +55,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment(), MenuProvider {
@Inject lateinit var rxBus: RxBus
@Inject lateinit var sp: SP
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var profilePlugin: ProfilePlugin
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var dateUtil: DateUtil
@ -241,8 +241,8 @@ class TreatmentsProfileSwitchFragment : DaggerFragment(), MenuProvider {
ValueWithUnit.SimpleString(profileSwitch.profileName)
)
val nonCustomized = profileSealed.convertToNonCustomizedProfile(dateUtil)
localProfilePlugin.addProfile(
localProfilePlugin.copyFrom(
profilePlugin.addProfile(
profilePlugin.copyFrom(
nonCustomized,
profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_")
)

View file

@ -34,7 +34,7 @@ import info.nightscout.androidaps.plugins.general.overview.OverviewFragment
import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment
import info.nightscout.androidaps.plugins.general.wear.WearFragment
import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
import info.nightscout.plugins.profile.ProfileFragment
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment
import info.nightscout.androidaps.plugins.source.BGSourceFragment
import info.nightscout.androidaps.utils.protection.PasswordCheck
@ -51,7 +51,7 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment
@ContributesAndroidInjector abstract fun contributesLocalProfileFragment(): LocalProfileFragment
@ContributesAndroidInjector abstract fun contributesLocalProfileFragment(): ProfileFragment
@ContributesAndroidInjector abstract fun contributesObjectivesFragment(): ObjectivesFragment
@ContributesAndroidInjector abstract fun contributesOpenAPSFragment(): OpenAPSFragment
@ContributesAndroidInjector abstract fun contributesOverviewFragment(): OverviewFragment

View file

@ -35,7 +35,7 @@ import info.nightscout.plugins.general.themes.ThemeSwitcherPlugin
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin
import info.nightscout.androidaps.plugins.general.wear.WearPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
import info.nightscout.androidaps.plugins.pump.eopatch.EopatchPumpPlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
@ -236,7 +236,7 @@ abstract class PluginsListModule {
@AllConfigs
@IntoMap
@IntKey(240)
abstract fun bindLocalProfilePlugin(plugin: LocalProfilePlugin): PluginBase
abstract fun bindLocalProfilePlugin(plugin: ProfilePlugin): PluginBase
@Binds
@AllConfigs

View file

@ -7,7 +7,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddAckWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientUpdateRemoveAckWorker
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.androidaps.plugins.source.AidexPlugin
import info.nightscout.androidaps.plugins.source.DexcomPlugin
import info.nightscout.androidaps.plugins.source.EversensePlugin
@ -30,7 +30,7 @@ abstract class WorkersModule {
@ContributesAndroidInjector abstract fun contributesTomatoWorker(): TomatoPlugin.TomatoWorker
@ContributesAndroidInjector abstract fun contributesEversenseWorker(): EversensePlugin.EversenseWorker
@ContributesAndroidInjector abstract fun contributesNSClientSourceWorker(): NSClientSourcePlugin.NSClientSourceWorker
@ContributesAndroidInjector abstract fun contributesNSProfileWorker(): LocalProfilePlugin.NSProfileWorker
@ContributesAndroidInjector abstract fun contributesNSProfileWorker(): ProfilePlugin.NSProfileWorker
@ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientAddUpdateWorker
@ContributesAndroidInjector abstract fun contributesNSClientAddAckWorker(): NSClientAddAckWorker
@ContributesAndroidInjector abstract fun contributesNSClientUpdateRemoveAckWorker(): NSClientUpdateRemoveAckWorker

View file

@ -1,3 +1,2 @@
package info.nightscout.androidaps.events
class EventProfileStoreChanged : Event()

View file

@ -6,6 +6,8 @@ import android.os.Bundle
import androidx.annotation.RawRes
import androidx.fragment.app.FragmentManager
import info.nightscout.androidaps.MainActivity
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.dialogs.ProfileSwitchDialog
import info.nightscout.androidaps.dialogs.WizardDialog
import info.nightscout.androidaps.interfaces.ActivityNames
import info.nightscout.androidaps.services.AlarmSoundService
@ -16,17 +18,12 @@ import javax.inject.Inject
class ActivityNamesImpl @Inject constructor() : ActivityNames {
override val mainActivityClass: Class<*>
get() = MainActivity::class.java
override val mainActivityClass: Class<*> = MainActivity::class.java
override val tddStatsActivity: Class<*> = TDDStatsActivity::class.java
override val errorHelperActivity: Class<*> = ErrorHelperActivity::class.java
override val bolusProgressHelperActivity: Class<*> = BolusProgressHelperActivity::class.java
override val singleFragmentActivity: Class<*> = SingleFragmentActivity::class.java
override val tddStatsActivity: Class<*>
get() = TDDStatsActivity::class.java
override val errorHelperActivity: Class<*>
get() = ErrorHelperActivity::class.java
override val bolusProgressHelperActivity: Class<*>
get() = BolusProgressHelperActivity::class.java
override fun runAlarm(ctx: Context, status: String, title: String, @RawRes soundId: Int) {
val i = Intent(ctx, errorHelperActivity)
@ -46,4 +43,10 @@ class ActivityNamesImpl @Inject constructor() : ActivityNames {
}.show(fragmentManager, "Food Item")
}
override fun runProfileSwitchDialog(fragmentManager: FragmentManager, profileName: String?) {
ProfileSwitchDialog()
.also { it.arguments = Bundle().also { bundle -> bundle.putString("profileName", profileName) } }
.show(fragmentManager, "ProfileSwitchDialog")
}
}

View file

@ -39,8 +39,8 @@ import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
import info.nightscout.androidaps.plugins.general.autotune.events.EventAutotuneUpdateGui
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.MidnightTime
@ -63,7 +63,7 @@ class AutotuneFragment : DaggerFragment() {
@Inject lateinit var sp: SP
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var profilePlugin: ProfilePlugin
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var rh: ResourceHelper
@ -134,7 +134,7 @@ class AutotuneFragment : DaggerFragment() {
rh.gs(R.string.autotune_copy_localprofile_button),
rh.gs(R.string.autotune_copy_local_profile_message) + "\n" + localName,
Runnable {
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(tunedProfile.getProfile(circadian), localName))
profilePlugin.addProfile(profilePlugin.copyFrom(tunedProfile.getProfile(circadian), localName))
rxBus.send(EventLocalProfileChanged())
uel.log(
UserEntry.Action.NEW_PROFILE,

View file

@ -14,8 +14,8 @@ import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.androidaps.plugins.general.autotune.events.EventAutotuneUpdateGui
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.MidnightTime
@ -46,7 +46,7 @@ class AutotunePlugin @Inject constructor(
private val profileFunction: ProfileFunction,
private val dateUtil: DateUtil,
private val activePlugin: ActivePlugin,
private val localProfilePlugin: LocalProfilePlugin,
private val profilePlugin: ProfilePlugin,
private val autotuneFS: AutotuneFS,
private val autotuneIob: AutotuneIob,
private val autotunePrep: AutotunePrep,
@ -318,15 +318,15 @@ class AutotunePlugin @Inject constructor(
if (profileList[p] == newProfile.profilename)
indexLocalProfile = p
if (indexLocalProfile == -1) {
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(newProfile.getProfile(circadian), newProfile.profilename))
profilePlugin.addProfile(profilePlugin.copyFrom(newProfile.getProfile(circadian), newProfile.profilename))
return
}
localProfilePlugin.currentProfileIndex = indexLocalProfile
localProfilePlugin.currentProfile()?.dia = newProfile.dia
localProfilePlugin.currentProfile()?.basal = newProfile.basal()
localProfilePlugin.currentProfile()?.ic = newProfile.ic(circadian)
localProfilePlugin.currentProfile()?.isf = newProfile.isf(circadian)
localProfilePlugin.storeSettings()
profilePlugin.currentProfileIndex = indexLocalProfile
profilePlugin.currentProfile()?.dia = newProfile.dia
profilePlugin.currentProfile()?.basal = newProfile.basal()
profilePlugin.currentProfile()?.ic = newProfile.ic(circadian)
profilePlugin.currentProfile()?.isf = newProfile.isf(circadian)
profilePlugin.storeSettings()
}
fun saveLastRun() {

View file

@ -10,7 +10,7 @@ import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.shared.sharedPreferences.SP
import javax.inject.Inject
@ -25,7 +25,7 @@ class DataSyncSelectorImplementation @Inject constructor(
private val nsClientPlugin: NSClientPlugin,
private val activePlugin: ActivePlugin,
private val appRepository: AppRepository,
private val localProfilePlugin: LocalProfilePlugin
private val profilePlugin: ProfilePlugin
) : DataSyncSelector {
class QueueCounter(
@ -940,8 +940,8 @@ class DataSyncSelectorImplementation @Inject constructor(
val lastChange = sp.getLong(R.string.key_local_profile_last_change, 0)
if (lastChange == 0L) return
if (lastChange > lastSync) {
if (localProfilePlugin.profile?.allProfilesValid != true) return
val profileJson = localProfilePlugin.profile?.data ?: return
if (profilePlugin.profile?.allProfilesValid != true) return
val profileJson = profilePlugin.profile?.data ?: return
nsClientPlugin.nsClientService?.dbAdd("profile", profileJson, DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "")
}
}

View file

@ -40,7 +40,7 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNo
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin.NSClientSourceWorker
import info.nightscout.androidaps.receivers.DataWorkerStorage
import info.nightscout.androidaps.utils.DateUtil
@ -467,7 +467,7 @@ class NSClientService : DaggerService() {
val profileStoreJson = profiles[profiles.length() - 1] as JSONObject
rxBus.send(EventNSClientNewLog("PROFILE", "profile received"))
dataWorkerStorage.enqueue(
OneTimeWorkRequest.Builder(LocalProfilePlugin.NSProfileWorker::class.java)
OneTimeWorkRequest.Builder(ProfilePlugin.NSProfileWorker::class.java)
.setInputData(dataWorkerStorage.storeInputData(profileStoreJson))
.build()
)

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview
import android.content.Context
import androidx.annotation.StringRes
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
@ -27,6 +28,7 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
@ -66,6 +68,25 @@ class OverviewPlugin @Inject constructor(
private var disposable: CompositeDisposable = CompositeDisposable()
override val overviewBus = RxBus(aapsSchedulers, aapsLogger)
@FunctionalInterface
interface RunnableWithContext : Runnable {
var context: Context?
}
override fun addNotificationWithDialogResponse(id: Int, text: String, level: Int, @StringRes actionButtonId: Int, title: String, message: String) {
rxBus.send(
EventNewNotification(
NotificationWithAction(injector, id, text, level)
.also { n ->
n.action(actionButtonId) {
n.contextForAction?.let { OKDialog.show(it, title, message, null) }
}
})
)
}
override fun addNotification(id: Int, text: String, level: Int, @StringRes actionButtonId: Int, action: Runnable) {
rxBus.send(
EventNewNotification(

View file

@ -27,8 +27,8 @@ import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragm
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus
import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.plugins.profile.ProfileFragment
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange
import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin
import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin
@ -62,7 +62,7 @@ class SWDefinition @Inject constructor(
rh: ResourceHelper,
private val sp: SP,
private val profileFunction: ProfileFunction,
private val localProfilePlugin: LocalProfilePlugin,
private val profilePlugin: ProfilePlugin,
private val activePlugin: ActivePlugin,
private val commandQueue: CommandQueue,
private val objectivesPlugin: ObjectivesPlugin,
@ -273,12 +273,12 @@ class SWDefinition @Inject constructor(
private val screenLocalProfile = SWScreen(injector, R.string.localprofile)
.skippable(false)
.add(SWFragment(injector, this)
.add(LocalProfileFragment()))
.add(ProfileFragment()))
.validator {
localProfilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, rh, rxBus, hardLimits, false).isValid }
profilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, rh, rxBus, hardLimits, false).isValid }
?: false
}
.visibility { localProfilePlugin.isEnabled() }
.visibility { profilePlugin.isEnabled() }
private val screenProfileSwitch = SWScreen(injector, R.string.careportal_profileswitch)
.skippable(false)
.add(SWInfoText(injector)

View file

@ -13,9 +13,8 @@ import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.events.EventProfileStoreChanged
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange
import info.nightscout.androidaps.setupwizard.elements.SWItem
import info.nightscout.androidaps.setupwizard.events.EventSWUpdate
@ -32,7 +31,7 @@ import kotlin.math.min
class SetupWizardActivity : NoSplashAppCompatActivity() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var profilePlugin: ProfilePlugin
@Inject lateinit var swDefinition: SWDefinition
@Inject lateinit var sp: SP
@Inject lateinit var fabricPrivacy: FabricPrivacy

View file

@ -2,147 +2,3 @@
package info.nightscout.androidaps.utils.ui
import android.view.MotionEvent
import android.view.View
import android.widget.AdapterView
import android.widget.Spinner
import android.widget.SpinnerAdapter
import kotlin.math.max
/**
* Spinner Helper class that works around some common issues
* with the stock Android Spinner
*
* A Spinner will normally call it's OnItemSelectedListener
* when you use setSelection(...) in your initialization code.
* This is usually unwanted behavior, and a common work-around
* is to use spinner.post(...) with a Runnable to assign the
* OnItemSelectedListener after layout.
*
* If you do not call setSelection(...) manually, the callback
* may be called with the first item in the adapter you have
* set. The common work-around for that is to count callbacks.
*
* While these workarounds usually *seem* to work, the callback
* may still be called repeatedly for other reasons while the
* selection hasn't actually changed. This will happen for
* example, if the user has accessibility options enabled -
* which is more common than you might think as several apps
* use this for different purposes, like detecting which
* notifications are active.
*
* Ideally, your OnItemSelectedListener callback should be
* coded defensively so that no problem would occur even
* if the callback was called repeatedly with the same values
* without any user interaction, so no workarounds are needed.
*
* This class does that for you. It keeps track of the values
* you have set with the setSelection(...) methods, and
* proxies the OnItemSelectedListener callback so your callback
* only gets called if the selected item's position differs
* from the one you have set by code, or the first item if you
* did not set it.
*
* This also means that if the user actually clicks the item
* that was previously selected by code (or the first item
* if you didn't set a selection by code), the callback will
* not fire.
*
* To implement, replace current occurrences of:
*
* Spinner spinner =
* (Spinner)findViewById(R.id.xxx);
*
* with:
*
* SpinnerHelper spinner =
* new SpinnerHelper(findViewById(R.id.xxx))
*
* SpinnerHelper proxies the (my) most used calls to Spinner
* but not all of them. Should a method not be available, use:
*
* spinner.getSpinner().someMethod(...)
*
* Or just add the proxy method yourself :)
*
* (Quickly) Tested on devices from 2.3.6 through 4.2.2
*
* @author Jorrit "Chainfire" Jongma
* @license WTFPL (do whatever you want with this, nobody cares)
*/
@Suppress("unused")
class SpinnerHelper(val spinner: Spinner) : AdapterView.OnItemSelectedListener {
private var userTouched = false
private var lastPosition = -1
private var proxiedItemSelectedListener: AdapterView.OnItemSelectedListener? = null
fun setSelection(position: Int) {
lastPosition = max(-1, position)
spinner.setSelection(position)
}
fun setSelection(position: Int, animate: Boolean) {
lastPosition = max(-1, position)
spinner.setSelection(position, animate)
}
fun setOnItemSelectedListener(listener: AdapterView.OnItemSelectedListener?) {
proxiedItemSelectedListener = listener
setTouchListener()
spinner.onItemSelectedListener = if (listener == null) null else this
}
private fun setTouchListener() {
spinner.setOnTouchListener { v: View, _: MotionEvent? ->
v.performClick()
userTouched = true
false
}
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (position != lastPosition && userTouched) {
lastPosition = position
proxiedItemSelectedListener?.onItemSelected(parent, view, position, id)
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
if (lastPosition != -1) {
lastPosition = -1
proxiedItemSelectedListener?.onNothingSelected(parent)
}
}
var adapter: SpinnerAdapter
get() = spinner.adapter
set(adapter) {
if (adapter.count > 0) {
lastPosition = 0
}
spinner.adapter = adapter
}
val count: Int
get() = spinner.count
fun getItemAtPosition(position: Int): Any = spinner.getItemAtPosition(position)
fun getItemIdAtPosition(position: Int): Long = spinner.getItemIdAtPosition(position)
val selectedItem: Any
get() = try {
spinner.selectedItem
} catch (e: IndexOutOfBoundsException) {
adapter.getItem(adapter.count - 1)
}
val selectedItemId: Long
get() = spinner.selectedItemId
val selectedItemPosition: Int
get() = spinner.selectedItemPosition
var isEnabled: Boolean
get() = spinner.isEnabled
set(enabled) {
spinner.isEnabled = enabled
}
}

View file

@ -88,7 +88,7 @@
android:drawableTop="@drawable/ic_calculator"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/overview_calculator_label"
android:text="@string/calculator_label"
android:textColor="?attr/icCalculatorColor"
app:iconPadding="-4dp" />

View file

@ -78,7 +78,7 @@
<string name="constraints_violation">Beperkings skending</string>
<string name="setbasalquestion">Aanvaar nuwe tydelike basale:</string>
<string name="overview_treatment_label">Behandeling</string>
<string name="overview_calculator_label">Rekenaar</string>
<string name="calculator_label">Rekenaar</string>
<string name="changeyourinput">Verander jou insette!</string>
<string name="configbuilder_bgsource">BG bron</string>
<string name="apsmode_title">APS modus</string>

View file

@ -84,7 +84,7 @@
<string name="constraints_violation">Нарушено ограничение</string>
<string name="setbasalquestion">Приложи нов временен базал:</string>
<string name="overview_treatment_label">Болус</string>
<string name="overview_calculator_label">Калкулатор</string>
<string name="calculator_label">Калкулатор</string>
<string name="changeyourinput">Променете данните!</string>
<string name="configbuilder_bgsource">Източник на данни за КЗ</string>
<string name="xdrip">xDrip+</string>

View file

@ -82,7 +82,7 @@
<string name="constraints_violation">Violació de restriccions</string>
<string name="setbasalquestion">Acceptar nova basal temporal:</string>
<string name="overview_treatment_label">Tractament</string>
<string name="overview_calculator_label">Calculadora</string>
<string name="calculator_label">Calculadora</string>
<string name="changeyourinput">Modifiqueu les dades!</string>
<string name="configbuilder_bgsource">Origen glucèmia</string>
<string name="xdrip">xDrip+</string>

View file

@ -89,7 +89,7 @@
<string name="constraints_violation">Mimo povolený rozsah</string>
<string name="setbasalquestion">Spustit nový dočasný bazál:</string>
<string name="overview_treatment_label">Bolus</string>
<string name="overview_calculator_label">Kalkulačka</string>
<string name="calculator_label">Kalkulačka</string>
<string name="changeyourinput">Změňte zadání!</string>
<string name="configbuilder_bgsource">Zdroj glykémie</string>
<string name="configbuilder_bgsource_description">Odkud má AAPS získávat glykémie?</string>

View file

@ -85,7 +85,7 @@
<string name="constraints_violation">Begrænsninger overtrådt</string>
<string name="setbasalquestion">Acceptér ny midlertidig basal:</string>
<string name="overview_treatment_label">Behandling</string>
<string name="overview_calculator_label">Beregner</string>
<string name="calculator_label">Beregner</string>
<string name="changeyourinput">Skift dit input!</string>
<string name="configbuilder_bgsource">BG kilde</string>
<string name="xdrip">xDrip+</string>

View file

@ -85,7 +85,7 @@
<string name="constraints_violation">Beschränkungen wurden verletzt oder Limit erreicht.</string>
<string name="setbasalquestion">Akzeptiere neue TBR:</string>
<string name="overview_treatment_label">Bolus</string>
<string name="overview_calculator_label">Rechner</string>
<string name="calculator_label">Rechner</string>
<string name="changeyourinput">Ändere deine Eingabe!</string>
<string name="configbuilder_bgsource">BZ-Quelle</string>
<string name="xdrip">xDrip+</string>

View file

@ -79,7 +79,7 @@
<string name="constraints_violation">Παραβίαση Περιορισμών</string>
<string name="setbasalquestion">Αποδοχή νέου Προσ Ρυθμού:</string>
<string name="overview_treatment_label">Θεραπεία</string>
<string name="overview_calculator_label">Υπολογιστής</string>
<string name="calculator_label">Υπολογιστής</string>
<string name="changeyourinput">Αλλάξτε αυτό που εισάγατε!</string>
<string name="configbuilder_bgsource">Πηγή BG</string>
<string name="apsmode_title">Λειτουργία APS</string>

View file

@ -89,7 +89,7 @@
<string name="constraints_violation">Violación de restricciones</string>
<string name="setbasalquestion">Aceptar nueva basal temporal:</string>
<string name="overview_treatment_label">Tratamiento</string>
<string name="overview_calculator_label">Calculadora</string>
<string name="calculator_label">Calculadora</string>
<string name="changeyourinput">¡Cambiar datos!</string>
<string name="configbuilder_bgsource">Origen de Glucosa</string>
<string name="configbuilder_bgsource_description">¿Desde dónde debería obtener AAPS los datos?</string>

View file

@ -89,7 +89,7 @@
<string name="constraints_violation">Violation des restrictions</string>
<string name="setbasalquestion">Accepter nouveau basal temporaire :</string>
<string name="overview_treatment_label">Traitement</string>
<string name="overview_calculator_label">Assistant</string>
<string name="calculator_label">Assistant</string>
<string name="changeyourinput">Changez vos entrées !</string>
<string name="configbuilder_bgsource">Source des glycémies</string>
<string name="configbuilder_bgsource_description">Quelle source de données doit être utilisée par AAPS ?</string>

View file

@ -89,7 +89,7 @@
<string name="constraints_violation">Violazione dei vincoli</string>
<string name="setbasalquestion">Accetta nuova basale temporanea:</string>
<string name="overview_treatment_label">Trattamento</string>
<string name="overview_calculator_label">Calcolatore</string>
<string name="calculator_label">Calcolatore</string>
<string name="changeyourinput">Cambia il tuo input!</string>
<string name="configbuilder_bgsource">Origine BG</string>
<string name="configbuilder_bgsource_description">Da dove AAPS dovrebbe ottenere i suoi dati?</string>

View file

@ -85,7 +85,7 @@
<string name="constraints_violation">הפרת מגבלות</string>
<string name="setbasalquestion">אשר בזאלי זמני חדש:</string>
<string name="overview_treatment_label">טיפול</string>
<string name="overview_calculator_label">מחשבון</string>
<string name="calculator_label">מחשבון</string>
<string name="changeyourinput">שנה קלט!</string>
<string name="configbuilder_bgsource">מקור ערכי הסוכר</string>
<string name="xdrip">xDrip+</string>

View file

@ -82,7 +82,7 @@
<string name="constraints_violation">제한 위반</string>
<string name="setbasalquestion">새 임시Basal 적용:</string>
<string name="overview_treatment_label">관리</string>
<string name="overview_calculator_label">계산기</string>
<string name="calculator_label">계산기</string>
<string name="changeyourinput">입력값을 변경하세요!</string>
<string name="configbuilder_bgsource">혈당 출처</string>
<string name="xdrip">xDrip+</string>

View file

@ -83,7 +83,7 @@
<string name="constraints_violation">Apribojimų pažeidimas</string>
<string name="setbasalquestion">Patvirtinti naują laikiną bazę:</string>
<string name="overview_treatment_label">Terapija</string>
<string name="overview_calculator_label">Skaičiuotuvas</string>
<string name="calculator_label">Skaičiuotuvas</string>
<string name="changeyourinput">Pakeiskite įvestus duomenis!</string>
<string name="configbuilder_bgsource">Glikemijos šaltinis</string>
<string name="xdrip">xDrip+</string>

View file

@ -85,7 +85,7 @@
<string name="constraints_violation">In strijd met beperkingen</string>
<string name="setbasalquestion">Accepteer nieuw tijdelijk basaal:</string>
<string name="overview_treatment_label">Bolus</string>
<string name="overview_calculator_label">Bolus wizard</string>
<string name="calculator_label">Bolus wizard</string>
<string name="changeyourinput">Wijzig het ingegevene!</string>
<string name="configbuilder_bgsource">BG bron</string>
<string name="xdrip">xDrip+</string>

View file

@ -89,7 +89,7 @@
<string name="constraints_violation">Brudd på begrensninger</string>
<string name="setbasalquestion">Aksepter ny temp basal:</string>
<string name="overview_treatment_label">Behandling</string>
<string name="overview_calculator_label">Kalkulator</string>
<string name="calculator_label">Kalkulator</string>
<string name="changeyourinput">Endre dine inndata!</string>
<string name="configbuilder_bgsource">BS-kilde</string>
<string name="configbuilder_bgsource_description">Hvor skal AAPS få sine data fra?</string>

View file

@ -83,7 +83,7 @@
<string name="constraints_violation">Naruszenie ograniczeń</string>
<string name="setbasalquestion">Akceptuj nową bazę tymczasową:</string>
<string name="overview_treatment_label">Terapia</string>
<string name="overview_calculator_label">Kalkulator</string>
<string name="calculator_label">Kalkulator</string>
<string name="changeyourinput">Zmień wprowadzone dane!</string>
<string name="configbuilder_bgsource">Źródło BG</string>
<string name="xdrip">xDrip+</string>

View file

@ -84,7 +84,7 @@
<string name="constraints_violation">Violação das restrições</string>
<string name="setbasalquestion">Aceitar nova basal temporária:</string>
<string name="overview_treatment_label">Tratamento</string>
<string name="overview_calculator_label">Calculadora</string>
<string name="calculator_label">Calculadora</string>
<string name="changeyourinput">Altere sua entrada!</string>
<string name="configbuilder_bgsource">Fonte de BG</string>
<string name="xdrip">xDrip+</string>

View file

@ -83,7 +83,7 @@
<string name="constraints_violation">Violação das restrições</string>
<string name="setbasalquestion">Aceitar nova basal temporária:</string>
<string name="overview_treatment_label">Tratamento</string>
<string name="overview_calculator_label">Calculadora</string>
<string name="calculator_label">Calculadora</string>
<string name="changeyourinput">Altere o seu input!</string>
<string name="configbuilder_bgsource">Fonte da Glicose</string>
<string name="xdrip">xDrip+</string>

View file

@ -83,7 +83,7 @@
<string name="constraints_violation">Încălcare a unei limite</string>
<string name="setbasalquestion">Acceptă noua bazală temporară:</string>
<string name="overview_treatment_label">Tratament</string>
<string name="overview_calculator_label">Calculator</string>
<string name="calculator_label">Calculator</string>
<string name="changeyourinput">Schimbați ceea ce ați introdus!</string>
<string name="configbuilder_bgsource">Sursă glicemie</string>
<string name="xdrip">xDrip+</string>

View file

@ -89,7 +89,7 @@
<string name="constraints_violation">ограничение нарушено</string>
<string name="setbasalquestion">принять новый врем базал:</string>
<string name="overview_treatment_label">болюс</string>
<string name="overview_calculator_label">калькулятор</string>
<string name="calculator_label">калькулятор</string>
<string name="changeyourinput">измените введенные данные</string>
<string name="configbuilder_bgsource">источник СК</string>
<string name="configbuilder_bgsource_description">Откуда должен получать данные AAPS?</string>

View file

@ -89,7 +89,7 @@
<string name="constraints_violation">Mimo povolený rozsah</string>
<string name="setbasalquestion">Povoliť nový dočasný bazál:</string>
<string name="overview_treatment_label">Bolus</string>
<string name="overview_calculator_label">Kalkulačka</string>
<string name="calculator_label">Kalkulačka</string>
<string name="changeyourinput">Zmeňte zadanie!</string>
<string name="configbuilder_bgsource">Zdroj glykémie</string>
<string name="configbuilder_bgsource_description">Odkiaľ má AndroidAPS získavať glykémie?</string>

View file

@ -85,7 +85,7 @@ Eversense-appen.</string>
<string name="constraints_violation">Begränsning nådd</string>
<string name="setbasalquestion">Acceptera ny temp basal:</string>
<string name="overview_treatment_label">Behandling</string>
<string name="overview_calculator_label">Kalkylator</string>
<string name="calculator_label">Kalkylator</string>
<string name="changeyourinput">Ändra inmatning</string>
<string name="configbuilder_bgsource">BG-källa</string>
<string name="xdrip">xDrip+</string>

View file

@ -89,7 +89,7 @@
<string name="constraints_violation">Kısıtlamalar ihlali</string>
<string name="setbasalquestion">Yeni geçici bazal oranını kabul et:</string>
<string name="overview_treatment_label">Tedavi</string>
<string name="overview_calculator_label">Hesap makinesi</string>
<string name="calculator_label">Hesap makinesi</string>
<string name="changeyourinput">Girişinizi değiştirin!</string>
<string name="configbuilder_bgsource">KŞ kaynağı</string>
<string name="configbuilder_bgsource_description">AAPS verilerini nereden alsın?</string>

View file

@ -84,7 +84,7 @@
<string name="constraints_violation">违反约束条件</string>
<string name="setbasalquestion">接受新的临时基础率</string>
<string name="overview_treatment_label">治疗</string>
<string name="overview_calculator_label">计算器</string>
<string name="calculator_label">计算器</string>
<string name="changeyourinput">更改您的输入!</string>
<string name="configbuilder_bgsource">血糖来源</string>
<string name="xdrip">xDrip+</string>

View file

@ -52,7 +52,6 @@
<string name="key_ns_effective_profile_switch_last_synced_id" translatable="false">ns_effective_profile_switch_last_synced_id</string>
<string name="key_ns_offline_event_last_synced_id" translatable="false">ns_offline_event_last_synced_id</string>
<string name="key_ns_profile_store_last_synced_timestamp" translatable="false">ns_profile_store_last_synced_timestamp</string>
<string name="key_local_profile_last_change" translatable="false">local_profile_last_change</string>
<string name="key_ns_sync_slow" translatable="false">ns_sync_slow</string>
<string name="key_last_cleanup_run" translatable="false">last_cleanup_run</string>
<string name="treatmentssafety_title">Treatments safety</string>
@ -75,7 +74,6 @@
<string name="description_smb_dynamic_isf">Most recent algorithm for advanced users with dynamic/automatic ISF</string>
<string name="description_overview">Displays the current state of your loop and buttons for most common actions</string>
<string name="description_persistent_notification">Shows an ongoing notification with a short overview of what your loop is doing</string>
<string name="description_profile_local">Define a profile which is available offline.</string>
<string name="description_pump_virtual">Pump integration for pumps which don\'t have any driver yet (Open Loop)</string>
<string name="description_sensitivity_aaps">Sensitivity is calculated the same way like Oref0, but you can specify timeframe to the past. Minimal carb absorption is calculated from max carb absorption time from preferences.</string>
<string name="description_sensitivity_oref1">Sensitivity is calculated from 8h or 24h data in the past (using either which is more sensitive). Carbs (if not absorbed) are cut after time specified in preferences. Plugin also calculates UAM.</string>
@ -139,7 +137,6 @@
<string name="constraints_violation">Constraints violation</string>
<string name="setbasalquestion">Accept new temp basal:</string>
<string name="overview_treatment_label">Treatment</string>
<string name="overview_calculator_label">Calculator</string>
<string name="changeyourinput">Change your input!</string>
<string name="configbuilder_bgsource">BG Source</string>
<string name="configbuilder_bgsource_description">Where should AAPS gain it\'s data from?</string>
@ -211,7 +208,6 @@
<string name="button1">Button 1</string>
<string name="button2">Button 2</string>
<string name="button3">Button 3</string>
<string name="units_colon">Units:</string>
<string name="units">Units</string>
<string name="prefs_range_title">Range for Visualization</string>
<string name="prefs_range_summary">High and low mark for the charts in Overview and Smartwatch</string>
@ -224,7 +220,6 @@
<string name="MM640g">MM640g</string>
<string name="ongoingnotificaction">Ongoing Notification</string>
<string name="old_data">OLD DATA</string>
<string name="localprofile">Profile</string>
<string name="openapsama">OpenAPS AMA</string>
<string name="array_of_elements">Array of %1$d elements.\nActual value:</string>
<string name="openapsma_autosensdata_label">Autosens data</string>
@ -237,7 +232,6 @@
<string name="loop_shortname">LOOP</string>
<string name="oaps_shortname">OAPS</string>
<string name="dynisf_shortname">DYNISF</string>
<string name="localprofile_shortname">LP</string>
<string name="overview_shortname">HOME</string>
<string name="virtualpump_shortname">VPUMP</string>
<string name="treatments_shortname">TREAT</string>
@ -392,7 +386,6 @@
<string name="absorption_maxtime_summary">Time in hours where is expected all carbs from meal will be absorbed</string>
<string name="openaps_short">OAPS</string>
<string name="uploader_short">UPLD</string>
<string name="basal_short">BAS</string>
<string name="keep_screen_on_title">Keep screen on</string>
<string name="keep_screen_on_summary">Prevent Android to turn screen off. It will consume lot of energy when not plugged to power outlet.</string>
<string name="sensitivity_warning">By turning on Autosense feature remember to enter all eated carbs. Otherwise carbs deviations will be identified wrong as sensitivity change !!</string>
@ -413,7 +406,6 @@
<string name="enablesmb">Enable SMB</string>
<string name="enablesmb_summary">Use Super Micro Boluses instead of temp basal for faster action</string>
<string name="enableuam_summary">Detection of Unannounced meals</string>
<string name="activate_profile">Activate profile</string>
<string name="invalid">INVALID</string>
<string name="key_wizard_include_cob" translatable="false">wizard_include_cob</string>
<string name="key_wizard_include_trend_bg" translatable="false">wizard_include_trend_bg</string>
@ -526,8 +518,6 @@
<string name="g5appnotdetected">Please update your Dexcom app to supported version</string>
<string name="dexcom_app_not_installed">Dexcom app is not installed.</string>
<string name="do_not_bolus_record_only">Do not bolus, record only</string>
<string name="category">Category</string>
<string name="subcategory">Subcategory</string>
<string name="bolusrecordedonly">Bolus will be recorded only (not delivered by pump)</string>
<string name="loop_smbsetbypump_label">SMB set by pump</string>
<string name="overview_show_activity">Activity</string>
@ -756,10 +746,8 @@
<string name="format_carbs_ic">%1$.0fg IC: %2$.1f</string>
<string name="format_cob_ic">%1$.1fg IC: %2$.1f</string>
<string name="format_percent">%1$d%%</string>
<string name="profile_name">Profile name:</string>
<string name="selected_profile">Selected:</string>
<string name="unitsnosemicolon">Units</string>
<string name="doyouwantswitchprofile">Do you want to switch profile and discard changes made to current profile?</string>
<string name="key_wizard_calculation_visible" translatable="false">wizard_calculation_visible</string>
<string name="key_wizard_correction_percent" translatable="false">wizard_correction_percent</string>
<string name="objectives_button_unfinish">Clear finished</string>
@ -768,15 +756,7 @@
<string name="setupwizard_units_prompt">Select units you want to display values in</string>
<string name="key_wear_detailediob" translatable="false">wear_detailediob</string>
<string name="key_wear_showbgi" translatable="false">wear_showbgi</string>
<string name="dia_short">DIA</string>
<string name="ic_short">IC</string>
<string name="isf_short">ISF</string>
<string name="target_short">TARG</string>
<string name="clone_label">Clone</string>
<string name="saveorresetchangesfirst">Save or reset current changes first</string>
<string name="deletecurrentprofile">Delete current profile?</string>
<string name="copytolocalprofile">Create new profile from this profile?</string>
<string name="profilenamecontainsdot">Profile name contains dots.\nThis is not supported by NS.\nProfile is not uploaded to NS.</string>
<string name="low_mark_comment">Lower value of in range area (display only)</string>
<string name="high_mark_comment">Higher value of in range area (display only)</string>
<string name="invalidpct">Invalid % entry</string>
@ -856,7 +836,6 @@
<string name="key_ns_upload" translatable="false">ns_upload</string>
<string name="ns_upload_summary">Profiles, boluses, carbs, temporary basals are uploaded to NS</string>
<string name="ns_upload">Upload data to NS</string>
<string name="key_ns_receive_profile_store" translatable="false">ns_receive_profile_store</string>
<string name="ns_receive_profile_store">Receive profile store</string>
<string name="ns_receive_profile_store_summary">Synchronize profiles from NS profile editor</string>
<string name="key_ns_receive_temp_target" translatable="false">ns_receive_temp_target</string>
@ -883,15 +862,7 @@
<string name="key_ns_receive_cgm" translatable="false">ns_receive_cgm</string>
<string name="ns_receive_cgm">Receive/backfill CGM data</string>
<string name="ns_receive_cgm_summary">Accept CGM data from NS</string>
<string name="missing_profile_name">Missing profile name</string>
<string name="error_in_ic_values">Error in IC values</string>
<string name="error_in_basal_values">Error in basal values</string>
<string name="error_in_target_values">Error in target values</string>
<string name="error_in_isf_values">Error in ISF values</string>
<string name="run_question">Run %s?</string>
<string name="invalid_profile_not_accepted">Invalid profile %1$s not accepted from NS</string>
<string name="view">View</string>
<string name="errors">Errors</string>
<string name="ns_sync_slow">Slow down uploads</string>
<string name="data_status">BG data status</string>
<string name="remove_bg_readings">Remove BG readings</string>
@ -917,10 +888,6 @@
<string name="a11y_insulin_label">insulin</string>
<string name="a11y_blood_glucose">blood glucose</string>
<string name="a11y_bg_outdated">outdated</string>
<string name="a11y_add_new_profile">add new profile</string>
<string name="a11y_clone_profile">clone current profile</string>
<string name="a11y_delete_current_profile">delete current profile</string>
<string name="a11y_add_new_to_list">add new to list</string>
<!-- WEAR OS-->
<string name="wear_action_tempt_preset_error">Temptarget unknown preset: %1$s</string>
<string name="wear_action_tempt_cancel_message">Cancelling running Temp-Targets?</string>
@ -956,7 +923,6 @@
<string name="app_default">Application default</string>
<string name="show_invalidated_records">Show invalidated / removed records</string>
<string name="hide_invalidated_records">Hide invalidated / removed records</string>
<string name="select_profile">Select profile to edit</string>
<string name="refresh_from_nightscout">Refresh from Nightscout</string>
<string name="remove_selected_items">Remove selected items</string>
<string name="select_for_removal">Select for removal</string>

View file

@ -26,7 +26,7 @@
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_show_wizard_button"
android:title="@string/overview_calculator_label" />
android:title="@string/calculator_label" />
<SwitchPreference
android:defaultValue="true"

View file

@ -109,7 +109,6 @@
<string name="please_choose_a_trigger_type">Choose a trigger type</string>
<string name="please_choose_a_operation_type">Choose a operation type</string>
<string name="triggers">Triggers:</string>
<string name="remove_label">REMOVE</string>
<string name="preconditions">Preconditions:</string>
<string name="automation_event">Automation event</string>
<string name="reorder_label">Reorder</string>

View file

@ -0,0 +1,3 @@
package info.nightscout.androidaps.events
class EventProfileStoreChanged : Event()

View file

@ -14,6 +14,7 @@ interface ActivityNames {
val tddStatsActivity: Class<*>
val errorHelperActivity: Class<*>
val bolusProgressHelperActivity: Class<*>
val singleFragmentActivity: Class<*>
/**
* Show ErrorHelperActivity and start alarm
@ -24,4 +25,5 @@ interface ActivityNames {
*/
fun runAlarm(ctx: Context, status: String, title: String, @RawRes soundId: Int = 0)
fun runWizard(fragmentManager: FragmentManager, carbs: Int, name: String)
fun runProfileSwitchDialog(fragmentManager: FragmentManager, profileName: String?)
}

View file

@ -7,6 +7,28 @@ interface Overview : ConfigExportImport {
val overviewBus: RxBus
/**
* Add notification that shows dialog after clicking button
* @param id if of notification
* @text text of notification
* @level urgency level of notification
* @actionButtonId label of button
* @title Dialog title
* @message Dialog body
*/
fun addNotificationWithDialogResponse(id: Int, text: String, level: Int, @StringRes actionButtonId: Int, title: String, message: String)
/**
* Add notification that executes [Runnable] after clicking button
* @param id if of notification
* @text text of notification
* @level urgency level of notification
* @actionButtonId label of button
* @action Runnable to be run
*/
fun addNotification(id: Int, text: String, level: Int, @StringRes actionButtonId: Int, action: Runnable)
/**
* Remove notification
* @param id if of notification
*/
fun dismissNotification(id: Int)
}

View file

@ -84,6 +84,8 @@
<string name="key_enable_carbs_required_alert_local" translatable="false">enable_carbs_required_alert_local</string>
<string name="key_smscommunicator_report_pump_unreachable" translatable="false">smscommunicator_report_pump_unreachable</string>
<string name="key_rangetodisplay" translatable="false">rangetodisplay</string>
<string name="key_local_profile_last_change" translatable="false">local_profile_last_change</string>
<string name="key_ns_receive_profile_store" translatable="false">ns_receive_profile_store</string>
<!-- General-->
<string name="refresh">Refresh</string>
@ -228,6 +230,9 @@
<string name="tir">TIR</string>
<string name="tdd_total">TDD Total</string>
<string name="none"><![CDATA[<none>]]></string>
<string name="remove_label">REMOVE</string>
<string name="activate_profile">Activate profile</string>
<string name="reset">reset</string>
<!-- Constraints-->
<string name="limitingbasalratio">Limiting max basal rate to %1$.2f U/h because of %2$s</string>

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.profile.local
package info.nightscout.plugins.profile
import android.os.Bundle
import android.text.Editable
@ -11,31 +11,29 @@ import android.widget.ArrayAdapter
import com.google.android.material.tabs.TabLayout
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.databinding.LocalprofileFragmentBinding
import info.nightscout.androidaps.dialogs.ProfileSwitchDialog
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ActivityNames
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.ui.TimeListEdit
import info.nightscout.plugins.R
import info.nightscout.plugins.databinding.ProfileFragmentBinding
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.plugins.ui.TimeListEdit
import info.nightscout.shared.SafeParse
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
@ -45,20 +43,21 @@ import java.math.RoundingMode
import java.text.DecimalFormat
import javax.inject.Inject
class LocalProfileFragment : DaggerFragment() {
class ProfileFragment : DaggerFragment() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBus
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var profilePlugin: ProfilePlugin
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var hardLimits: HardLimits
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var activityNames: ActivityNames
private var disposable: CompositeDisposable = CompositeDisposable()
private var inMenu = false
@ -68,7 +67,7 @@ class LocalProfileFragment : DaggerFragment() {
private val save = Runnable {
doEdit()
basalView?.updateLabel(rh.gs(R.string.basal_label) + ": " + sumLabel())
localProfilePlugin.getEditedProfile()?.let {
profilePlugin.getEditedProfile()?.let {
binding.basalGraph.show(ProfileSealed.Pure(it))
binding.icGraph.show(ProfileSealed.Pure(it))
binding.isfGraph.show(ProfileSealed.Pure(it))
@ -81,32 +80,32 @@ class LocalProfileFragment : DaggerFragment() {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
localProfilePlugin.currentProfile()?.dia = SafeParse.stringToDouble(binding.dia.text)
localProfilePlugin.currentProfile()?.name = binding.name.text.toString()
profilePlugin.currentProfile()?.dia = SafeParse.stringToDouble(binding.dia.text)
profilePlugin.currentProfile()?.name = binding.name.text.toString()
doEdit()
}
}
private fun sumLabel(): String {
val profile = localProfilePlugin.getEditedProfile()
val profile = profilePlugin.getEditedProfile()
val sum = profile?.let { ProfileSealed.Pure(profile).baseBasalSum() } ?: 0.0
return "" + DecimalFormatter.to2Decimal(sum) + rh.gs(R.string.insulin_unit_shortname)
}
private var _binding: LocalprofileFragmentBinding? = null
private var _binding: ProfileFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = LocalprofileFragmentBinding.inflate(inflater, container, false)
_binding = ProfileFragmentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val parentClass = this.activity?.let { it::class.java }
inMenu = parentClass == SingleFragmentActivity::class.java
inMenu = parentClass == activityNames.singleFragmentActivity
updateProtectedUi()
processVisibility(0)
binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
@ -120,16 +119,16 @@ class LocalProfileFragment : DaggerFragment() {
binding.diaLabel.labelFor = binding.dia.editTextId
binding.unlock.setOnClickListener { queryProtection() }
val profiles = localProfilePlugin.profile?.getProfileList() ?: ArrayList()
val profiles = profilePlugin.profile?.getProfileList() ?: ArrayList()
val activeProfile = profileFunction.getProfileName()
val profileIndex = profiles.indexOf(activeProfile)
localProfilePlugin.currentProfileIndex = if (profileIndex >= 0) profileIndex else 0
profilePlugin.currentProfileIndex = if (profileIndex >= 0) profileIndex else 0
}
fun build() {
val pumpDescription = activePlugin.activePump.pumpDescription
if (localProfilePlugin.numOfProfiles == 0) localProfilePlugin.addNewProfile()
val currentProfile = localProfilePlugin.currentProfile() ?: return
if (profilePlugin.numOfProfiles == 0) profilePlugin.addNewProfile()
val currentProfile = profilePlugin.currentProfile() ?: return
val units = if (currentProfile.mgdl) Constants.MGDL else Constants.MMOL
binding.name.removeTextChangedListener(textWatch)
@ -223,28 +222,28 @@ class LocalProfileFragment : DaggerFragment() {
}
context?.let { context ->
val profileList: ArrayList<CharSequence> = localProfilePlugin.profile?.getProfileList() ?: ArrayList()
val profileList: ArrayList<CharSequence> = profilePlugin.profile?.getProfileList() ?: ArrayList()
binding.profileList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, profileList))
} ?: return
binding.profileList.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
if (localProfilePlugin.isEdited) {
if (profilePlugin.isEdited) {
activity?.let { activity ->
OKDialog.showConfirmation(
activity, rh.gs(R.string.doyouwantswitchprofile),
activity, rh.gs(R.string.do_you_want_switch_profile),
{
localProfilePlugin.currentProfileIndex = position
localProfilePlugin.isEdited = false
profilePlugin.currentProfileIndex = position
profilePlugin.isEdited = false
build()
}, null
)
}
} else {
localProfilePlugin.currentProfileIndex = position
profilePlugin.currentProfileIndex = position
build()
}
}
localProfilePlugin.getEditedProfile()?.let {
profilePlugin.getEditedProfile()?.let {
binding.basalGraph.show(ProfileSealed.Pure(it))
binding.icGraph.show(ProfileSealed.Pure(it))
binding.isfGraph.show(ProfileSealed.Pure(it))
@ -253,40 +252,40 @@ class LocalProfileFragment : DaggerFragment() {
}
binding.profileAdd.setOnClickListener {
if (localProfilePlugin.isEdited) {
activity?.let { OKDialog.show(it, "", rh.gs(R.string.saveorresetchangesfirst)) }
if (profilePlugin.isEdited) {
activity?.let { OKDialog.show(it, "", rh.gs(R.string.save_or_reset_changes_first)) }
} else {
uel.log(Action.NEW_PROFILE, Sources.LocalProfile)
localProfilePlugin.addNewProfile()
uel.log(UserEntry.Action.NEW_PROFILE, UserEntry.Sources.LocalProfile)
profilePlugin.addNewProfile()
build()
}
}
binding.profileClone.setOnClickListener {
if (localProfilePlugin.isEdited) {
activity?.let { OKDialog.show(it, "", rh.gs(R.string.saveorresetchangesfirst)) }
if (profilePlugin.isEdited) {
activity?.let { OKDialog.show(it, "", rh.gs(R.string.save_or_reset_changes_first)) }
} else {
uel.log(
Action.CLONE_PROFILE, Sources.LocalProfile, ValueWithUnit.SimpleString(
localProfilePlugin.currentProfile()?.name
UserEntry.Action.CLONE_PROFILE, UserEntry.Sources.LocalProfile, ValueWithUnit.SimpleString(
profilePlugin.currentProfile()?.name
?: ""
)
)
localProfilePlugin.cloneProfile()
profilePlugin.cloneProfile()
build()
}
}
binding.profileRemove.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.deletecurrentprofile), {
OKDialog.showConfirmation(activity, rh.gs(R.string.delete_current_profile), {
uel.log(
Action.PROFILE_REMOVED, Sources.LocalProfile, ValueWithUnit.SimpleString(
localProfilePlugin.currentProfile()?.name
UserEntry.Action.PROFILE_REMOVED, UserEntry.Sources.LocalProfile, ValueWithUnit.SimpleString(
profilePlugin.currentProfile()?.name
?: ""
)
)
localProfilePlugin.removeCurrentProfile()
profilePlugin.removeCurrentProfile()
build()
}, null)
}
@ -299,27 +298,25 @@ class LocalProfileFragment : DaggerFragment() {
binding.units.text = rh.gs(R.string.units_colon) + " " + (if (currentProfile.mgdl) rh.gs(R.string.mgdl) else rh.gs(R.string.mmol))
binding.profileswitch.setOnClickListener {
ProfileSwitchDialog()
.also { it.arguments = Bundle().also { bundle -> bundle.putString("profileName", localProfilePlugin.currentProfile()?.name) } }
.show(childFragmentManager, "ProfileSwitchDialog")
activityNames.runProfileSwitchDialog(childFragmentManager, profilePlugin.currentProfile()?.name)
}
binding.reset.setOnClickListener {
localProfilePlugin.loadSettings()
profilePlugin.loadSettings()
build()
}
binding.save.setOnClickListener {
if (!localProfilePlugin.isValidEditState(activity)) {
if (!profilePlugin.isValidEditState(activity)) {
return@setOnClickListener //Should not happen as saveButton should not be visible if not valid
}
uel.log(
Action.STORE_PROFILE, Sources.LocalProfile, ValueWithUnit.SimpleString(
localProfilePlugin.currentProfile()?.name
UserEntry.Action.STORE_PROFILE, UserEntry.Sources.LocalProfile, ValueWithUnit.SimpleString(
profilePlugin.currentProfile()?.name
?: ""
)
)
localProfilePlugin.storeSettings(activity)
profilePlugin.storeSettings(activity)
build()
}
updateGUI()
@ -349,7 +346,7 @@ class LocalProfileFragment : DaggerFragment() {
}
fun doEdit() {
localProfilePlugin.isEdited = true
profilePlugin.isEdited = true
updateGUI()
}
@ -363,8 +360,8 @@ class LocalProfileFragment : DaggerFragment() {
private fun updateGUI() {
if (_binding == null) return
val isValid = localProfilePlugin.isValidEditState(activity)
val isEdited = localProfilePlugin.isEdited
val isValid = profilePlugin.isValidEditState(activity)
val isEdited = profilePlugin.isEdited
if (isValid) {
this.view?.setBackgroundColor(rh.gac(context, R.attr.okBackgroundColor))
binding.profileList.isEnabled = true

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.profile.local
package info.nightscout.plugins.profile
import android.content.Context
import androidx.fragment.app.FragmentActivity
@ -7,38 +7,48 @@ import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.events.EventProfileStoreChanged
import info.nightscout.androidaps.extensions.blockFromJsonArray
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.ProfileSource
import info.nightscout.androidaps.interfaces.ProfileStore
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.interfaces.XDripBroadcast
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.receivers.DataWorkerStorage
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.plugins.R
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.androidaps.receivers.DataWorkerStorage
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.lang.Integer.min
import java.util.*
import java.util.TimeZone
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.collections.ArrayList
@OpenForTesting
@Singleton
class LocalProfilePlugin @Inject constructor(
class ProfilePlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
private val rxBus: RxBus,
@ -49,9 +59,10 @@ class LocalProfilePlugin @Inject constructor(
private val hardLimits: HardLimits,
private val dateUtil: DateUtil,
private val config: Config
) : PluginBase(PluginDescription()
) : PluginBase(
PluginDescription()
.mainType(PluginType.PROFILE)
.fragmentClass(LocalProfileFragment::class.java.name)
.fragmentClass(ProfileFragment::class.java.name)
.enableByDefault(true)
.pluginIcon(R.drawable.ic_local_profile)
.pluginName(R.string.localprofile)
@ -74,10 +85,10 @@ class LocalProfilePlugin @Inject constructor(
internal var name: String? = null
internal var mgdl: Boolean = false
internal var dia: Double = Constants.defaultDIA
internal var ic: JSONArray? = null
internal var isf: JSONArray? = null
internal var basal: JSONArray? = null
var dia: Double = Constants.defaultDIA
var ic: JSONArray? = null
var isf: JSONArray? = null
var basal: JSONArray? = null
internal var targetLow: JSONArray? = null
internal var targetHigh: JSONArray? = null
@ -100,7 +111,7 @@ class LocalProfilePlugin @Inject constructor(
var profiles: ArrayList<SingleProfile> = ArrayList()
val numOfProfiles get() = profiles.size
internal var currentProfileIndex = 0
var currentProfileIndex = 0
fun currentProfile(): SingleProfile? = if (numOfProfiles > 0 && currentProfileIndex < numOfProfiles) profiles[currentProfileIndex] else null
@ -109,7 +120,7 @@ class LocalProfilePlugin @Inject constructor(
val pumpDescription = activePlugin.activePump.pumpDescription
with(profiles[currentProfileIndex]) {
if (dia < hardLimits.minDia() || dia > hardLimits.maxDia()) {
ToastUtils.errorToast(activity, rh.gs(R.string.value_out_of_hard_limits, rh.gs(info.nightscout.androidaps.core.R.string.profile_dia), dia))
ToastUtils.errorToast(activity, rh.gs(R.string.value_out_of_hard_limits, rh.gs(R.string.profile_dia), dia))
return false
}
if (name.isNullOrEmpty()) {
@ -216,7 +227,7 @@ class LocalProfilePlugin @Inject constructor(
if (name.contains(".")) namesOK = false
}
if (!namesOK) activity?.let {
OKDialog.show(it, "", rh.gs(R.string.profilenamecontainsdot))
OKDialog.show(it, "", rh.gs(R.string.profile_name_contains_dot))
}
}
@ -261,18 +272,14 @@ class LocalProfilePlugin @Inject constructor(
sp.name = p.toString()
newProfiles.add(sp)
} else {
val n = NotificationWithAction(
injector,
activePlugin.activeOverview.addNotificationWithDialogResponse(
Notification.INVALID_PROFILE_NOT_ACCEPTED,
rh.gs(R.string.invalid_profile_not_accepted, p.toString()),
Notification.NORMAL
Notification.NORMAL,
R.string.view,
rh.gs(R.string.errors),
validityCheck.reasons.joinToString(separator = "\n")
)
n.action(R.string.view) {
n.contextForAction?.let {
OKDialog.show(it, rh.gs(R.string.errors), validityCheck.reasons.joinToString(separator = "\n"), null)
}
}
rxBus.send(EventNewNotification(n))
}
}
if (newProfiles.size > 0) {
@ -460,7 +467,7 @@ class LocalProfilePlugin @Inject constructor(
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var sp: SP
@Inject lateinit var config: Config
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var profilePlugin: ProfilePlugin
@Inject lateinit var xDripBroadcast: XDripBroadcast
init {
@ -478,9 +485,9 @@ class LocalProfilePlugin @Inject constructor(
aapsLogger.debug(LTag.PROFILE, "Received profileStore: createdAt: $createdAt Local last modification: $lastLocalChange")
@Suppress("LiftReturnOrAssignment")
if (createdAt > lastLocalChange || createdAt % 1000 == 0L) {// whole second means edited in NS
localProfilePlugin.loadFromStore(store)
profilePlugin.loadFromStore(store)
aapsLogger.debug(LTag.PROFILE, "Received profileStore: $profileJson")
return Result.success(workDataOf("Data" to profileJson.toString().substring(0..min(5000, profileJson.length()))))
return Result.success(workDataOf("Data" to profileJson.toString().substring(0..Integer.min(5000, profileJson.length()))))
} else
return Result.success(workDataOf("Result" to "Unchanged. Ignoring"))
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.profile.local.events
package info.nightscout.plugins.profile.events
import info.nightscout.androidaps.events.Event

View file

@ -0,0 +1,146 @@
package info.nightscout.plugins.ui
import android.view.MotionEvent
import android.view.View
import android.widget.AdapterView
import android.widget.Spinner
import android.widget.SpinnerAdapter
import kotlin.math.max
/**
* Spinner Helper class that works around some common issues
* with the stock Android Spinner
*
* A Spinner will normally call it's OnItemSelectedListener
* when you use setSelection(...) in your initialization code.
* This is usually unwanted behavior, and a common work-around
* is to use spinner.post(...) with a Runnable to assign the
* OnItemSelectedListener after layout.
*
* If you do not call setSelection(...) manually, the callback
* may be called with the first item in the adapter you have
* set. The common work-around for that is to count callbacks.
*
* While these workarounds usually *seem* to work, the callback
* may still be called repeatedly for other reasons while the
* selection hasn't actually changed. This will happen for
* example, if the user has accessibility options enabled -
* which is more common than you might think as several apps
* use this for different purposes, like detecting which
* notifications are active.
*
* Ideally, your OnItemSelectedListener callback should be
* coded defensively so that no problem would occur even
* if the callback was called repeatedly with the same values
* without any user interaction, so no workarounds are needed.
*
* This class does that for you. It keeps track of the values
* you have set with the setSelection(...) methods, and
* proxies the OnItemSelectedListener callback so your callback
* only gets called if the selected item's position differs
* from the one you have set by code, or the first item if you
* did not set it.
*
* This also means that if the user actually clicks the item
* that was previously selected by code (or the first item
* if you didn't set a selection by code), the callback will
* not fire.
*
* To implement, replace current occurrences of:
*
* Spinner spinner =
* (Spinner)findViewById(R.id.xxx);
*
* with:
*
* SpinnerHelper spinner =
* new SpinnerHelper(findViewById(R.id.xxx))
*
* SpinnerHelper proxies the (my) most used calls to Spinner
* but not all of them. Should a method not be available, use:
*
* spinner.getSpinner().someMethod(...)
*
* Or just add the proxy method yourself :)
*
* (Quickly) Tested on devices from 2.3.6 through 4.2.2
*
* @author Jorrit "Chainfire" Jongma
* @license WTFPL (do whatever you want with this, nobody cares)
*/
@Suppress("unused")
class SpinnerHelper(val spinner: Spinner) : AdapterView.OnItemSelectedListener {
private var userTouched = false
private var lastPosition = -1
private var proxiedItemSelectedListener: AdapterView.OnItemSelectedListener? = null
fun setSelection(position: Int) {
lastPosition = max(-1, position)
spinner.setSelection(position)
}
fun setSelection(position: Int, animate: Boolean) {
lastPosition = max(-1, position)
spinner.setSelection(position, animate)
}
fun setOnItemSelectedListener(listener: AdapterView.OnItemSelectedListener?) {
proxiedItemSelectedListener = listener
setTouchListener()
spinner.onItemSelectedListener = if (listener == null) null else this
}
private fun setTouchListener() {
spinner.setOnTouchListener { v: View, _: MotionEvent? ->
v.performClick()
userTouched = true
false
}
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (position != lastPosition && userTouched) {
lastPosition = position
proxiedItemSelectedListener?.onItemSelected(parent, view, position, id)
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
if (lastPosition != -1) {
lastPosition = -1
proxiedItemSelectedListener?.onNothingSelected(parent)
}
}
var adapter: SpinnerAdapter
get() = spinner.adapter
set(adapter) {
if (adapter.count > 0) {
lastPosition = 0
}
spinner.adapter = adapter
}
val count: Int
get() = spinner.count
fun getItemAtPosition(position: Int): Any = spinner.getItemAtPosition(position)
fun getItemIdAtPosition(position: Int): Long = spinner.getItemIdAtPosition(position)
val selectedItem: Any
get() = try {
spinner.selectedItem
} catch (e: IndexOutOfBoundsException) {
adapter.getItem(adapter.count - 1)
}
val selectedItemId: Long
get() = spinner.selectedItemId
val selectedItemPosition: Int
get() = spinner.selectedItemPosition
var isEnabled: Boolean
get() = spinner.isEnabled
set(enabled) {
spinner.isEnabled = enabled
}
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.utils.ui;
package info.nightscout.plugins.ui;
import android.content.Context;
import android.text.Editable;
@ -24,10 +24,11 @@ import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.R;
import info.nightscout.shared.logging.AAPSLogger;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.ui.NumberPicker;
import info.nightscout.plugins.R;
import info.nightscout.shared.SafeParse;
import info.nightscout.shared.logging.AAPSLogger;
/**
* Created by mike on 29.12.2016.

View file

@ -89,7 +89,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:contentDescription="@string/overview_calculator_label"
android:contentDescription="@string/calculator_label"
android:orientation="horizontal"
android:paddingStart="-5dp"
android:paddingEnd="-5dp"

View file

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".plugins.profile.local.LocalProfileFragment">
tools:context="info.nightscout.plugins.profile.ProfileFragment">
<LinearLayout
android:layout_width="match_parent"

View file

@ -157,9 +157,12 @@
<string name="shortenergy">En</string>
<string name="shortprotein">Pr</string>
<string name="shortfat">Fat</string>
<string name="theme_switcher">Theme switcher</string>
<string name="category">Category</string>
<string name="subcategory">Subcategory</string>
<string name="calculator_label">Calculator</string>
<!-- Theme switcher dark and light mode-->
<string name="theme_switcher">Theme switcher</string>
<string name="theme_switcher_summary">Choose dark, light, or to follow the system theme</string>
<string name="app_color_scheme">App Color Scheme</string>
<string name="dark_theme">Dark theme</string>
@ -169,4 +172,34 @@
<string name="value_light_theme" translatable="false">light</string>
<string name="value_system_theme" translatable="false">system</string>
<!-- Profile -->
<string name="localprofile">Profile</string>
<string name="localprofile_shortname">LP</string>
<string name="description_profile_local">Define a profile which is available offline.</string>
<string name="a11y_add_new_to_list">add new to list</string>
<string name="do_you_want_switch_profile">Do you want to switch profile and discard changes made to current profile?</string>
<string name="save_or_reset_changes_first">Save or reset current changes first</string>
<string name="delete_current_profile">Delete current profile?</string>
<string name="units_colon">Units:</string>
<string name="missing_profile_name">Missing profile name</string>
<string name="error_in_ic_values">Error in IC values</string>
<string name="error_in_basal_values">Error in basal values</string>
<string name="error_in_target_values">Error in target values</string>
<string name="error_in_isf_values">Error in ISF values</string>
<string name="profile_name_contains_dot">Profile name contains dots.\nThis is not supported by NS.\nProfile is not uploaded to NS.</string>
<string name="invalid_profile_not_accepted">Invalid profile %1$s not accepted from NS</string>
<string name="view">View</string>
<string name="errors">Errors</string>
<string name="select_profile">Select profile to edit</string>
<string name="profile_name">Profile name:</string>
<string name="a11y_add_new_profile">add new profile</string>
<string name="a11y_clone_profile">clone current profile</string>
<string name="a11y_delete_current_profile">delete current profile</string>
<string name="dia_short">DIA</string>
<string name="ic_short">IC</string>
<string name="isf_short">ISF</string>
<string name="target_short">TARG</string>
<string name="clone_label">Clone</string>
<string name="basal_short">BAS</string>
</resources>

View file

@ -26,7 +26,6 @@
<string name="calculation_in_progress">Calculation in progress</string>
<string name="invalid_age">Invalid age entry</string>
<string name="invalid_weight">Invalid weight entry</string>
<string name="reset">reset</string>
<string name="id">ID:</string>
<string name="submit">Submit</string>
<string name="profile">Profile</string>