From 1e0988d5b26c6586d7915300e4af85a5c0523ffe Mon Sep 17 00:00:00 2001 From: Avior Date: Wed, 24 Aug 2022 22:26:13 +0200 Subject: [PATCH] feat(app): Add weight goal #88 --- .../java/com/dzeio/openhealth/Settings.kt | 8 +- .../com/dzeio/openhealth/core/BaseDialog.kt | 62 +----- .../dzeio/openhealth/core/BaseSimpleDialog.kt | 57 ++++++ .../com/dzeio/openhealth/core/Observable.kt | 48 +++++ .../com/dzeio/openhealth/di/SystemModule.kt | 7 + .../dzeio/openhealth/graphs/WeightChart.kt | 2 + .../dzeio/openhealth/ui/home/HomeFragment.kt | 11 +- .../dzeio/openhealth/ui/home/HomeViewModel.kt | 10 +- .../ui/settings/SettingsFragment.kt | 12 +- .../openhealth/ui/weight/AddWeightDialog.kt | 4 +- .../ui/weight/ListWeightFragment.kt | 24 ++- .../ui/weight/ListWeightViewModel.kt | 18 +- .../openhealth/ui/weight/WeightDialog.kt | 111 +++++++++++ .../ui/weight/WeightDialogViewModel.kt | 27 +++ .../dzeio/openhealth/utils/Configuration.kt | 177 ++++++++++++++++++ app/src/main/res/layout/dialog_weight.xml | 22 +++ .../main/res/navigation/mobile_navigation.xml | 70 +++---- app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/locales.xml | 4 +- app/src/main/res/xml/preferences.xml | 2 +- 21 files changed, 553 insertions(+), 125 deletions(-) create mode 100644 app/src/main/java/com/dzeio/openhealth/core/BaseSimpleDialog.kt create mode 100644 app/src/main/java/com/dzeio/openhealth/core/Observable.kt create mode 100644 app/src/main/java/com/dzeio/openhealth/ui/weight/WeightDialog.kt create mode 100644 app/src/main/java/com/dzeio/openhealth/ui/weight/WeightDialogViewModel.kt create mode 100644 app/src/main/java/com/dzeio/openhealth/utils/Configuration.kt create mode 100644 app/src/main/res/layout/dialog_weight.xml diff --git a/app/src/main/java/com/dzeio/openhealth/Settings.kt b/app/src/main/java/com/dzeio/openhealth/Settings.kt index 7f23c07..4425338 100644 --- a/app/src/main/java/com/dzeio/openhealth/Settings.kt +++ b/app/src/main/java/com/dzeio/openhealth/Settings.kt @@ -16,4 +16,10 @@ object Settings { */ const val APP_LANGUAGE = "com.dzeio.open-health.app.language" -} \ No newline at end of file + /*************************** + * Weight related settings * + ***************************/ + + const val WEIGHT_GOAL = "com.dzeio.open-health.weight.goal" + +} diff --git a/app/src/main/java/com/dzeio/openhealth/core/BaseDialog.kt b/app/src/main/java/com/dzeio/openhealth/core/BaseDialog.kt index 79e57e3..e72ee87 100644 --- a/app/src/main/java/com/dzeio/openhealth/core/BaseDialog.kt +++ b/app/src/main/java/com/dzeio/openhealth/core/BaseDialog.kt @@ -1,61 +1,17 @@ package com.dzeio.openhealth.core -import android.app.Dialog -import android.os.Bundle -import android.view.LayoutInflater -import androidx.appcompat.app.AlertDialog -import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import androidx.viewbinding.ViewBinding -import com.google.android.material.dialog.MaterialAlertDialogBuilder -abstract class BaseDialog(private val viewModelClass: Class) : DialogFragment() { +/** + * Base dialog + * + * note: Dialog crash app with viewmodel error? add @AndroidEntryPoint + */ +abstract class BaseDialog(private val viewModelClass: Class) : + BaseSimpleDialog() { + val viewModel by lazy { ViewModelProvider(this)[viewModelClass] } - - private var _binding: VB? = null - val binding get() = _binding!! - - /** - * Setup everything! - */ - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return activity?.let { act -> - val builder = MaterialAlertDialogBuilder(requireContext()) - - _binding = bindingInflater(act.layoutInflater) - - builder.setView(binding.root) - - onBuilderInit(builder) - - val dialog = builder.create() - - onDialogInit(dialog) - - onCreated() - - dialog - } ?: throw IllegalStateException("Activity cannot be null") - } - - open fun onBuilderInit(builder: MaterialAlertDialogBuilder): Unit {} - - open fun onDialogInit(dialog: AlertDialog): Unit {} - - open fun onCreated(): Unit {} - - /** - * Function to inflate the Fragment Bindings - */ - abstract val bindingInflater: (LayoutInflater) -> VB - - /** - * Destroy binding - */ - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/dzeio/openhealth/core/BaseSimpleDialog.kt b/app/src/main/java/com/dzeio/openhealth/core/BaseSimpleDialog.kt new file mode 100644 index 0000000..9adf943 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/core/BaseSimpleDialog.kt @@ -0,0 +1,57 @@ +package com.dzeio.openhealth.core + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import androidx.viewbinding.ViewBinding +import com.google.android.material.dialog.MaterialAlertDialogBuilder + +abstract class BaseSimpleDialog : DialogFragment() { + + private var _binding: VB? = null + val binding get() = _binding!! + + /** + * Setup everything! + */ + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return activity?.let { act -> + val builder = MaterialAlertDialogBuilder(requireContext()) + + _binding = bindingInflater(act.layoutInflater) + + builder.setView(binding.root) + + onBuilderInit(builder) + + val dialog = builder.create() + + onDialogInit(dialog) + + onCreated() + + dialog + } ?: throw IllegalStateException("Activity cannot be null") + } + + open fun onBuilderInit(builder: MaterialAlertDialogBuilder) {} + + open fun onDialogInit(dialog: AlertDialog) {} + + open fun onCreated() {} + + /** + * Function to inflate the Fragment Bindings + */ + abstract val bindingInflater: (LayoutInflater) -> VB + + /** + * Destroy binding + */ + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/com/dzeio/openhealth/core/Observable.kt b/app/src/main/java/com/dzeio/openhealth/core/Observable.kt new file mode 100644 index 0000000..a94a2b2 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/core/Observable.kt @@ -0,0 +1,48 @@ +package com.dzeio.openhealth.core + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData + +open class Observable(baseValue: T) { + + private val functionObservers: ArrayList<(T) -> Unit> = ArrayList() + + + fun addObserver(fn: (T) -> Unit) { + if (!functionObservers.contains(fn)) { + functionObservers.add(fn) + } + } + + fun removeObserver(fn: (T) -> Unit) { + if (functionObservers.contains(fn)) { + functionObservers.remove(fn) + } + } + + open var value: T = baseValue + set(value) { + field = value + notifyObservers() + } + + fun notifyObservers() { + + // Notify Functions + for (fn in functionObservers) { + notifyObserver(fn) + } + } + + fun notifyObserver(observer: (T) -> Unit) { + observer.invoke(value) + } + + fun toLiveData(): LiveData { + val ld = MutableLiveData(value) + addObserver { + ld.postValue(it) + } + return ld + } +} diff --git a/app/src/main/java/com/dzeio/openhealth/di/SystemModule.kt b/app/src/main/java/com/dzeio/openhealth/di/SystemModule.kt index 880b89f..2315607 100644 --- a/app/src/main/java/com/dzeio/openhealth/di/SystemModule.kt +++ b/app/src/main/java/com/dzeio/openhealth/di/SystemModule.kt @@ -3,6 +3,7 @@ package com.dzeio.openhealth.di import android.content.Context import android.content.SharedPreferences import androidx.preference.PreferenceManager +import com.dzeio.openhealth.utils.Configuration import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -19,4 +20,10 @@ class SystemModule { fun provideSettings(@ApplicationContext context: Context): SharedPreferences { return PreferenceManager.getDefaultSharedPreferences(context) } + + @Singleton + @Provides + fun provideConfig(sharedPreferences: SharedPreferences): Configuration { + return Configuration(sharedPreferences) + } } diff --git a/app/src/main/java/com/dzeio/openhealth/graphs/WeightChart.kt b/app/src/main/java/com/dzeio/openhealth/graphs/WeightChart.kt index 03fd993..70fd8e8 100644 --- a/app/src/main/java/com/dzeio/openhealth/graphs/WeightChart.kt +++ b/app/src/main/java/com/dzeio/openhealth/graphs/WeightChart.kt @@ -136,8 +136,10 @@ object WeightChart { limit.lineWidth = 1f limit.textColor = Color.BLACK + axisRight.removeAllLimitLines() axisRight.addLimitLine(limit) } + invalidate() } } } diff --git a/app/src/main/java/com/dzeio/openhealth/ui/home/HomeFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/home/HomeFragment.kt index 1aef91b..4fd89ca 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/home/HomeFragment.kt @@ -108,6 +108,15 @@ class HomeFragment : BaseFragment(HomeViewMo updateWater(0) } } + + viewModel.goalWeight.observe(viewLifecycleOwner) { + lifecycleScope.launchWhenStarted { + viewModel.fetchWeights().collectLatest { + updateGraph(it) + } + } + + } } private fun updateGraph(list: List) { @@ -116,7 +125,7 @@ class HomeFragment : BaseFragment(HomeViewMo requireView(), list, viewModel.weightUnit, - viewModel.goalWeight?.toFloat() + viewModel.goalWeight.value ) // legend.apply { diff --git a/app/src/main/java/com/dzeio/openhealth/ui/home/HomeViewModel.kt b/app/src/main/java/com/dzeio/openhealth/ui/home/HomeViewModel.kt index 0ccef05..3617ab4 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/home/HomeViewModel.kt @@ -6,22 +6,25 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.dzeio.openhealth.Application.Companion.TAG +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.data.weight.Weight import com.dzeio.openhealth.data.weight.WeightRepository import com.dzeio.openhealth.units.UnitFactory +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 HomeViewModel @Inject internal constructor( private val weightRepository: WeightRepository, private val waterRepository: WaterRepository, - settings: SharedPreferences + settings: SharedPreferences, + config: Configuration ) : BaseViewModel() { private val _water = MutableLiveData(null) @@ -35,8 +38,7 @@ class HomeViewModel @Inject internal constructor( var weightUnit = UnitFactory.mass(settings.getString("weight_unit", "kilogram") ?: "kilogram") - val goalWeight: Int? = - (settings.getString("weight_goal", null)?.toIntOrNull()) + val goalWeight = config.getFloat(Settings.WEIGHT_GOAL).toLiveData() val dailyWaterIntake: Int = ((settings.getString("water_intake", "1200")?.toFloatOrNull() ?: 1200f) * waterUnit.modifier) diff --git a/app/src/main/java/com/dzeio/openhealth/ui/settings/SettingsFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/settings/SettingsFragment.kt index 5fc4988..ed6b593 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/settings/SettingsFragment.kt @@ -20,7 +20,7 @@ import java.util.Locale class SettingsFragment : PreferenceFragmentCompat() { private companion object { - const val TAG = "${Application.TAG}/SttngsFrgmnt" + const val TAG = "${Application.TAG}/SettingsFragment" } val settings: SharedPreferences by lazy { @@ -31,12 +31,12 @@ class SettingsFragment : PreferenceFragmentCompat() { setPreferencesFromResource(R.xml.preferences, rootKey) // Force only numbers on Goal - val weightGoal = findPreference("weight_goal") + val weightGoal = findPreference(Settings.WEIGHT_GOAL) weightGoal?.apply { setOnBindEditTextListener { it.inputType = InputType.TYPE_CLASS_NUMBER } - val value = settings.getString("weight_goal", null) + val value = settings.getString(Settings.WEIGHT_GOAL, null) val modifier = UnitFactory.mass(settings.getString("weight_unit", null) ?: "kilogram") if (value != null && value.isNotEmpty()) { text = (value.toFloat() * modifier.modifier).toString() @@ -45,7 +45,7 @@ class SettingsFragment : PreferenceFragmentCompat() { val alue = ((newValue as String).toInt() / modifier.modifier).toInt().toString() settings.edit() .putString( - "weight_goal", + Settings.WEIGHT_GOAL, alue ) .apply() @@ -61,7 +61,7 @@ class SettingsFragment : PreferenceFragmentCompat() { setOnPreferenceChangeListener { _, newValue -> val unit = settings.getString("weight_unit", "kilogram") ?: return@setOnPreferenceChangeListener true - val goal = settings.getString("weight_goal", null) + val goal = settings.getString(Settings.WEIGHT_GOAL, null) ?: return@setOnPreferenceChangeListener true val modifier = UnitFactory.mass(newValue as String) val oldModifier = UnitFactory.mass(unit) @@ -69,7 +69,7 @@ class SettingsFragment : PreferenceFragmentCompat() { (goal.toFloat() / oldModifier.modifier * modifier.modifier).toInt().toString() settings.edit() .putString( - "weight_goal", + Settings.WEIGHT_GOAL, value ) .apply() diff --git a/app/src/main/java/com/dzeio/openhealth/ui/weight/AddWeightDialog.kt b/app/src/main/java/com/dzeio/openhealth/ui/weight/AddWeightDialog.kt index a2200dc..7bf1e2b 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/weight/AddWeightDialog.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/weight/AddWeightDialog.kt @@ -10,10 +10,10 @@ import com.dzeio.openhealth.databinding.DialogAddWeightBinding import com.dzeio.openhealth.ui.home.HomeViewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect @AndroidEntryPoint -class AddWeightDialog : BaseDialog(HomeViewModel::class.java) { +class AddWeightDialog : + BaseDialog(HomeViewModel::class.java) { override val bindingInflater: (LayoutInflater) -> DialogAddWeightBinding = DialogAddWeightBinding::inflate diff --git a/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightFragment.kt index 2952a61..5fdeda6 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightFragment.kt @@ -2,7 +2,11 @@ package com.dzeio.openhealth.ui.weight import android.content.SharedPreferences import android.os.Bundle -import android.view.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.preference.PreferenceManager @@ -36,10 +40,16 @@ class ListWeightFragment : // FIXME: deprecated setHasOptionsMenu(true) - if (viewModel.goalWeight != null) { + if (viewModel.goalWeight.value != null) { binding.goalButton.setText(R.string.edit_goal) } + binding.goalButton.setOnClickListener { + findNavController().navigate( + ListWeightFragmentDirections.actionNavListWeightToNavWeightDialog(WeightDialog.DialogTypes.EDIT_GOAL.ordinal) + ) + } + val recycler = binding.list val manager = LinearLayoutManager(requireContext()) @@ -70,6 +80,14 @@ class ListWeightFragment : } } + viewModel.goalWeight.observe(viewLifecycleOwner) { + viewLifecycleOwner.lifecycleScope.launchWhenCreated { + viewModel.fetchWeights().collectLatest { + updateGraph(it) + } + } + } + GraphUtils.lineChartSetup( binding.chart, MaterialColors.getColor( @@ -89,7 +107,7 @@ class ListWeightFragment : requireView(), list, viewModel.weightUnit, - viewModel.goalWeight?.toFloat(), + viewModel.goalWeight.value, false ) } diff --git a/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightViewModel.kt b/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightViewModel.kt index ceed1ed..6a1ce64 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightViewModel.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightViewModel.kt @@ -1,23 +1,29 @@ package com.dzeio.openhealth.ui.weight -import android.content.SharedPreferences +import com.dzeio.openhealth.Settings import com.dzeio.openhealth.core.BaseViewModel import com.dzeio.openhealth.data.weight.WeightRepository import com.dzeio.openhealth.units.UnitFactory +import com.dzeio.openhealth.utils.Configuration import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel class ListWeightViewModel @Inject internal constructor( private val weightRepository: WeightRepository, - settings: SharedPreferences + private val settings: Configuration ) : BaseViewModel() { - var weightUnit = - UnitFactory.mass(settings.getString("weight_unit", "kilogram") ?: "kilogram") + private val _goalWeight = settings.getFloat(Settings.WEIGHT_GOAL) - val goalWeight: Float? = - (settings.getString("weight_goal", null)?.toFloatOrNull()) + var weightUnit = + UnitFactory.mass(settings.getString("weight_unit").value ?: "kilogram") + + val goalWeight = _goalWeight.toLiveData() fun fetchWeights() = weightRepository.getWeights() + + fun setWeightGoal(value: Float) { + _goalWeight.value = value + } } diff --git a/app/src/main/java/com/dzeio/openhealth/ui/weight/WeightDialog.kt b/app/src/main/java/com/dzeio/openhealth/ui/weight/WeightDialog.kt new file mode 100644 index 0000000..9a1d5d4 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/ui/weight/WeightDialog.kt @@ -0,0 +1,111 @@ +package com.dzeio.openhealth.ui.weight + +import android.graphics.drawable.Drawable +import android.util.Log +import android.view.LayoutInflater +import androidx.navigation.fragment.navArgs +import com.dzeio.openhealth.R +import com.dzeio.openhealth.core.BaseDialog +import com.dzeio.openhealth.databinding.DialogAddWeightBinding +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class WeightDialog : + BaseDialog(WeightDialogViewModel::class.java) { + + private val args: WeightDialogArgs by navArgs() + + override val bindingInflater: (LayoutInflater) -> DialogAddWeightBinding = + DialogAddWeightBinding::inflate + + override fun onBuilderInit(builder: MaterialAlertDialogBuilder) { + super.onBuilderInit(builder) + + val dialogType = DialogTypes.values()[args.dialogType] + + builder.apply { + setTitle(dialogType.title) + setIcon(dialogType.icon) + setPositiveButton(R.string.validate) { dialog, _ -> + runAction(0) + dialog.dismiss() + } + setNegativeButton(R.string.cancel) { dialog, _ -> + runAction(1) + dialog.cancel() + } + if (dialogType.thirdText != null) { + setNeutralButton(dialogType.thirdText) { dialog, _ -> + runAction(2) + dialog.dismiss() + } + } + } + } + + override fun onCreated() { + super.onCreated() + setValue(0f) + + if (args.dialogType == DialogTypes.EDIT_GOAL.ordinal) { + Log.d("TAG", viewModel.toString()) + Log.d("TAG", viewModel.goalWeight.value.toString()) + viewModel.goalWeight.observe(this) { + if (it != null) setValue(it) + } + } + + // init form + binding.kg.maxValue = 999 + binding.kg.minValue = 0 + + binding.gram.maxValue = 9 + binding.gram.minValue = 0 + + } + + private fun setValue(value: Float) { + Log.d("TAG", "Setting dialog value to $value") + val kg = value.toInt() + val g = (value - kg) * 10 + + binding.kg.value = kg + binding.gram.value = g.toInt() + } + + private fun getValue(): Float { + var finalValue: Float = binding.kg.value.toFloat() + finalValue += binding.gram.value.toFloat() / 10 + return finalValue + } + + /** + * @param click 0 = validate, 1 = cancel, 2 = third option + */ + private fun runAction(click: Int) { + when (DialogTypes.values()[args.dialogType]) { + DialogTypes.EDIT_GOAL -> { + if (click == 1) { + return + } + + if (click == 2) { + viewModel.setWeightGoal(null) + return + } + + viewModel.setWeightGoal(getValue()) + return + } + } + } + + enum class DialogTypes( + val title: String, + val icon: Drawable? = null, + val thirdText: Int? = null + ) { + EDIT_GOAL("Edit Goal", null, R.string.goal_remove) + } +} diff --git a/app/src/main/java/com/dzeio/openhealth/ui/weight/WeightDialogViewModel.kt b/app/src/main/java/com/dzeio/openhealth/ui/weight/WeightDialogViewModel.kt new file mode 100644 index 0000000..36745c5 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/ui/weight/WeightDialogViewModel.kt @@ -0,0 +1,27 @@ +package com.dzeio.openhealth.ui.weight + +import com.dzeio.openhealth.Settings +import com.dzeio.openhealth.core.BaseViewModel +import com.dzeio.openhealth.data.weight.WeightRepository +import com.dzeio.openhealth.units.UnitFactory +import com.dzeio.openhealth.utils.Configuration +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class WeightDialogViewModel @Inject internal constructor( + private val weightRepository: WeightRepository, + private val settings: Configuration +) : BaseViewModel() { + + private val _goalWeight = settings.getFloat(Settings.WEIGHT_GOAL) + + var weightUnit = + UnitFactory.mass(settings.getString("weight_unit").value ?: "kilogram") + + val goalWeight = _goalWeight.toLiveData() + + fun setWeightGoal(value: Float?) { + _goalWeight.value = value + } +} diff --git a/app/src/main/java/com/dzeio/openhealth/utils/Configuration.kt b/app/src/main/java/com/dzeio/openhealth/utils/Configuration.kt new file mode 100644 index 0000000..8d3201b --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/utils/Configuration.kt @@ -0,0 +1,177 @@ +package com.dzeio.openhealth.utils + +import android.content.SharedPreferences +import android.util.Log +import androidx.core.content.edit +import com.dzeio.openhealth.Application +import com.dzeio.openhealth.core.Observable + +class Configuration( + private val prefs: SharedPreferences +) : SharedPreferences.OnSharedPreferenceChangeListener { + + private val cache: HashMap> = HashMap() + + private companion object { + const val TAG = "${Application.TAG}/Config" + } + + init { + prefs.registerOnSharedPreferenceChangeListener(this) + } + + fun getString(key: String): StringField { + if (cache[key] == null) { + Log.d(TAG, "$key does not exist in cache, creating new instance") + cache[key] = StringField(key) + } else { + Log.d(TAG, "$key in cache") + } + return cache[key] as StringField + } + + fun getLong(key: String): LongField { + if (cache[key] == null) { + Log.d(TAG, "$key does not exist in cache, creating new instance") + cache[key] = LongField(key) + } else { + Log.d(TAG, "$key in cache") + } + return cache[key] as LongField + } + + fun getBoolean(key: String): BooleanField { + if (cache[key] == null) { + Log.d(TAG, "$key does not exist in cache, creating new instance") + cache[key] = BooleanField(key) + } else { + Log.d(TAG, "$key in cache") + } + return cache[key] as BooleanField + } + + fun getInt(key: String): IntField { + if (cache[key] == null) { + Log.d(TAG, "$key does not exist in cache, creating new instance") + cache[key] = IntField(key) + } else { + Log.d(TAG, "$key in cache") + } + return cache[key] as IntField + } + + fun getFloat(key: String): FloatField { + if (cache[key] == null) { + Log.d(TAG, "$key does not exist in cache, creating new instance") + cache[key] = FloatField(key) + } else { + Log.d(TAG, "$key in cache") + } + return cache[key] as FloatField + } + + fun getStringSet(key: String): StringSetField { + if (cache[key] == null) { + Log.d(TAG, "$key does not exist in cache, creating new instance") + cache[key] = StringSetField(key) + } else { + Log.d(TAG, "$key in cache") + } + return cache[key] as StringSetField + } + + override fun onSharedPreferenceChanged(u: SharedPreferences, key: String) { + Log.d(TAG, "configuration update for key: $key") + cache[key]?.needUpdate = true + cache[key]?.notifyObservers() + } + + abstract class Field( + baseValue: T + ) : Observable(baseValue) { + abstract fun exists(): Boolean + abstract fun internalGet(): T + abstract fun internalSet(value: T) + var needUpdate = true + + override var value: T = baseValue + get() { + if (needUpdate) { + field = if (!exists()) { + null as T + } else { + internalGet() + } + } + return field + } + set(value) { + if (field == value) { + return + } + field = value + internalSet(value) + notifyObservers() + } + } + + inner class StringField( + private val key: String, + private val defaultValue: String? = null + ) : Field(defaultValue) { + override fun exists(): Boolean = prefs.contains(key) + override fun internalGet(): String? = prefs.getString(key, defaultValue) + override fun internalSet(value: String?) = + prefs.edit { if (value == null) remove(key) else putString(key, value) } + } + + inner class BooleanField( + private val key: String, + private val defaultValue: Boolean = false + ) : Field(defaultValue) { + override fun exists(): Boolean = prefs.contains(key) + override fun internalGet(): Boolean = prefs.getBoolean(key, defaultValue) + override fun internalSet(value: Boolean?) = + prefs.edit { if (value == null) remove(key) else putBoolean(key, value) } + } + + inner class IntField( + private val key: String, + private val defaultValue: Int = -1 + ) : Field(defaultValue) { + override fun exists(): Boolean = prefs.contains(key) + override fun internalGet(): Int = prefs.getInt(key, defaultValue) + override fun internalSet(value: Int?) = + prefs.edit { if (value == null) remove(key) else putInt(key, value) } + } + + inner class FloatField( + private val key: String, + private val defaultValue: Float = -1f + ) : Field(defaultValue) { + override fun exists(): Boolean = prefs.contains(key) + override fun internalGet(): Float = prefs.getFloat(key, defaultValue) + override fun internalSet(value: Float?) = + prefs.edit { if (value == null) remove(key) else putFloat(key, value) } + } + + inner class LongField( + private val key: String, + private val defaultValue: Long = -1 + ) : Field(defaultValue) { + override fun exists(): Boolean = prefs.contains(key) + override fun internalGet(): Long = prefs.getLong(key, defaultValue) + override fun internalSet(value: Long?) = + prefs.edit { if (value == null) remove(key) else putLong(key, value) } + } + + inner class StringSetField( + private val key: String, + private val defaultValue: MutableSet? = null + ) : Field?>(defaultValue) { + override fun exists(): Boolean = prefs.contains(key) + override fun internalGet(): MutableSet? = prefs.getStringSet(key, defaultValue) + override fun internalSet(value: MutableSet?) = + prefs.edit { if (value == null) remove(key) else putStringSet(key, value) } + } +} diff --git a/app/src/main/res/layout/dialog_weight.xml b/app/src/main/res/layout/dialog_weight.xml new file mode 100644 index 0000000..cb18277 --- /dev/null +++ b/app/src/main/res/layout/dialog_weight.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index e28e400..1a41735 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -4,6 +4,10 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/mobile_navigation" android:label="@string/app_name" + app:enterAnim="@android:anim/slide_in_left" + app:exitAnim="@android:anim/slide_out_right" + app:popEnterAnim="@android:anim/slide_in_left" + app:popExitAnim="@android:anim/slide_out_right" app:startDestination="@+id/nav_home"> + app:destination="@id/nav_list_weight" /> + app:destination="@id/nav_water_home" /> @@ -47,14 +43,13 @@ tools:layout="@layout/fragment_list_weight"> + app:destination="@id/nav_edit_weight" /> + + app:destination="@id/nav_water_edit" /> + app:destination="@id/nav_water_size_dialog" /> @@ -104,10 +87,6 @@ @@ -115,21 +94,13 @@ + android:label="@string/page_settings"> + + + + + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e0f0b41..a874c07 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -36,6 +36,7 @@ Browse Activity Ajouter un objectif + Supprimer l\'objectif Modifier l\'objectif Modifier le but journalier Vous avez décliné une permission, vous ne pouvez pas utiliser cette extension suaf si vous réactivez la permission manuellement diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6c31fec..6b2d8e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,6 +47,7 @@ Activity Add Goal + Remove Goal Modify Goal You declined a permission, you can\'t use this extension unless you enable it manually Modifiy daily goal diff --git a/app/src/main/res/xml/locales.xml b/app/src/main/res/xml/locales.xml index 2cd3887..33d6a34 100644 --- a/app/src/main/res/xml/locales.xml +++ b/app/src/main/res/xml/locales.xml @@ -1,5 +1,5 @@ - - + + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 22dab72..c3b2472 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -22,7 +22,7 @@ android:title="Height" />