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 -->
# Contributing to TCGdex
# Contributing to OpenHealth
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
[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
<!--
> 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>.
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:
- Open an [Issue](https://github.com/dzeiocom/OpenHealth/issues/new).
- 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.
@ -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.
- 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)).
- 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).
- 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/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.
- Collect information about the bug:
- Stack trace (Traceback)
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
- OS, Platform and Version (Phone, OS Version, App Version, etc...)
- Possibly your input and the output
- 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?
> 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. -->
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.
- 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.
Once it's filed:
Once it's filled:
- 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.
@ -106,28 +108,27 @@ Once it's filed:
### 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 -->
#### Before Submitting an Enhancement
- 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/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.
- 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.
- 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 -->
#### 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.
- 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.
- 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 -->
- **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.
- 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 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. -->
@ -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_
- 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 !
- 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
### Coding Guidelines
@ -184,4 +181,4 @@ In short, please name your Pull Requests/Commits following this format
## 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">
<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>
# ![App Icon](./app/src/main/ic_launcher-playstore.png) Open Health
# Open Health
Your privacy-friendly FOSS Health Application
TODO: Make a good text here
TODO: Change CONTRIBUTING.md to be correct
<a href="https://f-droid.org/repository/browse/?fdid=com.dzeio.openhealth" target="_blank">
<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
@ -31,9 +46,7 @@ See [CONTRIBUTING.md](https://github.com/dzeiocom/OpenHealth/blob/master/CONTRIB
TL::DR
- Fork
- Commit your changes
- Pull Request on this Repository
## License

View File

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

View File

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

View File

@ -1,8 +1,13 @@
package com.dzeio.openhealth
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 dagger.hilt.android.HiltAndroidApp
import java.util.Locale
@HiltAndroidApp
class Application : Application() {
@ -15,5 +20,16 @@ class Application : Application() {
// Android Dynamics Colors
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)
.cancelAllWork()
WaterReminderService.setup(this)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -107,7 +106,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
Log.e("MainActivity", "Error Creating Notification Channel", e)
}
}
}
}
@ -125,9 +123,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
)
true
}
R.id.action_about -> {
navController.navigate(
HomeFragmentDirections.actionNavHomeToAboutFragment()
)
true
}
else -> super.onOptionsItemSelected(item)
}
}
}

View File

@ -20,7 +20,7 @@ class WaterAdapter() : BaseAdapter<Water, LayoutItemListBinding>() {
position: Int
) {
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 {
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.data.weight.Weight
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
get() = LayoutItemListBinding::inflate
@ -19,7 +22,8 @@ class WeightAdapter() : BaseAdapter<Weight, LayoutItemListBinding>() {
item: Weight,
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.edit.setOnClickListener {
onItemClick?.invoke(item)

View File

@ -1,48 +1,14 @@
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.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 {
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
) {
// 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 androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.navigation.NavDeepLinkBuilder
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkerParameters
import com.dzeio.openhealth.Application
@ -42,23 +43,24 @@ class WaterReminderService(
Log.d(TAG, "Ran! ${Date().toLocaleString()}")
with(NotificationManagerCompat.from(context)) {
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 =
NotificationCompat.Builder(context, NotificationChannels.DEFAULT.channelName)
NotificationCompat.Builder(context, NotificationChannels.WATER.id)
.setContentTitle("Did you drink?")
.setContentText("Drink now! ${Date().toLocaleString()}")
.setSmallIcon(R.drawable.ic_logo_small)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(
PendingIntent.getActivity(
context,
0,
Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
},
flag
)
).build()
intent
)
.build()
notify(
NotificationIds.WaterIntake.ordinal,
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
import android.animation.ValueAnimator
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.RectF
import android.os.Bundle
import android.util.Log
@ -13,74 +13,61 @@ import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import com.dzeio.openhealth.Application
import com.dzeio.openhealth.core.BaseFragment
import com.dzeio.openhealth.data.water.Water
import com.dzeio.openhealth.data.weight.Weight
import com.dzeio.openhealth.databinding.FragmentHomeBinding
import com.dzeio.openhealth.graphs.WeightChart
import com.dzeio.openhealth.ui.weight.AddWeightDialog
import com.dzeio.openhealth.units.UnitFactory
import com.dzeio.openhealth.utils.DrawUtils
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 dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlin.math.min
import kotlin.properties.Delegates
import kotlinx.coroutines.flow.collectLatest
@AndroidEntryPoint
class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewModel::class.java) {
companion object {
const val TAG = "${Application.TAG}/HomeFragment"
}
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentHomeBinding
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?) {
super.onViewCreated(view, savedInstanceState)
// Bindings
binding.addWeight.setOnClickListener {
AddWeightDialog().show(requireActivity().supportFragmentManager, null)
}
binding.fragmentHomeWaterAdd.setOnClickListener {
val water = viewModel.water.value
if (water == null) {
if (water == null || !water.isToday()) {
val w = Water()
w.value = 200
w.value = viewModel.waterCupSize
viewModel.updateWater(w)
} else {
water.value += 200
water.value += viewModel.waterCupSize
viewModel.updateWater(water)
}
}
intake = PreferenceManager.getDefaultSharedPreferences(requireContext())
.getString("water_intake", "1200")?.toFloat() ?: 1200f
binding.fragmentHomeWaterTotal.text = "${intake.toInt()}ml"
binding.fragmentHomeWaterRemove.setOnClickListener { _ ->
binding.fragmentHomeWaterTotal.text =
String.format(
resources.getString(viewModel.waterUnit.unit),
viewModel.dailyWaterIntake
)
binding.fragmentHomeWaterRemove.setOnClickListener {
val water = viewModel.water.value
if (water != null) {
water.value -= 200
if (water.value == 0) {
water.value -= viewModel.waterCupSize
if (water.value <= 0) {
viewModel.deleteWater(water)
} else {
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 {
findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavListWeight())
}
@ -126,32 +97,13 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
}
private fun updateGraph(list: List<Weight>) {
if (list.isNotEmpty()) {
val entries = ArrayList<Entry>()
for (item in list) {
entries.add(Entry(item.timestamp.toFloat(), item.weight))
}
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()
WeightChart.setup(
binding.weightGraph,
requireView(),
list,
viewModel.weightUnit,
viewModel.goalWeight?.toFloat()
)
// legend.apply {
// isEnabled = true
@ -165,29 +117,6 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
// 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() {
@ -197,23 +126,28 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
viewModel.fetchWeights().collectLatest {
updateGraph(it)
}
updateWater(0)
updateWater(1234)
updateWater(0, 1)
}
viewModel.water.observe(viewLifecycleOwner) {
Log.d(TAG, "${it?.formatTimestamp()} $it")
if (it != null) {
updateWater(it.value)
updateWater(0, it.value)
} else {
updateWater(0)
updateWater(0, 1)
}
}
}
private fun updateWater(water: Int) {
val oldValue = binding.fragmentHomeWaterCurrent.text.toString().replace("ml", "").toInt()
binding.fragmentHomeWaterCurrent.text = "${water}ml"
private fun updateWater(oldValue: Int, newValue: Int) {
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 height = 750
@ -260,25 +194,27 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
3f
)
Log.d("Test", "${min(oldValue.toFloat(), intake)} ${min(water.toFloat(), intake)}")
ValueAnimator.ofFloat(min(oldValue.toFloat(), intake), min(water.toFloat(), intake))
.apply {
duration = 300
addUpdateListener {
DrawUtils.drawArc(
canvas,
100 * it.animatedValue as Float / intake,
rect,
MaterialColors.getColor(
requireView(),
com.google.android.material.R.attr.colorPrimary
),
6f
)
canvas.save()
binding.background.setImageBitmap(graph)
}
start()
ValueAnimator.ofInt(
min(oldValue, viewModel.dailyWaterIntake),
min(newValue, viewModel.dailyWaterIntake)
).apply {
duration = 300
addUpdateListener {
DrawUtils.drawArc(
canvas,
100 * it.animatedValue as Int / viewModel.dailyWaterIntake.toFloat(),
rect,
MaterialColors.getColor(
requireView(),
com.google.android.material.R.attr.colorPrimary
),
6f
)
canvas.save()
binding.background.setImageBitmap(graph)
}
start()
}
}
}

View File

@ -1,30 +1,62 @@
package com.dzeio.openhealth.ui.home
import android.content.SharedPreferences
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.dzeio.openhealth.Application.Companion.TAG
import com.dzeio.openhealth.core.BaseViewModel
import com.dzeio.openhealth.data.water.Water
import com.dzeio.openhealth.data.water.WaterRepository
import com.dzeio.openhealth.data.weight.Weight
import com.dzeio.openhealth.data.weight.WeightRepository
import com.dzeio.openhealth.units.UnitFactory
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class HomeViewModel @Inject internal constructor(
private val weightRepository: WeightRepository,
private val waterRepository: WaterRepository
private val waterRepository: WaterRepository,
settings: SharedPreferences
) : 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 {
viewModelScope.launch {
waterRepository.todayWater().collectLatest {
_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()
/**
* @deprecated
*/
@ -44,11 +75,7 @@ class HomeViewModel @Inject internal constructor(
suspend fun addWeight(weight: Weight) = weightRepository.addWeight(weight)
suspend fun fetchTodayWater() = waterRepository.todayWater()
private val _water = MutableLiveData<Water?>(null)
val water: LiveData<Water?> = _water
fun fetchTodayWater() = waterRepository.todayWater()
fun updateWater(water: Water) {
viewModelScope.launch {

View File

@ -1,18 +1,99 @@
package com.dzeio.openhealth.ui.settings
import android.content.SharedPreferences
import android.content.res.Configuration
import android.os.Bundle
import android.text.InputType
import android.util.Log
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.dzeio.openhealth.BuildConfig
import com.dzeio.openhealth.R
import com.dzeio.openhealth.units.UnitFactory
import java.util.Locale
class SettingsFragment : PreferenceFragmentCompat() {
val settings: SharedPreferences by lazy {
PreferenceManager.getDefaultSharedPreferences(requireContext())
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
// Force only numbers on Goal
val weightGoal = findPreference<EditTextPreference>("weight_goal")
weightGoal?.setOnBindEditTextListener {
it.inputType = InputType.TYPE_CLASS_NUMBER
weightGoal?.apply {
setOnBindEditTextListener {
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
binding.date.setText(water.formatTimestamp())
}
datePicker.show(fragManager, "dialog")
Log.d("Tag", "${date.year + 1900}, ${date.month}, ${date.day}")
@ -88,8 +87,6 @@ class EditWaterDialog :
} else {
TODO("VERSION.SDK_INT < N")
}
}
viewModel.init(args.id)
@ -133,6 +130,5 @@ class EditWaterDialog :
}
else -> super.onOptionsItemSelected(item)
}
}
}

View File

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

View File

@ -1,13 +1,102 @@
package com.dzeio.openhealth.ui.water
import android.graphics.Color
import android.view.LayoutInflater
import com.dzeio.openhealth.R
import com.dzeio.openhealth.core.BaseDialog
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 :
BaseDialog<WaterSizeSelectorViewModel, DialogWaterSizeSelectorBinding>(
WaterSizeSelectorViewModel::class.java
) {
override val bindingInflater: (LayoutInflater) -> DialogWaterSizeSelectorBinding
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
import android.content.SharedPreferences
import androidx.lifecycle.MutableLiveData
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, _ ->
dialog.cancel()
}
}
}
@ -40,7 +39,7 @@ class AddWeightDialog : BaseDialog<HomeViewModel, DialogAddWeightBinding>(HomeVi
viewModel.lastWeight().collect {
if (it != null) {
binding.kg.value = it.weight.toInt()
binding.gram.value = ((it.weight - it.weight.toInt()) * 10 ).toInt()
binding.gram.value = ((it.weight - it.weight.toInt()) * 10).toInt()
}
}
}
@ -56,12 +55,11 @@ class AddWeightDialog : BaseDialog<HomeViewModel, DialogAddWeightBinding>(HomeVi
val weight = Weight().apply {
weight = binding.kg.value + (binding.gram.value.toFloat() / 10)
source = "OpenHealth"
}
lifecycleScope.launchWhenCreated {
viewModel.addWeight(weight)
}
//callback?.invoke()
// callback?.invoke()
dialog?.dismiss()
}
}

View File

@ -2,15 +2,24 @@ package com.dzeio.openhealth.ui.weight
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.dzeio.openhealth.R
import com.dzeio.openhealth.adapters.WeightAdapter
import com.dzeio.openhealth.core.BaseFragment
import com.dzeio.openhealth.data.weight.Weight
import com.dzeio.openhealth.databinding.FragmentListWeightBinding
import com.dzeio.openhealth.graphs.WeightChart
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 kotlinx.coroutines.flow.collectLatest
@ -21,33 +30,82 @@ class ListWeightFragment :
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentListWeightBinding =
FragmentListWeightBinding::inflate
val settings by lazy {
PreferenceManager.getDefaultSharedPreferences(requireContext())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
val recycler = binding.list
val manager = LinearLayoutManager(requireContext())
recycler.layoutManager = manager
val adapter = WeightAdapter()
val unit = settings.getString("weight_unit", "Kilogram") ?: "Kilogram"
adapter.unit = WeightUnit.fromSettings(unit)
adapter.onItemClick = {
findNavController().navigate(
ListWeightFragmentDirections.actionNavListWeightToNavEditWeight(
it.id
)
)
//EditWeightDialog().show(requireActivity().supportFragmentManager, "dialog")
// EditWeightDialog().show(requireActivity().supportFragmentManager, "dialog")
}
recycler.adapter = adapter
viewLifecycleOwner.lifecycleScope.launchWhenCreated {
viewModel.fetchWeights().collectLatest {
updateGraph(it)
val itt = it.toMutableList()
itt.sortWith { o1, o2 -> if (o1.timestamp > o2.timestamp) -1 else 1 }
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.data.BarLineScatterCandleBubbleData
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.interfaces.datasets.IBarLineScatterCandleBubbleDataSet
import java.text.SimpleDateFormat
@ -21,6 +22,14 @@ object GraphUtils {
// 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) {
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"?>
<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:orientation="vertical"
android:padding="24dp"
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>

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

View File

@ -1,14 +1,36 @@
<?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:tools="http://schemas.android.com/tools"
android:clipToPadding="false"
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:orientation="vertical"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewFilledStyle"
android:layout_width="match_parent"
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:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/layout_item_list"
tools:context=".ui.weight.ListWeightFragment" />
</LinearLayout>
tools:listitem="@layout/layout_item_list"
tools:context=".ui.weight.ListWeightFragment" />

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"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
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
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="ifRoom"
android:title="@string/page_settings"
android:icon="@drawable/ic_baseline_settings_24"/>
<item
android:id="@+id/action_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>

View File

@ -38,6 +38,12 @@
app:exitAnim="@android:anim/slide_out_right"
app:popEnterAnim="@android:anim/slide_in_left"
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
@ -53,7 +59,7 @@
<fragment
android:id="@+id/nav_list_weight"
android:name="com.dzeio.openhealth.ui.weight.ListWeightFragment"
android:label="@string/menu_list_weight"
android:label="@string/weight"
tools:layout="@layout/fragment_list_weight" >
<action
android:id="@+id/action_nav_list_weight_to_nav_edit_weight"
@ -62,6 +68,9 @@
app:exitAnim="@android:anim/slide_out_right"
app:popEnterAnim="@android:anim/slide_in_left"
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
@ -88,11 +97,45 @@
app:exitAnim="@android:anim/slide_out_right"
app:popEnterAnim="@android:anim/slide_in_left"
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>
<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
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>
@ -125,4 +168,9 @@
app:argType="string" />
</fragment>
<fragment
android:id="@+id/aboutFragment"
android:name="com.dzeio.openhealth.ui.about.AboutFragment"
android:label="AboutFragment"
tools:layout="@layout/fragment_about"/>
</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>Female</item>
</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">
<item>Millimeters</item>
<item>Ounces</item>
<item name="ml">@string/unit_volume_milliliter_name_singular</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>
</resources>

View File

@ -1,20 +1,44 @@
<resources>
<string name="app_name">OpenHealth</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</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="app_name" translatable="false">OpenHealth</string>
<string name="tagline">Your privacy-friendly FOSS Health Application </string>
<string name="page_settings">Settings</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_list_weight">Weight List</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="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>
<!-- 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>

View File

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