feat: wear mulitple plus buttons

This commit is contained in:
Andries Smit 2022-06-23 20:31:40 +02:00
parent 4f0f918835
commit 4628f3136d
21 changed files with 645 additions and 109 deletions

View file

@ -56,9 +56,9 @@ class CarbsDialog : DialogFragmentWithDate() {
companion object {
private const val FAV1_DEFAULT = 5
private const val FAV2_DEFAULT = 10
private const val FAV3_DEFAULT = 20
const val FAV1_DEFAULT = 5
const val FAV2_DEFAULT = 10
const val FAV3_DEFAULT = 20
}
private var queryingProtection = false

View file

@ -58,9 +58,9 @@ class InsulinDialog : DialogFragmentWithDate() {
companion object {
private const val PLUS1_DEFAULT = 0.5
private const val PLUS2_DEFAULT = 1.0
private const val PLUS3_DEFAULT = 2.0
const val PLUS1_DEFAULT = 0.5
const val PLUS2_DEFAULT = 1.0
const val PLUS3_DEFAULT = 2.0
}
private var queryingProtection = false

View file

@ -12,6 +12,8 @@ import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.dialogs.CarbsDialog
import info.nightscout.androidaps.dialogs.InsulinDialog
import info.nightscout.androidaps.events.EventMobileToWear
import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.toStringShort
@ -694,7 +696,11 @@ class DataHandlerMobile @Inject constructor(
unitsMgdl = profileFunction.getUnits() == GlucoseUnit.MGDL,
bolusPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100),
maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48),
maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0)
maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0),
insulin_button_increment_1 = sp.getDouble(R.string.key_insulin_button_increment_1, InsulinDialog.PLUS1_DEFAULT),
insulin_button_increment_2 = sp.getDouble(R.string.key_insulin_button_increment_2, InsulinDialog.PLUS2_DEFAULT),
carbs_button_increment_1 = sp.getInt(R.string.key_carbs_button_increment_1, CarbsDialog.FAV1_DEFAULT),
carbs_button_increment_2 = sp.getInt(R.string.key_carbs_button_increment_2, CarbsDialog.FAV2_DEFAULT)
)
)
)
@ -1185,4 +1191,4 @@ class DataHandlerMobile @Inject constructor(
@Synchronized private fun sendError(errorMessage: String) {
rxBus.send(EventMobileToWear(EventData.ConfirmAction(rh.gs(R.string.error), errorMessage, returnCommand = EventData.Error(dateUtil.now())))) // ignore return path
}
}
}

View file

@ -208,7 +208,11 @@ sealed class EventData : Event() {
val unitsMgdl: Boolean,
val bolusPercentage: Int,
val maxCarbs: Int,
val maxBolus: Double
val maxBolus: Double,
val insulin_button_increment_1: Double,
val insulin_button_increment_2: Double,
val carbs_button_increment_1: Int,
val carbs_button_increment_2: Int
) : EventData()
@Serializable

View file

@ -53,6 +53,10 @@ android {
buildConfigField "String", "BUILDVERSION", generateGitBuild()
}
buildFeatures {
viewBinding true
}
flavorDimensions "standard"
productFlavors {
full {
@ -98,6 +102,7 @@ dependencies {
implementation "androidx.preference:preference-ktx:$preferencektx_version"
implementation 'androidx.wear:wear:1.2.0'
implementation "androidx.wear.tiles:tiles:1.0.1"
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
compileOnly "com.google.android.wearable:wearable:$wearable_version"
implementation "com.google.android.support:wearable:$wearable_version"

View file

@ -162,6 +162,10 @@ class DataHandlerWear @Inject constructor(
sp.putInt(R.string.key_bolus_wizard_percentage, it.bolusPercentage)
sp.putInt(R.string.key_treatments_safety_max_carbs, it.maxCarbs)
sp.putDouble(R.string.key_treatments_safety_max_bolus, it.maxBolus)
sp.putDouble(R.string.key_insulin_button_increment_1, it.insulin_button_increment_1)
sp.putDouble(R.string.key_insulin_button_increment_2, it.insulin_button_increment_2)
sp.putInt(R.string.key_carbs_button_increment_1, it.carbs_button_increment_1)
sp.putInt(R.string.key_carbs_button_increment_2, it.carbs_button_increment_2)
}
disposable += rxBus
.toObservable(EventData.QuickWizard::class.java)
@ -284,4 +288,4 @@ class DataHandlerWear @Inject constructor(
NotificationManagerCompat.from(context).cancel(DataLayerListenerServiceWear.BOLUS_PROGRESS_NOTIF_ID)
}.start()
}
}
}

View file

@ -10,10 +10,12 @@ import android.view.ViewGroup
import android.widget.ImageView
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.EventWearToMobile
import info.nightscout.androidaps.interaction.utils.EditPlusMinusViewAdapter
import info.nightscout.androidaps.interaction.utils.PlusMinusEditText
import info.nightscout.shared.SafeParse
import info.nightscout.shared.weardata.EventData.ActionBolusPreCheck
import java.text.DecimalFormat
import kotlin.math.roundToInt
class BolusActivity : ViewSelectorActivity() {
@ -33,13 +35,17 @@ class BolusActivity : ViewSelectorActivity() {
override fun getColumnCount(arg0: Int): Int = 2
override fun getRowCount(): Int = 1
val increment1 = (sp.getDouble(R.string.key_insulin_button_increment_1, 0.5) * 10).roundToInt() / 10.0
val increment2 = (sp.getDouble(R.string.key_insulin_button_increment_2, 1.0) * 10).roundToInt() / 10.0
override fun instantiateItem(container: ViewGroup, row: Int, col: Int): Any {
val view: View
if (col == 0) {
view = getInflatedPlusMinusView(container)
view = EditPlusMinusViewAdapter.getInflatedPlusMinusView(sp, applicationContext, container, true).root
val initValue = if (editInsulin != null) SafeParse.stringToDouble(editInsulin?.editText?.text.toString()) else 0.0
val maxBolus = sp.getDouble(getString(R.string.key_treatments_safety_max_bolus), 3.0)
editInsulin = PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, initValue, 0.0, maxBolus, 0.1, DecimalFormat("#0.0"), false)
val buttons = listOf(Pair(R.id.plusbutton, 0.1), Pair(R.id.plusbutton2, increment1), Pair(R.id.plusbutton3, increment2)) // When taken form phone settings round.
editInsulin = PlusMinusEditText(view, R.id.amountfield, buttons, R.id.minusbutton, initValue, 0.0, maxBolus, 0.1, DecimalFormat("#0.0"), false)
setLabelToPlusMinusView(view, getString(R.string.action_insulin))
container.addView(view)
view.requestFocus()
@ -62,4 +68,4 @@ class BolusActivity : ViewSelectorActivity() {
override fun isViewFromObject(view: View, `object`: Any): Boolean = view === `object`
}
}
}

View file

@ -30,19 +30,23 @@ class CarbActivity : ViewSelectorActivity() {
private inner class MyGridViewPagerAdapter : GridPagerAdapter() {
val increment1 = sp.getInt(R.string.key_carbs_button_increment_1, 5).toDouble()
val increment2 = sp.getInt(R.string.key_carbs_button_increment_2, 10).toDouble()
override fun getColumnCount(arg0: Int): Int = 2
override fun getRowCount(): Int = 1
override fun instantiateItem(container: ViewGroup, row: Int, col: Int): Any {
val view: View
if (col == 0) {
view = getInflatedPlusMinusView(container)
view = getInflatedPlusMinusView(container, true)
var def = 0.0
if (editCarbs != null) {
def = SafeParse.stringToDouble(editCarbs?.editText?.text.toString())
}
val maxCarbs = sp.getInt(getString(R.string.key_treatments_safety_max_carbs), 48)
editCarbs = PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), true)
val buttons = listOf(Pair(R.id.plusbutton, 1.0), Pair(R.id.plusbutton2, increment1), Pair(R.id.plusbutton3, increment2))
editCarbs = PlusMinusEditText(view, R.id.amountfield, buttons, R.id.minusbutton, def, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), true)
setLabelToPlusMinusView(view, getString(R.string.action_carbs))
container.addView(view)
view.requestFocus()
@ -69,4 +73,4 @@ class CarbActivity : ViewSelectorActivity() {
override fun isViewFromObject(view: View, `object`: Any): Boolean = view === `object`
}
}
}

View file

@ -36,15 +36,19 @@ class ECarbActivity : ViewSelectorActivity() {
override fun getColumnCount(arg0: Int): Int = 4
override fun getRowCount(): Int = 1
private val increment1 = sp.getInt(R.string.key_carbs_button_increment_1, 5).toDouble()
private val increment2 = sp.getInt(R.string.key_carbs_button_increment_2, 10).toDouble()
override fun instantiateItem(container: ViewGroup, row: Int, col: Int): Any {
return if (col == 0) {
val view = getInflatedPlusMinusView(container)
val view = getInflatedPlusMinusView(container, true)
var def = 0.0
if (editCarbs != null) {
def = stringToDouble(editCarbs?.editText?.text.toString())
}
val maxCarbs = sp.getInt(getString(R.string.key_treatments_safety_max_carbs), 48)
editCarbs = PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), true)
val buttons = listOf(Pair(R.id.plusbutton, 1.0), Pair(R.id.plusbutton2, increment1), Pair(R.id.plusbutton3, increment2))
editCarbs = PlusMinusEditText(view, R.id.amountfield, buttons, R.id.minusbutton, def, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), true)
setLabelToPlusMinusView(view, getString(R.string.action_carbs))
container.addView(view)
view.requestFocus()
@ -98,4 +102,4 @@ class ECarbActivity : ViewSelectorActivity() {
override fun isViewFromObject(view: View, `object`: Any): Boolean = view === `object`
}
}
}

View file

@ -15,6 +15,7 @@ import info.nightscout.shared.SafeParse.stringToDouble
import info.nightscout.shared.SafeParse.stringToInt
import info.nightscout.shared.weardata.EventData.ActionBolusPreCheck
import java.text.DecimalFormat
import kotlin.math.roundToInt
class TreatmentActivity : ViewSelectorActivity() {
@ -35,23 +36,30 @@ class TreatmentActivity : ViewSelectorActivity() {
override fun getColumnCount(arg0: Int): Int = 3
override fun getRowCount(): Int = 1
val incrementInsulin1 = (sp.getDouble(R.string.key_insulin_button_increment_1, 0.5) * 10).roundToInt() / 10.0
val incrementInsulin2 = (sp.getDouble(R.string.key_insulin_button_increment_2, 1.0) * 10).roundToInt() / 10.0
val incrementCarbs1 = sp.getInt(R.string.key_carbs_button_increment_1, 5).toDouble()
val incrementCarbs2 = sp.getInt(R.string.key_carbs_button_increment_2, 10).toDouble()
override fun instantiateItem(container: ViewGroup, row: Int, col: Int): Any {
return if (col == 0) {
val view = getInflatedPlusMinusView(container)
val view = getInflatedPlusMinusView(container, true)
var def = 0.0
if (editInsulin != null) def = stringToDouble(editInsulin?.editText?.text.toString())
val maxBolus = sp.getDouble(getString(R.string.key_treatments_safety_max_bolus), 3.0)
editInsulin = PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0.0, maxBolus, 0.1, DecimalFormat("#0.0"), false)
val buttons = listOf(Pair(R.id.plusbutton, 0.1), Pair(R.id.plusbutton2, incrementInsulin1), Pair(R.id.plusbutton3, incrementInsulin2))
editInsulin = PlusMinusEditText(view, R.id.amountfield, buttons, R.id.minusbutton, def, 0.0, maxBolus, 0.1, DecimalFormat("#0.0"), false)
setLabelToPlusMinusView(view, getString(R.string.action_insulin))
container.addView(view)
view.requestFocus()
view
} else if (col == 1) {
val view = getInflatedPlusMinusView(container)
val view = getInflatedPlusMinusView(container, true)
var def = 0.0
val maxCarbs = sp.getInt(getString(R.string.key_treatments_safety_max_carbs), 48)
if (editCarbs != null) def = stringToDouble(editCarbs?.editText?.text.toString())
editCarbs = PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false)
val buttons = listOf(Pair(R.id.plusbutton, 1.0), Pair(R.id.plusbutton2, incrementCarbs1), Pair(R.id.plusbutton3, incrementCarbs2))
editCarbs = PlusMinusEditText(view, R.id.amountfield, buttons, R.id.minusbutton, def, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false)
setLabelToPlusMinusView(view, getString(R.string.action_carbs))
container.addView(view)
view
@ -79,4 +87,4 @@ class TreatmentActivity : ViewSelectorActivity() {
override fun isViewFromObject(view: View, `object`: Any): Boolean = view === `object`
}
}
}

View file

@ -73,13 +73,16 @@ open class ViewSelectorActivity : DaggerActivity() {
}
}
fun getInflatedPlusMinusView(container: ViewGroup?): View =
when (sp.getInt(R.string.key_input_design, 1)) {
2 -> LayoutInflater.from(applicationContext).inflate(R.layout.action_editplusminus_item_quickrighty, container, false)
3 -> LayoutInflater.from(applicationContext).inflate(R.layout.action_editplusminus_item_quicklefty, container, false)
fun getInflatedPlusMinusView(container: ViewGroup?, mulitple: Boolean = false): View {
val layoutRight = if (mulitple) R.layout.action_editplusminus_item_quickrighty_plus else R.layout.action_editplusminus_item_quickrighty
val layoutLeft = if (mulitple) R.layout.action_editplusminus_item_quicklefty_plus else R.layout.action_editplusminus_item_quicklefty
return when (sp.getInt(R.string.key_input_design, 1)) {
2 -> LayoutInflater.from(applicationContext).inflate(layoutRight, container, false)
3 -> LayoutInflater.from(applicationContext).inflate(layoutLeft, container, false)
4 -> LayoutInflater.from(applicationContext).inflate(R.layout.action_editplusminus_item_viktoria, container, false)
else -> LayoutInflater.from(applicationContext).inflate(R.layout.action_editplusminus_item, container, false)
}
}
fun setLabelToPlusMinusView(view: View, labelText: String?) {
val textView = view.findViewById<TextView>(R.id.label)
@ -89,4 +92,4 @@ open class ViewSelectorActivity : DaggerActivity() {
fun showToast(context: Context?, text: Int) {
Toast.makeText(context, getString(text), Toast.LENGTH_LONG).show()
}
}
}

View file

@ -35,16 +35,19 @@ class WizardActivity : ViewSelectorActivity() {
override fun getColumnCount(arg0: Int): Int = if (hasPercentage) 3 else 2
override fun getRowCount(): Int = 1
private val increment1 = sp.getInt(R.string.key_carbs_button_increment_1, 5).toDouble()
private val increment2 = sp.getInt(R.string.key_carbs_button_increment_2, 10).toDouble()
override fun instantiateItem(container: ViewGroup, row: Int, col: Int): Any {
return if (col == 0) {
val view = getInflatedPlusMinusView(container)
val view = getInflatedPlusMinusView(container, true)
val maxCarbs = sp.getInt(getString(R.string.key_treatments_safety_max_carbs), 48)
val buttons = listOf(Pair(R.id.plusbutton, 1.0), Pair(R.id.plusbutton2, increment1), Pair(R.id.plusbutton3, increment2))
editCarbs = if (editCarbs == null) {
PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false)
PlusMinusEditText(view, R.id.amountfield, buttons, R.id.minusbutton, 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false)
} else {
val def = SafeParse.stringToDouble(editCarbs?.editText?.text.toString())
PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false)
PlusMinusEditText(view, R.id.amountfield, buttons, R.id.minusbutton, def, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false)
}
setLabelToPlusMinusView(view, getString(R.string.action_carbs))
container.addView(view)
@ -84,4 +87,4 @@ class WizardActivity : ViewSelectorActivity() {
override fun isViewFromObject(view: View, `object`: Any): Boolean = view === `object`
}
}
}

View file

@ -0,0 +1,78 @@
package info.nightscout.androidaps.interaction.utils
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.ActionEditplusminusItemBinding
import info.nightscout.androidaps.databinding.ActionEditplusminusItemQuickleftyPlusBinding
import info.nightscout.androidaps.databinding.ActionEditplusminusItemQuickleftyBinding
import info.nightscout.androidaps.databinding.ActionEditplusminusItemQuickrightyBinding
import info.nightscout.androidaps.databinding.ActionEditplusminusItemQuickrightyPlusBinding
import info.nightscout.androidaps.databinding.ActionEditplusminusItemViktoriaBinding
import info.nightscout.shared.sharedPreferences.SP
/**
* NumberPickerViewAdapter binds both NumberPickerLayoutBinding and NumberPickerLayoutVerticalBinding shared attributes to one common view adapter.
* Requires at least one of the ViewBinding as a parameter. Recommended to use the factory object to create the binding.
*/
class EditPlusMinusViewAdapter(
val eS: ActionEditplusminusItemBinding?,
val eQLP: ActionEditplusminusItemQuickleftyPlusBinding?,
val eQL: ActionEditplusminusItemQuickleftyBinding?,
val eQRP: ActionEditplusminusItemQuickleftyPlusBinding?,
val qQR: ActionEditplusminusItemQuickrightyBinding?,
val nQRP: ActionEditplusminusItemQuickrightyPlusBinding?,
val eV: ActionEditplusminusItemViktoriaBinding?
) {
init {
if (eS == null && eQLP == null && eQL == null && eQRP == null && qQR == null && nQRP == null && eV == null) {
throw IllegalArgumentException("Require at least on Binding parameter")
}
}
val amountField =
eS?.amountfield ?: eQLP?.amountfield ?: eQL?.amountfield ?: eQRP?.amountfield ?: qQR?.amountfield ?: nQRP?.amountfield ?: eV?.amountfield
?: throw IllegalArgumentException("Missing require View Binding parameter display")
val minusButton =
eS?.minusbutton ?: eQLP?.minusbutton ?: eQL?.minusbutton ?: eQRP?.minusbutton ?: qQR?.minusbutton ?: nQRP?.minusbutton ?: eV?.minusbutton
?: throw IllegalArgumentException("Missing require View Binding parameter display")
val plusButton =
eS?.plusbutton ?: eQLP?.plusbutton ?: eQL?.plusbutton ?: eQRP?.plusbutton ?: qQR?.plusbutton ?: nQRP?.plusbutton ?: eV?.plusbutton
?: throw IllegalArgumentException("Missing require View Binding parameter display")
val label =
eS?.label ?: eQLP?.label ?: eQL?.label ?: eQRP?.label ?: qQR?.label ?: nQRP?.label ?: eV?.label
?: throw IllegalArgumentException("Missing require View Binding parameter display")
val plusButton2 = eQLP?.plusbutton2 ?: eQRP?.plusbutton2
val plusButton3 = eQLP?.plusbutton3 ?: eQRP?.plusbutton3
val root =
eS?.root ?: eQLP?.root ?: eQL?.root ?: eQRP?.root ?: qQR?.root ?: nQRP?.root ?: eV?.root
?: throw IllegalArgumentException("Missing require View Binding parameter display")
companion object {
fun getInflatedPlusMinusView(sp: SP, context: Context, container: ViewGroup?, mulitple: Boolean = false): EditPlusMinusViewAdapter {
val inflater = LayoutInflater.from(context)
val bindLayout = ActionEditplusminusItemBinding.inflate(inflater, container, false)
val binding = EditPlusMinusViewAdapter(bindLayout, null, null, null, null, null, null)
return binding
// val layoutRight = if (mulitple) R.layout.action_editplusminus_item_quickrighty_plus else R.layout.action_editplusminus_item_quickrighty
// val layoutLeft = if (mulitple) R.layout.action_editplusminus_item_quicklefty_plus else R.layout.action_editplusminus_item_quicklefty
// return when (sp.getInt(R.string.key_input_design, 1)) {
// 2 -> LayoutInflater.from(applicationContext).inflate(layoutRight, container, false)
// 3 -> LayoutInflater.from(applicationContext).inflate(layoutLeft, container, false)
// 4 -> LayoutInflater.from(applicationContext).inflate(R.layout.action_editplusminus_item_viktoria, container, false)
// else -> LayoutInflater.from(applicationContext).inflate(R.layout.action_editplusminus_item, container, false)
// }
}
// fun getBinding(bindLayout: NumberPickerLayoutBinding): NumberPickerViewAdapter {
// return NumberPickerViewAdapter(bindLayout, null)
// }
//
// fun getBinding(bindLayout: NumberPickerLayoutVerticalBinding): NumberPickerViewAdapter {
// return NumberPickerViewAdapter(null, bindLayout)
// }
}
}

View file

@ -1,67 +1,82 @@
package info.nightscout.androidaps.interaction.utils
import android.os.Handler
import kotlin.jvm.JvmOverloads
import android.view.View.OnTouchListener
import android.view.View.OnGenericMotionListener
import android.widget.TextView
import android.view.MotionEvent
import android.os.Looper
import android.os.Message
import android.annotation.SuppressLint
import android.content.Context
import android.os.*
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.widget.ImageView
import android.view.View.OnGenericMotionListener
import android.view.View.OnTouchListener
import android.widget.TextView
import androidx.appcompat.widget.AppCompatButton
import androidx.core.view.InputDeviceCompat
import androidx.core.view.MotionEventCompat
import java.text.DecimalFormat
import java.text.NumberFormat
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import kotlin.Pair
/**
* Created by mike on 28.06.2016.
*/
class PlusMinusEditText @JvmOverloads constructor(
@SuppressLint("SetTextI18n") class PlusMinusEditText @JvmOverloads constructor(
view: View,
editTextID: Int,
plusID: Int,
private var plusButtons: List<Pair<Int, Double>>,
minusID: Int,
initValue: Double,
minValue: Double,
maxValue: Double,
step: Double,
formatter: NumberFormat,
allowZero: Boolean,
roundRobin: Boolean = false
private val minValue: Double,
private val maxValue: Double,
private val stepGeneral: Double,
private val formatter: NumberFormat,
private val allowZero: Boolean,
private val roundRobin: Boolean = false,
) : View.OnKeyListener, OnTouchListener, View.OnClickListener, OnGenericMotionListener {
constructor(
view: View,
editTextID: Int,
plusID: Int,
minusID: Int,
initValue: Double,
minValue: Double,
maxValue: Double,
stepGeneral: Double,
formatter: NumberFormat,
allowZero: Boolean,
roundRobin: Boolean = false
) : this(view, editTextID, listOf(Pair(plusID, stepGeneral)), minusID, initValue, minValue, maxValue, stepGeneral, formatter, allowZero, roundRobin)
var editText: TextView
private set
private var minusImage: ImageView
private var plusImage: ImageView
private var minusImage: View
private var plusImage1: View
private var plusImage2: AppCompatButton? = null
private var plusImage3: AppCompatButton? = null
private var value: Double
private var minValue: Double
private var maxValue: Double
private var step: Double
private var formatter: NumberFormat
private var allowZero: Boolean
private var roundRobin: Boolean
private val context: Context
private var mChangeCounter = 0
private var mLastChange: Long = 0
private val mHandler: Handler
private var mUpdater: ScheduledExecutorService? = null
private inner class UpdateCounterTask(private val mInc: Boolean) : Runnable {
private inner class UpdateCounterTask(private val mInc: Boolean, private val step: Double) : Runnable {
private var repeated = 0
private var multiplier = 1
override fun run() {
val msg = Message()
val doubleLimit = 5
if (repeated % doubleLimit == 0) multiplier *= 2
repeated++
msg.arg1 = multiplier
msg.arg2 = repeated
val multipleButtons = mInc && (plusImage2 != null || plusImage3 != null)
if (!multipleButtons && repeated % doubleLimit == 0) multiplier *= 2
val bundle = Bundle()
bundle.putDouble("step", step)
bundle.putInt("multiplier", multiplier)
msg.data = bundle
if (mInc) {
msg.what = MSG_INC
} else {
@ -71,7 +86,7 @@ class PlusMinusEditText @JvmOverloads constructor(
}
}
private fun inc(multiplier: Int) {
private fun inc(multiplier: Int, step: Double, vibrate: Boolean = true) {
value += step * multiplier
if (value > maxValue) {
if (roundRobin) {
@ -82,9 +97,10 @@ class PlusMinusEditText @JvmOverloads constructor(
}
}
updateEditText()
if (vibrate) vibrateDevice()
}
private fun dec(multiplier: Int) {
private fun dec(multiplier: Int, step: Double, vibrate: Boolean = true) {
value -= step * multiplier
if (value < minValue) {
if (roundRobin) {
@ -95,20 +111,39 @@ class PlusMinusEditText @JvmOverloads constructor(
}
}
updateEditText()
if (vibrate) vibrateDevice()
}
fun vibrateDevice() {
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager =
context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator
} else {
@Suppress("DEPRECATION")
context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
@Suppress("DEPRECATION")
vibrator.vibrate(10)
}
}
private fun updateEditText() {
if (value == 0.0 && !allowZero) editText.text = "" else editText.text = formatter.format(value)
}
private fun startUpdating(inc: Boolean) {
private fun startUpdating(inc: Boolean, step: Double) {
if (mUpdater != null) {
return
}
mUpdater = Executors.newSingleThreadScheduledExecutor()
mUpdater?.scheduleAtFixedRate(
UpdateCounterTask(inc), 200, 200,
UpdateCounterTask(inc, step), 200, 200,
TimeUnit.MILLISECONDS
)
}
@ -118,12 +153,30 @@ class PlusMinusEditText @JvmOverloads constructor(
mUpdater = null
}
private fun getStep(v: View): Double {
return when (v) {
plusImage1 -> plusButtons[0].second
plusImage2 -> plusButtons[1].second
plusImage3 -> plusButtons[2].second
else -> stepGeneral
}
}
private fun isIncrement(v: View): Boolean {
return when (v) {
plusImage1 -> true
plusImage2 -> true
plusImage3 -> true
else -> false
}
}
override fun onClick(v: View) {
if (mUpdater == null) {
if (v === plusImage) {
inc(1)
if (isIncrement(v)) {
inc(1, getStep(v))
} else {
dec(1)
dec(1, getStep(v))
}
}
}
@ -135,7 +188,7 @@ class PlusMinusEditText @JvmOverloads constructor(
if (isKeyOfInterest && isReleased) {
stopUpdating()
} else if (isKeyOfInterest && isPressed) {
startUpdating(v === plusImage)
startUpdating(isIncrement(v), stepGeneral)
}
return false
}
@ -146,7 +199,7 @@ class PlusMinusEditText @JvmOverloads constructor(
if (isReleased) {
stopUpdating()
} else if (isPressed) {
startUpdating(v === plusImage)
startUpdating(isIncrement(v), getStep(v))
}
return false
}
@ -158,9 +211,9 @@ class PlusMinusEditText @JvmOverloads constructor(
val dynamicMultiplier = if (mChangeCounter < THRESHOLD_COUNTER) 1 else if (mChangeCounter < THRESHOLD_COUNTER_LONG) 2 else 4
val delta = -ev.getAxisValue(MotionEventCompat.AXIS_SCROLL)
if (delta > 0) {
inc(dynamicMultiplier)
inc(dynamicMultiplier, stepGeneral, false)
} else {
dec(dynamicMultiplier)
dec(dynamicMultiplier, stepGeneral, false)
}
mLastChange = System.currentTimeMillis()
mChangeCounter++
@ -179,39 +232,59 @@ class PlusMinusEditText @JvmOverloads constructor(
}
init {
context = view.context
editText = view.findViewById(editTextID)
minusImage = view.findViewById(minusID)
plusImage = view.findViewById(plusID)
plusImage1 = view.findViewById(plusButtons.first().first)
val format = DecimalFormat("#.#")
plusButtons.getOrNull(1)?.let {
plusImage2 = view.findViewById(it.first)
plusImage2?.text = "+${format.format(it.second).replaceFirst("^0+(?!$)".toRegex(), "")}"
plusImage2?.visibility = View.VISIBLE
}
plusButtons.getOrNull(2)?.let {
plusImage3 = view.findViewById(it.first)
plusImage3?.text = "+${format.format(it.second).replaceFirst("^0+(?!$)".toRegex(), "")}"
plusImage3?.visibility = View.VISIBLE
}
value = initValue
this.minValue = minValue
this.maxValue = maxValue
this.step = step
this.formatter = formatter
this.allowZero = allowZero
this.roundRobin = roundRobin
mHandler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
val multiplier = msg.data.getInt("multiplier")
val step = msg.data.getDouble("step")
when (msg.what) {
MSG_INC -> {
inc(msg.arg1)
inc(multiplier, step)
return
}
MSG_DEC -> {
dec(msg.arg1)
dec(multiplier, step)
return
}
}
super.handleMessage(msg)
}
}
editText.showSoftInputOnFocus = false
editText.setTextIsSelectable(false)
minusImage.setOnTouchListener(this)
minusImage.setOnKeyListener(this)
minusImage.setOnClickListener(this)
plusImage.setOnTouchListener(this)
plusImage.setOnKeyListener(this)
plusImage.setOnClickListener(this)
plusImage1.setOnTouchListener(this)
plusImage1.setOnKeyListener(this)
plusImage1.setOnClickListener(this)
plusImage2?.setOnTouchListener(this)
plusImage2?.setOnKeyListener(this)
plusImage2?.setOnClickListener(this)
plusImage3?.setOnTouchListener(this)
plusImage3?.setOnKeyListener(this)
plusImage3?.setOnClickListener(this)
editText.setOnGenericMotionListener(this)
updateEditText()
}
}

View file

@ -7,7 +7,7 @@
android:gravity="center"
android:orientation="horizontal">
<ImageView
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/minusbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -53,7 +53,7 @@
</LinearLayout>
<ImageView
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/plusbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -65,4 +65,3 @@
android:tint="@color/white" />
</LinearLayout>

View file

@ -7,16 +7,52 @@
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/plusbutton"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:contentDescription="@string/increment"
android:src="@drawable/ic_action_add"
android:tint="@color/white" />
android:layout_marginEnd="-8dp"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/plusbutton3"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:gravity="center"
android:textColor="@color/white"
android:textSize="25sp"
android:visibility="gone"
tools:text="+10" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/plusbutton2"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="start"
android:layout_marginVertical="7dp"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:gravity="center"
android:textColor="@color/white"
android:textSize="25sp"
android:visibility="gone"
tools:text="+5" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/plusbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:contentDescription="@string/increment"
android:src="@drawable/ic_action_add"
android:tint="@color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
@ -52,7 +88,7 @@
tools:ignore="LabelFor"
tools:text="label" />
<ImageView
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/minusbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View file

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/center"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/amountfield"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="-10dp"
android:layout_marginHorizontal="10dp"
android:cursorVisible="false"
android:inputType="numberDecimal"
android:textColor="@color/white"
android:textSize="40sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="@+id/value_background"
app:layout_constraintStart_toStartOf="@+id/value_background"
app:layout_constraintTop_toTopOf="@+id/value_background"
tools:text="123" />
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-8dp"
android:layout_below="@+id/amountfield"
android:gravity="center"
android:labelFor="@+id/amountfield"
android:textColor="@color/white"
android:textSize="25sp"
app:layout_constraintEnd_toEndOf="@+id/amountfield"
app:layout_constraintStart_toStartOf="@+id/amountfield"
app:layout_constraintTop_toBottomOf="@id/amountfield"
tools:ignore="LabelFor"
tools:text="label" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/minusbutton"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:contentDescription="@string/decrement"
android:src="@drawable/ic_action_minus"
android:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="145"
app:layout_constraintCircleRadius="70dp"
app:layout_constraintLeft_toLeftOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/plusbutton3"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:gravity="center"
android:textColor="@color/white"
android:textSize="25sp"
android:visibility="visible"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="325"
app:layout_constraintCircleRadius="70dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="+10" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/plusbutton2"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:gravity="center"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/plusbutton"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="270"
app:layout_constraintCircleRadius="70dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/plusbutton3"
tools:text="+5" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/value_background"
android:layout_width="108dp"
android:layout_height="108dp"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
android:background="@drawable/circle"
android:backgroundTint="@color/gray_700"
android:contentDescription="@string/increment"
app:layout_constraintBottom_toTopOf="@+id/minusbutton"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="55"
app:layout_constraintCircleRadius="40dp"
app:layout_constraintRight_toLeftOf="@id/plusbutton">
</androidx.appcompat.widget.AppCompatImageView>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/plusbutton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:contentDescription="@string/increment"
android:src="@drawable/ic_action_add"
android:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="215"
app:layout_constraintCircleRadius="70dp"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -41,7 +41,7 @@
tools:ignore="LabelFor"
tools:text="label" />
<ImageView
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/minusbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -54,15 +54,52 @@
</LinearLayout>
<ImageView
android:id="@+id/plusbutton"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:contentDescription="@string/increment"
android:src="@drawable/ic_action_add"
android:tint="@color/white" />
android:layout_marginStart="-8dp"
android:orientation="vertical"
tools:visibility="visible">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/plusbutton3"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:gravity="center"
android:textColor="@color/white"
android:textSize="25sp"
android:visibility="gone"
tools:text="+10" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/plusbutton2"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="end"
android:layout_marginVertical="7dp"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:gravity="center"
android:textColor="@color/white"
android:visibility="gone"
android:textSize="25sp"
tools:text="+5" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/plusbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:contentDescription="@string/increment"
android:src="@drawable/ic_action_add"
android:tint="@color/white" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/center"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/amountfield"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="-10dp"
android:layout_marginHorizontal="10dp"
android:cursorVisible="false"
android:inputType="numberDecimal"
android:textColor="@color/white"
android:textSize="40sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="@+id/value_background"
app:layout_constraintStart_toStartOf="@+id/value_background"
app:layout_constraintTop_toTopOf="@+id/value_background"
tools:text="123" />
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-8dp"
android:layout_below="@+id/amountfield"
android:gravity="center"
android:labelFor="@+id/amountfield"
android:textColor="@color/white"
android:textSize="25sp"
app:layout_constraintEnd_toEndOf="@+id/amountfield"
app:layout_constraintStart_toStartOf="@+id/amountfield"
app:layout_constraintTop_toBottomOf="@id/amountfield"
tools:ignore="LabelFor"
tools:text="label" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/minusbutton"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:contentDescription="@string/decrement"
android:src="@drawable/ic_action_minus"
android:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="215"
app:layout_constraintCircleRadius="70dp"
app:layout_constraintLeft_toLeftOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/plusbutton3"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:gravity="center"
android:textColor="@color/white"
android:textSize="25sp"
android:visibility="visible"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="35"
app:layout_constraintCircleRadius="70dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="+10" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/plusbutton2"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:gravity="center"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/plusbutton"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="90"
app:layout_constraintCircleRadius="70dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/plusbutton3"
tools:text="+5" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/value_background"
android:layout_width="108dp"
android:layout_height="108dp"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
android:background="@drawable/circle"
android:backgroundTint="@color/gray_700"
android:contentDescription="@string/increment"
app:layout_constraintBottom_toTopOf="@+id/minusbutton"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="305"
app:layout_constraintCircleRadius="40dp"
app:layout_constraintRight_toLeftOf="@id/plusbutton">
</androidx.appcompat.widget.AppCompatImageView>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/plusbutton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:background="@drawable/circle"
android:backgroundTint="@color/white"
android:contentDescription="@string/increment"
android:src="@drawable/ic_action_add"
android:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintCircle="@+id/center"
app:layout_constraintCircleAngle="145"
app:layout_constraintCircleRadius="70dp"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -25,7 +25,7 @@
android:layout_gravity="start|center_vertical|center_horizontal"
android:orientation="vertical">
<ImageView
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/plusbutton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -50,7 +50,7 @@
android:textSize="45sp"
tools:text="112" />
<ImageView
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/minusbutton"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View file

@ -206,6 +206,10 @@
<string name="key_dark" translatable="false">dark</string>
<string name="key_input_design" translatable="false">input_design</string>
<string name="key_complication_tap_action" translatable="false">complication_tap_action</string>
<string name="key_insulin_button_increment_1" translatable="false">insulin_button_increment_1</string>
<string name="key_insulin_button_increment_2" translatable="false">insulin_button_increment_2</string>
<string name="key_carbs_button_increment_1" translatable="false">carbs_button_increment_1</string>
<string name="key_carbs_button_increment_2" translatable="false">carbs_button_increment_2</string>
<string name="increment">increment</string>
<string name="decrement">decrement</string>
<string name="first_char_high">H</string>