1
0
mirror of https://github.com/dzeiocom/OpenHealth.git synced 2025-04-22 10:52:13 +00:00
Signed-off-by: Avior <github@avior.me>
This commit is contained in:
Florian Bouillon 2022-06-28 17:43:29 +02:00
parent 6f33e2a1c3
commit 939dcd24d3
Signed by: Florian Bouillon
GPG Key ID: 0A288052C94BD2C8
49 changed files with 1433 additions and 366 deletions

View File

@ -1,6 +1,6 @@
<!-- omit in toc --> <!-- omit in toc -->
# Contributing to TCGdex # Contributing to OpenHealth
First off, thanks for taking the time to contribute! ❤️ First off, thanks for taking the time to contribute! ❤️
@ -31,19 +31,22 @@ All types of contributions are encouraged and valued. See the [Table of Contents
This project and everyone participating in it is governed by the This project and everyone participating in it is governed by the
[DZEIO Code of Conduct](https://github.com/dzeiocom/OpenHealth/blob/master/CODE_OF_CONDUCT.md). [DZEIO Code of Conduct](https://github.com/dzeiocom/OpenHealth/blob/master/CODE_OF_CONDUCT.md).
By participating, you are expected to uphold this code. Please report unacceptable behavior to <contact@tcgdex.net>. By participating, you are expected to uphold this code. Please report unacceptable behavior to <contact.openhealth@dze.io>.
## I Have a Question ## I Have a Question
<!--
> If you want to ask a question, we assume that you have read the available Documentation at <https://www.tcgdex.dev>. > If you want to ask a question, we assume that you have read the available Documentation at <https://www.tcgdex.dev>.
The best way to ask questions is to join our Discord server at <https://discord.gg/NehYTAhsZE>. The best way to ask questions is to join our Discord server at <https://discord.gg/NehYTAhsZE>.
You can also ask them on the Github Repository, it is best to search for existing [Issues](https://github.com/dzeiocom/OpenHealth/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in a new issue. It is also advisable to search the internet for answers first. -->
You <!--can also--> ask them on the Github Repository, it is best to search for existing [Issues](https://github.com/dzeiocom/OpenHealth/issues) that might help you.
In case you have found a suitable issue and still need clarification, you can write your question in a new issue. It is also advisable to search the internet for answers first.
If you then still feel the need to ask a question and need clarification, we recommend the following: If you then still feel the need to ask a question and need clarification, we recommend the following:
- Open an [Issue](https://github.com/dzeiocom/OpenHealth/issues/new). - Open an [Issue](https://github.com/dzeiocom/OpenHealth/issues/new).
- Provide as much context as you can about what you're running into. - Provide as much context as you can about what you're running into.
- Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant. - Provide project and platform versions (Android Version, Application Version), depending on what seems relevant.
We will then take care of the issue as soon as possible. We will then take care of the issue as soon as possible.
@ -73,13 +76,12 @@ Depending on how large the project is, you may want to outsource the questioning
A good issue report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. A good issue report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.
- Determine if your issue is not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](https://www.tcgdex.dev). If you are looking for support, you might want to check [this section](#i-have-a-question)). - Determine if your issue is not an error on your side e.g. using incompatible environment components/versions. If you are looking for support, you might want to check [this section](#i-have-a-question)).
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already an issue report existing for your bug or error in the [bug tracker](https://github.com/tcgdex/javascript-sdk/issues). - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already an issue report existing for your bug or error in the [bug tracker](https://github.com/dzeiocom/OpenHealth/issues).
- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue. - Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue.
- Collect information about the bug: - Collect information about the bug:
- Stack trace (Traceback) - Stack trace (Traceback)
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM) - OS, Platform and Version (Phone, OS Version, App Version, etc...)
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
- Possibly your input and the output - Possibly your input and the output
- Can you reliably reproduce the issue? And can you also reproduce it with older versions? - Can you reliably reproduce the issue? And can you also reproduce it with older versions?
@ -87,16 +89,16 @@ A good issue report shouldn't leave others needing to chase you up for more info
#### How Do I Submit a Good Bug Report? #### How Do I Submit a Good Bug Report?
> You must never report security related issues, vulnerabilities or bugs to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <security@tcgdex.net>. > You must never report security related issues, vulnerabilities or bugs to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <security@dze.io>.
<!-- You may add a PGP key to allow the messages to be sent encrypted as well. --> <!-- You may add a PGP key to allow the messages to be sent encrypted as well. -->
We use GitHub issues to track bugs and errors. If you run into an issue with the project: We use GitHub issues to track bugs and errors. If you run into an issue with the project:
- Open an [Issue](https://github.com/tcgdex/javascript-sdk/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) - Open an [Issue](https://github.com/dzeiocom/OpenHealth/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.)
- Explain the behavior you would expect and the actual behavior. - Explain the behavior you would expect and the actual behavior.
- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. - Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case.
- Provide the information you collected in the previous section. - Provide the information you collected in the previous section.
Once it's filed: Once it's filled:
- The project team will label the issue accordingly. - The project team will label the issue accordingly.
- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced. - A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
@ -106,28 +108,27 @@ Once it's filed:
### Suggesting Enhancements ### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for TCGdex, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. This section guides you through submitting an enhancement suggestion for OpenHealth, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
<!-- omit in toc --> <!-- omit in toc -->
#### Before Submitting an Enhancement #### Before Submitting an Enhancement
- Make sure that you are using the latest version. - Make sure that you are using the latest version.
- Read the [documentation](https://www.tcgdex.dev) carefully and find out if the functionality is already covered, maybe by an individual configuration. - Perform a [search](https://github.com/dzeiocom/OpenHealth/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
- Perform a [search](https://github.com/tcgdex/javascript-sdk/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
<!-- omit in toc --> <!-- omit in toc -->
#### How Do I Submit a Good Enhancement Suggestion? #### How Do I Submit a Good Enhancement Suggestion?
Enhancement suggestions are tracked as [GitHub issues](https://github.com/tcgdex/javascript-sdk/issues). Enhancement suggestions are tracked as [GitHub issues](https://github.com/dzeiocom/OpenHealth/issues).
- Use a **clear and descriptive title** for the issue to identify the suggestion. - Use a **clear and descriptive title** for the issue to identify the suggestion.
- Provide a **step-by-step description of the suggested enhancement** in as many details as possible. - Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. - **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. <!-- this should only be included if the project has a GUI --> - You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to.
- **Explain why this enhancement would be useful** to most TCGdex users. You may also want to point out the other projects that solved it better and which could serve as inspiration. - **Explain why this enhancement would be useful** to most OpenHealth users. You may also want to point out the other projects that solved it better and which could serve as inspiration.
<!-- You might want to create an issue template for enhancement suggestions that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. --> <!-- You might want to create an issue template for enhancement suggestions that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->
@ -137,14 +138,10 @@ _note: Follow the different styleguides listed below when contributing_
- Fork 🍴 the project. _see the `fork` button at the top right of the screen_ - Fork 🍴 the project. _see the `fork` button at the top right of the screen_
- make the changes you want in your repository. - make the changes you want in your repository.
- Create a Pull request here https://github.com/tcgdex/javascript-sdk/compare by selecting your repository patch with our `master` branch _If it's not finished put WIP: before_ - Create a Pull request here https://github.com/dzeiocom/OpenHealth/compare by selecting your repository patch with our `master` branch _If it's not finished put WIP: before_
- we don't like ❌, so if your pull request has its automated checks ending with the red cross, please double check your changes until it show the awesome 🟢, or ask for help ! - we don't like ❌, so if your pull request has its automated checks ending with the red cross, please double check your changes until it show the awesome 🟢, or ask for help !
- If your pull request is ready for review remove WIP: put it s ready for review and we will handle the rest ! - If your pull request is ready for review remove WIP: put it s ready for review and we will handle the rest !
### Improving The Documentation
The documentation is updated in the Documentation repository at <https://github.com/tcgdex/documentation>
## Styleguides ## Styleguides
### Coding Guidelines ### Coding Guidelines
@ -184,4 +181,4 @@ In short, please name your Pull Requests/Commits following this format
## Attribution ## Attribution
This guide is based on [Contribution Gen]((https://github.com/bttger/contributing-gen)) and was adapted by the TCGdex community ! This guide is based on [Contribution Gen]((https://github.com/bttger/contributing-gen)) and was adapted by the OpenHealth community !

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Florian Bouillon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,28 +1,43 @@
<p align="center"> # ![App Icon](./app/src/main/ic_launcher-playstore.png) Open Health
<a href="http://npmjs.com/@tcgdex/sdk">
<img src="https://img.shields.io/npm/v/@tcgdex/sdk?style=flat-square" alt="NOM Version">
</a>
<a href="http://npmjs.com/@tcgdex/sdk">
<img src="https://img.shields.io/npm/dm/@tcgdex/sdk?style=flat-square" alt="NPM Downloads">
</a>
<a href="https://app.codecov.io/gh/tcgdex/javascript-sdk/">
<img src="https://img.shields.io/codecov/c/github/tcgdex/javascript-sdk?style=flat-square&token=FR4BI94N4Q" alt="npm version">
</a>
<a href="https://github.com/tcgdex/javascript-sdk/stargazers">
<img src="https://img.shields.io/github/stars/tcgdex/javascript-sdk?style=flat-square" alt="Github stars">
</a>
<a href="https://github.com/tcgdex/javascript-sdk/actions/workflows/build.yml">
<img src="https://img.shields.io/github/workflow/status/tcgdex/javascript-sdk/Build%20&%20Test?style=flat-square" alt="the TCGdex JAvascript SDK is released under the MIT license." />
</a>
<a href="https://discord.gg/NehYTAhsZE">
<img src="https://img.shields.io/discord/857231041261076491?color=%235865F2&label=Discord&style=flat-square" alt="Discord Link">
</a>
</p>
# Open Health Your privacy-friendly FOSS Health Application
TODO: Make a good text here <a href="https://f-droid.org/repository/browse/?fdid=com.dzeio.openhealth" target="_blank">
TODO: Change CONTRIBUTING.md to be correct <img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="80"/>
</a>
<a href="https://play.google.com/store/apps/details?id=com.dzeio.openhealth" target="_blank">
<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="80"/>
</a>
## Features
- Material 3 Design !
- Log Multiple Health information like your Weight, Water Intake, Food Consumption and Steps
- An enormous Food Database through OpenFoodFact !
- no account creation !
- External Services supported (not available on F-Droid) : Google Fit, Samsung Health and Withings Mate
- Your data are entirely in your control ! (when not syncing to an external service)
## Privacy and Permissions
No Ads are served through this app.
Permissions requests are for specifics usage and are only requests the first time they are needed:
| Permission | Why is it requested |
| :--------------------: | :--------------------------------------------------------------- |
| ACCESS_FINE_LOCATION | Google Fit Extension Requirement (maybe not, still have to test) |
| ACCESS_COARSE_LOCATION | Same as above |
| ACTIVITY_RECOGNITION | Device Steps Usage |
No other permissions are used (even the internet permission ;)).
## Build
- Install Android Studio
- Select your variant from the Build Variant pane (bottom left, default to debug)
- click on the debug icon for debug
- it will be running on your emulator/device
## Contributing ## Contributing
@ -31,9 +46,7 @@ See [CONTRIBUTING.md](https://github.com/dzeiocom/OpenHealth/blob/master/CONTRIB
TL::DR TL::DR
- Fork - Fork
- Commit your changes - Commit your changes
- Pull Request on this Repository - Pull Request on this Repository
## License ## License

View File

@ -51,6 +51,12 @@ android {
versionName "1.0.0" versionName "1.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// Languages
def locales = ["en", "fr"]
buildConfigField "String[]", "LOCALES", "new String[]{\""+locales.join("\",\"")+"\"}"
resConfigs locales
} }
buildTypes { buildTypes {

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">OpenHealth - Debug</string> <string name="app_name" translatable="false">OpenHealth - Debug</string>
</resources> </resources>

View File

@ -1,8 +1,13 @@
package com.dzeio.openhealth package com.dzeio.openhealth
import android.app.Application import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.content.res.Resources
import androidx.preference.PreferenceManager
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import java.util.Locale
@HiltAndroidApp @HiltAndroidApp
class Application : Application() { class Application : Application() {
@ -15,5 +20,16 @@ class Application : Application() {
// Android Dynamics Colors // Android Dynamics Colors
DynamicColors.applyToActivitiesIfAvailable(this) DynamicColors.applyToActivitiesIfAvailable(this)
// Change application Language based on setting
val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val lang = preferences.getString("global_language", Locale.getDefault().language)
val locale = Locale(lang)
Locale.setDefault(locale)
val overrideConfiguration = baseContext.resources.configuration
overrideConfiguration.locale = locale
val context: Context = createConfigurationContext(overrideConfiguration)
val resources: Resources = context.getResources()
} }
} }

View File

@ -57,7 +57,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
WorkManager.getInstance(this) WorkManager.getInstance(this)
.cancelAllWork() .cancelAllWork()
WaterReminderService.setup(this) WaterReminderService.setup(this)
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -107,7 +106,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
Log.e("MainActivity", "Error Creating Notification Channel", e) Log.e("MainActivity", "Error Creating Notification Channel", e)
} }
} }
} }
} }
@ -125,9 +123,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
) )
true true
} }
R.id.action_about -> {
navController.navigate(
HomeFragmentDirections.actionNavHomeToAboutFragment()
)
true
}
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
} }
} }

View File

@ -20,7 +20,7 @@ class WaterAdapter() : BaseAdapter<Water, LayoutItemListBinding>() {
position: Int position: Int
) { ) {
holder.binding.value.text = "${item.value}ml" holder.binding.value.text = "${item.value}ml"
holder.binding.datetime.text = "${item.formatTimestamp()} ${item.timestamp}" holder.binding.datetime.text = "${item.formatTimestamp()}"
holder.binding.edit.setOnClickListener { holder.binding.edit.setOnClickListener {
onItemClick?.invoke(item) onItemClick?.invoke(item)
} }

View File

@ -6,8 +6,11 @@ import com.dzeio.openhealth.core.BaseAdapter
import com.dzeio.openhealth.core.BaseViewHolder import com.dzeio.openhealth.core.BaseViewHolder
import com.dzeio.openhealth.data.weight.Weight import com.dzeio.openhealth.data.weight.Weight
import com.dzeio.openhealth.databinding.LayoutItemListBinding import com.dzeio.openhealth.databinding.LayoutItemListBinding
import com.dzeio.openhealth.units.WeightUnit
class WeightAdapter() : BaseAdapter<Weight, LayoutItemListBinding>() { class WeightAdapter : BaseAdapter<Weight, LayoutItemListBinding>() {
var unit: WeightUnit = WeightUnit.KG
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> LayoutItemListBinding override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> LayoutItemListBinding
get() = LayoutItemListBinding::inflate get() = LayoutItemListBinding::inflate
@ -19,7 +22,8 @@ class WeightAdapter() : BaseAdapter<Weight, LayoutItemListBinding>() {
item: Weight, item: Weight,
position: Int position: Int
) { ) {
holder.binding.value.text = "${item.weight}kg" val weightTxt = String.format("%.1f", item.weight * unit.fromKG)
holder.binding.value.text = "$weightTxt${unit.unit}"
holder.binding.datetime.text = item.formatTimestamp() holder.binding.datetime.text = item.formatTimestamp()
holder.binding.edit.setOnClickListener { holder.binding.edit.setOnClickListener {
onItemClick?.invoke(item) onItemClick?.invoke(item)

View File

@ -1,48 +1,14 @@
package com.dzeio.openhealth.core package com.dzeio.openhealth.core
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
abstract class BaseFragment<VM : BaseViewModel, VB : ViewBinding>(private val viewModelClass: Class<VM>) : Fragment() { abstract class BaseFragment<VM : BaseViewModel, VB : ViewBinding>(
private val viewModelClass: Class<VM>
) :
BaseStaticFragment<VB>() {
val viewModel by lazy { val viewModel by lazy {
ViewModelProvider(this)[viewModelClass] ViewModelProvider(this)[viewModelClass]
} }
private var _binding: VB? = null
val binding get() = _binding!!
/**
* Setup everything!
*/
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
super.onCreateView(inflater, container, savedInstanceState)
_binding = bindingInflater(inflater, container, false)
return binding.root
}
/**
* Function to inflate the Fragment Bindings
*
* use like this: `ViewBinding::inflater`
*/
abstract val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> VB
/**
* Destroy binding
*/
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
} }

View File

@ -0,0 +1,44 @@
package com.dzeio.openhealth.core
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
abstract class BaseStaticFragment<VB : ViewBinding> : Fragment() {
private var _binding: VB? = null
val binding get() = _binding!!
/**
* Setup everything!
*/
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
super.onCreateView(inflater, container, savedInstanceState)
_binding = bindingInflater(inflater, container, false)
return binding.root
}
/**
* Function to inflate the Fragment Bindings
*
* use like this: `ViewBinding::inflater`
*/
abstract val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> VB
/**
* Destroy binding
*/
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@ -0,0 +1,22 @@
package com.dzeio.openhealth.di
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
@Module
class SystemModule {
@Singleton
@Provides
fun provideSettings(@ApplicationContext context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context)
}
}

View File

@ -0,0 +1,143 @@
package com.dzeio.openhealth.graphs
import android.graphics.Color
import android.view.View
import com.dzeio.openhealth.data.weight.Weight
import com.dzeio.openhealth.units.Units
import com.dzeio.openhealth.utils.GraphUtils
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.components.LimitLine
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.color.MaterialColors
import kotlin.math.max
import kotlin.math.min
object WeightChart {
fun setup(
chart: LineChart,
view: View,
data: List<Weight>,
modifier: Units.Mass,
goal: Float?,
limit: Boolean = true
) {
GraphUtils.lineChartSetup(
chart,
MaterialColors.getColor(
view,
com.google.android.material.R.attr.colorPrimary
),
MaterialColors.getColor(
view,
com.google.android.material.R.attr.colorOnBackground
)
)
if (data.isEmpty()) {
return
}
// Axis Max/Min
var axisMin = max(data.minOf { it.weight } - 10, 0f)
var axisMax = data.maxOf { it.weight } + 10
if (goal != null) {
axisMax = max(axisMax, goal)
axisMin = min(axisMin, goal)
}
// Average calculation
val averageCalculation = min(30, max(3, data.size / 2))
val isEven = averageCalculation % 2 == 1
val midValue = averageCalculation / 2
val averageYs = data.mapIndexed { index, entry ->
var minItem = index - midValue
var maxItem = index + if (!isEven) midValue + 1 else midValue
val lastEntry = data.size - 1
if (minItem < 0) {
maxItem += kotlin.math.abs(minItem)
minItem = 0
}
if (maxItem >= lastEntry) {
val diff = maxItem - lastEntry
minItem = max(0, minItem - diff)
maxItem -= diff
}
var average = 0f
for (i in minItem..maxItem) {
average += data[i].weight
}
return@mapIndexed Entry(
entry.timestamp.toFloat(),
(average / (maxItem - minItem + 1)) * modifier.modifier
)
}
val rawData = GraphUtils.lineDataSet(
LineDataSet(
data.mapIndexed { _, weight ->
return@mapIndexed Entry(
weight.timestamp.toFloat(),
weight.weight * modifier.modifier
)
},
"Weight"
)
).apply {
axisDependency = YAxis.AxisDependency.RIGHT
}
val averageData = GraphUtils.lineDataSet(LineDataSet(averageYs, "Average")).apply {
axisDependency = YAxis.AxisDependency.RIGHT
color = Color.GREEN
}
val entries = ArrayList<Entry>()
for (item in data) {
entries.add(
Entry(
item.timestamp.toFloat(),
item.weight * modifier.modifier
)
)
}
chart.apply {
this.data = LineData(rawData, averageData)
val twoWeeks = (data[data.size - 1].timestamp - data[0].timestamp) > 1290000000f
if (twoWeeks && limit) {
// idk what I did but it works lol
setVisibleXRange(
0f, data[data.size - 1].timestamp / 1000f
)
axisRight.axisMinimum = axisMin * modifier.modifier
axisRight.axisMaximum = axisMax * modifier.modifier
// BIS... :(
// Also it invalidate the view so I don't have to call invalidate
moveViewToX(data[data.size - 1].timestamp - 1290000000f)
}
if (goal != null) {
val limit = LimitLine(goal * modifier.modifier)
limit.lineColor = Color.RED
val dash = 30f
limit.enableDashedLine(dash, dash, 1f)
limit.lineWidth = 1f
limit.textColor = Color.BLACK
axisRight.addLimitLine(limit)
}
}
}
}

View File

@ -6,5 +6,5 @@ enum class NotificationChannels(
val importance: Int val importance: Int
) { ) {
// 3 is IMPORTANCE_DEFAULT // 3 is IMPORTANCE_DEFAULT
DEFAULT("openhealth_default", "Default Channel", 3) WATER("water", "Water Notifications", 3)
} }

View File

@ -8,6 +8,7 @@ import android.os.Build
import android.util.Log import android.util.Log
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.navigation.NavDeepLinkBuilder
import androidx.work.PeriodicWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.dzeio.openhealth.Application import com.dzeio.openhealth.Application
@ -42,23 +43,24 @@ class WaterReminderService(
Log.d(TAG, "Ran! ${Date().toLocaleString()}") Log.d(TAG, "Ran! ${Date().toLocaleString()}")
with(NotificationManagerCompat.from(context)) { with(NotificationManagerCompat.from(context)) {
val flag = val flag =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_IMMUTABLE else 0
val intent = NavDeepLinkBuilder(context)
.setGraph(R.navigation.mobile_navigation)
.setDestination(R.id.nav_home)
// Will nav to water home when there will be a way to add it there
// .setDestination(R.id.nav_water_home)
.createTaskStackBuilder()
.getPendingIntent(0, flag)
val builder = val builder =
NotificationCompat.Builder(context, NotificationChannels.DEFAULT.channelName) NotificationCompat.Builder(context, NotificationChannels.WATER.id)
.setContentTitle("Did you drink?") .setContentTitle("Did you drink?")
.setContentText("Drink now! ${Date().toLocaleString()}") .setContentText("Drink now! ${Date().toLocaleString()}")
.setSmallIcon(R.drawable.ic_logo_small) .setSmallIcon(R.drawable.ic_logo_small)
.setPriority(NotificationCompat.PRIORITY_HIGH) .setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent( .setContentIntent(
PendingIntent.getActivity( intent
context,
0,
Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
},
flag
) )
).build() .build()
notify( notify(
NotificationIds.WaterIntake.ordinal, NotificationIds.WaterIntake.ordinal,
builder builder

View File

@ -0,0 +1,45 @@
package com.dzeio.openhealth.ui.about
import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import com.dzeio.openhealth.BuildConfig
import com.dzeio.openhealth.R
import com.dzeio.openhealth.core.BaseStaticFragment
import com.dzeio.openhealth.databinding.FragmentAboutBinding
class AboutFragment : BaseStaticFragment<FragmentAboutBinding>() {
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentAboutBinding
get() = FragmentAboutBinding::inflate
@SuppressLint("StringFormatInvalid")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.version.text =
resources.getString(R.string.version_number, BuildConfig.VERSION_NAME)
binding.contactUs.setOnClickListener {
openLink("mailto:context.openhealth@dze.io")
}
binding.github.setOnClickListener {
openLink("https://github.com/dzeiocom/OpenHealth")
}
}
private fun openLink(url: String) {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(requireContext(), "Could not handle link $url", Toast.LENGTH_LONG).show()
}
}
}

View File

@ -1,9 +1,9 @@
package com.dzeio.openhealth.ui.home package com.dzeio.openhealth.ui.home
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.content.SharedPreferences
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color
import android.graphics.RectF import android.graphics.RectF
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@ -13,74 +13,61 @@ import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.dzeio.openhealth.Application
import com.dzeio.openhealth.core.BaseFragment import com.dzeio.openhealth.core.BaseFragment
import com.dzeio.openhealth.data.water.Water import com.dzeio.openhealth.data.water.Water
import com.dzeio.openhealth.data.weight.Weight import com.dzeio.openhealth.data.weight.Weight
import com.dzeio.openhealth.databinding.FragmentHomeBinding import com.dzeio.openhealth.databinding.FragmentHomeBinding
import com.dzeio.openhealth.graphs.WeightChart
import com.dzeio.openhealth.ui.weight.AddWeightDialog import com.dzeio.openhealth.ui.weight.AddWeightDialog
import com.dzeio.openhealth.units.UnitFactory
import com.dzeio.openhealth.utils.DrawUtils import com.dzeio.openhealth.utils.DrawUtils
import com.dzeio.openhealth.utils.GraphUtils import com.dzeio.openhealth.utils.GraphUtils
import com.github.mikephil.charting.components.LimitLine
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlin.math.min import kotlin.math.min
import kotlin.properties.Delegates import kotlinx.coroutines.flow.collectLatest
@AndroidEntryPoint @AndroidEntryPoint
class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewModel::class.java) { class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewModel::class.java) {
companion object {
const val TAG = "${Application.TAG}/HomeFragment"
}
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentHomeBinding override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentHomeBinding
get() = FragmentHomeBinding::inflate get() = FragmentHomeBinding::inflate
private var intake by Delegates.notNull<Float>() private val settings: SharedPreferences by lazy {
PreferenceManager.getDefaultSharedPreferences(requireContext())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// Bindings
binding.addWeight.setOnClickListener { binding.addWeight.setOnClickListener {
AddWeightDialog().show(requireActivity().supportFragmentManager, null) AddWeightDialog().show(requireActivity().supportFragmentManager, null)
} }
binding.fragmentHomeWaterAdd.setOnClickListener { binding.fragmentHomeWaterAdd.setOnClickListener {
val water = viewModel.water.value val water = viewModel.water.value
if (water == null || !water.isToday()) {
if (water == null) {
val w = Water() val w = Water()
w.value = 200 w.value = viewModel.waterCupSize
viewModel.updateWater(w) viewModel.updateWater(w)
} else { } else {
water.value += 200 water.value += viewModel.waterCupSize
viewModel.updateWater(water) viewModel.updateWater(water)
} }
} }
intake = PreferenceManager.getDefaultSharedPreferences(requireContext()) binding.fragmentHomeWaterTotal.text =
.getString("water_intake", "1200")?.toFloat() ?: 1200f String.format(
resources.getString(viewModel.waterUnit.unit),
binding.fragmentHomeWaterTotal.text = "${intake.toInt()}ml" viewModel.dailyWaterIntake
)
binding.fragmentHomeWaterRemove.setOnClickListener { _ ->
binding.fragmentHomeWaterRemove.setOnClickListener {
val water = viewModel.water.value val water = viewModel.water.value
if (water != null) { if (water != null) {
water.value -= viewModel.waterCupSize
water.value -= 200 if (water.value <= 0) {
if (water.value == 0) {
viewModel.deleteWater(water) viewModel.deleteWater(water)
} else { } else {
viewModel.updateWater(water) viewModel.updateWater(water)
@ -88,22 +75,6 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
} }
} }
binding.fragmentHomeWaterRemove.setOnClickListener {
lifecycleScope.launch {
val item = viewModel.fetchTodayWater().first()
Log.d(TAG, "Collected latest $it")
if (item != null) {
item.value -= 200
if (item.value == 0) {
viewModel.deleteWater(item)
} else {
viewModel.updateWater(item)
}
}
}
}
binding.listWeight.setOnClickListener { binding.listWeight.setOnClickListener {
findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavListWeight()) findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavListWeight())
} }
@ -126,33 +97,14 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
} }
private fun updateGraph(list: List<Weight>) { private fun updateGraph(list: List<Weight>) {
if (list.isNotEmpty()) { WeightChart.setup(
val entries = ArrayList<Entry>() binding.weightGraph,
for (item in list) { requireView(),
entries.add(Entry(item.timestamp.toFloat(), item.weight)) list,
} viewModel.weightUnit,
viewModel.goalWeight?.toFloat()
val dataSet = LineDataSet(entries, "Label").apply {
axisDependency = YAxis.AxisDependency.RIGHT
setDrawCircles(false)
setDrawCircleHole(false)
mode = LineDataSet.Mode.HORIZONTAL_BEZIER
}
binding.weightGraph.apply {
// Apply new dataset
data = LineData(dataSet)
// idk what I did but it works lol
setVisibleXRange(
0f, entries[entries.size - 1].x / 1000f
) )
val goal = PreferenceManager.getDefaultSharedPreferences(requireContext())
.getString("weight_goal", null)?.toFloatOrNull()
// legend.apply { // legend.apply {
// isEnabled = true // isEnabled = true
// form = Legend.LegendForm.LINE // form = Legend.LegendForm.LINE
@ -165,29 +117,6 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
// setCustom(arrayOf(legendEntry)) // setCustom(arrayOf(legendEntry))
// } // }
// } // }
setDrawBorders(false)
// BIS... :(
// Also it invalidate the view so I don't have to call invalidate
moveViewToX(entries[entries.size - 1].x - 1600000000f)
if (goal != null) {
axisRight.axisMinimum = goal
val limit = LimitLine(goal)
limit.lineColor = Color.RED
val dash = 30f
limit.enableDashedLine(dash, dash, 0f)
limit.lineWidth = 1f
limit.textColor = Color.BLACK
limit.textSize = 12f
axisRight.addLimitLine(limit)
} else {
isAutoScaleMinMaxEnabled = true
}
}
}
} }
override fun onStart() { override fun onStart() {
@ -197,23 +126,28 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
viewModel.fetchWeights().collectLatest { viewModel.fetchWeights().collectLatest {
updateGraph(it) updateGraph(it)
} }
updateWater(0) updateWater(0, 1)
updateWater(1234)
} }
viewModel.water.observe(viewLifecycleOwner) { viewModel.water.observe(viewLifecycleOwner) {
Log.d(TAG, "${it?.formatTimestamp()} $it")
if (it != null) { if (it != null) {
updateWater(it.value) updateWater(0, it.value)
} else { } else {
updateWater(0) updateWater(0, 1)
} }
} }
} }
private fun updateWater(water: Int) { private fun updateWater(oldValue: Int, newValue: Int) {
val oldValue = binding.fragmentHomeWaterCurrent.text.toString().replace("ml", "").toInt()
binding.fragmentHomeWaterCurrent.text = "${water}ml" val waterUnit =
UnitFactory.volume(settings.getString("water_unit", "milliliter") ?: "Milliliter")
binding.fragmentHomeWaterCurrent.text =
String.format(
resources.getString(waterUnit.unit),
(newValue * waterUnit.modifier).toInt()
)
var width = 1500 var width = 1500
var height = 750 var height = 750
@ -260,14 +194,16 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
3f 3f
) )
Log.d("Test", "${min(oldValue.toFloat(), intake)} ${min(water.toFloat(), intake)}") ValueAnimator.ofInt(
ValueAnimator.ofFloat(min(oldValue.toFloat(), intake), min(water.toFloat(), intake)) min(oldValue, viewModel.dailyWaterIntake),
.apply { min(newValue, viewModel.dailyWaterIntake)
).apply {
duration = 300 duration = 300
addUpdateListener { addUpdateListener {
DrawUtils.drawArc( DrawUtils.drawArc(
canvas, canvas,
100 * it.animatedValue as Float / intake, 100 * it.animatedValue as Int / viewModel.dailyWaterIntake.toFloat(),
rect, rect,
MaterialColors.getColor( MaterialColors.getColor(
requireView(), requireView(),

View File

@ -1,30 +1,62 @@
package com.dzeio.openhealth.ui.home package com.dzeio.openhealth.ui.home
import android.content.SharedPreferences
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.dzeio.openhealth.Application.Companion.TAG
import com.dzeio.openhealth.core.BaseViewModel import com.dzeio.openhealth.core.BaseViewModel
import com.dzeio.openhealth.data.water.Water import com.dzeio.openhealth.data.water.Water
import com.dzeio.openhealth.data.water.WaterRepository import com.dzeio.openhealth.data.water.WaterRepository
import com.dzeio.openhealth.data.weight.Weight import com.dzeio.openhealth.data.weight.Weight
import com.dzeio.openhealth.data.weight.WeightRepository import com.dzeio.openhealth.data.weight.WeightRepository
import com.dzeio.openhealth.units.UnitFactory
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel @HiltViewModel
class HomeViewModel @Inject internal constructor( class HomeViewModel @Inject internal constructor(
private val weightRepository: WeightRepository, private val weightRepository: WeightRepository,
private val waterRepository: WaterRepository private val waterRepository: WaterRepository,
settings: SharedPreferences
) : BaseViewModel() { ) : BaseViewModel() {
private val _water = MutableLiveData<Water?>(null)
val water: LiveData<Water?> = _water
var waterCupSize = settings.getInt("water_cup_size", 200)
var waterUnit =
UnitFactory.volume(settings.getString("water_unit", "milliliter") ?: "Milliliter")
var weightUnit =
UnitFactory.mass(settings.getString("weight_unit", "kilogram") ?: "kilogram")
val goalWeight: Int? =
(settings.getString("weight_goal", null)?.toIntOrNull())
val dailyWaterIntake: Int =
((settings.getString("water_intake", "1200")?.toFloatOrNull() ?: 1200f) * waterUnit.modifier)
.toInt()
init { init {
viewModelScope.launch { viewModelScope.launch {
waterRepository.todayWater().collectLatest { waterRepository.todayWater().collectLatest {
_water.value = it _water.value = it
} }
} }
// don't listen for prefs changes
settings.registerOnSharedPreferenceChangeListener { _, key ->
Log.d(TAG, "Pref changed: $key")
when (key) {
"water_cup_size" -> {
waterCupSize = settings.getInt("water_cup_size", 200)
}
}
}
} }
/** /**
@ -32,7 +64,6 @@ class HomeViewModel @Inject internal constructor(
*/ */
fun fetchWeights() = weightRepository.getWeights() fun fetchWeights() = weightRepository.getWeights()
/** /**
* @deprecated * @deprecated
*/ */
@ -44,11 +75,7 @@ class HomeViewModel @Inject internal constructor(
suspend fun addWeight(weight: Weight) = weightRepository.addWeight(weight) suspend fun addWeight(weight: Weight) = weightRepository.addWeight(weight)
suspend fun fetchTodayWater() = waterRepository.todayWater() fun fetchTodayWater() = waterRepository.todayWater()
private val _water = MutableLiveData<Water?>(null)
val water: LiveData<Water?> = _water
fun updateWater(water: Water) { fun updateWater(water: Water) {
viewModelScope.launch { viewModelScope.launch {

View File

@ -1,18 +1,99 @@
package com.dzeio.openhealth.ui.settings package com.dzeio.openhealth.ui.settings
import android.content.SharedPreferences
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import android.util.Log
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.dzeio.openhealth.BuildConfig
import com.dzeio.openhealth.R import com.dzeio.openhealth.R
import com.dzeio.openhealth.units.UnitFactory
import java.util.Locale
class SettingsFragment : PreferenceFragmentCompat() { class SettingsFragment : PreferenceFragmentCompat() {
val settings: SharedPreferences by lazy {
PreferenceManager.getDefaultSharedPreferences(requireContext())
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey) setPreferencesFromResource(R.xml.preferences, rootKey)
// Force only numbers on Goal
val weightGoal = findPreference<EditTextPreference>("weight_goal") val weightGoal = findPreference<EditTextPreference>("weight_goal")
weightGoal?.setOnBindEditTextListener { weightGoal?.apply {
setOnBindEditTextListener {
it.inputType = InputType.TYPE_CLASS_NUMBER it.inputType = InputType.TYPE_CLASS_NUMBER
} }
val value = settings.getString("weight_goal", null)
val modifier = UnitFactory.mass(settings.getString("weight_unit", null) ?: "kilogram")
if (value != null && value.isNotEmpty()) {
text = (value.toFloat() * modifier.modifier).toString()
}
setOnPreferenceChangeListener { _, newValue ->
val alue = ((newValue as String).toInt() / modifier.modifier).toInt().toString()
settings.edit()
.putString(
"weight_goal",
alue
)
.apply()
text = alue
return@setOnPreferenceChangeListener false
}
}
// 251 kg
// 553 lb
findPreference<ListPreference>("weight_unit")?.apply {
setOnPreferenceChangeListener { _, newValue ->
val unit = settings.getString("weight_unit", "kilogram")
?: return@setOnPreferenceChangeListener true
val goal = settings.getString("weight_goal", null)
?: return@setOnPreferenceChangeListener true
val modifier = UnitFactory.mass(newValue as String)
val oldModifier = UnitFactory.mass(unit)
val value =
(goal.toFloat() / oldModifier.modifier * modifier.modifier).toInt().toString()
settings.edit()
.putString(
"weight_goal",
value
)
.apply()
weightGoal?.text = value
return@setOnPreferenceChangeListener true
}
}
val languagesPreference = findPreference<ListPreference>("global_language")
Log.d("TAG", Locale.getDefault().language)
languagesPreference?.apply {
entries = BuildConfig.LOCALES
entryValues = BuildConfig.LOCALES
setDefaultValue(Locale.getDefault().language)
}
// Update App Locale
languagesPreference?.setOnPreferenceChangeListener { _, newValue ->
val locale = Locale(newValue as String)
Locale.setDefault(locale)
val config = Configuration()
config.locale = locale
requireActivity().baseContext.resources.updateConfiguration(
config,
requireActivity().baseContext.resources.displayMetrics
)
requireActivity().recreate()
return@setOnPreferenceChangeListener true
}
} }
} }

View File

@ -75,7 +75,6 @@ class EditWaterDialog :
water.timestamp = tsp water.timestamp = tsp
binding.date.setText(water.formatTimestamp()) binding.date.setText(water.formatTimestamp())
} }
datePicker.show(fragManager, "dialog") datePicker.show(fragManager, "dialog")
Log.d("Tag", "${date.year + 1900}, ${date.month}, ${date.day}") Log.d("Tag", "${date.year + 1900}, ${date.month}, ${date.day}")
@ -88,8 +87,6 @@ class EditWaterDialog :
} else { } else {
TODO("VERSION.SDK_INT < N") TODO("VERSION.SDK_INT < N")
} }
} }
viewModel.init(args.id) viewModel.init(args.id)
@ -133,6 +130,5 @@ class EditWaterDialog :
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
} }
} }

View File

@ -1,13 +1,11 @@
package com.dzeio.openhealth.ui.water package com.dzeio.openhealth.ui.water
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.dzeio.openhealth.Application.Companion.TAG
import com.dzeio.openhealth.adapters.WaterAdapter import com.dzeio.openhealth.adapters.WaterAdapter
import com.dzeio.openhealth.core.BaseFragment import com.dzeio.openhealth.core.BaseFragment
import com.dzeio.openhealth.databinding.FragmentMainWaterHomeBinding import com.dzeio.openhealth.databinding.FragmentMainWaterHomeBinding
@ -17,9 +15,6 @@ import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.BarEntry import com.github.mikephil.charting.data.BarEntry
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import java.util.Calendar
import java.util.Date
import java.util.TimeZone
@AndroidEntryPoint @AndroidEntryPoint
class WaterHomeFragment : class WaterHomeFragment :
@ -62,6 +57,10 @@ class WaterHomeFragment :
) )
) )
binding.buttonEditDefaultIntake.setOnClickListener {
findNavController().navigate(WaterHomeFragmentDirections.actionNavWaterHomeToNavWaterSizeDialog())
}
chart.xAxis.valueFormatter = GraphUtils.DateValueFormatter(1000 * 60 * 60 * 24) chart.xAxis.valueFormatter = GraphUtils.DateValueFormatter(1000 * 60 * 60 * 24)
viewModel.items.observe(viewLifecycleOwner) { list -> viewModel.items.observe(viewLifecycleOwner) { list ->

View File

@ -1,13 +1,102 @@
package com.dzeio.openhealth.ui.water package com.dzeio.openhealth.ui.water
import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import com.dzeio.openhealth.R
import com.dzeio.openhealth.core.BaseDialog import com.dzeio.openhealth.core.BaseDialog
import com.dzeio.openhealth.databinding.DialogWaterSizeSelectorBinding import com.dzeio.openhealth.databinding.DialogWaterSizeSelectorBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class WaterSizeSelectorDialog : class WaterSizeSelectorDialog :
BaseDialog<WaterSizeSelectorViewModel, DialogWaterSizeSelectorBinding>( BaseDialog<WaterSizeSelectorViewModel, DialogWaterSizeSelectorBinding>(
WaterSizeSelectorViewModel::class.java WaterSizeSelectorViewModel::class.java
) { ) {
override val bindingInflater: (LayoutInflater) -> DialogWaterSizeSelectorBinding override val bindingInflater: (LayoutInflater) -> DialogWaterSizeSelectorBinding
get() = DialogWaterSizeSelectorBinding::inflate get() = DialogWaterSizeSelectorBinding::inflate
override fun onCreated() {
super.onCreated()
binding.cancel.setOnClickListener {
dismiss()
}
binding.validate.setOnClickListener {
dismiss()
}
viewModel.cupSize.observe(this) {
binding.customSizeText.text = String.format(
getString(R.string.custom_amount),
"${it}ml"
)
binding.size100ml.setBackgroundColor(Color.TRANSPARENT)
binding.size250ml.setBackgroundColor(Color.TRANSPARENT)
binding.size500ml.setBackgroundColor(Color.TRANSPARENT)
binding.size1000ml.setBackgroundColor(Color.TRANSPARENT)
binding.customSize.setBackgroundColor(Color.TRANSPARENT)
val back = resources.getColor(
com.google.android.material.R.color.material_dynamic_primary95,
)
when (it) {
100 -> {
binding.size100ml.setBackgroundColor(back)
}
250 -> {
binding.size250ml.setBackgroundColor(back)
}
500 -> {
binding.size500ml.setBackgroundColor(back)
}
1000 -> {
binding.size1000ml.setBackgroundColor(back)
}
else -> {
binding.customSize.setBackgroundColor(back)
}
}
}
binding.size100ml.setOnClickListener {
viewModel.setCupSize(100)
}
binding.size250ml.setOnClickListener {
viewModel.setCupSize(250)
}
binding.size500ml.setOnClickListener {
viewModel.setCupSize(500)
}
binding.size1000ml.setOnClickListener {
viewModel.setCupSize(1000)
}
binding.customSize.setOnClickListener {
val editTextLayout = TextInputLayout(requireContext())
val editText = TextInputEditText(requireContext())
editText.setText(viewModel.cupSize.value.toString())
editTextLayout.addView(editText)
MaterialAlertDialogBuilder(requireContext())
.setView(editTextLayout)
.setTitle("Custom Cup Size")
.setOnCancelListener {
it.dismiss()
}
.setPositiveButton(
R.string.validate
) { dialog, _ ->
viewModel.setCupSize(editText.text.toString().toInt())
dismiss()
dialog.dismiss()
}
.show()
}
}
} }

View File

@ -1,6 +1,32 @@
package com.dzeio.openhealth.ui.water package com.dzeio.openhealth.ui.water
import android.content.SharedPreferences
import androidx.lifecycle.MutableLiveData
import com.dzeio.openhealth.core.BaseViewModel import com.dzeio.openhealth.core.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
class WaterSizeSelectorViewModel : BaseViewModel() { @HiltViewModel
class WaterSizeSelectorViewModel @Inject constructor(
private val settings: SharedPreferences
) : BaseViewModel() {
private val _cupSize = MutableLiveData(0)
val cupSize = _cupSize
init {
val cup = settings.getInt("water_cup_size", -1)
if (cup != -1) {
_cupSize.value = cup
}
}
fun setCupSize(value: Int) {
settings.edit()
.putInt("water_cup_size", value)
.apply()
_cupSize.value = value
}
} }

View File

@ -29,7 +29,6 @@ class AddWeightDialog : BaseDialog<HomeViewModel, DialogAddWeightBinding>(HomeVi
setNegativeButton("Cancel") { dialog, _ -> setNegativeButton("Cancel") { dialog, _ ->
dialog.cancel() dialog.cancel()
} }
} }
} }
@ -56,7 +55,6 @@ class AddWeightDialog : BaseDialog<HomeViewModel, DialogAddWeightBinding>(HomeVi
val weight = Weight().apply { val weight = Weight().apply {
weight = binding.kg.value + (binding.gram.value.toFloat() / 10) weight = binding.kg.value + (binding.gram.value.toFloat() / 10)
source = "OpenHealth" source = "OpenHealth"
} }
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
viewModel.addWeight(weight) viewModel.addWeight(weight)

View File

@ -2,15 +2,24 @@ package com.dzeio.openhealth.ui.weight
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.dzeio.openhealth.R
import com.dzeio.openhealth.adapters.WeightAdapter import com.dzeio.openhealth.adapters.WeightAdapter
import com.dzeio.openhealth.core.BaseFragment import com.dzeio.openhealth.core.BaseFragment
import com.dzeio.openhealth.data.weight.Weight
import com.dzeio.openhealth.databinding.FragmentListWeightBinding import com.dzeio.openhealth.databinding.FragmentListWeightBinding
import com.dzeio.openhealth.graphs.WeightChart
import com.dzeio.openhealth.ui.home.HomeViewModel import com.dzeio.openhealth.ui.home.HomeViewModel
import com.dzeio.openhealth.units.WeightUnit
import com.dzeio.openhealth.utils.GraphUtils
import com.google.android.material.color.MaterialColors
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
@ -21,15 +30,26 @@ class ListWeightFragment :
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentListWeightBinding = override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentListWeightBinding =
FragmentListWeightBinding::inflate FragmentListWeightBinding::inflate
val settings by lazy {
PreferenceManager.getDefaultSharedPreferences(requireContext())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
val recycler = binding.list val recycler = binding.list
val manager = LinearLayoutManager(requireContext()) val manager = LinearLayoutManager(requireContext())
recycler.layoutManager = manager recycler.layoutManager = manager
val adapter = WeightAdapter() val adapter = WeightAdapter()
val unit = settings.getString("weight_unit", "Kilogram") ?: "Kilogram"
adapter.unit = WeightUnit.fromSettings(unit)
adapter.onItemClick = { adapter.onItemClick = {
findNavController().navigate( findNavController().navigate(
ListWeightFragmentDirections.actionNavListWeightToNavEditWeight( ListWeightFragmentDirections.actionNavListWeightToNavEditWeight(
@ -42,12 +62,50 @@ class ListWeightFragment :
viewLifecycleOwner.lifecycleScope.launchWhenCreated { viewLifecycleOwner.lifecycleScope.launchWhenCreated {
viewModel.fetchWeights().collectLatest { viewModel.fetchWeights().collectLatest {
updateGraph(it)
val itt = it.toMutableList() val itt = it.toMutableList()
itt.sortWith { o1, o2 -> if (o1.timestamp > o2.timestamp) -1 else 1 } itt.sortWith { o1, o2 -> if (o1.timestamp > o2.timestamp) -1 else 1 }
adapter.set(itt) adapter.set(itt)
} }
} }
GraphUtils.lineChartSetup(
binding.chart,
MaterialColors.getColor(
requireView(),
com.google.android.material.R.attr.colorPrimary
),
MaterialColors.getColor(
requireView(),
com.google.android.material.R.attr.colorOnBackground
)
)
}
private fun updateGraph(list: List<Weight>) {
WeightChart.setup(
binding.chart,
requireView(),
list,
viewModel.weightUnit,
viewModel.goalWeight?.toFloat(),
false
)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.action_add).isVisible = true
super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_add -> {
findNavController().navigate(ListWeightFragmentDirections.actionNavListWeightToNavAddWeightDialog())
true
}
else -> super.onOptionsItemSelected(item)
}
} }
} }

View File

@ -0,0 +1,20 @@
package com.dzeio.openhealth.units
object UnitFactory {
fun mass(unit: String): Units.Mass {
return when (unit.lowercase()) {
"kilogram", "kilograms", "kg" -> Units.Mass.KILOGRAM
"pound", "pounds", "lb" -> Units.Mass.POUND
else -> Units.Mass.KILOGRAM
}
}
fun volume(unit: String): Units.Volume {
return when (unit.lowercase()) {
"milliliter", "milliliters", "ml" -> Units.Volume.MILLILITER
"imperial ounce", "imperial ounces", "oz" -> Units.Volume.IMPERIAL_OUNCE
"us ounce", "us ounces" -> Units.Volume.US_OUNCE
else -> Units.Volume.MILLILITER
}
}
}

View File

@ -0,0 +1,57 @@
package com.dzeio.openhealth.units
import com.dzeio.openhealth.R
object Units {
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(
0.45359237f,
R.string.unit_mass_pound_name_singular,
R.string.unit_mass_pound_name_plural,
R.string.unit_mass_pound_unit
)
}
enum class Volume(
/**
* Value based on the Kilogram
*/
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
)
}
}

View File

@ -0,0 +1,21 @@
package com.dzeio.openhealth.units
enum class WaterUnit(
val unit: String,
val fromML: Float
) {
ML("ml", 1f),
US_OZ("oz", 0.03381413f),
IMP_OZ("oz", 0.03519503f);
companion object {
fun fromSettings(value: String): WaterUnit {
return when (value.lowercase()) {
"milliliter" -> ML
"us ounce" -> US_OZ
"imperial ounce" -> IMP_OZ
else -> ML
}
}
}
}

View File

@ -0,0 +1,19 @@
package com.dzeio.openhealth.units
enum class WeightUnit(
val unit: String,
val fromKG: Float
) {
KG("kg", 1f),
LBS("lbs", 2.2046226218488f);
companion object {
fun fromSettings(value: String): WeightUnit {
return when (value.lowercase()) {
"kilogram" -> KG
"pounds" -> LBS
else -> KG
}
}
}
}

View File

@ -8,6 +8,7 @@ import com.github.mikephil.charting.components.Description
import com.github.mikephil.charting.components.XAxis import com.github.mikephil.charting.components.XAxis
import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData
import com.github.mikephil.charting.data.Entry import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineDataSet
import com.github.mikephil.charting.formatter.ValueFormatter import com.github.mikephil.charting.formatter.ValueFormatter
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -21,6 +22,14 @@ object GraphUtils {
// chart.isAutoScaleMinMaxEnabled = true // chart.isAutoScaleMinMaxEnabled = true
} }
fun lineDataSet(lineDataSet: LineDataSet): LineDataSet {
return lineDataSet.apply {
setDrawCircles(false)
setDrawCircleHole(false)
mode = LineDataSet.Mode.HORIZONTAL_BEZIER
}
}
fun barChartSetup(chart: BarChart, mainColor: Int, textColor: Int) { fun barChartSetup(chart: BarChart, mainColor: Int, textColor: Int) {
barLineChartSetup(chart, mainColor, textColor) barLineChartSetup(chart, mainColor, textColor)
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M3,16h5v-2H3V16zM9.5,16h5v-2h-5V16zM16,16h5v-2h-5V16zM3,20h2v-2H3V20zM7,20h2v-2H7V20zM11,20h2v-2h-2V20zM15,20h2v-2h-2V20zM19,20h2v-2h-2V20zM3,12h8v-2H3V12zM13,12h8v-2h-8V12zM3,4v4h18V4H3z"/>
</vector>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:pathData="M10.9,2.1c-4.6,0.5 -8.3,4.2 -8.8,8.7c-0.5,4.7 2.2,8.9 6.3,10.5C8.7,21.4 9,21.2 9,20.8v-1.6c0,0 -0.4,0.1 -0.9,0.1c-1.4,0 -2,-1.2 -2.1,-1.9c-0.1,-0.4 -0.3,-0.7 -0.6,-1C5.1,16.3 5,16.3 5,16.2C5,16 5.3,16 5.4,16c0.6,0 1.1,0.7 1.3,1c0.5,0.8 1.1,1 1.4,1c0.4,0 0.7,-0.1 0.9,-0.2c0.1,-0.7 0.4,-1.4 1,-1.8c-2.3,-0.5 -4,-1.8 -4,-4c0,-1.1 0.5,-2.2 1.2,-3C7.1,8.8 7,8.3 7,7.6C7,7.2 7,6.6 7.3,6c0,0 1.4,0 2.8,1.3C10.6,7.1 11.3,7 12,7s1.4,0.1 2,0.3C15.3,6 16.8,6 16.8,6C17,6.6 17,7.2 17,7.6c0,0.8 -0.1,1.2 -0.2,1.4c0.7,0.8 1.2,1.8 1.2,3c0,2.2 -1.7,3.5 -4,4c0.6,0.5 1,1.4 1,2.3v2.6c0,0.3 0.3,0.6 0.7,0.5c3.7,-1.5 6.3,-5.1 6.3,-9.3C22,6.1 16.9,1.4 10.9,2.1z"
android:fillColor="@android:color/white"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M14.06,9.02l0.92,0.92L5.92,19L5,19v-0.92l9.06,-9.06M17.66,3c-0.25,0 -0.51,0.1 -0.7,0.29l-1.83,1.83 3.75,3.75 1.83,-1.83c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.2,-0.2 -0.45,-0.29 -0.71,-0.29zM14.06,6.19L3,17.25L3,21h3.75L17.81,9.94l-3.75,-3.75z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M4,19h16v2L4,21zM20,3L4,3v10c0,2.21 1.79,4 4,4h6c2.21,0 4,-1.79 4,-4v-3h2c1.11,0 2,-0.9 2,-2L22,5c0,-1.11 -0.89,-2 -2,-2zM16,13c0,1.1 -0.9,2 -2,2L8,15c-1.1,0 -2,-0.9 -2,-2L6,5h10v8zM20,8h-2L18,5h2v3z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M3,2l2.01,18.23C5.13,21.23 5.97,22 7,22h10c1.03,0 1.87,-0.77 1.99,-1.77L21,2L3,2zM17,20l-10,0.01L5.89,10L18.1,10L17,20zM18.33,8L5.67,8l-0.44,-4h13.53l-0.43,4zM12,19c1.66,0 3,-1.34 3,-3 0,-2 -3,-5.4 -3,-5.4S9,14 9,16c0,1.66 1.34,3 3,3zM12,13.91c0.59,0.91 1,1.73 1,2.09 0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1c0,-0.37 0.41,-1.19 1,-2.09z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M22,6c0,-1.1 -0.9,-2 -2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6zM20,6l-8,4.99L4,6h16zM20,18L4,18L4,8l8,5 8,-5v10z"/>
</vector>

View File

@ -1,7 +1,182 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout 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_width="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:padding="24dp"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_outline_local_drink_24" />
<TextView
style="@style/TextAppearance.Material3.HeadlineSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:textAlignment="center"
android:text="@string/switch_cup_size" />
<TableLayout
android:layout_width="match_parent"
android:layout_marginBottom="8dp"
android:layout_height="wrap_content">
<TableRow
android:layout_width="match_parent"
android:layout_marginBottom="8dp"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:id="@+id/size_100ml"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_outline_local_drink_24" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.Material3.BodyLarge"
android:layout_marginStart="8dp"
android:text="100ml" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:id="@+id/size_250ml"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:gravity="center"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_outline_local_drink_24" />
<TextView
android:layout_width="match_parent"
style="@style/TextAppearance.Material3.BodyLarge"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="250ml" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/size_500ml"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginEnd="8dp"
android:padding="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_outline_local_drink_24" />
<TextView
android:layout_width="match_parent"
style="@style/TextAppearance.Material3.BodyLarge"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="500ml" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/size_1000ml"
android:layout_marginStart="8dp"
android:gravity="center"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_outline_local_drink_24" />
<TextView
android:layout_width="match_parent"
style="@style/TextAppearance.Material3.BodyLarge"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="1000ml" />
</LinearLayout>
</TableRow>
</TableLayout>
<LinearLayout
android:layout_width="match_parent"
android:id="@+id/custom_size"
android:layout_height="wrap_content"
android:background="@color/material_dynamic_primary90"
android:gravity="center"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_outline_edit_24" />
<TextView
android:layout_width="match_parent"
android:id="@+id/custom_size_text"
android:layout_height="wrap_content"
style="@style/TextAppearance.Material3.BodyLarge"
android:layout_marginStart="8dp"
android:text="@string/custom_amount"
tools:text="Custom amount: 100ml" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<Button
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_marginEnd="16dp"
android:text="@android:string/cancel" />
<Button
android:id="@+id/validate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/validate" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:layout_margin="16dp"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="32dp"
app:srcCompat="@drawable/ic_logo_full" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/tagline"
android:textAlignment="center" />
<TextView
android:id="@+id/version"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="8dp"
android:drawablePadding="16dp"
android:gravity="center_vertical"
android:text="@string/version_number"
app:drawableStartCompat="@drawable/ic_baseline_extension_24" />
<TextView
android:id="@+id/github"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="8dp"
android:drawablePadding="16dp"
android:gravity="center_vertical"
android:text="@string/about_star_on_github"
app:drawableStartCompat="@drawable/ic_logo_github" />
<TextView
android:id="@+id/contact_us"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="8dp"
android:drawablePadding="16dp"
android:gravity="center_vertical"
android:text="@string/contact_us"
app:drawableStartCompat="@drawable/ic_outline_mail_24" />
<TextView
android:id="@+id/licenses"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="8dp"
android:drawablePadding="16dp"
android:gravity="center_vertical"
android:text="@string/licenses"
app:drawableStartCompat="@drawable/ic_baseline_line_style_24" />
</LinearLayout>

View File

@ -15,7 +15,7 @@
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewFilledStyle" style="?attr/materialCardViewFilledStyle"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
@ -40,7 +40,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Weight" /> android:text="@string/water_intake" />
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -48,12 +48,6 @@
android:layout_weight="1" android:layout_weight="1"
android:gravity="end"> android:gravity="end">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginRight="16dp"
android:src="@drawable/ic_baseline_add_24" />
<ImageView <ImageView
android:id="@+id/goto_water_home" android:id="@+id/goto_water_home"
android:layout_width="24dp" android:layout_width="24dp"
@ -75,7 +69,7 @@
android:id="@+id/fragment_home_water_remove" android:id="@+id/fragment_home_water_remove"
android:layout_width="18dp" android:layout_width="18dp"
android:layout_height="18dp" android:layout_height="18dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="8dp"
android:layout_weight="1" android:layout_weight="1"
android:src="@drawable/ic_outline_hexagon_24" android:src="@drawable/ic_outline_hexagon_24"
app:layout_constraintBottom_toBottomOf="@+id/linearLayout" app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
@ -98,7 +92,7 @@
style="@style/TextAppearance.Material3.LabelMedium" style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:text="900ml" android:text="@string/unit_volume_milliliter_unit"
android:textAlignment="center" /> android:textAlignment="center" />
@ -107,7 +101,7 @@
style="@style/TextAppearance.Material3.LabelMedium" style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:text="1200ml" android:text="@string/unit_volume_milliliter_unit"
android:textAlignment="center" /> android:textAlignment="center" />
</LinearLayout> </LinearLayout>
@ -117,7 +111,7 @@
android:id="@+id/fragment_home_water_add" android:id="@+id/fragment_home_water_add"
android:layout_width="18dp" android:layout_width="18dp"
android:layout_height="18dp" android:layout_height="18dp"
android:layout_marginStart="16dp" android:layout_marginStart="8dp"
android:layout_weight="1" android:layout_weight="1"
android:src="@drawable/ic_baseline_add_24" android:src="@drawable/ic_baseline_add_24"
app:layout_constraintBottom_toBottomOf="@+id/linearLayout" app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
@ -180,7 +174,7 @@
style="@style/TextAppearance.Material3.TitleMedium" style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Weight" android:text="@string/weight"
android:layout_weight="1" /> android:layout_weight="1" />
<LinearLayout <LinearLayout
@ -193,7 +187,7 @@
android:id="@+id/add_weight" android:id="@+id/add_weight"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginRight="16dp" android:layout_marginEnd="16dp"
android:src="@drawable/ic_baseline_add_24" /> android:src="@drawable/ic_baseline_add_24" />
<ImageView <ImageView
@ -203,7 +197,6 @@
android:src="@drawable/ic_outline_hexagon_24" /> android:src="@drawable/ic_outline_hexagon_24" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<com.github.mikephil.charting.charts.LineChart <com.github.mikephil.charting.charts.LineChart
@ -212,16 +205,6 @@
android:layout_height="200dp" android:layout_height="200dp"
android:minHeight="200dp" /> android:minHeight="200dp" />
<!-- <com.jjoe64.graphview.GraphView-->
<!-- android:id="@+id/weight_graph"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="200dp"-->
<!-- android:minHeight="200dp"-->
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toBottomOf="@+id/constraintLayout2" />-->
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>

View File

@ -1,14 +1,36 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:clipToPadding="false" style="?attr/materialCardViewFilledStyle"
android:id="@+id/list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp">
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/chart"
android:layout_width="match_parent"
android:layout_height="200dp"
android:minHeight="200dp" />
</com.google.android.material.card.MaterialCardView>
<androidx.recyclerview.widget.RecyclerView
android:clipToPadding="false"
android:id="@+id/list"
android:padding="16dp" android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/layout_item_list" tools:listitem="@layout/layout_item_list"
tools:context=".ui.weight.ListWeightFragment" /> tools:context=".ui.weight.ListWeightFragment" />
</LinearLayout>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar"
android:gravity="bottom"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/nav_header_desc"
android:paddingTop="@dimen/nav_header_vertical_spacing"
app:srcCompat="@mipmap/ic_launcher_round" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text="@string/nav_header_title"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/nav_header_subtitle" />
</LinearLayout>

View File

@ -1,16 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_add"
android:visible="false"
android:icon="@drawable/ic_baseline_add_24"
android:title="@string/add"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/action_settings" android:id="@+id/action_settings"
android:orderInCategory="100" android:title="@string/page_settings"
android:title="@string/action_settings"
app:showAsAction="ifRoom"
android:icon="@drawable/ic_baseline_settings_24"/> android:icon="@drawable/ic_baseline_settings_24"/>
<item <item
android:id="@+id/action_extensions" android:id="@+id/action_extensions"
android:title="@string/menu_extensions" android:title="@string/menu_extensions"
app:showAsAction="ifRoom" android:icon="@drawable/ic_baseline_extension_24" />
android:icon="@drawable/ic_baseline_extension_24"
/> <item
android:id="@+id/action_about"
android:title="@string/about" />
</menu> </menu>

View File

@ -38,6 +38,12 @@
app:exitAnim="@android:anim/slide_out_right" app:exitAnim="@android:anim/slide_out_right"
app:popEnterAnim="@android:anim/slide_in_left" app:popEnterAnim="@android:anim/slide_in_left"
app:popExitAnim="@android:anim/slide_out_right" /> app:popExitAnim="@android:anim/slide_out_right" />
<action
android:id="@+id/action_nav_home_to_nav_add_weight_dialog"
app:destination="@id/nav_add_weight_dialog" />
<action
android:id="@+id/action_nav_home_to_aboutFragment"
app:destination="@id/aboutFragment" />
</fragment> </fragment>
<fragment <fragment
@ -53,7 +59,7 @@
<fragment <fragment
android:id="@+id/nav_list_weight" android:id="@+id/nav_list_weight"
android:name="com.dzeio.openhealth.ui.weight.ListWeightFragment" android:name="com.dzeio.openhealth.ui.weight.ListWeightFragment"
android:label="@string/menu_list_weight" android:label="@string/weight"
tools:layout="@layout/fragment_list_weight" > tools:layout="@layout/fragment_list_weight" >
<action <action
android:id="@+id/action_nav_list_weight_to_nav_edit_weight" android:id="@+id/action_nav_list_weight_to_nav_edit_weight"
@ -62,6 +68,9 @@
app:exitAnim="@android:anim/slide_out_right" app:exitAnim="@android:anim/slide_out_right"
app:popEnterAnim="@android:anim/slide_in_left" app:popEnterAnim="@android:anim/slide_in_left"
app:popExitAnim="@android:anim/slide_out_right" /> app:popExitAnim="@android:anim/slide_out_right" />
<action
android:id="@+id/action_nav_list_weight_to_nav_add_weight_dialog"
app:destination="@id/nav_add_weight_dialog" />
</fragment> </fragment>
<fragment <fragment
@ -88,11 +97,45 @@
app:exitAnim="@android:anim/slide_out_right" app:exitAnim="@android:anim/slide_out_right"
app:popEnterAnim="@android:anim/slide_in_left" app:popEnterAnim="@android:anim/slide_in_left"
app:popExitAnim="@android:anim/slide_out_right" /> app:popExitAnim="@android:anim/slide_out_right" />
<action
android:id="@+id/action_nav_water_home_to_nav_water_size_dialog"
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:destination="@id/nav_water_size_dialog" />
</fragment> </fragment>
<dialog
android:id="@+id/nav_water_size_dialog"
android:name="com.dzeio.openhealth.ui.water.WaterSizeSelectorDialog"
tools:layout="@layout/dialog_water_size_selector"
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">
</dialog>
<dialog
android:id="@+id/nav_add_weight_dialog"
android:name="com.dzeio.openhealth.ui.weight.AddWeightDialog"
tools:layout="@layout/dialog_water_size_selector"
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">
</dialog>
<fragment <fragment
android:id="@+id/nav_settings" android:id="@+id/nav_settings"
android:name="com.dzeio.openhealth.ui.settings.SettingsFragment"> android:label="@string/page_settings"
android:name="com.dzeio.openhealth.ui.settings.SettingsFragment"
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">
</fragment> </fragment>
@ -125,4 +168,9 @@
app:argType="string" /> app:argType="string" />
</fragment> </fragment>
<fragment
android:id="@+id/aboutFragment"
android:name="com.dzeio.openhealth.ui.about.AboutFragment"
android:label="AboutFragment"
tools:layout="@layout/fragment_about"/>
</navigation> </navigation>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="unit_mass_kilogram_name_singular">Kilograme</string>
<string name="unit_mass_kilogram_name_plural">Kilogrames</string>
<string name="unit_mass_pound_name_singular">Livre</string>
<string name="unit_mass_pound_name_plural">Livres</string>
<string name="unit_volume_milliliter_name_singular">Millilitre</string>
<string name="unit_volume_milliliter_name_plural">Millilitres</string>
<string name="unit_volume_imperial_ounce_name_singular">Once Impérial</string>
<string name="unit_volume_imperial_ounce_name_plural">OncesImpérial</string>
<string name="unit_volume_us_ounce_name_singular">Once États Unis</string>
<string name="unit_volume_us_ounce_name_plural">Onces États Unis</string>
<string name="extension_informations">Les importations sont faites au démarrage de l\'application\nLes exports sont faits lorsque qu\'il y a des nouvelles valeurs dans l\'application</string>
<string name="menu_home">Accueil</string>
<string name="page_settings">Paramètres</string>
<string name="nav_water_home">Truc D\'eau</string>
<string name="languages">Langages</string>
<string name="settings_global">Paramètres Globaux</string>
<string name="weight">Poids</string>
<string name="water_intake">Consomation d\'eau</string>
<string name="switch_cup_size">Changer la taille du verre</string>
<string name="validate">Valider</string>
<string name="custom_amount">Montant personnalisée: %1$s</string>
<string name="menu_import">Importer</string>
<string name="menu_edit_weight">Changer le poid</string>
<string name="menu_extensions">Extensions</string>
<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="about_star_on_github">Mettez une étoile sur Github</string>
<string name="contact_us">Contactez-nous</string>
<string name="licenses">Licenses</string>
</resources>

View File

@ -4,8 +4,13 @@
<item>Male</item> <item>Male</item>
<item>Female</item> <item>Female</item>
</string-array> </string-array>
<string-array name="weight_units">
<item name="kg">@string/unit_mass_kilogram_name_plural</item>
<item name="lb">@string/unit_mass_pound_name_singular</item>
</string-array>
<string-array name="water_units"> <string-array name="water_units">
<item>Millimeters</item> <item name="ml">@string/unit_volume_milliliter_name_singular</item>
<item>Ounces</item> <item name="us_oz">@string/unit_volume_imperial_ounce_name_singular</item>
<item name="im_oz">@string/unit_volume_us_ounce_name_singular</item>
</string-array> </string-array>
</resources> </resources>

View File

@ -1,20 +1,44 @@
<resources> <resources>
<string name="app_name">OpenHealth</string> <string name="app_name" translatable="false">OpenHealth</string>
<string name="navigation_drawer_open">Open navigation drawer</string> <string name="tagline">Your privacy-friendly FOSS Health Application </string>
<string name="navigation_drawer_close">Close navigation drawer</string> <string name="page_settings">Settings</string>
<string name="nav_header_title">Android Studio</string>
<string name="nav_header_subtitle">android.studio@android.com</string>
<string name="nav_header_desc">Navigation header</string>
<string name="action_settings">Settings</string>
<string name="menu_home">Home</string> <string name="menu_home">Home</string>
<string name="menu_gallery">Gallery</string>
<string name="menu_slideshow">Slideshow</string>
<string name="menu_import">Import</string> <string name="menu_import">Import</string>
<string name="menu_list_weight">Weight List</string>
<string name="menu_edit_weight">Edit Weight</string> <string name="menu_edit_weight">Edit Weight</string>
<string name="nav_list_water">Water Intake</string>
<string name="nav_water_home">Water Intake</string> <string name="nav_water_home">Water Intake</string>
<string name="menu_extensions">Extensions</string> <string name="menu_extensions">Extensions</string>
<string name="extension_informations">Imports are done at app startup\nExports are done when new inputs are give to the app</string> <string name="extension_informations">Imports are done at app startup\nExports are done when new inputs are give to the app</string>
<!-- 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_pound_name_singular">Pound</string>
<string name="unit_mass_pound_name_plural">Pounds</string>
<string name="unit_mass_pound_unit" translatable="false">%1$slb</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_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="languages">Languages</string>
<string name="settings_global">Global Settings</string>
<string name="weight">Weight</string>
<string name="water_intake">Water Intake</string>
<string name="switch_cup_size">Switch cup size</string>
<string name="validate">Validate</string>
<string name="custom_amount">Custom amount: %1$s</string>
<string name="add">Add</string>
<string name="about">About</string>
<string name="version_number" formatted="false">Version %1$s</string>
<string name="about_star_on_github">Star on Github</string>
<string name="contact_us">Contact us</string>
<string name="licenses">Licenses</string>
</resources> </resources>

View File

@ -1,13 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Global Settings"> <PreferenceCategory android:title="@string/settings_global">
<ListPreference <ListPreference
android:defaultValue="Male" android:defaultValue="Male"
android:entries="@array/genders" android:entries="@array/genders"
android:entryValues="@array/genders" android:entryValues="@array/genders"
android:key="global_gender" android:key="global_gender"
android:title="Gender" /> android:title="Gender" />
<ListPreference
android:key="global_language"
android:title="@string/languages" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="Weight Settings"> <PreferenceCategory android:title="Weight Settings">
@ -24,6 +28,14 @@
android:digits="0123456789" android:digits="0123456789"
android:inputType="numberDecimal" android:inputType="numberDecimal"
android:title="Goal Weight" /> android:title="Goal Weight" />
<ListPreference
android:defaultValue="1"
android:entries="@array/weight_units"
android:entryValues="@array/weight_units"
android:key="weight_unit"
android:title="Weight Unit" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="Water Settings"> <PreferenceCategory android:title="Water Settings">