BG unit preference

This commit is contained in:
Milos Kozak 2022-05-27 20:07:35 +02:00
parent 8c4378c455
commit 6e7b5ab7db
9 changed files with 195 additions and 137 deletions

View file

@ -16,7 +16,7 @@
android:summary="@string/patientage_summary"
android:title="@string/patientage" />
<EditTextPreference
<info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference
android:defaultValue="3"
android:inputType="numberDecimal"
android:key="@string/key_treatmentssafety_maxbolus"
@ -25,9 +25,9 @@
validate:floatminNumber="0.1"
validate:testType="floatNumericRange" />
<EditTextPreference
<info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference
android:defaultValue="48"
android:inputType="numberDecimal"
android:inputType="number"
android:key="@string/key_treatmentssafety_maxcarbs"
android:title="@string/treatmentssafety_maxcarbs_title"
validate:floatmaxNumber="200"

View file

@ -14,7 +14,8 @@ import javax.inject.Singleton
includes = [
CoreReceiversModule::class,
CoreFragmentsModule::class,
CoreDataClassesModule::class
CoreDataClassesModule::class,
ValidatorsModule::class
]
)
open class CoreModule {

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.utils.textValidator.DefaultEditTextValidator
import info.nightscout.androidaps.utils.textValidator.EditTextValidator
import info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference
@Module
@Suppress("unused")
abstract class ValidatorsModule {
@ContributesAndroidInjector abstract fun defaultEditTextValidatorInjector(): DefaultEditTextValidator
@ContributesAndroidInjector abstract fun editTextValidatorInjector(): EditTextValidator
@ContributesAndroidInjector abstract fun validatingEditTextPreferenceInjector(): ValidatingEditTextPreference
}

View file

@ -6,8 +6,12 @@ import android.text.TextUtils
import android.text.TextWatcher
import android.widget.EditText
import com.google.android.material.textfield.TextInputLayout
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.utils.textValidator.validators.*
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class DefaultEditTextValidator : EditTextValidator {
@ -27,17 +31,23 @@ class DefaultEditTextValidator : EditTextValidator {
private var minLength = 0
private var minNumber = 0
private var maxNumber = 0
private var minMgdl = 0
private var maxMgdl = 0
private var floatminNumber = 0f
private var floatmaxNumber = 0f
@Inject lateinit var profileFunction: ProfileFunction
@Suppress("unused")
constructor(editTextView: EditText, context: Context) {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
testType = EditTextValidator.TEST_NOCHECK
setEditText(editTextView)
resetValidators(context)
}
constructor(editTextView: EditText, parameters: Parameters, context: Context) {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
emptyAllowed = parameters.emptyAllowed
testType = parameters.testType
testErrorString = parameters.testErrorString
@ -48,6 +58,8 @@ class DefaultEditTextValidator : EditTextValidator {
minLength = parameters.minLength
minNumber = parameters.minNumber
maxNumber = parameters.maxNumber
minMgdl = parameters.minMgdl
maxMgdl = parameters.maxMgdl
floatminNumber = parameters.floatminNumber
floatmaxNumber = parameters.floatmaxNumber
@ -63,10 +75,10 @@ class DefaultEditTextValidator : EditTextValidator {
private fun setEditText(editText: EditText) {
//editTextView?.removeTextChangedListener(textWatcher)
editTextView = editText
editText.addTextChangedListener(textWatcher)
editText.addTextChangedListener(getTextWatcher())
}
override fun getTextWatcher(): TextWatcher {
override fun getTextWatcher(): TextWatcher? {
if (tw == null) {
tw = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
@ -98,28 +110,31 @@ class DefaultEditTextValidator : EditTextValidator {
defaultEmptyErrorString = context.getString(R.string.error_field_must_not_be_empty)
setEmptyErrorString(emptyErrorStringDef)
mValidator = AndValidator()
val toAdd: Validator
val toAdd: Validator =
when (testType) {
EditTextValidator.TEST_NOCHECK -> toAdd = DummyValidator()
EditTextValidator.TEST_ALPHA -> toAdd = AlphaValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_only_standard_letters_are_allowed) else testErrorString)
EditTextValidator.TEST_ALPHANUMERIC -> toAdd = AlphaNumericValidator(
if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_this_field_cannot_contain_special_character) else testErrorString)
EditTextValidator.TEST_NUMERIC -> toAdd = NumericValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_only_numeric_digits_allowed) else testErrorString)
EditTextValidator.TEST_NUMERIC_RANGE -> toAdd = NumericRangeValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_only_numeric_digits_range_allowed, minNumber.toString(), maxNumber.toString()) else testErrorString, minNumber, maxNumber)
EditTextValidator.TEST_FLOAT_NUMERIC_RANGE -> toAdd = FloatNumericRangeValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_only_numeric_digits_range_allowed, floatminNumber.toString(), floatmaxNumber.toString()) else testErrorString, floatminNumber, floatmaxNumber)
EditTextValidator.TEST_REGEXP -> toAdd = RegexpValidator(testErrorString, customRegexp ?: "")
EditTextValidator.TEST_CREDITCARD -> toAdd = CreditCardValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_creditcard_number_not_valid) else testErrorString)
EditTextValidator.TEST_EMAIL -> toAdd = EmailValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_email_address_not_valid) else testErrorString)
EditTextValidator.TEST_PHONE -> toAdd = PhoneValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_phone_not_valid) else testErrorString)
EditTextValidator.TEST_MULTI_PHONE -> toAdd = MultiPhoneValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_phone_not_valid) else testErrorString)
EditTextValidator.TEST_PIN_STRENGTH -> toAdd = PinStrengthValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_pin_not_valid) else testErrorString)
EditTextValidator.TEST_DOMAINNAME -> toAdd = DomainValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_domain_not_valid) else testErrorString)
EditTextValidator.TEST_IPADDRESS -> toAdd = IpAddressValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_ip_not_valid) else testErrorString)
EditTextValidator.TEST_WEBURL -> toAdd = WebUrlValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_url_not_valid) else testErrorString)
EditTextValidator.TEST_HTTPS_URL -> toAdd = HttpsUrlValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_url_not_valid) else testErrorString)
EditTextValidator.TEST_PERSONNAME -> toAdd = PersonNameValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_notvalid_personname) else testErrorString)
EditTextValidator.TEST_PERSONFULLNAME -> toAdd = PersonFullNameValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_notvalid_personfullname) else testErrorString)
EditTextValidator.TEST_MIN_LENGTH -> toAdd = MinDigitLengthValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_not_a_minimum_length) else testErrorString, minLength)
EditTextValidator.TEST_NOCHECK -> DummyValidator()
EditTextValidator.TEST_ALPHA -> AlphaValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_only_standard_letters_are_allowed) else testErrorString)
EditTextValidator.TEST_ALPHANUMERIC -> AlphaNumericValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_this_field_cannot_contain_special_character) else testErrorString)
EditTextValidator.TEST_NUMERIC -> NumericValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_only_numeric_digits_allowed) else testErrorString)
EditTextValidator.TEST_NUMERIC_RANGE -> NumericRangeValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_only_numeric_digits_range_allowed, minNumber.toString(), maxNumber.toString()) else testErrorString, minNumber, maxNumber)
EditTextValidator.TEST_FLOAT_NUMERIC_RANGE -> FloatNumericRangeValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_only_numeric_digits_range_allowed, floatminNumber.toString(), floatmaxNumber.toString()) else testErrorString, floatminNumber, floatmaxNumber)
EditTextValidator.TEST_REGEXP -> RegexpValidator(testErrorString, customRegexp ?: "")
EditTextValidator.TEST_CREDITCARD -> CreditCardValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_creditcard_number_not_valid) else testErrorString)
EditTextValidator.TEST_EMAIL -> EmailValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_email_address_not_valid) else testErrorString)
EditTextValidator.TEST_PHONE -> PhoneValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_phone_not_valid) else testErrorString)
EditTextValidator.TEST_MULTI_PHONE -> MultiPhoneValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_phone_not_valid) else testErrorString)
EditTextValidator.TEST_PIN_STRENGTH -> PinStrengthValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_pin_not_valid) else testErrorString)
EditTextValidator.TEST_DOMAINNAME -> DomainValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_domain_not_valid) else testErrorString)
EditTextValidator.TEST_IPADDRESS -> IpAddressValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_ip_not_valid) else testErrorString)
EditTextValidator.TEST_WEBURL -> WebUrlValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_url_not_valid) else testErrorString)
EditTextValidator.TEST_HTTPS_URL -> HttpsUrlValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_url_not_valid) else testErrorString)
EditTextValidator.TEST_PERSONNAME -> PersonNameValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_notvalid_personname) else testErrorString)
EditTextValidator.TEST_PERSONFULLNAME -> PersonFullNameValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_notvalid_personfullname) else testErrorString)
EditTextValidator.TEST_MIN_LENGTH -> MinDigitLengthValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_not_a_minimum_length) else testErrorString, minLength)
EditTextValidator.TEST_BG_RANGE -> BgRangeValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_only_numeric_digits_range_allowed,
Profile.fromMgdlToUnits(minMgdl.toDouble(), profileFunction
.getUnits()).toString(), Profile.fromMgdlToUnits(maxMgdl.toDouble(), profileFunction.getUnits()).toString()) else
testErrorString, minMgdl, maxMgdl, profileFunction)
EditTextValidator.TEST_CUSTOM -> {
// must specify the fully qualified class name & an error message
@ -139,16 +154,15 @@ class DefaultEditTextValidator : EditTextValidator {
} catch (e: ClassNotFoundException) {
throw RuntimeException(String.format("Unable to load class for custom validator (%s).", classType))
}
toAdd = try {
try {
customValidatorClass.getConstructor(String::class.java).newInstance(testErrorString)
} catch (e: Exception) {
throw RuntimeException(String.format("Unable to construct custom validator (%s) with argument: %s", classType,
testErrorString))
throw RuntimeException(String.format("Unable to construct custom validator (%s) with argument: %s", classType, testErrorString))
}
}
EditTextValidator.TEST_DATE -> toAdd = DateValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_date_not_valid) else testErrorString, customFormat)
else -> toAdd = DummyValidator()
EditTextValidator.TEST_DATE -> DateValidator(if (TextUtils.isEmpty(testErrorString)) context.getString(R.string.error_date_not_valid) else testErrorString, customFormat)
else -> DummyValidator()
}
val tmpValidator: MultiValidator
if (!emptyAllowed) { // If the xml tells us that this is a required field, we will add the EmptyValidator.
@ -255,6 +269,8 @@ class DefaultEditTextValidator : EditTextValidator {
var minLength: Int = 0,
var minNumber: Int = 0,
var maxNumber: Int = 0,
var minMgdl: Int = 0,
var maxMgdl: Int = 0,
var floatminNumber: Float = 0f,
var floatmaxNumber: Float = 0f
)

View file

@ -1,96 +0,0 @@
package info.nightscout.androidaps.utils.textValidator;
import android.content.Context;
import android.text.TextWatcher;
import info.nightscout.androidaps.utils.textValidator.validators.Validator;
/**
* Interface for encapsulating validation of an EditText control
*/
public interface EditTextValidator {
/**
* Add a validator to this FormEditText. The validator will be added in the
* queue of the current validators.
*
* @throws IllegalArgumentException if the validator is null
*/
void addValidator(Validator theValidator)
throws IllegalArgumentException;
/**
* This should be used with { #addTextChangedListener(TextWatcher)}. It
* fixes the non-hiding error popup behaviour.
*/
TextWatcher getTextWatcher();
@SuppressWarnings("unused") boolean isEmptyAllowed();
/**
* Resets the {@link Validator}s
*/
void resetValidators(Context context);
/**
* Calling *testValidity()* will cause the EditText to go through
* customValidators and call {#Validator.isValid(EditText)}
* Same as {@link #testValidity(boolean)} with first parameter true
*
* @return true if the validity passes false otherwise.
*/
boolean testValidity();
/**
* Calling *testValidity()* will cause the EditText to go through
* customValidators and call {#Validator.isValid(EditText)}
*
* @param showUIError determines if this call should show the UI error.
* @return true if the validity passes false otherwise.
*/
boolean testValidity(boolean showUIError);
void showUIError();
int TEST_REGEXP = 0;
int TEST_NUMERIC = 1;
int TEST_ALPHA = 2;
int TEST_ALPHANUMERIC = 3;
int TEST_EMAIL = 4;
int TEST_CREDITCARD = 5;
int TEST_PHONE = 6;
int TEST_DOMAINNAME = 7;
int TEST_IPADDRESS = 8;
int TEST_WEBURL = 9;
int TEST_NOCHECK = 10;
int TEST_CUSTOM = 11;
int TEST_PERSONNAME = 12;
int TEST_PERSONFULLNAME = 13;
int TEST_DATE = 14;
int TEST_NUMERIC_RANGE = 15;
int TEST_FLOAT_NUMERIC_RANGE = 16;
int TEST_HTTPS_URL = 17;
int TEST_MIN_LENGTH = 18;
int TEST_MULTI_PHONE = 19;
int TEST_PIN_STRENGTH = 20;
}

View file

@ -0,0 +1,79 @@
package info.nightscout.androidaps.utils.textValidator
import android.content.Context
import kotlin.Throws
import android.text.TextWatcher
import info.nightscout.androidaps.utils.textValidator.validators.Validator
import java.lang.IllegalArgumentException
/**
* Interface for encapsulating validation of an EditText control
*/
interface EditTextValidator {
/**
* Add a validator to this FormEditText. The validator will be added in the
* queue of the current validators.
*
* @throws IllegalArgumentException if the validator is null
*/
@Throws(IllegalArgumentException::class)
fun addValidator(theValidator: Validator)
/**
* This should be used with { #addTextChangedListener(TextWatcher)}. It
* fixes the non-hiding error popup behaviour.
*/
fun getTextWatcher(): TextWatcher?
fun isEmptyAllowed(): Boolean
/**
* Resets the [Validator]s
*/
fun resetValidators(context: Context)
/**
* Calling *testValidity()* will cause the EditText to go through
* customValidators and call {#Validator.isValid(EditText)}
* Same as [.testValidity] with first parameter true
*
* @return true if the validity passes false otherwise.
*/
fun testValidity(): Boolean
/**
* Calling *testValidity()* will cause the EditText to go through
* customValidators and call {#Validator.isValid(EditText)}
*
* @param showUIError determines if this call should show the UI error.
* @return true if the validity passes false otherwise.
*/
fun testValidity(showUIError: Boolean): Boolean
fun showUIError()
companion object {
const val TEST_REGEXP = 0
const val TEST_NUMERIC = 1
const val TEST_ALPHA = 2
const val TEST_ALPHANUMERIC = 3
const val TEST_EMAIL = 4
const val TEST_CREDITCARD = 5
const val TEST_PHONE = 6
const val TEST_DOMAINNAME = 7
const val TEST_IPADDRESS = 8
const val TEST_WEBURL = 9
const val TEST_NOCHECK = 10
const val TEST_CUSTOM = 11
const val TEST_PERSONNAME = 12
const val TEST_PERSONFULLNAME = 13
const val TEST_DATE = 14
const val TEST_NUMERIC_RANGE = 15
const val TEST_FLOAT_NUMERIC_RANGE = 16
const val TEST_HTTPS_URL = 17
const val TEST_MIN_LENGTH = 18
const val TEST_MULTI_PHONE = 19
const val TEST_PIN_STRENGTH = 20
const val TEST_BG_RANGE = 21
}
}

View file

@ -4,21 +4,23 @@ import android.content.Context
import android.util.AttributeSet
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceViewHolder
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import javax.inject.Inject
class ValidatingEditTextPreference(ctx: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int)
: EditTextPreference(ctx, attrs, defStyleAttr, defStyleRes) {
class ValidatingEditTextPreference(ctx: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : EditTextPreference(ctx, attrs, defStyleAttr, defStyleRes) {
private val validatorParameters: DefaultEditTextValidator.Parameters = obtainValidatorParameters(attrs)
private var validator: DefaultEditTextValidator? = null
@Inject lateinit var profileFunction: ProfileFunction
init {
setOnBindEditTextListener { editText ->
validator = DefaultEditTextValidator(editText, validatorParameters, context)
}
setOnPreferenceChangeListener { _, _ ->
validator?.testValidity(false) ?: true
}
(ctx.applicationContext as HasAndroidInjector).androidInjector().inject(this)
setOnBindEditTextListener { editText -> validator = DefaultEditTextValidator(editText, validatorParameters, context) }
setOnPreferenceChangeListener { _, _ -> validator?.testValidity(false) ?: true }
}
constructor(ctx: Context, attrs: AttributeSet, defStyle: Int)
@ -42,7 +44,8 @@ class ValidatingEditTextPreference(ctx: Context, attrs: AttributeSet, defStyleAt
classType = typedArray.getString(R.styleable.FormEditText_classType),
customRegexp = typedArray.getString(R.styleable.FormEditText_customRegexp),
emptyErrorStringDef = typedArray.getString(R.styleable.FormEditText_emptyErrorString),
customFormat = typedArray.getString(R.styleable.FormEditText_customFormat)).also { params ->
customFormat = typedArray.getString(R.styleable.FormEditText_customFormat)
).also { params ->
if (params.testType == EditTextValidator.TEST_MIN_LENGTH)
params.minLength = typedArray.getInt(R.styleable.FormEditText_minLength, 0)
if (params.testType == EditTextValidator.TEST_NUMERIC_RANGE) {
@ -53,7 +56,25 @@ class ValidatingEditTextPreference(ctx: Context, attrs: AttributeSet, defStyleAt
params.floatminNumber = typedArray.getFloat(R.styleable.FormEditText_floatminNumber, Float.MIN_VALUE)
params.floatmaxNumber = typedArray.getFloat(R.styleable.FormEditText_floatmaxNumber, Float.MAX_VALUE)
}
if (params.testType == EditTextValidator.TEST_BG_RANGE) {
params.minMgdl = typedArray.getInt(R.styleable.FormEditText_minMgdl, Int.MIN_VALUE)
params.maxMgdl = typedArray.getInt(R.styleable.FormEditText_maxMgdl, Int.MAX_VALUE)
}
typedArray.recycle()
}
}
override fun onSetInitialValue(defaultValue: Any?) {
text =
if (validatorParameters.testType == EditTextValidator.TEST_BG_RANGE)
Profile.fromMgdlToUnits(getPersistedString(defaultValue as String?).toDouble(), profileFunction.getUnits()).toString()
else
getPersistedString(defaultValue as String?)
}
override fun persistString(value: String?): Boolean =
if (validatorParameters.testType == EditTextValidator.TEST_BG_RANGE)
super.persistString(Profile.toMgdl(value?.toDouble() ?: 0.0, profileFunction.getUnits()).toString())
else
super.persistString(value)
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.utils.textValidator.validators
import android.widget.EditText
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
class BgRangeValidator(_customErrorMessage: String?, private val min: Int, private val max: Int, private val profileFunction: ProfileFunction) : Validator(_customErrorMessage) {
override fun isValid(editText: EditText): Boolean {
return try {
val value = editText.text.toString().toDouble()
value in Profile.fromMgdlToUnits(min.toDouble(), profileFunction.getUnits())..Profile.fromMgdlToUnits(max.toDouble(), profileFunction.getUnits())
} catch (e: NumberFormatException) {
false
}
}
}

View file

@ -24,6 +24,7 @@
<enum name="minLength" value="18" />
<enum name="multiPhone" value="19" />
<enum name="pinStrength" value="20" />
<enum name="bgRange" value="21" />
</attr>
<attr name="testErrorString" format="string" />
<attr name="emptyErrorString" format="string" />
@ -36,6 +37,8 @@
<attr name="maxNumber" format="integer" />
<attr name="floatminNumber" format="float" />
<attr name="floatmaxNumber" format="float" />
<attr name="minMgdl" format="integer" />
<attr name="maxMgdl" format="integer" />
</declare-styleable>
</resources>