mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-04-23 11:22:10 +00:00
fix: Allow to use other volumes units than Milliliter (#156)
This commit is contained in:
parent
fa56970fb6
commit
497cc58057
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -40,6 +40,7 @@ jobs:
|
||||
- name: Zip artifacts
|
||||
run: zip -r assemble.zip . -i '**/build/*.apk' '**/build/*.aab' '**/build/*.aar' '**/build/*.so'
|
||||
- name: Upload artifacts
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: assemble
|
||||
|
@ -40,6 +40,13 @@ object Settings {
|
||||
*/
|
||||
const val WATER_INTAKE_SIZE = "com.dzeio.open-health.water.size"
|
||||
|
||||
/**
|
||||
* volume unit used
|
||||
*/
|
||||
const val VOLUME_UNIT = "com.dzeio.open-health.volume-unit"
|
||||
|
||||
const val WATER_INTAKE_DAILY_GOAL = "com.dzeio.open-health.water.daily"
|
||||
|
||||
/**
|
||||
* the default value for the setting above
|
||||
*/
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.dzeio.openhealth.ui.home
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
@ -10,21 +9,22 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.dzeio.charts.Entry
|
||||
import com.dzeio.charts.axis.Line
|
||||
import com.dzeio.charts.series.LineSerie
|
||||
import com.dzeio.openhealth.BuildConfig
|
||||
import com.dzeio.openhealth.R
|
||||
import com.dzeio.openhealth.core.BaseFragment
|
||||
import com.dzeio.openhealth.data.water.Water
|
||||
import com.dzeio.openhealth.data.weight.Weight
|
||||
import com.dzeio.openhealth.databinding.FragmentHomeBinding
|
||||
import com.dzeio.openhealth.ui.weight.WeightDialog
|
||||
import com.dzeio.openhealth.units.Units
|
||||
import com.dzeio.openhealth.utils.ChartUtils
|
||||
import com.dzeio.openhealth.utils.DrawUtils
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@ -34,10 +34,6 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
||||
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentHomeBinding
|
||||
get() = FragmentHomeBinding::inflate
|
||||
|
||||
private val settings: SharedPreferences by lazy {
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
// Bindings
|
||||
@ -63,14 +59,14 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
||||
viewModel.updateWater(water)
|
||||
}
|
||||
}
|
||||
val waterUnit =
|
||||
Units.Volume.find(settings.getString("water_unit", "milliliter") ?: "Milliliter")
|
||||
|
||||
binding.fragmentHomeWaterTotal.text =
|
||||
String.format(
|
||||
resources.getString(waterUnit.unit),
|
||||
viewModel.dailyWaterIntake
|
||||
)
|
||||
viewModel.waterUnit.observe(viewLifecycleOwner) {
|
||||
updateWaterTotal()
|
||||
}
|
||||
|
||||
viewModel.dailyWaterIntake.observe(viewLifecycleOwner) {
|
||||
updateWaterTotal()
|
||||
}
|
||||
|
||||
binding.fragmentHomeWaterRemove.setOnClickListener {
|
||||
val water = viewModel.water.value
|
||||
@ -95,12 +91,21 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
||||
}
|
||||
|
||||
binding.weightGraph.apply {
|
||||
val serie = LineSerie(this)
|
||||
LineSerie(this)
|
||||
animator.enabled = false
|
||||
xAxis.dataWidth = 2.4192e+9
|
||||
xAxis.scrollEnabled = true
|
||||
xAxis.apply {
|
||||
dataWidth = 2.4192e+9
|
||||
scrollEnabled = true
|
||||
onValueFormat = onValueFormat@{
|
||||
val formatter = DateFormat.getDateTimeInstance(
|
||||
DateFormat.SHORT,
|
||||
DateFormat.SHORT,
|
||||
Locale.getDefault()
|
||||
)
|
||||
return@onValueFormat formatter.format(Date(it.toLong()))
|
||||
}
|
||||
}
|
||||
ChartUtils.materielTheme(this, requireView())
|
||||
series = arrayListOf(serie)
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
@ -116,75 +121,84 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
||||
|
||||
// Update the water intake Graph when the water intake changes
|
||||
viewModel.water.observe(viewLifecycleOwner) {
|
||||
if (it != null) {
|
||||
updateWater(it.value)
|
||||
} else {
|
||||
updateWater(0)
|
||||
}
|
||||
updateWaterGraph()
|
||||
}
|
||||
|
||||
// Update the steps Graph when the steps count changes
|
||||
viewModel.steps.observe(viewLifecycleOwner) {
|
||||
binding.stepsCurrent.text = it.toString()
|
||||
binding.stepsCurrent.text = getString(
|
||||
R.string.steps_count,
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
// Update the steps Graph when the goal changes
|
||||
viewModel.stepsGoal.observe(viewLifecycleOwner) {
|
||||
if (it == null) {
|
||||
binding.stepsTotal.text = ""
|
||||
binding.stepsTotal.visibility = View.GONE
|
||||
return@observe
|
||||
}
|
||||
binding.stepsTotal.text = it.toString()
|
||||
binding.stepsTotal.visibility = View.VISIBLE
|
||||
binding.stepsTotal.text = getString(
|
||||
R.string.steps_count,
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
// update the graph when the weight changes
|
||||
viewModel.weights.observe(viewLifecycleOwner) {
|
||||
if (it != null) {
|
||||
updateGraph(it)
|
||||
}
|
||||
updateWeightGraph()
|
||||
}
|
||||
|
||||
// update the graph when the goal weight change
|
||||
viewModel.goalWeight.observe(viewLifecycleOwner) {
|
||||
if (viewModel.weights.value != null) updateGraph(viewModel.weights.value!!)
|
||||
updateWeightGraph()
|
||||
}
|
||||
|
||||
// update the graph when the weight unit change
|
||||
viewModel.massUnit.observe(viewLifecycleOwner) {
|
||||
if (viewModel.weights.value != null) updateGraph(viewModel.weights.value!!)
|
||||
updateWeightGraph()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that update the graph for the weight
|
||||
*/
|
||||
private fun updateGraph(list: List<Weight>) {
|
||||
val chart = binding.weightGraph
|
||||
private fun updateWeightGraph() {
|
||||
val values = viewModel.weights.value ?: arrayListOf()
|
||||
val goal = viewModel.goalWeight.value
|
||||
val unit = viewModel.massUnit.value
|
||||
|
||||
if (unit == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val chart = binding.weightGraph.apply {
|
||||
yAxis.onValueFormat = { getString(unit.unit, unit.fromKilogram(it)) }
|
||||
}
|
||||
val serie = chart.series[0] as LineSerie
|
||||
|
||||
val entries: ArrayList<Entry> = arrayListOf()
|
||||
|
||||
list.forEach {
|
||||
entries.add(
|
||||
Entry(
|
||||
it.timestamp.toDouble(),
|
||||
it.weight
|
||||
)
|
||||
val entries: ArrayList<Entry> = values.map {
|
||||
Entry(
|
||||
it.timestamp.toDouble(),
|
||||
it.weight * unit.modifier
|
||||
)
|
||||
}
|
||||
} as ArrayList<Entry>
|
||||
|
||||
serie.entries = entries
|
||||
|
||||
if (viewModel.goalWeight.value != null) {
|
||||
chart.yAxis.clearLines()
|
||||
if (goal != null) {
|
||||
chart.yAxis.addLine(
|
||||
viewModel.goalWeight.value!!,
|
||||
goal,
|
||||
Line(true, Paint(chart.yAxis.linePaint).apply { strokeWidth = 4f })
|
||||
)
|
||||
}
|
||||
|
||||
if (list.isEmpty()) {
|
||||
if (entries.isEmpty()) {
|
||||
chart.xAxis.x = 0.0
|
||||
} else {
|
||||
chart.xAxis.x = list.last().timestamp - chart.xAxis.dataWidth!!
|
||||
chart.xAxis.x = entries.last().x - chart.xAxis.dataWidth!!
|
||||
}
|
||||
|
||||
chart.refresh()
|
||||
@ -193,29 +207,30 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
||||
/**
|
||||
* the waterintake old value to keep for value update
|
||||
*/
|
||||
private var oldValue = 0f
|
||||
private var oldValue = 0
|
||||
|
||||
/**
|
||||
* function that update the water count in the home page
|
||||
*/
|
||||
private fun updateWater(newValue: Int) {
|
||||
// get the current Unit
|
||||
val waterUnit =
|
||||
Units.Volume.find(settings.getString("water_unit", "milliliter") ?: "Milliliter")
|
||||
private fun updateWaterGraph() {
|
||||
val newValue = viewModel.water.value?.value ?: 0
|
||||
val daily = viewModel.dailyWaterIntake.value
|
||||
val unit = viewModel.waterUnit.value
|
||||
|
||||
if (unit == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Update the count
|
||||
binding.fragmentHomeWaterCurrent.text =
|
||||
String.format(
|
||||
resources.getString(waterUnit.unit),
|
||||
(newValue * waterUnit.modifier).toInt()
|
||||
getString(
|
||||
unit.unit,
|
||||
(newValue * unit.modifier).toInt()
|
||||
)
|
||||
|
||||
// TODO: move it elsewhere
|
||||
binding.fragmentHomeWaterTotal.text =
|
||||
String.format(
|
||||
resources.getString(waterUnit.unit),
|
||||
viewModel.dailyWaterIntake
|
||||
)
|
||||
if (daily == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// get the with/height of the ImageView
|
||||
var width = 1500
|
||||
@ -228,7 +243,7 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
||||
|
||||
// Prepare the update animation
|
||||
val animator = ValueAnimator.ofInt(
|
||||
this.oldValue.toInt(),
|
||||
this.oldValue,
|
||||
newValue
|
||||
)
|
||||
animator.duration = 300 // ms
|
||||
@ -237,8 +252,8 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
||||
if (localView == null) {
|
||||
return@addUpdateListener
|
||||
}
|
||||
this.oldValue = (it.animatedValue as Int).toFloat()
|
||||
val value = 100 * it.animatedValue as Int / viewModel.dailyWaterIntake
|
||||
this.oldValue = (it.animatedValue as Int)
|
||||
val value = 100 * it.animatedValue as Int / daily.toFloat()
|
||||
|
||||
val graph = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(graph)
|
||||
@ -283,4 +298,23 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
||||
// start the animation
|
||||
animator.start()
|
||||
}
|
||||
|
||||
private fun updateWaterTotal() {
|
||||
val unit = viewModel.waterUnit.value
|
||||
val value = viewModel.dailyWaterIntake.value
|
||||
if (unit == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
binding.fragmentHomeWaterTotal.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
binding.fragmentHomeWaterTotal.visibility = View.VISIBLE
|
||||
|
||||
binding.fragmentHomeWaterTotal.text = getString(
|
||||
unit.unit,
|
||||
unit.fromMilliliter(value)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ import com.dzeio.openhealth.data.water.Water
|
||||
import com.dzeio.openhealth.data.water.WaterRepository
|
||||
import com.dzeio.openhealth.data.weight.Weight
|
||||
import com.dzeio.openhealth.data.weight.WeightRepository
|
||||
import com.dzeio.openhealth.units.Units
|
||||
import com.dzeio.openhealth.units.Mass
|
||||
import com.dzeio.openhealth.units.Volume
|
||||
import com.dzeio.openhealth.utils.Configuration
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
@ -54,26 +55,25 @@ class HomeViewModel @Inject internal constructor(
|
||||
/**
|
||||
* The size of a cup for the quick water intake add
|
||||
*/
|
||||
var waterCupSize = config.getInt("water_cup_size").toLiveData()
|
||||
var waterCupSize = config.getInt(Settings.WATER_INTAKE_SIZE).toLiveData()
|
||||
|
||||
/**
|
||||
* The unit used to display the water intake of the user
|
||||
*/
|
||||
var waterUnit = Units.Volume.find(config.getString("water_unit").value ?: "ml")
|
||||
private val _waterUnit = MutableLiveData(Volume.MILLILITER)
|
||||
val waterUnit: LiveData<Volume> = _waterUnit
|
||||
|
||||
private val _massUnit = MutableLiveData(Units.Mass.KILOGRAM)
|
||||
private val _massUnit = MutableLiveData(Mass.KILOGRAM)
|
||||
|
||||
/**
|
||||
* The Mass unit used by the user
|
||||
*/
|
||||
val massUnit: LiveData<Units.Mass> = _massUnit
|
||||
val massUnit: LiveData<Mass> = _massUnit
|
||||
|
||||
/**
|
||||
* the User weight goal
|
||||
*/
|
||||
val goalWeight = config.getFloat(Settings.WEIGHT_GOAL).toLiveData()
|
||||
|
||||
val dailyWaterIntake: Float = (config.getFloat("water_intake").value ?: 1200f) * waterUnit.modifier
|
||||
private val _dailyWaterIntake = MutableLiveData<Int?>(null)
|
||||
val dailyWaterIntake: LiveData<Int?> = _dailyWaterIntake
|
||||
|
||||
init {
|
||||
// Fetch today's water intake
|
||||
@ -95,15 +95,31 @@ class HomeViewModel @Inject internal constructor(
|
||||
}
|
||||
}
|
||||
|
||||
config.getString(Settings.MASS_UNIT).apply {
|
||||
config.getInt(Settings.MASS_UNIT).apply {
|
||||
addObserver {
|
||||
if (it == null) return@addObserver
|
||||
_massUnit.postValue(Units.Mass.find(it))
|
||||
_massUnit.postValue(Mass.find(it))
|
||||
}
|
||||
if (value != null) {
|
||||
_massUnit.postValue(Units.Mass.find(value!!))
|
||||
_massUnit.postValue(Mass.find(value!!))
|
||||
}
|
||||
}
|
||||
|
||||
config.getInt((Settings.VOLUME_UNIT)).apply {
|
||||
addObserver {
|
||||
if (it === null) return@addObserver
|
||||
_waterUnit.postValue(Volume.find(it))
|
||||
}
|
||||
if (value != null) _waterUnit.postValue(Volume.find(value!!))
|
||||
}
|
||||
|
||||
config.getInt(Settings.WATER_INTAKE_DAILY_GOAL).apply {
|
||||
addObserver {
|
||||
if (it === null) return@addObserver
|
||||
_dailyWaterIntake.postValue((it * (_waterUnit.value?.modifier ?: 0f)).toInt())
|
||||
}
|
||||
if (value != null) _dailyWaterIntake.postValue((value!! * (_waterUnit.value?.modifier ?: 0f)).toInt())
|
||||
}
|
||||
}
|
||||
|
||||
fun updateWater(water: Water) {
|
||||
|
@ -13,9 +13,11 @@ import com.dzeio.openhealth.Application
|
||||
import com.dzeio.openhealth.BuildConfig
|
||||
import com.dzeio.openhealth.R
|
||||
import com.dzeio.openhealth.Settings
|
||||
import com.dzeio.openhealth.units.Units
|
||||
import com.dzeio.openhealth.units.Mass
|
||||
import com.dzeio.openhealth.units.Volume
|
||||
import com.dzeio.openhealth.utils.Configuration
|
||||
import com.dzeio.openhealth.utils.LocaleUtils
|
||||
import com.dzeio.openhealth.utils.fields.IntEditTextPreference
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
@ -48,11 +50,11 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||
val value = config.getFloat(Settings.WEIGHT_GOAL)
|
||||
setOnPreferenceClickListener {
|
||||
if (value.value != null) {
|
||||
val unit = config.getString(Settings.MASS_UNIT).value
|
||||
val unit = config.getInt(Settings.MASS_UNIT).value
|
||||
text = if (unit == null) {
|
||||
value.value!!.toString()
|
||||
} else {
|
||||
val modifier = Units.Mass.find(unit).modifier
|
||||
val modifier = Mass.find(unit).modifier
|
||||
(value.value!! * modifier).toString()
|
||||
}
|
||||
}
|
||||
@ -60,10 +62,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||
}
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val unit = config.getString(Settings.MASS_UNIT).value
|
||||
var modifier = Units.Mass.KILOGRAM.modifier
|
||||
val unit = config.getInt(Settings.MASS_UNIT).value
|
||||
var modifier = Mass.KILOGRAM.modifier
|
||||
if (unit != null) {
|
||||
modifier = Units.Mass.find(unit).modifier
|
||||
modifier = Mass.find(unit).modifier
|
||||
}
|
||||
|
||||
value.value = ((newValue as String).toFloat() / modifier)
|
||||
@ -132,6 +134,52 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||
setDefaultValue(Locale.getDefault().language)
|
||||
}
|
||||
|
||||
findPreference<ListPreference>("tmp." + Settings.VOLUME_UNIT)?.apply {
|
||||
entries = Volume.values().map { getString(it.singular) }.toTypedArray()
|
||||
entryValues = Volume.values().map { it.ordinal.toString() }.toTypedArray()
|
||||
val unit = config.getInt(Settings.VOLUME_UNIT)
|
||||
|
||||
setOnPreferenceClickListener {
|
||||
if (unit.value != null) setValueIndex(unit.value!!)
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val nv = (newValue as String).toIntOrNull()
|
||||
unit.value = nv
|
||||
return@setOnPreferenceChangeListener false
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<IntEditTextPreference>("tmp." + Settings.WATER_INTAKE_DAILY_GOAL)?.apply {
|
||||
val value = config.getInt(Settings.WATER_INTAKE_DAILY_GOAL)
|
||||
val unit = config.getInt(Settings.VOLUME_UNIT)
|
||||
|
||||
setOnPreferenceClickListener {
|
||||
if (value.value == null) {
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
text = if (unit.value == null) {
|
||||
value.value!!.toString()
|
||||
} else {
|
||||
Volume.find(unit.value!!).fromMilliliter(value.value!!).toString()
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val nv = (newValue as String).toIntOrNull()
|
||||
|
||||
value.value = if (unit.value == null || nv == null) {
|
||||
nv
|
||||
} else {
|
||||
Volume.find(unit.value!!).toMilliliter(nv)
|
||||
}
|
||||
|
||||
return@setOnPreferenceChangeListener false
|
||||
}
|
||||
}
|
||||
|
||||
// Update App Locale
|
||||
languagesPreference?.setOnPreferenceChangeListener { _, newValue ->
|
||||
LocaleUtils.setLanguage(requireContext(), newValue as String)
|
||||
|
@ -13,7 +13,6 @@ import com.dzeio.openhealth.adapters.ItemAdapter
|
||||
import com.dzeio.openhealth.core.BaseFragment
|
||||
import com.dzeio.openhealth.data.water.Water
|
||||
import com.dzeio.openhealth.databinding.FragmentMainWaterHomeBinding
|
||||
import com.dzeio.openhealth.units.Units
|
||||
import com.dzeio.openhealth.utils.ChartUtils
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.text.SimpleDateFormat
|
||||
@ -27,17 +26,18 @@ class WaterHomeFragment :
|
||||
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentMainWaterHomeBinding =
|
||||
FragmentMainWaterHomeBinding::inflate
|
||||
|
||||
private val adapter = ItemAdapter<Water>()
|
||||
|
||||
private val serie by lazy { BarSerie(binding.chart) }
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
viewModel.init()
|
||||
|
||||
val recycler = binding.list
|
||||
|
||||
val manager = LinearLayoutManager(requireContext())
|
||||
recycler.layoutManager = manager
|
||||
recycler.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
val adapter = ItemAdapter<Water>().apply {
|
||||
adapter.apply {
|
||||
onItemClick = {
|
||||
findNavController().navigate(
|
||||
WaterHomeFragmentDirections.actionNavWaterHomeToNavWaterEdit(
|
||||
@ -48,20 +48,16 @@ class WaterHomeFragment :
|
||||
recycler.adapter = this
|
||||
}
|
||||
|
||||
val chart = binding.chart
|
||||
|
||||
val serie = BarSerie(chart)
|
||||
|
||||
chart.apply {
|
||||
ChartUtils.materielTheme(chart, requireView())
|
||||
binding.chart.apply {
|
||||
serie
|
||||
ChartUtils.materielTheme(this, requireView())
|
||||
|
||||
yAxis.apply {
|
||||
// onValueFormat
|
||||
setYMin(0f)
|
||||
}
|
||||
|
||||
xAxis.apply {
|
||||
dataWidth = 604800000.0
|
||||
textPaint.textSize = 32f
|
||||
scrollEnabled = true
|
||||
onValueFormat = onValueFormat@{
|
||||
return@onValueFormat SimpleDateFormat(
|
||||
@ -79,37 +75,50 @@ class WaterHomeFragment :
|
||||
}
|
||||
|
||||
viewModel.items.observe(viewLifecycleOwner) { list ->
|
||||
val unit = Units.Volume.MILLILITER
|
||||
adapter.set(
|
||||
list.map {
|
||||
ItemAdapter.Item(
|
||||
it,
|
||||
getString(
|
||||
unit.unit,
|
||||
unit.formatToString(it.value)
|
||||
),
|
||||
it.formatTimestamp(),
|
||||
icon = R.drawable.ic_outline_edit_24
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return@observe
|
||||
}
|
||||
|
||||
val dataset = list.map {
|
||||
return@map Entry(
|
||||
it.timestamp.toDouble(),
|
||||
it.value.toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
serie.entries = dataset as ArrayList<Entry>
|
||||
chart.xAxis.x = chart.xAxis.getXMin()
|
||||
chart.xAxis.x =
|
||||
chart.xAxis.getXMax() - chart.xAxis.dataWidth!! + chart.xAxis.dataWidth!! / 7
|
||||
chart.refresh()
|
||||
updateData()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateData() {
|
||||
val data = viewModel.items.value
|
||||
val unit = viewModel.unit.value
|
||||
|
||||
if (unit == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (data.isNullOrEmpty()) {
|
||||
adapter.clear()
|
||||
serie.entries = arrayListOf()
|
||||
binding.chart.refresh()
|
||||
return
|
||||
}
|
||||
|
||||
// Update adapter
|
||||
adapter.set(
|
||||
data.map {
|
||||
ItemAdapter.Item(
|
||||
it,
|
||||
getString(
|
||||
unit.unit,
|
||||
unit.fromMilliliter(it.value)
|
||||
),
|
||||
it.formatTimestamp(),
|
||||
icon = R.drawable.ic_outline_edit_24
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// update graph
|
||||
val dataset = data.map {
|
||||
return@map Entry(
|
||||
it.timestamp.toDouble(),
|
||||
unit.fromMilliliter(it.value).toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
serie.entries = dataset as ArrayList<Entry>
|
||||
binding.chart.xAxis.x = 0.0
|
||||
binding.chart.refresh()
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
package com.dzeio.openhealth.ui.water
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.dzeio.openhealth.Settings
|
||||
import com.dzeio.openhealth.core.BaseViewModel
|
||||
import com.dzeio.openhealth.data.water.Water
|
||||
import com.dzeio.openhealth.data.water.WaterRepository
|
||||
import com.dzeio.openhealth.units.Volume
|
||||
import com.dzeio.openhealth.utils.Configuration
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
@ -12,15 +16,25 @@ import kotlinx.coroutines.launch
|
||||
|
||||
@HiltViewModel
|
||||
class WaterHomeViewModel@Inject internal constructor(
|
||||
private val waterRepository: WaterRepository
|
||||
private val waterRepository: WaterRepository,
|
||||
config: Configuration
|
||||
) : BaseViewModel() {
|
||||
val items: MutableLiveData<List<Water>> = MutableLiveData()
|
||||
|
||||
fun init() {
|
||||
private val _unit = MutableLiveData<Volume?>()
|
||||
val unit: LiveData<Volume?> = _unit
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
waterRepository.getWaters().collectLatest {
|
||||
items.postValue(it)
|
||||
}
|
||||
}
|
||||
config.getInt((Settings.VOLUME_UNIT)).apply {
|
||||
addObserver {
|
||||
if (it === null) return@addObserver
|
||||
_unit.postValue(Volume.find(it))
|
||||
}
|
||||
if (value != null) _unit.postValue(Volume.find(value!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import com.dzeio.openhealth.R
|
||||
import com.dzeio.openhealth.Settings
|
||||
import com.dzeio.openhealth.core.BaseDialog
|
||||
import com.dzeio.openhealth.databinding.DialogWaterSizeSelectorBinding
|
||||
import com.dzeio.openhealth.units.Volume
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
@ -30,14 +31,30 @@ class WaterSizeSelectorDialog :
|
||||
}
|
||||
|
||||
viewModel.cupSize.observe(this) {
|
||||
var real = it ?: Settings.WATER_INTAKE_SIZE_DEFAULT
|
||||
binding.customSize.text = String.format(
|
||||
getString(R.string.custom_amount),
|
||||
"${real}ml"
|
||||
)
|
||||
if (binding.inputParent.visibility == View.GONE) {
|
||||
binding.input.setText(real.toString())
|
||||
setCustomSizeText()
|
||||
}
|
||||
|
||||
viewModel.unit.observe(this) {
|
||||
if (it == null) {
|
||||
return@observe
|
||||
}
|
||||
setCustomSizeText()
|
||||
binding.size1000ml.text = getString(
|
||||
it.unit,
|
||||
it.fromMilliliter(1000)
|
||||
)
|
||||
binding.size100ml.text = getString(
|
||||
it.unit,
|
||||
it.fromMilliliter(100)
|
||||
)
|
||||
binding.size250ml.text = getString(
|
||||
it.unit,
|
||||
it.fromMilliliter(250)
|
||||
)
|
||||
binding.size500ml.text = getString(
|
||||
it.unit,
|
||||
it.fromMilliliter(500)
|
||||
)
|
||||
}
|
||||
|
||||
binding.size100ml.setOnClickListener {
|
||||
@ -61,10 +78,11 @@ class WaterSizeSelectorDialog :
|
||||
setTextFieldStatus(true)
|
||||
}
|
||||
|
||||
binding.input.doOnTextChanged { text, start, before, count ->
|
||||
binding.input.doOnTextChanged { text, _, _, _ ->
|
||||
val newSize = text.toString().toIntOrNull()
|
||||
if (newSize != null) {
|
||||
viewModel.setCupSize(newSize)
|
||||
if (newSize != null && binding.inputParent.visibility == View.VISIBLE) {
|
||||
val unit = viewModel.unit.value ?: Volume.MILLILITER
|
||||
viewModel.setCupSize(unit.toMilliliter(newSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,4 +91,22 @@ class WaterSizeSelectorDialog :
|
||||
binding.inputParent.visibility = if (displayed) View.VISIBLE else View.GONE
|
||||
binding.customSize.visibility = if (displayed) View.GONE else View.VISIBLE
|
||||
}
|
||||
|
||||
private fun setCustomSizeText() {
|
||||
val size = viewModel.cupSize.value ?: Settings.WATER_INTAKE_SIZE_DEFAULT
|
||||
val unit = viewModel.unit.value ?: Volume.MILLILITER
|
||||
|
||||
val modified = unit.fromMilliliter(size)
|
||||
|
||||
binding.customSize.text = String.format(
|
||||
getString(
|
||||
R.string.custom_amount,
|
||||
getString(unit.unit, modified)
|
||||
)
|
||||
)
|
||||
|
||||
if (binding.inputParent.visibility == View.GONE) {
|
||||
binding.input.setText(modified.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package com.dzeio.openhealth.ui.water
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.dzeio.openhealth.Settings
|
||||
import com.dzeio.openhealth.core.BaseViewModel
|
||||
import com.dzeio.openhealth.units.Volume
|
||||
import com.dzeio.openhealth.utils.Configuration
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
@ -12,8 +15,20 @@ class WaterSizeSelectorViewModel @Inject constructor(
|
||||
) : BaseViewModel() {
|
||||
|
||||
val cupSize = settings.getInt(Settings.WATER_INTAKE_SIZE).toLiveData()
|
||||
private val _unit = MutableLiveData<Volume?>()
|
||||
val unit: LiveData<Volume?> = _unit
|
||||
|
||||
fun setCupSize(value: Int) {
|
||||
settings.getInt(Settings.WATER_INTAKE_SIZE).value = value
|
||||
}
|
||||
|
||||
init {
|
||||
settings.getInt(Settings.VOLUME_UNIT).apply {
|
||||
addObserver {
|
||||
if (it === null) return@addObserver
|
||||
_unit.postValue(Volume.find(it))
|
||||
}
|
||||
if (value != null) _unit.postValue(Volume.find(value!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import com.dzeio.openhealth.core.BaseViewModel
|
||||
import com.dzeio.openhealth.data.water.Water
|
||||
import com.dzeio.openhealth.data.weight.Weight
|
||||
import com.dzeio.openhealth.data.weight.WeightRepository
|
||||
import com.dzeio.openhealth.units.Units
|
||||
import com.dzeio.openhealth.units.Mass
|
||||
import com.dzeio.openhealth.utils.Configuration
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
@ -30,7 +30,7 @@ class EditWeightDialogViewModel @Inject internal constructor(
|
||||
|
||||
private val _weights = MutableLiveData<List<Weight>?>(null)
|
||||
|
||||
private val _massUnit = MutableLiveData(Units.Mass.KILOGRAM)
|
||||
private val _massUnit = MutableLiveData(Mass.KILOGRAM)
|
||||
|
||||
init {
|
||||
|
||||
@ -41,13 +41,13 @@ class EditWeightDialogViewModel @Inject internal constructor(
|
||||
}
|
||||
}
|
||||
|
||||
config.getString(Settings.MASS_UNIT).apply {
|
||||
config.getInt(Settings.MASS_UNIT).apply {
|
||||
addObserver {
|
||||
if (it == null) return@addObserver
|
||||
_massUnit.postValue(Units.Mass.find(it))
|
||||
_massUnit.postValue(Mass.find(it))
|
||||
}
|
||||
if (value != null) {
|
||||
_massUnit.postValue(Units.Mass.find(value!!))
|
||||
_massUnit.postValue(Mass.find(value!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.dzeio.openhealth.ui.weight
|
||||
import android.Manifest
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Paint
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
@ -25,7 +26,7 @@ import com.dzeio.openhealth.adapters.ItemAdapter
|
||||
import com.dzeio.openhealth.core.BaseFragment
|
||||
import com.dzeio.openhealth.data.weight.Weight
|
||||
import com.dzeio.openhealth.databinding.FragmentListWeightBinding
|
||||
import com.dzeio.openhealth.units.Units
|
||||
import com.dzeio.openhealth.units.Mass
|
||||
import com.dzeio.openhealth.utils.ChartUtils
|
||||
import com.dzeio.openhealth.utils.PermissionsUtils
|
||||
import com.google.android.material.color.MaterialColors
|
||||
@ -99,16 +100,22 @@ class ListWeightFragment :
|
||||
}
|
||||
|
||||
binding.bluetoothButton.setOnClickListener {
|
||||
val permissions = arrayOf(
|
||||
val permissions = arrayListOf(
|
||||
Manifest.permission.BLUETOOTH,
|
||||
Manifest.permission.BLUETOOTH_CONNECT,
|
||||
Manifest.permission.BLUETOOTH_SCAN,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
)
|
||||
val hasPermission = PermissionsUtils.hasPermission(requireContext(), permissions)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
permissions.addAll(
|
||||
arrayOf(
|
||||
Manifest.permission.BLUETOOTH_CONNECT,
|
||||
Manifest.permission.BLUETOOTH_SCAN
|
||||
)
|
||||
)
|
||||
}
|
||||
val hasPermission = PermissionsUtils.hasPermission(requireContext(), permissions.toTypedArray())
|
||||
if (!hasPermission) {
|
||||
button = binding.bluetoothButton
|
||||
activityResult.launch(permissions)
|
||||
activityResult.launch(permissions.toTypedArray())
|
||||
return@setOnClickListener
|
||||
}
|
||||
findNavController().navigate(
|
||||
@ -140,16 +147,20 @@ class ListWeightFragment :
|
||||
updateGraph(list, it)
|
||||
}
|
||||
|
||||
viewModel.massUnit.observe(viewLifecycleOwner) { unit ->
|
||||
binding.chart.yAxis.onValueFormat = { getString(unit.unit, unit.fromKilogram(it)) }
|
||||
}
|
||||
|
||||
viewModel.weights.observe(viewLifecycleOwner) { list ->
|
||||
if (list != null) {
|
||||
val unit = viewModel.massUnit.value ?: Units.Mass.KILOGRAM
|
||||
val unit = viewModel.massUnit.value ?: Mass.KILOGRAM
|
||||
adapter.set(
|
||||
list.map {
|
||||
ItemAdapter.Item(
|
||||
it,
|
||||
getString(
|
||||
unit.unit,
|
||||
unit.formatToString(it.weight)
|
||||
it.weight
|
||||
),
|
||||
getString(
|
||||
R.string.weight_item,
|
||||
@ -185,8 +196,6 @@ class ListWeightFragment :
|
||||
|
||||
yAxis.apply {
|
||||
setYMin(null)
|
||||
|
||||
onValueFormat = { value -> "${value.toInt()}" }
|
||||
}
|
||||
|
||||
xAxis.apply {
|
||||
@ -228,8 +237,6 @@ class ListWeightFragment :
|
||||
val serie = chart.series[0] as LineSerie
|
||||
val avSerie = chart.series[1] as LineSerie
|
||||
|
||||
val entries: ArrayList<Entry> = arrayListOf()
|
||||
|
||||
val previous = 5
|
||||
val next = 5
|
||||
val averageList = arrayListOf<Entry>()
|
||||
@ -245,6 +252,13 @@ class ListWeightFragment :
|
||||
)
|
||||
}
|
||||
|
||||
val entries: ArrayList<Entry> = list.map {
|
||||
Entry(
|
||||
it.timestamp.toDouble(),
|
||||
it.weight
|
||||
)
|
||||
} as ArrayList<Entry>
|
||||
|
||||
val direction = averageList.last().y - averageList[averageList.size - 2].y
|
||||
|
||||
chart.yAxis.clearLines()
|
||||
@ -257,39 +271,30 @@ class ListWeightFragment :
|
||||
}
|
||||
|
||||
val dt = averageList.last().x - averageList[list.size - previous].x
|
||||
val timeUntilGoal = ((list.last().weight - goal) / abs(direction)).toLong() * dt
|
||||
val timeUntilGoal = ((entries.last().y - goal) / abs(direction)).toLong() * dt
|
||||
|
||||
binding.goalText.text = getString(
|
||||
R.string.days_until_goal_is_achieved,
|
||||
(timeUntilGoal / 1000 / 60 / 60 / 24).toInt()
|
||||
)
|
||||
averageList.add(
|
||||
entries.add(
|
||||
Entry(
|
||||
(list.last().timestamp + timeUntilGoal),
|
||||
(entries.last().x + timeUntilGoal),
|
||||
goal
|
||||
)
|
||||
)
|
||||
binding.goalText.visibility = View.VISIBLE
|
||||
} else {
|
||||
averageList.add(
|
||||
entries.add(
|
||||
Entry(
|
||||
averageList.last().x + chart.xAxis.dataWidth!!,
|
||||
averageList.last().y + direction * 7 * 4
|
||||
entries.last().x + chart.xAxis.dataWidth!!,
|
||||
entries.last().y + direction * 7 * 4
|
||||
)
|
||||
)
|
||||
binding.goalText.visibility = View.GONE
|
||||
}
|
||||
|
||||
avSerie.entries = averageList
|
||||
|
||||
list.forEach {
|
||||
entries.add(
|
||||
Entry(
|
||||
it.timestamp.toDouble(),
|
||||
it.weight
|
||||
)
|
||||
)
|
||||
}
|
||||
serie.entries = entries
|
||||
|
||||
if (list.isEmpty()) {
|
||||
|
@ -7,7 +7,7 @@ import com.dzeio.openhealth.Settings
|
||||
import com.dzeio.openhealth.core.BaseViewModel
|
||||
import com.dzeio.openhealth.data.weight.Weight
|
||||
import com.dzeio.openhealth.data.weight.WeightRepository
|
||||
import com.dzeio.openhealth.units.Units
|
||||
import com.dzeio.openhealth.units.Mass
|
||||
import com.dzeio.openhealth.utils.Configuration
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
@ -21,8 +21,8 @@ class ListWeightViewModel @Inject internal constructor(
|
||||
settings: Configuration
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val _massUnit = MutableLiveData(Units.Mass.KILOGRAM)
|
||||
val massUnit: LiveData<Units.Mass> = _massUnit
|
||||
private val _massUnit = MutableLiveData(Mass.KILOGRAM)
|
||||
val massUnit: LiveData<Mass> = _massUnit
|
||||
|
||||
private val _goalWeight = settings.getFloat(Settings.WEIGHT_GOAL)
|
||||
val goalWeight = _goalWeight.toLiveData()
|
||||
@ -37,13 +37,13 @@ class ListWeightViewModel @Inject internal constructor(
|
||||
}
|
||||
}
|
||||
|
||||
settings.getString(Settings.MASS_UNIT).apply {
|
||||
settings.getInt(Settings.MASS_UNIT).apply {
|
||||
addObserver {
|
||||
if (it == null) return@addObserver
|
||||
_massUnit.postValue(Units.Mass.find(it))
|
||||
_massUnit.postValue(Mass.find(it))
|
||||
}
|
||||
if (value != null) {
|
||||
_massUnit.postValue(Units.Mass.find(value!!))
|
||||
_massUnit.postValue(Mass.find(value!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ class ScanScalesDialog :
|
||||
override val bindingInflater: (LayoutInflater) -> DialogSearchBinding =
|
||||
DialogSearchBinding::inflate
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onBuilderInit(builder: MaterialAlertDialogBuilder) {
|
||||
super.onBuilderInit(builder)
|
||||
|
||||
@ -102,7 +103,7 @@ class ScanScalesDialog :
|
||||
it,
|
||||
it.name,
|
||||
"${it.item!!.name} (${it.item!!.address})",
|
||||
icon = R.drawable.ic_baseline_add_24
|
||||
icon = R.drawable.vector_add
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -8,12 +8,12 @@ import com.dzeio.openhealth.core.BaseViewModel
|
||||
import com.dzeio.openhealth.data.weight.Weight
|
||||
import com.dzeio.openhealth.data.weight.WeightRepository
|
||||
import com.dzeio.openhealth.units.ActivityLevel
|
||||
import com.dzeio.openhealth.units.Units
|
||||
import com.dzeio.openhealth.units.Mass
|
||||
import com.dzeio.openhealth.utils.Configuration
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class WeightDialogViewModel @Inject internal constructor(
|
||||
@ -33,7 +33,7 @@ class WeightDialogViewModel @Inject internal constructor(
|
||||
val weight: LiveData<Weight?> = _weight
|
||||
|
||||
val format =
|
||||
Units.Mass.find(settings.getString(Settings.MASS_UNIT).value ?: Units.Mass.KILOGRAM.id)
|
||||
Mass.find(settings.getInt(Settings.MASS_UNIT).value ?: Mass.KILOGRAM.ordinal)
|
||||
|
||||
fun initWithWeight(id: Long?) {
|
||||
viewModelScope.launch {
|
||||
|
48
app/src/main/java/com/dzeio/openhealth/units/Mass.kt
Normal file
48
app/src/main/java/com/dzeio/openhealth/units/Mass.kt
Normal file
@ -0,0 +1,48 @@
|
||||
package com.dzeio.openhealth.units
|
||||
|
||||
import com.dzeio.openhealth.R
|
||||
|
||||
enum class Mass(
|
||||
/**
|
||||
* Value based on the Kilogram
|
||||
*/
|
||||
val modifier: Float,
|
||||
val singular: Int,
|
||||
val plural: Int,
|
||||
val unit: Int
|
||||
) {
|
||||
KILOGRAM(
|
||||
1f,
|
||||
R.string.unit_mass_kilogram_name_singular,
|
||||
R.string.unit_mass_kilogram_name_plural,
|
||||
R.string.unit_mass_kilogram_unit
|
||||
),
|
||||
POUND(
|
||||
2.204623f,
|
||||
R.string.unit_mass_pound_name_singular,
|
||||
R.string.unit_mass_pound_name_plural,
|
||||
R.string.unit_mass_pound_unit
|
||||
);
|
||||
|
||||
companion object {
|
||||
fun find(index: Int): Mass {
|
||||
return Mass.values().getOrNull(index) ?: KILOGRAM
|
||||
}
|
||||
}
|
||||
|
||||
fun format(value: Float): Float {
|
||||
return value * modifier
|
||||
}
|
||||
|
||||
/**
|
||||
* transform the value from the specified unit to kilograms
|
||||
*/
|
||||
fun toKilogram(value: Float): Float = value / modifier
|
||||
fun toKilogram(value: Int): Int = toKilogram(value.toFloat()).toInt()
|
||||
|
||||
/**
|
||||
* transform the value from the specified unit to kilograms
|
||||
*/
|
||||
fun fromKilogram(value: Float): Float = value * modifier
|
||||
fun fromKilogram(value: Int): Int = fromKilogram(value.toFloat()).toInt()
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package com.dzeio.openhealth.units
|
||||
|
||||
import com.dzeio.openhealth.R
|
||||
|
||||
/**
|
||||
* Object containing the differents units and how they are converted
|
||||
*/
|
||||
object Units {
|
||||
/**
|
||||
* the Mass Unit
|
||||
*/
|
||||
enum class Mass(
|
||||
val id: String,
|
||||
/**
|
||||
* Value based on the Kilogram
|
||||
*/
|
||||
val modifier: Float,
|
||||
val singular: Int,
|
||||
val plural: Int,
|
||||
val unit: Int
|
||||
) {
|
||||
KILOGRAM(
|
||||
"kg",
|
||||
1f,
|
||||
R.string.unit_mass_kilogram_name_singular,
|
||||
R.string.unit_mass_kilogram_name_plural,
|
||||
R.string.unit_mass_kilogram_unit
|
||||
),
|
||||
POUND(
|
||||
"lbs",
|
||||
2.204623f,
|
||||
R.string.unit_mass_pound_name_singular,
|
||||
R.string.unit_mass_pound_name_plural,
|
||||
R.string.unit_mass_pound_unit
|
||||
);
|
||||
|
||||
companion object {
|
||||
fun find(value: String): Mass {
|
||||
return Mass.values().find {
|
||||
it.id == value
|
||||
} ?: KILOGRAM
|
||||
}
|
||||
}
|
||||
|
||||
fun format(value: Float): Float {
|
||||
return value * modifier
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the value and let the hundred of grams to be outputed
|
||||
*/
|
||||
fun formatToString(value: Float): String {
|
||||
return String.format("%.1f", value * modifier)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* the Volume unit
|
||||
*/
|
||||
enum class Volume(
|
||||
val id: String,
|
||||
/**
|
||||
* Value based on the Milliliter
|
||||
*/
|
||||
val modifier: Float,
|
||||
val singular: Int,
|
||||
val plural: Int,
|
||||
val unit: Int
|
||||
) {
|
||||
MILLILITER(
|
||||
"ml",
|
||||
1f,
|
||||
R.string.unit_volume_milliliter_name_singular,
|
||||
R.string.unit_volume_milliliter_name_plural,
|
||||
R.string.unit_volume_milliliter_unit
|
||||
),
|
||||
IMPERIAL_OUNCE(
|
||||
"ioz",
|
||||
0.03519503f,
|
||||
R.string.unit_volume_imperial_ounce_name_singular,
|
||||
R.string.unit_volume_imperial_ounce_name_plural,
|
||||
R.string.unit_volume_ounce_unit
|
||||
),
|
||||
US_OUNCE(
|
||||
"uoz",
|
||||
0.03381413f,
|
||||
R.string.unit_volume_us_ounce_name_singular,
|
||||
R.string.unit_volume_us_ounce_name_plural,
|
||||
R.string.unit_volume_ounce_unit
|
||||
);
|
||||
|
||||
fun formatToString(value: Int): String {
|
||||
return String.format("%.0f", (value * modifier))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun find(value: String): Volume {
|
||||
return Volume.values().find {
|
||||
it.id == value
|
||||
} ?: MILLILITER
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
app/src/main/java/com/dzeio/openhealth/units/Volume.kt
Normal file
53
app/src/main/java/com/dzeio/openhealth/units/Volume.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package com.dzeio.openhealth.units
|
||||
|
||||
import com.dzeio.openhealth.R
|
||||
|
||||
/**
|
||||
* the Volume unit
|
||||
*/
|
||||
enum class Volume(
|
||||
/**
|
||||
* Value based on the Milliliter
|
||||
*/
|
||||
val modifier: Float,
|
||||
val singular: Int,
|
||||
val plural: Int,
|
||||
val unit: Int
|
||||
) {
|
||||
MILLILITER(
|
||||
1f,
|
||||
R.string.unit_volume_milliliter_name_singular,
|
||||
R.string.unit_volume_milliliter_name_plural,
|
||||
R.string.unit_volume_milliliter_unit
|
||||
),
|
||||
IMPERIAL_OUNCE(
|
||||
0.03519503f,
|
||||
R.string.unit_volume_imperial_ounce_name_singular,
|
||||
R.string.unit_volume_imperial_ounce_name_plural,
|
||||
R.string.unit_volume_ounce_unit
|
||||
),
|
||||
US_OUNCE(
|
||||
0.03381413f,
|
||||
R.string.unit_volume_us_ounce_name_singular,
|
||||
R.string.unit_volume_us_ounce_name_plural,
|
||||
R.string.unit_volume_ounce_unit
|
||||
);
|
||||
|
||||
companion object {
|
||||
fun find(index: Int): Volume {
|
||||
return Volume.values().getOrNull(index) ?: MILLILITER
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* transform the value from the specified unit to kilograms
|
||||
*/
|
||||
fun toMilliliter(value: Float): Float = value / modifier
|
||||
fun toMilliliter(value: Int): Int = toMilliliter(value.toFloat()).toInt()
|
||||
|
||||
/**
|
||||
* transform the value from the specified unit to kilograms
|
||||
*/
|
||||
fun fromMilliliter(value: Float): Float = value * modifier
|
||||
fun fromMilliliter(value: Int): Int = fromMilliliter(value.toFloat()).toInt()
|
||||
}
|
@ -2,7 +2,6 @@ package com.dzeio.openhealth.utils
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.dzeio.openhealth.Application
|
||||
import com.dzeio.openhealth.core.Observable
|
||||
|
||||
/**
|
||||
@ -17,7 +16,7 @@ class Configuration(
|
||||
private val cache: HashMap<String, Field<*>> = HashMap()
|
||||
|
||||
private companion object {
|
||||
const val TAG = "${Application.TAG}/Config"
|
||||
const val TAG = "Configuration"
|
||||
}
|
||||
|
||||
init {
|
||||
@ -145,10 +144,12 @@ class Configuration(
|
||||
) : Field<Int?>(defaultValue) {
|
||||
override fun exists(): Boolean = prefs.contains(key)
|
||||
override fun internalGet(): Int? {
|
||||
if (exists()) {
|
||||
return prefs.getInt(key, 0)
|
||||
return try {
|
||||
prefs.getInt(key, 0)
|
||||
} catch (e: ClassCastException) {
|
||||
val it = prefs.getString(key, "")
|
||||
it?.toIntOrNull() ?: defaultValue
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
override fun internalSet(value: Int?) =
|
||||
prefs.edit { if (value == null) remove(key) else putInt(key, value) }
|
||||
|
5
app/src/main/res/drawable/vector_remove.xml
Normal file
5
app/src/main/res/drawable/vector_remove.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,13H5v-2h14v2z"/>
|
||||
</vector>
|
@ -29,7 +29,7 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:text="Activity"
|
||||
android:text="@string/activity"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="4dp"
|
||||
android:text="Steps" />
|
||||
android:text="@string/steps" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
@ -94,13 +94,13 @@
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_baseline_add_24" />
|
||||
android:src="@drawable/vector_add" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:text="Vitals"
|
||||
android:text="@string/vitals"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
@ -138,14 +138,14 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Heart Rate"
|
||||
android:text="@string/heart_rate"
|
||||
android:textColor="?attr/colorSurfaceInverse" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Coming soon"
|
||||
android:text="@string/coming_soon"
|
||||
android:textColor="?attr/colorSurfaceInverse" />
|
||||
</LinearLayout>
|
||||
|
||||
@ -169,7 +169,7 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:text="Measurements"
|
||||
android:text="@string/measurements"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
@ -207,7 +207,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Weight" />
|
||||
android:text="@string/weight" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
@ -256,13 +256,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?attr/colorSurfaceInverse"
|
||||
android:paddingBottom="4dp"
|
||||
android:text="Height" />
|
||||
android:text="@string/height" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?attr/colorSurfaceInverse"
|
||||
android:text="Coming soon" />
|
||||
android:text="@string/coming_soon" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@ -285,7 +285,7 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:text="Food"
|
||||
android:text="@string/food"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
@ -323,7 +323,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Water Intake" />
|
||||
android:text="@string/water_intake" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@ -364,7 +364,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Food Calories" />
|
||||
android:text="@string/food_consumption" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="?attr/materialCardViewFilledStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
@ -72,7 +72,7 @@
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1"
|
||||
android:src="@drawable/ic_outline_hexagon_24"
|
||||
android:src="@drawable/vector_remove"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
|
||||
app:layout_constraintEnd_toStartOf="@+id/linearLayout"
|
||||
app:layout_constraintTop_toTopOf="@+id/linearLayout" />
|
||||
@ -103,6 +103,8 @@
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:id="@+id/fragment_home_water_total"
|
||||
style="@style/TextAppearance.Material3.LabelMedium"
|
||||
android:layout_width="wrap_content"
|
||||
@ -120,7 +122,7 @@
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1"
|
||||
android:src="@drawable/ic_baseline_add_24"
|
||||
android:src="@drawable/vector_add"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
|
||||
app:layout_constraintStart_toEndOf="@+id/linearLayout"
|
||||
app:layout_constraintTop_toTopOf="@+id/linearLayout" />
|
||||
@ -130,7 +132,7 @@
|
||||
android:id="@+id/background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:src="@drawable/ic_outline_hexagon_24"
|
||||
tools:src="@drawable/ic_outline_hexagon_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="2:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@ -145,7 +147,7 @@
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="?attr/materialCardViewFilledStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
@ -225,6 +227,8 @@
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/unit_volume_milliliter_unit"
|
||||
android:textAlignment="center"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:textColor="?attr/colorOnBackground" />
|
||||
|
||||
</LinearLayout>
|
||||
@ -298,7 +302,7 @@
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_baseline_add_24" />
|
||||
android:src="@drawable/vector_add" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/list_weight"
|
||||
|
@ -5,7 +5,7 @@
|
||||
<item
|
||||
android:id="@+id/action_add"
|
||||
android:visible="false"
|
||||
android:icon="@drawable/ic_baseline_add_24"
|
||||
android:icon="@drawable/vector_add"
|
||||
android:title="@string/add"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
|
@ -170,9 +170,9 @@
|
||||
</dialog>
|
||||
<fragment
|
||||
android:id="@+id/foodHomeFragment"
|
||||
tools:layout="@layout/fragment_food_home"
|
||||
android:name="com.dzeio.openhealth.ui.food.FoodHomeFragment"
|
||||
android:label="FoodHomeFragment" >
|
||||
android:label="@string/food_consumption"
|
||||
tools:layout="@layout/fragment_food_home">
|
||||
<action
|
||||
android:id="@+id/action_foodHomeFragment_to_nav_dialog_food_search"
|
||||
app:destination="@id/nav_dialog_food_search" />
|
||||
|
@ -27,7 +27,7 @@
|
||||
<string name="tagline">Ton Application de santé libre, open source et respectueuse de la vie privée</string>
|
||||
<string name="add">Ajouter</string>
|
||||
<string name="about">A Propos</string>
|
||||
<string name="version_number" formatted="false">Version %1$</string>
|
||||
<string name="version_number" formatted="false">Version %1$s</string>
|
||||
<string name="about_star_on_github">Mettez une étoile sur Github</string>
|
||||
<string name="contact_us">Contactez-nous</string>
|
||||
<string name="licenses">Licenses</string>
|
||||
@ -40,7 +40,7 @@
|
||||
<string name="edit_goal">Modifier l\'objectif</string>
|
||||
<string name="edit_daily_goal">Modifier le but journalier</string>
|
||||
<string name="permission_declined">Vous avez décliné une permission, vous ne pouvez pas utiliser cette extension suaf si vous réactivez la permission manuellement</string>
|
||||
<string name="menu_steps">Pas</string>
|
||||
<string name="steps">Pas</string>
|
||||
<string name="weight_current">Poid actuel: %1$s%2$s</string>
|
||||
<string name="delete">Supprimer</string>
|
||||
<string name="close">Fermer</string>
|
||||
@ -57,7 +57,7 @@
|
||||
<string name="connectivity_error">Il semplerais que nous ne pouvons pas communiquer avec OpenFoodFact, Merci de re-essayer plus tard</string>
|
||||
<string name="bluetooth_scale">Balance bluetooth</string>
|
||||
<string name="sync">Synchroniser</string>
|
||||
<string name="searching_scales">Recherche de balances connecté...</string>
|
||||
<string name="searching_scales">Recherche de balances connecté…</string>
|
||||
<string name="import_export_data">Importer & Exporter les données de l\'application.</string>
|
||||
<string name="export">Exporter</string>
|
||||
<string name="importTxt">Importer</string>
|
||||
@ -78,4 +78,12 @@
|
||||
<item>Femme</item>
|
||||
<item>Homme</item>
|
||||
</string-array>
|
||||
<string name="activity">Acitivité</string>
|
||||
<string name="vitals">signes vitaux</string>
|
||||
<string name="heart_rate">Rythme cardiaque</string>
|
||||
<string name="coming_soon">À venir</string>
|
||||
<string name="measurements">Mesurements</string>
|
||||
<string name="height">Taille</string>
|
||||
<string name="food">Nouriturre</string>
|
||||
<string name="food_consumption">Consomation Alimentaire</string>
|
||||
</resources>
|
||||
|
@ -13,21 +13,21 @@
|
||||
<!-- Units -->
|
||||
<string name="unit_mass_kilogram_name_singular">Kilogram</string>
|
||||
<string name="unit_mass_kilogram_name_plural">Kilograms</string>
|
||||
<string name="unit_mass_kilogram_unit" translatable="false">%1$skg</string>
|
||||
<string name="unit_mass_kilogram_unit" translatable="false">%1$.2fkg</string>
|
||||
|
||||
<string name="unit_mass_pound_name_singular">Pound</string>
|
||||
<string name="unit_mass_pound_name_plural">Pounds</string>
|
||||
<string name="unit_mass_pound_unit" translatable="false">%1$slbs</string>
|
||||
<string name="unit_mass_pound_unit" translatable="false">%1$.2flbs</string>
|
||||
|
||||
<string name="unit_volume_milliliter_name_singular">Milliliter</string>
|
||||
<string name="unit_volume_milliliter_name_plural">Milliliters</string>
|
||||
<string name="unit_volume_milliliter_unit" translatable="false">%1$sml</string>
|
||||
<string name="unit_volume_milliliter_unit" translatable="false">%1$dml</string>
|
||||
|
||||
<string name="unit_volume_imperial_ounce_name_singular">Imperial Ounce</string>
|
||||
<string name="unit_volume_imperial_ounce_name_plural">Imperial Ounces</string>
|
||||
<string name="unit_volume_us_ounce_name_singular">US Ounce</string>
|
||||
<string name="unit_volume_us_ounce_name_plural">US Ounces</string>
|
||||
<string name="unit_volume_ounce_unit" translatable="false">%1$soz</string>
|
||||
<string name="unit_volume_ounce_unit" translatable="false">%1$doz</string>
|
||||
<string name="languages">Languages</string>
|
||||
<string name="settings_global">Global Settings</string>
|
||||
<string name="weight">Weight</string>
|
||||
@ -51,7 +51,7 @@
|
||||
<string name="edit_goal">Modify Goal</string>
|
||||
<string name="permission_declined">You declined a permission, you can\'t use this extension unless you enable it manually</string>
|
||||
<string name="edit_daily_goal">Modifiy daily goal</string>
|
||||
<string name="menu_steps">Steps</string>
|
||||
<string name="steps">Steps</string>
|
||||
<string name="weight_current">Current weight: %1$s</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="close">Close</string>
|
||||
@ -79,6 +79,14 @@
|
||||
<string name="import_export">Import/Export</string>
|
||||
<string name="days_until_goal_is_achieved">Days until goal is achieved: ~%1$d days</string>
|
||||
<string name="weight_item">Date: %1$s\nBMI: %2$.2f\nBody water: %3$.2f\nMuscles: %4$.2f\nLean body mass: %5$.2f\nBody fat: %6$.2f\nBone mass: %7$.2f\nVisceral fat: %8$.2f\nBasal metabolic rate: %9$d\nTotal daily energy expendure: %10$d\n</string>
|
||||
<string name="activity">Activity</string>
|
||||
<string name="vitals">Vitals</string>
|
||||
<string name="heart_rate">Heart Rate</string>
|
||||
<string name="coming_soon">Coming soon</string>
|
||||
<string name="measurements">Measurements</string>
|
||||
<string name="height">Height</string>
|
||||
<string name="food">Food</string>
|
||||
<string name="food_consumption">Food Consumption</string>
|
||||
<string-array name="activity_levels">
|
||||
<item>Bedridden</item>
|
||||
<item>Sedentary</item>
|
||||
|
@ -53,14 +53,10 @@
|
||||
android:key="water_hourly_notification"
|
||||
android:title="Enable Hourly Notification" />
|
||||
<ListPreference
|
||||
android:defaultValue="ml"
|
||||
android:entries="@array/volume_units"
|
||||
android:entryValues="@array/volume_units"
|
||||
android:key="water_unit"
|
||||
android:key="tmp.com.dzeio.open-health.volume-unit"
|
||||
android:title="Volume Unit" />
|
||||
<EditTextPreference
|
||||
android:defaultValue="2700"
|
||||
android:key="water_intake"
|
||||
<com.dzeio.openhealth.utils.fields.IntEditTextPreference
|
||||
android:key="tmp.com.dzeio.open-health.water.daily"
|
||||
android:selectAllOnFocus="true"
|
||||
android:singleLine="true"
|
||||
android:inputType="number"
|
||||
@ -70,7 +66,7 @@
|
||||
<PreferenceCategory android:title="Steps settings">
|
||||
<com.dzeio.openhealth.utils.fields.IntEditTextPreference
|
||||
android:inputType="number"
|
||||
android:key="com.dzeio.open-health.steps.goal-daily"
|
||||
android:key="tmp.com.dzeio.open-health.steps.goal-daily"
|
||||
android:selectAllOnFocus="true"
|
||||
android:singleLine="true"
|
||||
android:title="Number of steps each days" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user